@metricinsights/pp-dev 1.0.0-beta.1 → 1.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +60 -0
  3. package/dist/CHANGELOG.md +50 -0
  4. package/dist/README.md +60 -0
  5. package/dist/cjs/cli.js +1 -1
  6. package/dist/cjs/cli.js.map +1 -1
  7. package/dist/cjs/index-DeUfAH6g.js +2 -0
  8. package/dist/cjs/index-DeUfAH6g.js.map +1 -0
  9. package/dist/cjs/index.js +1 -1
  10. package/dist/cjs/package.json +2 -2
  11. package/dist/cjs/{plugin-34c8_AjI.js → plugin-Cm9vRLCu.js} +2 -2
  12. package/dist/cjs/plugin-Cm9vRLCu.js.map +1 -0
  13. package/dist/cjs/plugin.js +1 -1
  14. package/dist/cjs/{version-plugin-BOFuJ-Rp.js → version-plugin-DsC5NLoX.js} +2 -2
  15. package/dist/cjs/{version-plugin-BOFuJ-Rp.js.map → version-plugin-DsC5NLoX.js.map} +1 -1
  16. package/dist/client/client.css +1 -1
  17. package/dist/client/client.css.map +1 -1
  18. package/dist/client/client.js +481 -30
  19. package/dist/client/client.js.map +1 -1
  20. package/dist/client/index.html +38 -2
  21. package/dist/esm/cli.js +1 -1
  22. package/dist/esm/cli.js.map +1 -1
  23. package/dist/esm/index-DOjGFYJ7.js +2 -0
  24. package/dist/esm/index-DOjGFYJ7.js.map +1 -0
  25. package/dist/esm/index.js +1 -1
  26. package/dist/esm/package.json +2 -2
  27. package/dist/esm/plugin-B2ocw-wb.js +2 -0
  28. package/dist/esm/plugin-B2ocw-wb.js.map +1 -0
  29. package/dist/esm/plugin.js +1 -1
  30. package/dist/esm/{version-plugin-vu8VWkws.js → version-plugin-BGWVTSsN.js} +2 -2
  31. package/dist/esm/{version-plugin-vu8VWkws.js.map → version-plugin-BGWVTSsN.js.map} +1 -1
  32. package/dist/types/index.d.ts +14 -1
  33. package/package.json +6 -6
  34. package/dist/cjs/index-MzGAxNTB.js +0 -2
  35. package/dist/cjs/index-MzGAxNTB.js.map +0 -1
  36. package/dist/cjs/plugin-34c8_AjI.js.map +0 -1
  37. package/dist/esm/index-C7dYPskY.js +0 -2
  38. package/dist/esm/index-C7dYPskY.js.map +0 -1
  39. package/dist/esm/plugin-Dvva-sxE.js +0 -2
  40. package/dist/esm/plugin-Dvva-sxE.js.map +0 -1
@@ -1,2 +1,2 @@
1
- "use strict";const e=require("http-proxy-middleware"),t=require("url"),n=require("vite"),s=require("picocolors"),r=require("axios"),i=require("node:http"),a=require("node:https"),o=require("jsdom"),c=require("path"),l=require("fs"),d=require("crypto"),p=require("process"),u=require("child_process"),h=require("os"),f=require("module"),m=require("extract-zip"),g=require("jszip"),y=require("memory-cache"),b=require("zlib"),v=require("express"),x=require("node:crypto");var w="undefined"!=typeof document?document.currentScript:null;const k=e=>e&&"object"==typeof e&&"default"in e?e.default:e;function E(e){if(e&&"object"==typeof e&&"default"in e)return e;const t=Object.create(null);if(e)for(const n in e)t[n]=e[n];return t.default=e,Object.freeze(t)}const T=k(r),S=k(i),$=k(a),B=k(c),A=E(c),I=E(d),R=E(p),P=E(u),C=E(h),L=k(m),F=k(g),j=E(y),q=k(v),H=c.resolve("undefined"!=typeof __filename&&__filename||t.fileURLToPath("undefined"==typeof document?require("url").pathToFileURL(__filename).href:w&&"SCRIPT"===w.tagName.toUpperCase()&&w.src||new URL("plugin-34c8_AjI.js",document.baseURI).href),"../../.."),N=c.resolve("undefined"!=typeof __filename&&__filename||t.fileURLToPath("undefined"==typeof document?require("url").pathToFileURL(__filename).href:w&&"SCRIPT"===w.tagName.toUpperCase()&&w.src||new URL("plugin-34c8_AjI.js",document.baseURI).href),"../.."),D=l.existsSync(c.resolve(H,"package.json"))?H:N,O=c.resolve(D,"dist/client/client.js"),{version:M,name:U}=JSON.parse(l.readFileSync(c.resolve(D,"package.json")).toString()),_=M,z=U;let V=new Map;const Z="default",W=(e="info",t=Z)=>{if(V&&"function"==typeof V.has||(V=new Map),V.has(t))return V.get(t);if(t===Z){const s=n.createLogger(e);return V.set(t,s),s}const s=n.createLogger(e);return V.set(t,s),s},J=s.createColors(),G=(e,t,n)=>{const s=new RegExp(`(!!)?(https?(:(\\\\)?/(\\\\)?/)${e})`,"gi");return n.replace(s,(e,...n)=>e.startsWith("!!")?n[1]:`http${n[2]}${t}`)},K=(e,t,n)=>{e.setHeader("location",t),e.statusCode=n,e.end()};const X=function(){const e={personal:{placeholder:"API Token",caption:`You can get the API Token\n <a href="https://${host}/api-token" target="_blank" \n style="color: #007bff; text-decoration: none;">here</a>`,endpoint:"/@api/login"},regular:{placeholder:"Legacy Token",caption:`You can get the Legacy Token\n <a href="https://${host}/api/get_token" target="_blank" \n style="color: #007bff; text-decoration: none;">here</a>`,endpoint:"/@api/login"}};function t(t,n,s){const r=e[t];r&&(n.placeholder=r.placeholder,s.innerHTML=r.caption)}function n(e,t){t.textContent=e,t.classList.add("show")}function s(e){e.classList.remove("show")}function r(e,t,r,i){r.addEventListener("click",async r=>{if(r.preventDefault(),s(i),!t.value.trim())return n(`${t.placeholder} is required`,i),void t.focus();const a=document.getElementById("token-type-switcher"),o=a?.querySelector(".token-type-option.active"),c=o?.dataset.value||"personal";await async function(e,t,s,r){try{s.classList.add("loading");const i=await fetch("/@api/login",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:e,tokenType:t}),redirect:"follow"}),a=i.url&&new URL(i.url,window.location.origin);if(!a||a.pathname.startsWith("/login")||a.pathname.startsWith("/@api/"))try{const e=await i.json();n(`Login failed: ${e?.error||"Unknown error occurred"}`,r)}catch(e){n("Login failed: Unable to parse response",r)}else window.location.href=i.url}catch(e){n("Login failed: Network error. Please check your connection.",r)}finally{s.classList.remove("loading")}}(t.value.trim(),c,e,i)}),t.addEventListener("keydown",e=>{"Enter"===e.key&&(e.preventDefault(),r.click())}),t.addEventListener("input",()=>{s(i)})}function i(e){const n=e.firstChild;if(!n)return;const s=n.lastChild?.cloneNode(!0);if(!s)return;s.innerHTML="";const i=s.cloneNode(!0);i.innerText="OR",i.setAttribute("role","separator"),i.setAttribute("aria-label","Alternative login method");const a=s.cloneNode(!0),o=document.createElement("div"),c=document.createElement("style");c.textContent="\n.helper-login-wrapper {\n font-family: Arial, sans-serif;\n color: #222;\n padding: 8px;\n background-color: #f8f9fa;\n border-radius: 8px;\n border: 1px solid #ddd;\n width: 100%;\n max-width: 400px;\n margin: 0 auto;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\n}\n\n.helper-login-wrapper .title {\n font-weight: bold;\n font-size: 18px;\n margin-bottom: 12px;\n text-align: center;\n}\n\n.helper-login-wrapper .control {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.helper-login-wrapper .control input {\n border: 1px solid #ddd;\n border-radius: 4px;\n width: 100%;\n box-sizing: border-box;\n text-align: center;\n height: 26px;\n padding: 0 8px;\n font-size: 14px;\n}\n\n.helper-login-wrapper .control input:focus {\n outline: none;\n border-color: #007bff;\n box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.helper-login-wrapper .control .caption {\n font-size: 12px;\n color: #666;\n text-align: center;\n}\n\n.helper-login-wrapper .footer {\n margin-top: 16px;\n text-align: center;\n}\n\n.helper-login-wrapper .footer .btn.submit {\n background-color: #007bff;\n color: white;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n width: 100%;\n font-size: 14px;\n transition: background-color 0.3s;\n height: 26px;\n font-weight: 500;\n}\n\n.helper-login-wrapper .footer .btn.submit:hover {\n background-color: #0056b3;\n}\n\n.helper-login-wrapper .footer .btn.submit:disabled {\n background-color: #6c757d;\n cursor: not-allowed;\n}\n\n.helper-login-wrapper .token-type-switcher {\n display: flex;\n border: 1px solid #ccc;\n border-radius: 4px;\n overflow: hidden;\n margin-bottom: 12px;\n width: 100%;\n}\n\n.helper-login-wrapper .token-type-option {\n padding: 4px 8px;\n cursor: pointer;\n background-color: #f8f9fa;\n color: #666;\n font-size: 12px;\n text-align: center;\n transition: background-color 0.2s, color 0.2s;\n border-right: 1px solid #ccc;\n flex: 1;\n user-select: none;\n}\n\n.helper-login-wrapper .token-type-option:last-child {\n border-right: none;\n}\n\n.helper-login-wrapper .token-type-option.active {\n background-color: #007bff;\n color: white;\n font-weight: bold;\n}\n\n.helper-login-wrapper .token-type-option:not(.active):hover {\n background-color: #e9ecef;\n}\n\n.helper-login-wrapper .error-message {\n color: #dc3545;\n font-size: 12px;\n text-align: center;\n margin-top: 8px;\n display: none;\n}\n\n.helper-login-wrapper .error-message.show {\n display: block;\n}\n\n.helper-login-wrapper .loading {\n opacity: 0.6;\n pointer-events: none;\n}\n",document.head.appendChild(c),o.innerHTML=`\n<div class="helper-login-wrapper" role="form" aria-label="PP Dev Helper Login">\n <div class="title" role="heading" aria-level="2">PP Dev Helper</div>\n\n <div class="token-type-switcher" id="token-type-switcher" role="tablist" aria-label="Token type selection">\n <div class="token-type-option active" data-value="personal" role="tab" aria-selected="true" tabindex="0">\n API Token\n </div>\n <div class="token-type-option" data-value="regular" role="tab" aria-selected="false" tabindex="0">\n Legacy Token\n </div>\n </div>\n\n <div class="control">\n <input \n type="password" \n id="helper-token" \n placeholder="API Token"\n aria-label="Token input"\n aria-describedby="token-caption"\n >\n <span class="caption" id="token-caption">\n You can get the API Token\n <a href="https://${host}/api-token" target="_blank" style="color: #007bff; text-decoration: none;">here</a>\n </span>\n </div>\n\n <div class="error-message" id="error-message" role="alert" aria-live="polite"></div>\n\n <div class="footer">\n <button id="helper-token-submit" class="btn submit" type="button">\n Token Login\n </button>\n </div>\n</div>`,a.appendChild(o),n.appendChild(i),n.appendChild(a);const l=document.getElementById("token-type-switcher"),d=document.getElementById("helper-token"),p=document.getElementById("token-caption"),u=document.getElementById("helper-token-submit"),h=document.getElementById("error-message"),f=o.querySelector(".helper-login-wrapper");l&&d&&p&&u&&h&&f?(!function(e,n,s){const r=e.querySelectorAll(".token-type-option"),i=e=>{const i=e.dataset.value;r.forEach(e=>{e.classList.remove("active"),e.setAttribute("aria-selected","false"),e.setAttribute("tabindex","-1")}),e.classList.add("active"),e.setAttribute("aria-selected","true"),e.setAttribute("tabindex","0"),t(i,n,s)};r.forEach(e=>{e.addEventListener("click",e=>{const t=e.currentTarget;i(t)}),e.addEventListener("keydown",t=>{"Enter"!==t.key&&" "!==t.key||(t.preventDefault(),i(e))})})}(l,d,p),r(f,d,u,h),t("personal",d,p)):console.error("Failed to find required form elements")}const a='#mi-react-root form:not([id]):has(input[name="login"]):has(input[name="password"])',o=new MutationObserver(e=>{for(const t of e)if("childList"===t.type){const e=document.querySelector(a);if(e){o.disconnect(),i(e);break}}});o.observe(document.body,{childList:!0,subtree:!0});const c=document.querySelector(a);c&&(o.disconnect(),i(c))},Y=/^(https?:\/\/)([^/]+)(\/.*)?$/i,Q="X-PP-Proxy";function ee(n){const{rewritePath:s=/^\/(?!p[tl]).*/i,baseURL:r="",disableSSLValidation:i=!1,miAPI:a,templateName:o}=n;if(!r)throw new Error("Base url is required");const c=r.replace(Y,"$2"),l=r.replace(Y,"$1$2"),d=import("file-type"),p=W();return e.createProxyMiddleware({selfHandleResponse:!0,pathFilter:(e,t)=>!(n.proxyIgnore||[]).some(t=>"string"==typeof t?e.startsWith(t):"function"!=typeof t.test||t.test(e))&&("string"==typeof s?e.startsWith(s):Array.isArray(s)?s.some(t=>e.startsWith(t)):"function"==typeof s.test&&s.test(e)),target:r,changeOrigin:!0,autoRewrite:!0,cookieDomainRewrite:{[c]:"localhost"},logger:{info:()=>{},warn:()=>{},error:()=>{}},secure:!i,headers:{host:c,origin:l},on:{proxyReq(e,n,s){const i=n.headers.host,c=e.getHeader("referer");i&&c&&"string"==typeof c&&e.setHeader("referer",c.replace(new RegExp(`https?://${i}`),r)),a.personalAccessToken&&e.setHeader("Authorization",`Bearer ${a.personalAccessToken}`);const l=n.url??"/",d=function(e,n,s,r){if(!n||!s||!r||s===r)return e;try{const n=new t.URL(e,"http://localhost"),i=n.pathname.match(/^\/data\/page\/([^/]+)(\/.*)?$/);if(!i)return e;let a;try{a=decodeURIComponent(i[1])}catch{a=i[1]}if(a!==s)return e;return`/data/page/${r}${i[2]??""}`+n.search+n.hash}catch{return e}}(l,a.v7Features,o,a.internalPageName);return d!==l&&(n.url=d,e.path=d,console.info(`${J.cyan("Rewrites data page to correct internal page name")}: ${J.yellow(l)} ${J.blue("→")} ${J.green(d)}`)),p.info(`${J.blue("Proxies request:")} ${J.green(n.method)} ${n.url} -> ${J.green(e.method)} ${e.protocol}//${e.host}${e.path}`),n.socket.setMaxListeners(0),n.socket.once("close",()=>{setTimeout(()=>{e.destroyed||e.destroy()},200)}),e},proxyRes:(t,n,s)=>{if(t.headers["content-type"]?.includes("text/event-stream")||t.headers["transfer-encoding"]?.includes("chunked")&&"no"===t.headers["x-accel-buffering"]){p.info(`${J.blue("Start streaming for request:")} ${J.green(n.method)} ${n.url}`);const e=async(e,t,n)=>{n.setHeader(Q,1),n.setHeaders(new Map(Object.entries(e.headers))),e.pipe(n)};return e(t,n,s)}const r=e.responseInterceptor(async(e,t,n,s)=>{s.setHeader(Q,1);if(await(await d).fileTypeFromBuffer(e))return e;{let t=e.toString("utf8");try{const e=new URL(n.url??"",`http://${c}`);if(e.searchParams&&e.searchParams.has("proxyRedirect")){const e=function(){const e="pp-dev::redirectCount",t="pp-dev::lastRedirect";let n=+(localStorage.getItem(e)??0);localStorage.getItem(t);Number.isNaN(n)&&(n=0);let s=window.location.href;const r=function(){const i=new URLSearchParams(window.location.search);if(!i.has("proxyRedirect"))return console.debug("No proxyRedirect param. Cleaning up."),void localStorage.removeItem(e);s!==window.location.href?(s=window.location.href,setTimeout(r,n<3?3e3:5e3)):window.location.href=i.get("proxyRedirect"),localStorage.setItem(e,""+ ++n),localStorage.setItem(t,(new Date).toISOString())};setTimeout(r,3e3)};t+=`<script>(${e.toString()})()<\/script>`}e.pathname.startsWith("/login")&&(s.setHeader("Cache-Control","no-cache, no-store, must-revalidate"),s.setHeader("Pragma","no-cache"),s.setHeader("Expires","0"));const r=t.slice(0,100);(r.includes("<html")||r.includes("<body")||r.includes("<head")||r.includes("html>"))&&(t+=`<script>const host = "${c}";\n(${X.toString()})()<\/script>`)}catch{}const r=n.headers.host??"";return((e,t,n)=>{const s=new RegExp(`${e.replace(/\\*\//gi,"\\\\/")}`,"gi"),r=new RegExp(`${e}`,"gi"),i=n.replace(s,t);return i===n?i.replace(r,t):i})("/auth/saml/login","/login",G(c,r,t))}});return r(t,n,s)},error(e,t,n){const s=`Proxy error: "${e.message}" when trying to "${t.method} ${t.url}"\n\n${e.stack}`;p.error(s),n.writable&&n.writeHead(500,{"Content-Type":"text/plain"}),n.end(s)}}})}class te{axios;constructor(e){this.axios=e}async checkAuth(e){return this.axios.get("/data/page/index/auth/info",{headers:Object.assign({},e,{accept:"text/html"}),maxRedirects:0}).then(()=>!0).catch(()=>!1)}}class ne extends te{formdataModulePromise=import("formdata-node");constructor(e){super(e)}getDownloadUrl(e){return`/admin/page/downloadassets/id/${e}`}getDownloadTemplateUrl(e){return`/admin/pagetemplate/downloadassets/id/${e}`}getUploadUrl(e){return`/admin/page/uploadassets/id/${e}`}getUploadTemplateUrl(e){return`/admin/pagetemplate/uploadassets/id/${e}`}async downloadPageAssets(e,t){return this.axios.get(this.getDownloadUrl(e),{withCredentials:!0,headers:Object.assign({},t,{accept:"*/*"}),responseType:"arraybuffer"}).then(e=>e.data)}async uploadPageAssets(e,t,n){const s=new(await this.formdataModulePromise).FormData,{File:r}=await this.formdataModulePromise,i=new r([t],"file.zip",{type:"application/zip"});s.append("file",i);const a=this.getUploadUrl(e);return this.axios.post(a,s,{withCredentials:!0,headers:Object.assign({},n,{accept:"application/json","Content-Type":"multipart/form-data",Referer:this.axios.getUri({url:a})})}).then(e=>e.data)}async downloadTemplateAssets(e,t){return this.axios.get(this.getDownloadTemplateUrl(e),{withCredentials:!0,headers:Object.assign({},t,{accept:"*/*"}),responseType:"arraybuffer"}).then(e=>e.data)}async uploadTemplateAssets(e,t,n){const s=new(await this.formdataModulePromise).FormData,{File:r}=await this.formdataModulePromise,i=new r([t],"file.zip",{type:"application/zip"});s.append("file",i,"file.zip");const a=this.getUploadTemplateUrl(e);return this.axios.post(a,s,{withCredentials:!0,headers:Object.assign({},n,{accept:"application/json","Content-Type":"multipart/form-data",Referer:this.axios.getUri({url:a})})}).then(e=>e.data)}}class se extends ne{getDownloadUrl(e){return`/api/page/id/${e}/asset/download`}getDownloadTemplateUrl(e){return`/api/page_template/id/${e}/asset/download`}getUploadUrl(e){return`/api/page/id/${e}/asset/upload`}getUploadTemplateUrl(e){return`/api/page_template/id/${e}/asset/upload`}}const re="Something went wrong when fetching page data",ie="Something went wrong when fetching page template data",ae=" The instance may be down or in maintenance mode. Please try again later.";function oe(){return new Error(`${ie}.${ae}`)}function ce(e){return e instanceof Error&&(e.message.includes(re)||e.message.includes(ie))}class le extends te{async getAll(e){return(await this.axios.get("/api/page",{withCredentials:!0,headers:Object.assign({},e,{Accept:"application/json","Content-Type":"application/json","Cache-Control":"no-cache",Pragma:"no-cache",Expires:"0"})})).data.pages}async get(e,t){const n=(await this.axios.get(`/api/page/id/${e}`,{withCredentials:!0,headers:Object.assign({},t,{accept:"application/json","content-type":"application/json"})})).data;if("object"!=typeof n||null===n)throw new Error(`${re}.${ae}`);return n.page}async getPageContent(e,t){return(await this.axios.get(`/p/${e}`,{withCredentials:!0,headers:Object.assign({},t,{accept:"text/html","content-type":"application/json"})})).data}async create(e,t){return(await this.axios.post("/api/page",e,{withCredentials:!0,headers:Object.assign({},t,{accept:"application/json","content-type":"application/json"})})).data.page}}class de extends te{async getAll(e,t){const n=(await this.axios.get("/api/page_template",{withCredentials:!0,headers:Object.assign({},t,{Accept:"application/json","Content-Type":"application/json","Cache-Control":"no-cache",Pragma:"no-cache",Expires:"0"}),params:{internal_name:e}})).data;if("object"!=typeof n||null===n||!Array.isArray(n.page_templates))throw oe();return n.page_templates}async get(e,t){const n=(await this.axios.get(`/api/page_template/id/${e}`,{withCredentials:!0,headers:Object.assign({},t,{accept:"application/json","content-type":"application/json"})})).data;if("object"!=typeof n||null===n||"object"!=typeof n.page_template||null===n.page_template)throw oe();return n.page_template}}function pe(e){const t=e.response?.status||0,n=e.response?.data?.message||e.message||"Unknown error";switch(t){case 412:const e=n.toLowerCase();return e.includes("session expired")||e.includes("session has expired")?{status:t,message:n,code:"SESSION_EXPIRED",userFriendlyMessage:"Your session has expired",suggestions:["Refresh your token in the portal","Re-authenticate with the portal","Check if your token has been revoked","Ensure your token has the correct permissions"]}:{status:t,message:n,code:"AUTH_FAILED",userFriendlyMessage:"Authentication failed",suggestions:["Verify your token is correct","Check token permissions","Ensure the token hasn't expired","Try generating a new token"]};case 401:return{status:t,message:n,code:"UNAUTHORIZED",userFriendlyMessage:"Unauthorized access",suggestions:["Check if your token is valid","Verify you have the required permissions","Ensure the token hasn't been revoked","Try logging in again"]};case 403:return{status:t,message:n,code:"FORBIDDEN",userFriendlyMessage:"Access forbidden",suggestions:["Check your user permissions","Verify the portal page ID is correct","Ensure your token has the right scope","Contact your administrator"]};default:return{status:t,message:n,code:"UNKNOWN_ERROR",userFriendlyMessage:"An unexpected error occurred",suggestions:["Check your network connection","Verify the portal URL is correct","Try again in a few moments","Check the portal status"]}}}const ue="[DEV PAGE. DO NOT DELETE]",he=new Map;class fe{#e;#t;#n=null;#s;#r;#i;#a;#o;#c;#l;internalPageName;appId;templateLess;assetsApi;pageApi;pageTemplateApi;logger;constructor(e,t){const{headers:n={},appId:s,templateLess:r=!0,disableSSLValidation:i=!1,v7Features:a=!1,personalAccessToken:o}=t||{};this.#e=n,this.#r=[],this.#s="",this.#i=a,this.#a=o,this.#o=!1,this.#c=Promise.resolve(!1);const c=`${e}:${i}`;if(he.has(c))this.#t=he.get(c);else{i&&(process.env.NODE_TLS_REJECT_UNAUTHORIZED="0");const t=new $.Agent({keepAlive:!1,...i?{rejectUnauthorized:!1}:{}});this.#t=T.create({baseURL:e,headers:n,timeout:3e4,maxRedirects:5,httpAgent:new S.Agent({keepAlive:!1}),httpsAgent:t}),i&&(T.defaults.httpsAgent=t),he.set(c,this.#t)}this.appId=s,this.templateLess=r,this.assetsApi=new(a?se:ne)(this.#t),this.pageApi=new le(this.#t),this.pageTemplateApi=new de(this.#t),this.logger=W()}async isTemplateLoaded(){return this.#c}get isV710OrHigher(){return this.#o}get v7Features(){return this.#i}#d(e){const t=Object.assign({},e,{host:void 0,referer:void 0});return Object.keys(t).forEach(e=>void 0===t[e]&&delete t[e]),this.#e=t,!t.authorization&&this.#a&&(t.authorization=`Bearer ${this.#a}`),t}#p(e){return e.replace(/[.*+?^${}()|[\]\\\/]/g,"\\$&")}get personalAccessToken(){return this.#a}set personalAccessToken(e){this.#a=e}updateHeaders(e){this.#d(e)}get localTemplateHTML(){return'<!DOCTYPE html>\n <html lang="en">\n <head>\n <meta charset="UTF-8" />\n <title>%%PAGE TITLE%%</title>\n <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />\n <meta name="theme-color" content="rgba(255, 255, 255, 1)" />\n <link rel="shortcut icon" type="image/x-icon" sizes="any" href="/img/favicon/favicon.ico" />\n <link rel="icon" type="image/png" sizes="16x16" href="/img/favicon/favicon-16x16.png" />\n <link rel="icon" type="image/png" sizes="32x32" href="/img/favicon/favicon-32x32.png" />\n <link rel="icon" type="image/png" sizes="48x48" href="/img/favicon/favicon-48x48.png" />\n <meta name="msapplication-config" content="/auth/browserconfig.xml" />\n <link rel="apple-touch-icon" sizes="180x180" href="/img/favicon/apple-touch-icon.png" />\n <link rel="manifest" href="/auth/site.webmanifest" />\n <link rel="manifest" href="/auth/manifest.json" />\n <link rel="stylesheet" type="text/css" href="/auth/theme-vars.css" />\n <link rel="icon" type="image/svg+xml" href="/favicon.ico" />\n <meta name="viewport" content="width=device-width, initial-scale=1.0" />\n <script src="/js/libs/underscore-latest.min.js" charset="utf-8"><\/script>\n <script src="/js/jquery/jquery-latest.min.js" charset="utf-8"><\/script>\n <script src="/js/application/rating_component.js" charset="utf-8"><\/script>\n </head>\n <body>\n <div id="mi-react-root"></div>\n <script src="/auth/info.js"><\/script>\n <script src="/js/main.js" defer><\/script>\n <link rel="stylesheet" href="/css/main.css" />\n </body>\n </html>'}async getPageTemplate(e){if(await this.#c||this.#n||(this.#c=new Promise(e=>{this.#l=e})),this.#n)return Promise.resolve(this.#n);if(void 0===this.templateLess&&(this.templateLess=!1),this.#i){const t=await this.pageApi.get(this.appId,this.#d(e)).then(e=>(this.logger.info(J.green("Page fetched")),e)).catch(async t=>{if(this.#l(!1),ce(t))throw t;throw await this.pageApi.checkAuth(this.#d(e))?(this.logger.error(J.red(`Error fetching page data: ${t.message}\n${t.stack}`)),new Error("The current user does not have access to this page. Check your configuration to ensure app.id is correct.")):t});return void 0!==t.template_id&&(this.#o=!0),this.internalPageName=t.internal_name,this.#n=this.localTemplateHTML,this.#n=this.#n.replace(/%%PAGE TITLE%%/g,t.name||"Local template"),this.logger.info(J.green("Local page template fetched")),this.#l(!0),this.#n}let t=(await this.pageApi.getAll(this.#d(e)).then(e=>(this.logger.info(J.green("Page list fetched")),e)).catch(async t=>{throw this.#l(!1),await this.pageApi.checkAuth(this.#d(e))?(this.logger.error(J.red(`Error fetching page list: ${t.message}\n${t.stack}`)),new Error("Current user does not have access to page list")):t})).find(e=>e.name===ue);return t||(this.logger.warn(J.yellow("Creating dev page template...")),t=await this.pageApi.create({enabled:"Y",name:ue,internal_name:"dev-page-template",visible_in_homepage:"Y"},this.#d(e)).then(e=>(this.logger.info(J.green("Dev page created")),e)).catch(e=>{throw this.logger.error(J.red(`Error creating dev page: ${e.message}`)),this.#l(!1),new Error(`Error when creating dev page.\n That can be caused by missing permissions or page with name "${ue}" already exists`)})),await this.pageApi.getPageContent(t.internal_name,this.#d(e)).then(e=>(this.#n=e,e)).then(e=>(this.#l(!0),this.logger.info(J.green("Page template fetched")),e)).catch(e=>{throw this.#l(!1),this.logger.error(J.red(`Error fetching page template: ${e.message}`)),new Error("Error fetching page template")})}async getPageVariables(e,t){return void 0===this.appId&&(this.appId=e,this.templateLess=!1),this.#n=await this.getPageTemplate(t),await this.pageApi.get(e,this.#d(t)).then(e=>{const{tags:t="[]",name:n,template:s}=e;if(s&&t){const e=JSON.parse(t);return this.#r=e,this.#s=n,e}return this.#r=[],this.#s=n,[]}).catch(t=>{if(this.logger.error(J.red(`Error fetching page variables: ${t.message}`)),404===t.response?.status)throw new Error(`Portal Page with id "${e}" not found on instance ${this.#t.getUri()}`);if(401===t.response?.status)throw this.logger.warn(`Unauthorized error: ${t}`),new Error(`Current user does not have access to page with id "${e}" on instance ${this.#t.getUri()}`);throw t})}async getPageInfo(e,t){void 0===this.appId&&(this.appId=e);const n=this.#d(t),s=this.appId;try{return this.#i?await this.#u(s,n):await this.#h(s,n)}catch(e){this.#f(e,s)}}async#u(e,t){const n=await this.pageApi.get(e,t);return this.logger.info(J.green("Page fetched")),void 0!==n.template_id&&(this.#o=!0),this.internalPageName=n.internal_name,n}async#h(e,t){return await this.pageApi.get(e,t)}#f(e,t){if(ce(e))throw e;if(this.#i&&e.message?.includes("access"))throw new Error("The current user does not have access to this page. Check your configuration to ensure app.id is correct.");if(404===e.response?.status)throw new Error(`Portal Page with id "${t}" not found on instance ${this.#t.getUri()}`);throw 401===e.response?.status&&this.logger.error(J.red(`Current user does not have access to page with id "${t}" on instance ${this.#t.getUri()}`)),e}buildPage(e,t=!1){let n="string"==typeof e?e:e.toString("utf-8");for(const e of this.#r){const t=this.#p(e.name),s=new RegExp(`\\[${t}\\]`,"g");n=n.replace(s,()=>e.value)}const s=new o.JSDOM(t?n:this.#n),r="%%PLACEHOLDER%%";if(!t){const e=s.window.document.createElement("div");e.innerHTML=r;const t=s.window.document.querySelector(".main-side");if(t){const n=t.querySelectorAll("script");n.length?t.insertBefore(e,n.item(n.length-1)):t.append(e)}else{const t=s.window.document.createElement("div");t.append(e),s.window.document.body.append(t)}const n=s.window.document.querySelector("head"),i=n.querySelector("title");if(i)i.text=this.#s;else{const e=s.window.document.createElement("title");e.textContent=this.#s,n.appendChild(e)}}return s.serialize().replace(new RegExp(`<div>\\s*${r}\\s*<\\/div>`,"i"),n)}async getAssets(){if(this.appId){if(this.templateLess)return await this.assetsApi.downloadPageAssets(this.appId,this.#e);{const e=await this.pageApi.get(this.appId,this.#e);if(this.#o){const t=await this.pageTemplateApi.get(e.template_id,this.#e);return await this.assetsApi.downloadTemplateAssets(t.id,this.#e)}if(e.template)return await this.assetsApi.downloadTemplateAssets(e.template,this.#e)}}}async updateAssets(e){if(this.appId){if(this.templateLess)return await this.assetsApi.uploadPageAssets(this.appId,e,this.#e);{const t=await this.pageApi.get(this.appId,this.#e);if(this.#o){const n=await this.pageTemplateApi.get(t.template_id,this.#e);return await this.assetsApi.uploadTemplateAssets(n.id,e,this.#e)}if(t.template)return await this.assetsApi.uploadTemplateAssets(t.template,e,this.#e)}}}async validateCredentials(e){try{const t=e||this.#e;return await this.get("/api/user",t,!0),{isValid:!0}}catch(e){const t=pe(e);return{isValid:!1,error:t.userFriendlyMessage,code:t.code}}}async get(e,t,n=!1){const s=n?t:Object.assign({},this.#d(this.#e),t);try{return await this.#t.get(e,{headers:s})}catch(e){if("UNKNOWN_ERROR"!==pe(e).code){const t=pe(e);throw this.logger.error(J.red(`API request failed: ${t.userFriendlyMessage}`)),function(e,t,n){const s=pe(t),r=`[${n}] `;e.error(J.red(`${r}${s.userFriendlyMessage}`)),e.error(J.red(`Status: ${s.status} (${s.code})`)),e.error(J.red(`Details: ${s.message}`)),s.suggestions.length>0&&(e.info(J.yellow("Suggestions:")),s.suggestions.forEach((t,n)=>{e.info(J.yellow(` ${n+1}. ${t}`))}))}(this.logger,e,"API Request"),e.response||(e.response={status:t.status,data:{message:t.message}}),e.tokenErrorInfo=t,e}throw e}}}const me="BUILD-MANIFEST.json";function ge(e,t,n=new Set){const s=l.readdirSync(e,{withFileTypes:!0}),r=[];for(const i of s){const s=B.join(e,i.name),a=B.relative(t,s).replace(/\\/g,"/");i.isDirectory()?r.push(...ge(s,t,n)):i.isFile()&&!n.has(a)&&r.push(s)}return r}function ye(e){const t=l.readFileSync(e);return d.createHash("sha256").update(t).digest("hex")}function be(e){return e.replace(/^([a-z][a-z\d+.-]*:\/\/)(?:[^/?#@]+(?::[^/?#@]*)?@)/i,"$1")}function ve(e){const t=e.trim().replace(/^git\+/,"").replace(/\.git$/i,""),n=t.match(/^([^@]+)@([^:]+):(.+)$/);if(n){const[,,e,t]=n;return be(`https://${e}/${t.replace(/^\/+/,"")}`)}const s=t.match(/^ssh:\/\/(?:[^@]+@)?([^/:]+)(?::\d+)?\/(.+)$/i);if(s){const[,e,t]=s;return be(`https://${e}/${t.replace(/^\/+/,"")}`)}const r=t.match(/^git:\/\/([^/]+)\/(.+)$/i);if(r){const[,e,t]=r;return be(`https://${e}/${t.replace(/^\/+/,"")}`)}return be(t)}function xe(e){if(!1===e.enabled)return;const{outDir:n,versionFileTemplate:s,packageVersion:r}=e,i=B.resolve(process.cwd(),n);if(!l.statSync(i,{throwIfNoEntry:!1})?.isDirectory())return;const a=(new Date).toISOString(),o=s??"VERSION-v{packageversion}-{currentDate}.json",c=function(e,t,n){return e.replace(/\{packageversion\}/g,t).replace(/\{currentDate\}/g,n)}(o,r,a.replace(/:/g,"-")),p=ge(i,i,new Set([c,me])),h={};for(const e of p){h[B.relative(i,e).replace(/\\/g,"/")]=ye(e)}const f=function(e){const t=Object.keys(e).sort().map(t=>e[t]).join("");return d.createHash("sha256").update(t).digest("hex")}(h),m=function(e){const t=Object.keys(e).sort().map(t=>`${t}:${e[t]}`).join("\n");return d.createHash("sha256").update(t,"utf-8").digest("hex")}(h),g=function(){const e=t.fileURLToPath("undefined"==typeof document?require("url").pathToFileURL(__filename).href:w&&"SCRIPT"===w.tagName.toUpperCase()&&w.src||new URL("plugin-34c8_AjI.js",document.baseURI).href);let n=B.dirname(e);for(;;){const e=B.join(n,"package.json"),t=l.statSync(e,{throwIfNoEntry:!1});if(t?.isFile())try{const t=JSON.parse(l.readFileSync(e,"utf-8"));if("string"==typeof t.version)return t.version}catch{}const s=B.dirname(n);if(s===n)return"unknown";n=s}}(),y=function(e){if("string"==typeof e&&e.trim())return e.trim();const t=["GITHUB_HEAD_REF","GITHUB_REF_NAME","CI_COMMIT_REF_NAME","BITBUCKET_BRANCH","BUILD_SOURCEBRANCHNAME","BRANCH_NAME","TRAVIS_BRANCH","CIRCLE_BRANCH","BUILDKITE_BRANCH","VERCEL_GIT_COMMIT_REF"];for(const e of t){const t=process.env[e];if("string"==typeof t&&t.trim())return t.trim()}try{const e=u.execSync("git rev-parse --abbrev-ref HEAD",{encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim();if(e&&"HEAD"!==e)return e}catch{}}(e.packageBranchName),b={schemaVersion:1,version:`v${r}`,date:a,checksum:f,files:h,repositoryUrl:e.packageRepositoryUrl?ve(e.packageRepositoryUrl):void 0,branchName:y,helperVersion:g},v=B.join(i,c),x=B.join(i,me),k={schemaVersion:1,manifestType:"pp-dev-build-manifest",generatedAt:a,versionFile:c,versionFileTemplate:o,versionFileSchemaVersion:1,buildFingerprint:m,compat:{versionFileRequired:!0}};l.writeFileSync(v,JSON.stringify(b,null,2),"utf-8"),l.writeFileSync(x,JSON.stringify(k,null,2),"utf-8")}const we="pageName",ke="date",Ee=A.dirname("undefined"!=typeof __filename&&__filename||t.fileURLToPath("undefined"==typeof document?require("url").pathToFileURL(__filename).href:w&&"SCRIPT"===w.tagName.toUpperCase()&&w.src||new URL("plugin-34c8_AjI.js",document.baseURI).href)),Te=A.resolve(Ee,"..",".."),Se=A.resolve(Te,"..",".."),$e=A.resolve(Se,".pp-dev-meta"),Be=A.resolve($e,"sync-service.meta.json"),Ae="BUILD-MANIFEST.json",Ie="__template_variables.json";class Re{backupFolder;backupNameTemplate;dateFormat;pageName;currentMeta=null;distZipFolder;distZipFilename;versionFileTemplate;nextBuild;logger;constructor(e,t){this.pageName=e;const{backupFolder:n=A.resolve(R.cwd(),"backups"),distZipFolder:s=A.resolve(R.cwd(),"dist-zip"),distZipFilename:r=`${this.pageName}.zip`,backupNameTemplate:i=`{${we}}-{${ke}}.zip`,versionFileTemplate:a="VERSION-v{packageversion}-{currentDate}.json",dateFormat:o=e=>e.toISOString().replace(/:/g,"-").replace(/\..*$/,""),nextBuild:c}=t||{};this.backupFolder=n,this.backupNameTemplate=i,this.dateFormat=o,this.distZipFolder=s,this.distZipFilename=r,this.versionFileTemplate=a,this.nextBuild=c,this.syncMeta(),this.logger=W()}async checkMeta(){try{await l.promises.stat($e)}catch{await l.promises.mkdir($e)}try{await l.promises.stat(this.backupFolder)}catch{await l.promises.mkdir(this.backupFolder)}}async readMetaFile(){return await this.checkMeta(),await l.promises.readFile(Be,{encoding:"utf-8"}).catch(()=>"{}")}async writeMetaFile(e){return await this.checkMeta(),await l.promises.writeFile(Be,JSON.stringify(e,null,2),{encoding:"utf-8"})}async syncMeta(){this.currentMeta?await this.writeMetaFile(this.currentMeta):this.currentMeta=JSON.parse(await this.readMetaFile())}async getLatestSavedBackup(){const{lastBackupName:e}=this.currentMeta;if(!e)return null;const t=A.resolve(this.backupFolder,e);try{return await l.promises.stat(t),t}catch{return null}}backupName(e,t=new Date){return this.backupNameTemplate.replace(`{${we}}`,e).replace(`{${ke}}`,this.dateFormat(t))}getBackupMeta(){return this.currentMeta}pathToPosix(e){return e.replace(/\\/g,"/")}async normalizeExtractedRootDir(e){let t=e;for(;;){const e=await l.promises.readdir(t,{withFileTypes:!0});if(1!==e.length||!e[0]?.isDirectory())return t;t=A.join(t,e[0].name)}}async listFilesRecursive(e){const t=await l.promises.readdir(e,{withFileTypes:!0}),n=[];for(const s of t){const t=A.join(e,s.name);s.isDirectory()?n.push(...await this.listFilesRecursive(t)):s.isFile()&&n.push(t)}return n}buildManifestChecksum(e){const t=Object.keys(e).sort().map(t=>e[t]).join("");return I.createHash("sha256").update(t).digest("hex")}buildBackupFingerprint(e){const t=Object.keys(e).sort().map(t=>`${t}:${e[t]}`).join("\n");return I.createHash("sha256").update(t,"utf-8").digest("hex")}escapeRegExp(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}versionFileTemplateMatcher(){const e=/\{[^}]+\}/g,t=[];let n=0,s=null;for(;s=e.exec(this.versionFileTemplate),s;){const[e]=s,r=this.versionFileTemplate.slice(n,s.index);t.push(this.escapeRegExp(r)),t.push("[^/]+"),n=s.index+e.length}const r=this.versionFileTemplate.slice(n);return t.push(this.escapeRegExp(r)),new RegExp(`^${t.join("")}$`,"i")}isTemplateVariablesBasename(e){return e===Ie}async pickTemplateVariablesFromBackupZip(e,t){const n=e.find(e=>A.basename(e)===Ie);if(!n)return null;const s=A.join(t,n),r=await l.promises.readFile(s);return{relativePath:n,content:r,actualHash:I.createHash("sha256").update(r).digest("hex"),manifestHash:null}}async resolveBackupAnalysisFromZip(e){const t=await l.promises.mkdtemp(A.join(C.tmpdir(),"pp-dev-backup-")),n=A.join(t,"backup.zip"),s=A.join(t,"extracted");try{await l.promises.mkdir(s,{recursive:!0}),await l.promises.writeFile(n,e),await L(n,{dir:s});const t=await this.normalizeExtractedRootDir(s),r=(await this.listFilesRecursive(t)).map(e=>this.pathToPosix(A.relative(t,e))),i=r.filter(e=>A.basename(e)===Ae);if(i.length>1)throw new Error(`Expected at most one ${Ae} in backup ZIP, found ${i.length}`);const a=1===i.length?i[0]:null;let o=null;if(a){const e=A.join(t,a);if(o=JSON.parse(await l.promises.readFile(e,"utf-8")),!(o&&"pp-dev-build-manifest"===o.manifestType&&"string"==typeof o.versionFile&&o.versionFile.trim()&&"string"==typeof o.versionFileTemplate&&o.versionFileTemplate.trim()&&"string"==typeof o.buildFingerprint&&o.buildFingerprint.trim()))throw new Error("Build manifest is missing required fields")}let c=null;if(o){const e=this.pathToPosix(o.versionFile.trim()),t=r.filter(t=>t===e),n=r.filter(t=>A.basename(t)===A.basename(e)),s=t.length>0?t:n;if(s.length>1)throw new Error(`Build manifest version file "${o.versionFile}" must resolve to exactly one file in backup ZIP, found ${s.length}`);if(1===s.length&&(c=s[0]),!0===o.compat?.versionFileRequired&&null===c)throw new Error(`Build manifest requires version file "${o.versionFile}", but it is missing in backup ZIP`)}else{const e=this.versionFileTemplateMatcher(),t=r.filter(t=>e.test(A.basename(t)));if(t.length>1)throw new Error(`Expected exactly one version file matching template "${this.versionFileTemplate}" in backup ZIP, found ${t.length}`);1===t.length&&(c=t[0])}const d={};let p=null,u=null;const h={};let f=[];if(c){const e=A.join(t,c),n=JSON.parse(await l.promises.readFile(e,"utf-8"));if(!n?.files||"object"!=typeof n.files)throw new Error("VERSION manifest files map is missing or invalid");if("string"!=typeof n.checksum||!n.checksum)throw new Error("VERSION manifest checksum is missing or invalid");u=n.checksum;for(const[e,t]of Object.entries(n.files)){if("string"!=typeof t||!t.trim())throw new Error(`VERSION manifest contains invalid hash for file: ${e}`);d[this.pathToPosix(e)]=t}if(this.buildManifestChecksum(d)!==n.checksum)throw new Error("VERSION manifest checksum does not match its files map");for(const[e,n]of Object.entries(d)){const s=A.join(t,e),r=await l.promises.readFile(s).catch(()=>null);if(!r)throw new Error(`VERSION manifest references missing file: ${e}`);const i=I.createHash("sha256").update(r).digest("hex");h[e]=i,i!==n&&f.push({relativePath:e,expected:n,actual:i}),this.isTemplateVariablesBasename(A.basename(e))&&(p={relativePath:e,content:r,actualHash:i,manifestHash:n})}f.length>0&&this.logger.warn(J.yellow(`[DistService] VERSION manifest hash mismatch for ${f.length} file(s) (confirm in UI): ${f.map(e=>e.relativePath).join(", ")}`))}const m=new Set(f.map(e=>e.relativePath)),g={},y=[];for(const e of r){if(e===a)continue;if(c&&e===c)continue;if(c&&Object.prototype.hasOwnProperty.call(g,e))continue;const n=A.join(t,e),s=await l.promises.readFile(n),r=I.createHash("sha256").update(s).digest("hex");c&&Object.prototype.hasOwnProperty.call(d,e)?g[e]=m.has(e)?h[e]:d[e]:c?(g[e]="__NOT_IN_VERSION_FILE__",y.push(e)):g[e]=r}p||(p=await this.pickTemplateVariablesFromBackupZip(r,t));const b=this.buildBackupFingerprint(g);let v=null;return o&&c&&b!==o.buildFingerprint&&(v={expected:o.buildFingerprint,actual:b},this.logger.warn(J.yellow(`[DistService] Build manifest fingerprint mismatch (confirm in UI): expected ${o.buildFingerprint}, got ${b}`))),{backupFingerprint:b,unknownFiles:y,templateVariables:p,mainHash:u,allFilesInMeta:Boolean(c&&0===y.length),buildManifestMismatch:v,versionManifestHashMismatches:f}}finally{await l.promises.rm(t,{recursive:!0,force:!0})}}async analyzeBackup(e){if("PK"!==e.toString("utf-8").slice(0,4))throw new Error("Backup file is not a ZIP file");return await this.resolveBackupAnalysisFromZip(e)}async getPublicTemplateVariablesHash(){const e=A.resolve(R.cwd(),"public",Ie),t=await l.promises.readFile(e).catch(()=>null);return t?I.createHash("sha256").update(t).digest("hex"):null}async saveTemplateVariablesFile(e){const t=A.resolve(R.cwd(),"public",Ie);return await l.promises.mkdir(A.dirname(t),{recursive:!0}),await l.promises.writeFile(t,e),t}async saveBackup(e,t){const n=t??await this.analyzeBackup(e),s=n.backupFingerprint;if(await this.getLatestSavedBackup()){const{lastBackupHash:e,lastMainHash:t}=this.currentMeta;if(e===s)return void this.logger.info(J.yellow(`[DistService] Backup skipped: same backup fingerprint as last saved (${s.slice(0,12)}...)`));if(n.allFilesInMeta&&n.mainHash&&t===n.mainHash)return void this.logger.info(J.yellow("[DistService] Backup skipped: same VERSION manifest checksum (main hash) and all files listed in VERSION"))}const r=new Date,i=this.backupName(this.pageName,r);return this.currentMeta.lastBackupName=i,this.currentMeta.lastBackupHash=s,this.currentMeta.lastBackupDate=r.toISOString(),n.mainHash?this.currentMeta.lastMainHash=n.mainHash:delete this.currentMeta.lastMainHash,await this.syncMeta(),await l.promises.writeFile(A.resolve(this.backupFolder,i),e).finally(()=>{this.logger.info(`Backup saved to ${i}`)})}async buildNewAssets(){if(this.nextBuild)return await this.buildNextAssets();const e=new Promise((e,t)=>{let n="";this.logger.info(J.cyan("[DistService] Build started"));const s=P.spawn("node",[A.resolve(Te,"./bin/pp-dev.js"),"build"],{cwd:R.cwd(),env:Object.assign({},R.env,{NODE_ENV:"production"}),stdio:"inherit"});s.on("message",e=>{n+=e}),s.on("close",s=>{0===s?e(n):t(new Error(`build command exited with code ${s}`))}),s.on("error",e=>{t(e)})});try{await e.finally(()=>{this.logger.info(J.cyan("[DistService] Build finished"))});const t=A.resolve(R.cwd(),this.distZipFolder,this.distZipFilename);if(!await l.promises.stat(t))throw new Error(`File ${t} not found`);return await l.promises.readFile(t)}catch(e){throw this.logger.error(e instanceof Error?e.stack??e.message:String(e)),e}}async saveBackupAndBuild(e){return await this.saveBackup(e),await this.buildNewAssets()}async buildNextAssets(){const{projectRoot:e,distDir:t}=this.nextBuild;this.logger.info(J.cyan("[DistService] Next.js build started")),await this.runNextBuild(e),this.logger.info(J.cyan("[DistService] Next.js build finished"));const n=A.resolve(e,t);if(!await this.isDirectory(n))throw new Error(`Next.js build output directory not found: ${n}`);return xe({outDir:n,packageVersion:this.nextBuild.packageVersion,versionFileTemplate:this.versionFileTemplate,packageRepositoryUrl:this.nextBuild.packageRepositoryUrl,packageBranchName:this.nextBuild.packageBranchName}),await this.zipDirectory(n)}runNextBuild(e){return new Promise((t,n)=>{let s;try{const t=f.createRequire(A.join(e,"package.json")).resolve("next/package.json");s=A.resolve(A.dirname(t),"dist/bin/next")}catch{return void n(new Error(`Unable to resolve the "next" binary from ${e}. Is Next.js installed?`))}const r=P.spawn(R.execPath,[s,"build"],{cwd:e,env:Object.assign({},R.env,{NODE_ENV:"production"}),stdio:"inherit"});r.on("close",e=>{0===e?t():n(new Error(`next build exited with code ${e}`))}),r.on("error",n)})}async zipDirectory(e){const t=new F,n=async s=>{const r=await l.promises.readdir(s,{withFileTypes:!0});for(const i of r){const r=A.join(s,i.name);if(i.isDirectory())await n(r);else if(i.isFile()){const n=A.relative(e,r).replace(/\\/g,"/");t.file(n,await l.promises.readFile(r))}}};return await n(e),await t.generateAsync({type:"nodebuffer",compression:"DEFLATE"})}async isDirectory(e){try{return(await l.promises.stat(e)).isDirectory()}catch{return!1}}}class Pe{server;opts;eventMap;logger;syncActionResolvers=new Map;constructor(e,t){this.server=e,this.opts=t||{},this.eventMap=new Map,this.eventMap.set("info-data:request",this.onInfoDataRequest.bind(this)),this.eventMap.set("template:sync",this.onTemplateSync.bind(this)),this.eventMap.set("template:sync:action-response",this.onTemplateSyncActionResponse.bind(this)),this.logger=W(),this.init()}init(){const{ws:e}=this.server;for(const[t,n]of this.eventMap)e.on(t,n);e.on("close",()=>{this.clearAllPendingSyncActions(!1)}),e.on("error",()=>{this.clearAllPendingSyncActions(!1)})}resolveSyncAction(e,t){const n=this.syncActionResolvers.get(e);n&&(clearTimeout(n.timeoutId),this.syncActionResolvers.delete(e),n.resolve(t))}clearAllPendingSyncActions(e){for(const t of[...this.syncActionResolvers.keys()])this.resolveSyncAction(t,e)}onInfoDataRequest(e,t){t.send("info-data:response",{})}onTemplateSyncActionResponse(e){e&&"string"==typeof e.requestId&&"boolean"==typeof e.approved&&this.resolveSyncAction(e.requestId,e.approved)}async requestSyncAction(e,t){const n=d.randomUUID(),s=this.opts.syncActionTimeoutMs??12e4;return await new Promise(r=>{const i=setTimeout(()=>{this.resolveSyncAction(n,!1)},s);this.syncActionResolvers.set(n,{resolve:r,timeoutId:i}),t.send("template:sync:action-required",{...e,requestId:n})})}async onTemplateSync(e,t){if(!this.opts.distService||!this.opts.miAPI)return t.send("template:sync:response",{error:"Dist service or MiAPI is not defined"}),void this.logger.error(J.red("Dist service or MiAPI is not defined"));{const{distService:e,miAPI:n}=this.opts;try{if(this.server.config.clientInjectionPlugin?.v7Features){if(!n?.isV710OrHigher)return void t.send("template:sync:response",{error:"This feature is available only for MI v7.1.0 or higher",config:{canSync:!1}});t.send("client:config:update",{config:{canSync:!0}})}const s=await(n?.getAssets().catch(async e=>{if(r.isAxiosError(e)){if("SESSION_EXPIRED"===pe(e).code){this.logger.info(J.yellow("Session expired - attempting to validate credentials"));try{const e=await(n?.validateCredentials());e&&!e.isValid?(this.logger.error(J.red(`Authentication error: ${e.error}`)),t.send("template:sync:response",{error:e.error,code:e.code,refresh:!0})):(this.logger.info(J.yellow("Session expired")),t.send("template:sync:response",{error:"Session expired",code:"SESSION_EXPIRED",refresh:!0}))}catch(e){this.logger.info(J.yellow("Session expired")),t.send("template:sync:response",{error:"Session expired",code:"SESSION_EXPIRED",refresh:!0})}return e}if(e.cause instanceof Error&&("ECONNRESET"===e.cause.code||"ENOTFOUND"===e.cause.code))return this.logger.info(J.yellow("Server in maintenance mode, VPN connection is needed or no internet connection")),t.send("template:sync:response",{error:"Server in maintenance mode, VPN connection is needed or no internet connection",code:"CONNECTION_ERROR"}),e;const s=e.response?.status;if(502===s||503===s||504===s)return this.logger.info(J.yellow(`Server in maintenance mode or unreachable (HTTP ${s}); VPN may be required`)),t.send("template:sync:response",{error:"Server in maintenance mode, VPN connection is needed or no internet connection",code:"CONNECTION_ERROR"}),e}if(ce(e))return this.logger.info(J.yellow("Server in maintenance mode or non-JSON page / page-template API response (unavailable HTML body)")),t.send("template:sync:response",{error:"Server in maintenance mode, VPN connection is needed or no internet connection",code:"CONNECTION_ERROR"}),e;throw e}));if(s instanceof Error)return;let i;if(s){const n=await(e?.analyzeBackup(s).catch(e=>"Backup file is not a ZIP file"===e.message?(this.logger.error(J.red("Backup file is not a ZIP file")),e):e));if(n instanceof Error)i=n;else if(n){await(e?.saveBackup(s,n));const r=0===n.unknownFiles.length&&1===n.versionManifestHashMismatches.length&&A.basename(n.versionManifestHashMismatches[0].relativePath)===Ie,a=n.versionManifestHashMismatches,o=a.filter(e=>A.basename(e.relativePath)===Ie),c=a.filter(e=>A.basename(e.relativePath)!==Ie),l=!r&&o.length>0&&c.length>0,d=r?[]:c.map(e=>e.relativePath),p=async()=>{if(!n.templateVariables||!e)return;const{templateVariables:s}=n;if(await e.getPublicTemplateVariablesHash()!==s.actualHash){await this.requestSyncAction({title:"Template variables (server backup)",content:"The server backup includes __template_variables.json. It differs from your local public/__template_variables.json (or that file is missing). Replace your project copy with the server backup?",confirmText:"Replace from server",cancelText:"Keep local"},t)&&await e.saveTemplateVariablesFile(s.content)}};if(n.unknownFiles.length>0){if(!await this.requestSyncAction({title:"Unknown files found in backup",content:`Backup contains files not listed in VERSION: ${n.unknownFiles.join(", ")}. Continue sync or cancel?`,confirmText:"Continue sync",cancelText:"Cancel sync"},t))return t.send("template:sync:response",{cancelled:!0,message:"Sync cancelled by user. Backup was saved."}),void this.logger.info(J.yellow("Sync cancelled by user"))}if(l&&await p(),!r&&d.length>0){const e=40,n=d,s=n.slice(0,e),r=n.length-s.length,i=s.map(e=>`• ${e}`).join("\n"),a=r>0?`\n... and ${r} more file${1===r?"":"s"}`:"";if(!await this.requestSyncAction({title:"VERSION manifest out of date",content:`These files no longer match the hashes recorded in the VERSION manifest:\n\n${i}${a}\n\nCancel sync, or override and continue using the files on disk?`,confirmText:"Override and continue",cancelText:"Cancel sync"},t))return t.send("template:sync:response",{cancelled:!0,message:"Sync cancelled by user. Backup was saved."}),void this.logger.info(J.yellow("Sync cancelled by user"))}if(!r&&n.buildManifestMismatch&&0===n.versionManifestHashMismatches.length){const{expected:e,actual:s}=n.buildManifestMismatch;if(!await this.requestSyncAction({title:"Build manifest fingerprint mismatch",content:`The recomputed backup fingerprint does not match BUILD-MANIFEST.json.\nManifest: ${e.slice(0,12)}...\nComputed: ${s.slice(0,12)}...\nContinue sync anyway?`,confirmText:"Continue sync",cancelText:"Cancel sync"},t))return t.send("template:sync:response",{cancelled:!0,message:"Sync cancelled by user. Backup was saved."}),void this.logger.info(J.yellow("Sync cancelled by user"))}l||await p(),i=await(e?.buildNewAssets())}}else i=await(e?.buildNewAssets());if(!(i&&i instanceof Buffer))return i instanceof Error?(t.send("template:sync:response",{error:i.message}),void this.logger.error(J.red(i.message))):(t.send("template:sync:response",{error:"Failed to build new assets"}),void this.logger.error(J.red("Failed to build new assets")));{const s=await(n?.updateAssets(i));if("OK"===s?.status){const n=e?.getBackupMeta(),{lastBackupName:s,lastBackupHash:r,lastBackupDate:i}=n||{lastBackupName:"",lastBackupHash:"",lastBackupDate:(new Date).toISOString()};t.send("template:sync:response",{syncedAt:new Date(i),currentHash:r,backupFilename:s}),this.logger.info(J.green("Template synced"))}else t.send("template:sync:response",{error:"Failed to update assets"}),this.logger.error(J.red("Failed to update assets"))}}catch(e){const n=e instanceof Error?e.message:"Template sync failed";return this.logger.error(J.red(`Template sync failed: ${n}`)),void t.send("template:sync:response",{error:n,code:"SYNC_FAILED"})}}}}const Ce=/\.(?:[a-z0-9]+)$/i,Le=6e5,Fe=104857600,je=1e3;class qe extends j.Cache{totalSize=0;maxSize;maxItems;constructor(e,t){super(),this.maxSize=e,this.maxItems=t}put(e,t,n){const s=this.get(e);s&&(this.totalSize-=s.size);const r=super.put(e,t,n);return this.totalSize+=t.size,this.cleanup(),r}cleanup(){const e=this.keys();if(e.length>this.maxItems){const t=e.length-this.maxItems,n=e.sort((e,t)=>{const n=this.get(e),s=this.get(t);return(n?.timestamp||0)-(s?.timestamp||0)});for(let e=0;e<t;e++){const t=n[e],s=this.get(t);s&&(this.totalSize-=s.size,this.del(t))}}for(;this.totalSize>this.maxSize;){const e=this.keys();if(0===e.length)break;const t=e.sort((e,t)=>{const n=this.get(e),s=this.get(t);return(n?.timestamp||0)-(s?.timestamp||0)})[0],n=this.get(t);if(!n)break;this.totalSize-=n.size,this.del(t)}}getTotalSize(){return this.totalSize}getItemCount(){return this.keys().length}}const He=new qe(Fe,je);function Ne(e){if(0===e.length)return Buffer.alloc(0);if(1===e.length)return e[0];const t=e.reduce((e,t)=>Buffer.byteLength(t)+e,0);return Buffer.concat(e,t)}function De(e){const{devServer:t,ttl:n=Le,maxSize:s=Fe,maxItems:r=je}=e,i=W();return s===Fe&&r===je||(He.maxSize=s,He.maxItems=r),t.cache=He,(e,t,s)=>{const r=e.originalUrl||e.url||"",a=function(e){const t=e.split("?")[0];return Ce.test(t)?t.includes("/auth/info.js")?"":e:""}(r);if(!a)return s();const o=He.get(a);if(o)return i.info(`${J.blue("Proxies request:")} ${J.green(e.method)} ${r} -> ${J.blue("Cache")} ${a}`),function(e,t){try{for(const[n,s]of Object.entries(t))null!=s&&e.setHeader(n,s)}catch(e){W().warn(`Failed to set some response headers: ${e instanceof Error?e.message:String(e)}`)}}(t,o.headers),t.setHeader("X-PP-Cache","hit"),t.write(o.content),void t.end();const c=t.end,l=t.write,d=[];t.write=function(e,n,s){if(!t.hasHeader(Q))return l.call(this,e,n,s);if("string"==typeof e){const t=n||"utf8";d.push(Buffer.from(e,t))}else Buffer.isBuffer(e)?d.push(e):null!=e&&d.push(Buffer.from(String(e),"utf8"));return s?.(),!0},t.end=function(e,s,r){if(!t.hasHeader(Q))return c.call(this,e,s,r);if(null!=e)if("string"==typeof e){const t=s||"utf8";d.push(Buffer.from(e,t))}else Buffer.isBuffer(e)?d.push(e):d.push(Buffer.from(String(e),"utf8"));let o;try{o=Ne(d);const e=Buffer.byteLength(o);if(e>0&&e<10485760){const s={headers:t.getHeaders(),content:o,timestamp:Date.now(),size:e};He.put(a,s,n),i.info(`${J.blue("[Cached]")} ${a} (${(e/1024).toFixed(1)}KB)`)}}catch(e){i.error(`Failed to cache response for ${a}: ${e}`),o=Ne(d)}const l=s||"utf8";return c.call(this,o,l,r)},s()}}function Oe(e,t){return async(n,s,r)=>{if(await e(n.url??"",n,s)){W().info(`Rewrite response for ${n.url}`);const e=s.end,r=[];s.write=function(e){return r.push(e),!0},s.end=function(i,a,o){"string"==typeof i&&r.push(Buffer.from(i));const c=s.getHeader("content-encoding"),l=function(e,t){switch(t){case"gzip":return b.unzipSync(e);case"br":return b.brotliDecompressSync(e);case"deflate":return b.inflateSync(e);default:return e}}(Buffer.from(Buffer.concat(r)),c),d="function"!=typeof a&&a?a:"utf-8",p="function"==typeof a?a:o;return e.call(this,function(e,t){switch(t){case"gzip":return b.gzipSync(e);case"br":return b.brotliCompressSync(e);case"deflate":return b.deflateSync(e);default:return e}}(t(l,n,s),c),d,p),this}}r()}}function Me(e,n){const s=(e=(e=e.startsWith("/")?e:`/${e}`).endsWith("/")?e:`${e}/`).endsWith("/")?e.slice(0,-1):e,r=W();return(i,a,o)=>{const c=new t.URL(i.url??"","http://localhost"),{pathname:l,search:d}=c,p=["/",s];if(n&&(p.push(`/pt/${n}`),p.push(`/pl/${n}`)),p.includes(l)){const t=`${e}${d}`;return r.info(J.yellow(`Redirecting to: ${t}`)),K(a,t,302)}o()}}class Ue{state={isAuthenticated:!1,isRedirected:!1,lastChecked:0};listeners=new Set;getState(){return{...this.state}}isAuthenticated(){return this.state.isAuthenticated}isRedirected(){return this.state.isRedirected}setAuthenticated(e){this.state.isAuthenticated=e,this.state.lastChecked=Date.now(),this.notifyListeners()}setRedirected(e){this.state.isRedirected=e,this.notifyListeners()}updateState(e){void 0!==e.isAuthenticated&&(this.state.isAuthenticated=e.isAuthenticated),void 0!==e.isRedirected&&(this.state.isRedirected=e.isRedirected),this.state.lastChecked=Date.now(),this.notifyListeners()}reset(){this.state={isAuthenticated:!1,isRedirected:!1,lastChecked:0},this.notifyListeners()}subscribe(e){return this.listeners.add(e),()=>{this.listeners.delete(e)}}notifyListeners(){this.listeners.forEach(e=>{try{e(this.getState())}catch(e){console.error("Error in auth state listener:",e)}})}}const _e=new Ue,ze=new Map;function Ve(e){const t=ze.get(e);return t?Date.now()-t.timestamp>18e4?(ze.delete(e),null):t.data:null}function Ze(e,t){ze.set(e,{timestamp:Date.now(),data:t})}const We="/home?proxyRedirect=";function Je(e,t,n){const{templateLess:s=!1,miHudLess:r=!1,appId:i,base:a,appBase:o,v7Features:c}=n,l=W();if(s&&r&&void 0===i)throw new Error("Custom App ID is required when both templateLess and miHudLess are true");return async(n,d,p)=>{try{const u=!(s&&r),h=function(e){return e.split("?")[0]}(n.url??""),f=e.test(h),m=!f&&!h.startsWith("/home")&&function(e,t){if(!t||"/"===t)return!0;const n=t.endsWith("/")?t:`${t}/`;return e===n||e===n.slice(0,-1)||e.startsWith(n)}(h,o)&&function(e){const t=e.headers["sec-fetch-dest"];if("string"==typeof t)return"document"===t;const n=e.headers.accept;return"string"==typeof n&&n.includes("text/html")}(n),g=_e.getState();if(!g.isAuthenticated&&!g.isRedirected&&n.url?.startsWith("/home")&&!n.url?.startsWith(We)){l.info(J.blue("Trying to authenticate and redirect to base"));try{return u?await Ke(t,{templateLess:s,miHudLess:r,appId:i,redirectUrl:c&&a?`${a}`:`${We}${encodeURIComponent("/")}`},n,d,()=>{},l):await Ge(t,{templateLess:s,miHudLess:r,appId:i,redirectUrl:c&&a?`${a}`:`${We}${encodeURIComponent("/")}`},n,d,()=>{},l),_e.setRedirected(!0),l.info(J.blue("Successfully authenticated. Redirecting to base")),K(d,a??"/",302)}catch(e){return p()}}if(!f&&!m)return p();if(!u){return await Ge(t,{templateLess:s,miHudLess:r,appId:i,redirectUrl:c&&a?`${a}`:`${We}${encodeURIComponent("/")}`},n,d,p,l)}return await Ke(t,{templateLess:s,miHudLess:r,appId:i,redirectUrl:c&&a?`${a}`:`${We}${encodeURIComponent("/")}`},n,d,p,l)}catch(e){return l.error(J.red(`Unexpected error in load-pp-data middleware: ${e instanceof Error?e.message:String(e)}`)),_e.reset(),p(e)}}}async function Ge(e,t,n,s,r,i){const{appId:a,redirectOnAuthFailure:o,redirectUrl:c}=t;if(void 0===a){const e=new Error("Custom App ID is required for page info only mode");return i.error(J.red(e.message)),r(e)}const l=n.headers??{};i.info(J.green("Start loading page info"));try{const t=`pageInfo:${a}`;return Ve(t)?(i.info(J.green("Page info loaded from cache")),r()):(await e.getPageInfo(a,l),i.info(J.blue("Clearing proxy cache after successful login")),He.clear(),Ze(t,{success:!0}),_e.updateState({isAuthenticated:!0}),i.info(J.green("Page info loaded successfully")),r())}catch(e){return _e.reset(),Xe(e,o??!0,c||`${We}${encodeURIComponent("/")}`,s,r,i,"Page info")}}async function Ke(e,t,n,s,r,i){const{templateLess:a,appId:o,redirectOnAuthFailure:c,redirectUrl:l}=t,d=n.headers??{};i.info(J.green("Start loading page data"));try{const t=`pageData:${a}:${o}`;if(Ve(t))return i.info(J.green("Page data loaded from cache")),r();const n=a||void 0===o?e.getPageTemplate(d):e.getPageVariables(o,d);return await n,i.info(J.blue("Clearing proxy cache after successful login")),He.clear(),Ze(t,{success:!0}),i.info(J.green("Page data loaded successfully")),r()}catch(e){return _e.reset(),Xe(e,c??!0,l||`${We}${encodeURIComponent("/")}`,s,r,i,"Page data")}}function Xe(e,t,n,s,r,i,a){if(function(e){return null!==e&&"object"==typeof e&&"response"in e&&void 0!==e.response}(e)&&(i.info(J.red(`${a} loading failed. Not authorized`)),t)){const e=`${n}`;return i.info(J.yellow(`Redirecting to: ${e}`)),K(s,e,302)}const o=e instanceof Error?e.message:String(e);return i.info(J.red(`${a} loading failed. Error: ${o}`)),r(e)}function Ye(){const e=q();return e.use(q.json()),e.use(q.urlencoded({extended:!0})),e}const Qe=Ye();class et{entries=new Map;insertOrder=[];totalSize=0;maxMemory;constructor(e=1073741824){this.maxMemory=e}allocateId(){return x.randomUUID()}add(e){let t=e,n=(e.requestBody?.byteLength??0)+(e.responseBody?.byteLength??0);n>this.maxMemory&&(t={...e,requestBody:null,responseBody:null,requestBodyTruncated:e.requestBodyTruncated||null!==e.requestBody,responseBodyTruncated:e.responseBodyTruncated||null!==e.responseBody},n=0);const s=n+512;for(;this.totalSize+s>this.maxMemory&&this.insertOrder.length>0;){const e=this.insertOrder.shift(),t=this.entries.get(e);t&&(this.totalSize-=(t.requestBody?.byteLength??0)+(t.responseBody?.byteLength??0)+512,this.entries.delete(e))}this.entries.set(t.id,t),this.insertOrder.push(t.id),this.totalSize+=s}get(e){return this.entries.get(e)}list(e){let t=this.insertOrder.slice().map(e=>{const t=this.entries.get(e);return{id:t.id,timestamp:t.timestamp,method:t.method,url:t.url,statusCode:t.statusCode,duration:t.duration,source:t.source,requestSize:t.requestBody?.byteLength??0,responseSize:t.responseBody?.byteLength??0,requestBodyTruncated:t.requestBodyTruncated,responseBodyTruncated:t.responseBodyTruncated}});if(e?.method&&(t=t.filter(t=>t.method===e.method.toUpperCase())),e?.search){const n=e.search.toLowerCase();t=t.filter(e=>e.url.toLowerCase().includes(n))}const n=Number.isFinite(e?.limit)?e.limit:t.length;if(void 0===e?.offset)return t.slice(Math.max(0,t.length-n));const s=Number.isFinite(e.offset)?e.offset:0;return t.slice(s,s+n)}clear(){this.entries.clear(),this.insertOrder=[],this.totalSize=0}get size(){return this.entries.size}get memoryUsage(){return this.totalSize}}const tt=["/@pp-dev/","/@api/","/_next/","/__nextjs_","/@vite","/@metricinsights","/@"],nt=new Set(["/favicon.ico","/installHook.js.map"]);function st(e,t=10485760){return(n,s,r)=>{const i=n.url||"/",a=i.split("?")[0];if(tt.some(e=>i.startsWith(e))||nt.has(a))return void r();const o=e.allocateId(),c=Date.now(),l=[];let d=0,p=!1;n.on("data",e=>{if(p)return;const n=Buffer.isBuffer(e)?e:Buffer.from(e);d+=n.byteLength,d<=t?l.push(n):(p=!0,l.length=0)});const u=[];let h=0,f=!1;const m=s.write.bind(s),g=s.end.bind(s);function y(e,n){if(!f&&null!=e)try{const s=Buffer.isBuffer(e)?e:"string"==typeof e?Buffer.from(e,n):ArrayBuffer.isView(e)?Buffer.from(e.buffer,e.byteOffset,e.byteLength):e instanceof ArrayBuffer?Buffer.from(e):Buffer.from(String(e));h+=s.byteLength,h<=t?u.push(s):(f=!0,u.length=0)}catch{}}function b(e){return"string"==typeof e[0]?e[0]:void 0}function v(){const e=s.getHeaders();return e["x-pp-proxy"]?"hit"===e["x-pp-cache"]?"proxy-cache":"proxy":"local"}let x=!1;s.write=function(e,...t){return y(e,b(t)),m(e,...t)},s.end=function(t,...r){return"function"!=typeof t&&y(t,b(r)),function(){if(x)return;x=!0;const t=!p&&l.length>0?Buffer.concat(l):null,r=!f&&u.length>0?Buffer.concat(u):null;e.add({id:o,timestamp:c,method:n.method||"GET",url:i,statusCode:s.statusCode??null,duration:Date.now()-c,source:v(),requestHeaders:n.headers,requestBody:t,requestBodyTruncated:p,responseHeaders:s.getHeaders(),responseBody:r,responseBodyTruncated:f})}(),g(t,...r)},r()}}const rt="/@pp-dev/inspector";let it,at=!1;function ot(e,t,n=10485760){it={store:t,captureLimit:n},at||(at=!0,e.get("/@api/requests",(e,t)=>{const{store:n}=it,{limit:s,offset:r,method:i,search:a}=e.query,o=void 0!==s?parseInt(s,10):void 0,c=n.list({limit:Number.isFinite(o)?o:500,offset:void 0!==r&&Number.isFinite(parseInt(r,10))?parseInt(r,10):void 0,method:i,search:a});t.json({requests:c,total:n.size,memoryUsage:n.memoryUsage,maxMemory:n.maxMemory})}),e.get("/@api/requests/stats",(e,t)=>{const{store:n}=it;t.json({total:n.size,memoryUsage:n.memoryUsage,maxMemory:n.maxMemory,memoryUsageFormatted:lt(n.memoryUsage),maxMemoryFormatted:lt(n.maxMemory)})}),e.get("/@api/requests/:id",(e,t)=>{const n=it.store.get(e.params.id);if(!n)return void t.status(404).json({error:"Not found",id:e.params.id});const s=ct(n.requestHeaders["content-type"]),r=ct(n.responseHeaders["content-type"]);t.json({id:n.id,timestamp:n.timestamp,method:n.method,url:n.url,statusCode:n.statusCode,duration:n.duration,source:n.source,requestHeaders:n.requestHeaders,requestBody:n.requestBody?n.requestBody.toString("base64"):null,requestBodySize:n.requestBody?.byteLength??0,requestBodyTruncated:n.requestBodyTruncated,requestContentType:s,responseHeaders:n.responseHeaders,responseBody:n.responseBody?n.responseBody.toString("base64"):null,responseBodySize:n.responseBody?.byteLength??0,responseBodyTruncated:n.responseBodyTruncated,responseContentType:r})}),e.delete("/@api/requests",(e,t)=>{it.store.clear(),t.json({ok:!0})}),e.get(rt,(e,t)=>{t.setHeader("Content-Type","text/html; charset=utf-8"),t.setHeader("Cache-Control","no-cache"),t.end(function(e){return`<!DOCTYPE html>\n<html lang="en">\n<head>\n<meta charset="utf-8">\n<meta name="viewport" content="width=device-width,initial-scale=1">\n<title>Request Inspector — pp-dev</title>\n<style>\n*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}\n:root{\n --bg:#1a1a1e;--bg2:#222228;--bg3:#2a2a32;--bg4:#32323c;\n --border:#3a3a46;--border2:#4a4a58;\n --text:#e0e0f0;--text2:#a0a0b8;--text3:#606078;\n --accent:#6e8efb;--accent2:#a78bfa;\n --green:#4ade80;--red:#f87171;--yellow:#fbbf24;--blue:#60a5fa;--orange:#fb923c;--purple:#c084fc;\n --font-mono:'Cascadia Code','Fira Code','JetBrains Mono',Consolas,monospace;\n --font-ui:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;\n}\nhtml,body{height:100%;overflow:hidden;background:var(--bg);color:var(--text);font-family:var(--font-ui);font-size:13px;line-height:1.5}\n\n/* ── Layout ── */\n.app{display:flex;flex-direction:column;height:100vh}\n.toolbar{display:flex;align-items:center;gap:8px;padding:8px 12px;background:var(--bg2);border-bottom:1px solid var(--border);flex-shrink:0;flex-wrap:wrap}\n.toolbar-title{font-weight:600;font-size:14px;color:var(--text);margin-right:4px}\n.toolbar-badge{font-size:11px;padding:2px 7px;border-radius:10px;background:var(--bg4);color:var(--text2)}\n.toolbar-spacer{flex:1}\n.mem-bar-wrap{display:flex;align-items:center;gap:6px;font-size:11px;color:var(--text2)}\n.mem-bar{width:80px;height:6px;background:var(--bg4);border-radius:3px;overflow:hidden}\n.mem-bar-fill{height:100%;background:var(--accent);transition:width .4s}\n\n.split{display:flex;flex:1;overflow:hidden}\n.pane-list{width:380px;min-width:200px;max-width:60%;border-right:1px solid var(--border);display:flex;flex-direction:column;flex-shrink:0}\n.pane-detail{flex:1;display:flex;flex-direction:column;overflow:hidden}\n\n/* ── Filter bar ── */\n.filter-bar{display:flex;flex-wrap:wrap;gap:6px;padding:8px 10px;background:var(--bg2);border-bottom:1px solid var(--border);flex-shrink:0}\n.filter-input{flex:1 1 120px;min-width:0;background:var(--bg3);border:1px solid var(--border);color:var(--text);padding:4px 8px;border-radius:4px;font-size:12px;outline:none}\n.filter-input:focus{border-color:var(--accent)}\n.filter-selects{display:flex;gap:6px;flex-shrink:0}\n.filter-select{background:var(--bg3);border:1px solid var(--border);color:var(--text);padding:4px 6px;border-radius:4px;font-size:12px;outline:none;cursor:pointer;max-width:90px}\n.filter-select:focus{border-color:var(--accent)}\n\n/* ── Request list ── */\n.req-list{flex:1;overflow-y:auto;overflow-x:hidden}\n.req-list::-webkit-scrollbar{width:6px}\n.req-list::-webkit-scrollbar-track{background:var(--bg)}\n.req-list::-webkit-scrollbar-thumb{background:var(--bg4);border-radius:3px}\n.req-item{display:flex;align-items:center;gap:8px;padding:7px 10px 7px 9px;border-bottom:1px solid var(--border);border-left:3px solid transparent;cursor:pointer;user-select:none;transition:background .1s,border-left-color .1s}\n.req-item:hover{background:var(--bg3)}\n.req-item.selected{background:var(--bg4);border-left-color:var(--accent) !important}\n.req-item.src-proxy{border-left-color:#6366f1}\n.req-item.src-proxy-cache{border-left-color:#d97706}\n.req-item.src-local{border-left-color:transparent}\n\n.method-badge{font-size:10px;font-weight:700;padding:2px 5px;border-radius:3px;min-width:38px;text-align:center;letter-spacing:.4px;flex-shrink:0}\n.m-GET{background:#1a3a2a;color:var(--green)}\n.m-POST{background:#1a2a3a;color:var(--blue)}\n.m-PUT{background:#3a2a1a;color:var(--orange)}\n.m-PATCH{background:#2a2a1a;color:var(--yellow)}\n.m-DELETE{background:#3a1a1a;color:var(--red)}\n.m-HEAD,.m-OPTIONS{background:#2a1a3a;color:var(--purple)}\n.m-other{background:var(--bg4);color:var(--text2)}\n\n.req-url{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;color:var(--text)}\n.req-meta{display:flex;flex-direction:column;align-items:flex-end;gap:1px;flex-shrink:0;font-size:10px;color:var(--text3)}\n.req-status-row{display:flex;align-items:center;gap:4px}\n.source-letter{font-size:9px;font-weight:700;width:15px;height:15px;display:flex;align-items:center;justify-content:center;border-radius:2px;flex-shrink:0}\n.src-letter-proxy{background:#1e1e3a;color:#6366f1}\n.src-letter-proxy-cache{background:#2a2010;color:#d97706}\n.src-letter-local{background:var(--bg4);color:var(--text3)}\n.status-badge{font-size:10px;font-weight:600;padding:1px 4px;border-radius:2px}\n.s-2xx{color:var(--green)}\n.s-3xx{color:var(--blue)}\n.s-4xx{color:var(--yellow)}\n.s-5xx{color:var(--red)}\n.s-pending{color:var(--text3)}\n\n.source-chip{display:inline-flex;align-items:center;gap:4px;font-size:11px;font-weight:500;padding:2px 7px;border-radius:10px}\n.src-local{background:#1e2a1e;color:#86efac}\n.src-proxy{background:#1e1e3a;color:#a5b4fc}\n.src-proxy-cache{background:#2a2010;color:#fcd34d}\n\n.empty-state{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;color:var(--text3);gap:8px;padding:40px}\n.empty-icon{font-size:40px;opacity:.5}\n\n/* ── Detail pane ── */\n.detail-empty{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;color:var(--text3);gap:8px}\n.detail-scroll{flex:1;overflow-y:auto;overflow-x:hidden;padding:0}\n.detail-scroll::-webkit-scrollbar{width:6px}\n.detail-scroll::-webkit-scrollbar-track{background:var(--bg)}\n.detail-scroll::-webkit-scrollbar-thumb{background:var(--bg4);border-radius:3px}\n\n.detail-section{border-bottom:1px solid var(--border)}\n.detail-section-hdr{display:flex;align-items:center;gap:8px;padding:8px 12px;background:var(--bg2);cursor:pointer;user-select:none;position:sticky;top:0;z-index:1}\n.detail-section-hdr:hover{background:var(--bg3)}\n.detail-section-hdr .toggle{font-size:10px;color:var(--text3)}\n.section-title{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.6px;color:var(--text2)}\n.section-badge{font-size:10px;padding:1px 5px;border-radius:8px;background:var(--bg4);color:var(--text3)}\n.section-actions{display:flex;gap:4px;margin-left:auto;align-items:center}\n.section-btn{font-size:10px;padding:1px 7px;border-radius:3px;border:1px solid var(--border);background:transparent;color:var(--text3);cursor:pointer;line-height:1.6;transition:all .1s;font-family:var(--font-ui)}\n.section-btn:hover{background:var(--bg4);color:var(--text);border-color:var(--border2)}\n.detail-section-body{padding:8px 12px;font-family:var(--font-mono);font-size:12px}\n.detail-section-body.collapsed{display:none}\n\n.kv-table{width:100%;border-collapse:collapse}\n.kv-table tr:hover td{background:var(--bg3)}\n.kv-table td{padding:2px 0;vertical-align:top}\n.kv-table td:first-child{color:var(--text2);white-space:nowrap;padding-right:12px;min-width:140px;max-width:220px;overflow:hidden;text-overflow:ellipsis}\n.kv-table td:last-child{color:var(--text);word-break:break-all}\n\n.body-content{white-space:pre-wrap;word-break:break-all;color:var(--text);line-height:1.6;max-height:400px;overflow:auto}\n.body-content.json{color:#c9d1d9}\n.body-json-key{color:#79b8ff}\n.body-json-str{color:#9ecbff}\n.body-json-num{color:#f8c555}\n.body-json-bool{color:#f97583}\n.body-json-null{color:#f97583;font-style:italic}\n.body-binary{color:var(--text3);font-style:italic;padding:8px 0}\n.body-image{max-width:100%;max-height:300px;object-fit:contain;margin-top:4px;border:1px solid var(--border);border-radius:4px}\n.truncated-note{font-size:11px;color:var(--yellow);margin-top:4px;font-family:var(--font-ui)}\n\n.detail-info-bar{display:flex;gap:16px;padding:10px 12px;background:var(--bg2);border-bottom:1px solid var(--border);flex-shrink:0;flex-wrap:wrap}\n.info-item{display:flex;flex-direction:column;gap:2px}\n.info-label{font-size:10px;color:var(--text3);text-transform:uppercase;letter-spacing:.5px}\n.info-value{font-size:13px;font-weight:600;color:var(--text)}\n.info-url{display:flex;align-items:center;gap:8px;font-size:11px;font-family:var(--font-mono);color:var(--text2);padding:6px 12px;border-bottom:1px solid var(--border);background:var(--bg)}\n.info-url-text{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n\n/* ── Buttons ── */\n.btn{padding:4px 10px;border-radius:4px;border:1px solid var(--border);background:var(--bg3);color:var(--text);font-size:12px;cursor:pointer;transition:background .1s}\n.btn:hover{background:var(--bg4);border-color:var(--border2)}\n.btn-danger:hover{background:#3a1a1a;border-color:#6a2a2a;color:var(--red)}\n.btn-sm{padding:2px 7px;font-size:11px}\n\n/* ── Live indicator ── */\n.live-dot{width:7px;height:7px;border-radius:50%;background:var(--green);animation:pulse 2s infinite}\n@keyframes pulse{0%,100%{opacity:1}50%{opacity:.3}}\n\n/* ── Scrollbar ── */\n::-webkit-scrollbar{width:6px;height:6px}\n::-webkit-scrollbar-track{background:var(--bg)}\n::-webkit-scrollbar-thumb{background:var(--bg4);border-radius:3px}\n</style>\n</head>\n<body>\n<div class="app">\n <div class="toolbar">\n <span class="toolbar-title">🔍 Request Inspector</span>\n <span class="live-dot" title="Live"></span>\n <span class="toolbar-badge" id="req-count">0 requests</span>\n <div class="mem-bar-wrap">\n <div class="mem-bar"><div class="mem-bar-fill" id="mem-fill" style="width:0%"></div></div>\n <span id="mem-text">0 B / 1 GB</span>\n </div>\n <div class="toolbar-spacer"></div>\n <button class="btn btn-sm btn-danger" onclick="clearAll()">Clear</button>\n </div>\n <div class="split">\n <div class="pane-list">\n <div class="filter-bar">\n <input class="filter-input" id="search-input" placeholder="Filter by URL…" oninput="applyFilter()" />\n <div class="filter-selects">\n <select class="filter-select" id="method-select" onchange="applyFilter()">\n <option value="">All</option>\n <option>GET</option><option>POST</option><option>PUT</option>\n <option>PATCH</option><option>DELETE</option><option>HEAD</option>\n </select>\n <select class="filter-select" id="status-select" onchange="applyFilter()">\n <option value="">All</option>\n <option value="2">2xx</option><option value="3">3xx</option>\n <option value="4">4xx</option><option value="5">5xx</option>\n </select>\n <select class="filter-select" id="source-select" onchange="applyFilter()">\n <option value="">All</option>\n <option value="local">Local</option>\n <option value="proxy">Proxy</option>\n <option value="proxy-cache">Cached</option>\n </select>\n </div>\n </div>\n <div class="req-list" id="req-list">\n <div class="empty-state">\n <div class="empty-icon">📡</div>\n <div>No requests captured yet</div>\n <div style="font-size:11px">Requests to the dev server will appear here</div>\n </div>\n </div>\n </div>\n <div class="pane-detail">\n <div class="detail-empty" id="detail-empty">\n <div style="font-size:32px;opacity:.3">←</div>\n <div>Select a request to inspect</div>\n </div>\n <div id="detail-panel" style="display:none;flex:1;flex-direction:column;overflow:hidden">\n <div class="detail-info-bar" id="detail-info"></div>\n <div class="info-url" id="detail-url"></div>\n <div class="detail-scroll" id="detail-scroll"></div>\n </div>\n </div>\n </div>\n</div>\n<script>\n(function(){\n'use strict';\n\nconst CAPTURE_LIMIT = ${e};\n\nlet allRequests = [];\nlet filteredRequests = [];\nlet selectedId = null;\nlet currentEntry = null;\n\nconst listEl = document.getElementById('req-list');\nconst detailEmpty = document.getElementById('detail-empty');\nconst detailPanel = document.getElementById('detail-panel');\nconst detailInfo = document.getElementById('detail-info');\nconst detailUrl = document.getElementById('detail-url');\nconst detailScroll = document.getElementById('detail-scroll');\n\n// ── Fetch request list ────────────────────────────────────────────────────────\nasync function fetchList() {\n try {\n const r = await fetch('/@api/requests');\n const data = await r.json();\n\n allRequests = data.requests || [];\n updateStats(data);\n applyFilter();\n\n if (selectedId && !allRequests.find(r => r.id === selectedId)) {\n selectedId = null;\n showDetailEmpty();\n }\n } catch(e) {}\n}\n\nfunction updateStats(data) {\n const pct = data.maxMemory > 0 ? Math.min(100, (data.memoryUsage / data.maxMemory) * 100) : 0;\n\n document.getElementById('req-count').textContent = data.total + ' request' + (data.total !== 1 ? 's' : '');\n document.getElementById('mem-fill').style.width = pct + '%';\n document.getElementById('mem-text').textContent = formatBytes(data.memoryUsage) + ' / ' + formatBytes(data.maxMemory);\n}\n\n// ── Filter ────────────────────────────────────────────────────────────────────\nfunction applyFilter() {\n const search = document.getElementById('search-input').value.toLowerCase();\n const method = document.getElementById('method-select').value;\n const status = document.getElementById('status-select').value;\n const source = document.getElementById('source-select').value;\n\n filteredRequests = allRequests.filter(r => {\n if (method && r.method !== method) {\n return false;\n }\n\n if (status && !String(r.statusCode || '').startsWith(status)) {\n return false;\n }\n\n if (source && r.source !== source) {\n return false;\n }\n\n if (search && !r.url.toLowerCase().includes(search)) {\n return false;\n }\n\n return true;\n });\n\n renderList();\n}\n\n// ── Render list ───────────────────────────────────────────────────────────────\nfunction isAtBottom() {\n return listEl.scrollTop + listEl.clientHeight >= listEl.scrollHeight - 16;\n}\n\nfunction renderList() {\n const atBottom = isAtBottom();\n\n if (!filteredRequests.length) {\n listEl.innerHTML = '<div class="empty-state"><div class="empty-icon">🔍</div><div>No matching requests</div></div>';\n\n return;\n }\n\n const html = filteredRequests.map(r => {\n const sel = r.id === selectedId ? ' selected' : '';\n const mClass = methodClass(r.method);\n const sClass = statusClass(r.statusCode);\n const dur = r.duration != null ? formatDur(r.duration) : '…';\n const size = formatBytes((r.responseSize || 0));\n const srcCls = 'src-' + (r.source || 'local');\n const sLetter = sourceLetter(r.source);\n const sLetterCls = 'src-letter-' + (r.source || 'local').replace(/-/g, '-');\n\n return \`<div class="req-item \${srcCls}\${sel}" data-id="\${esc(r.id)}" onclick="selectRequest('\${esc(r.id)}')">\n <span class="method-badge \${mClass}">\${esc(r.method)}</span>\n <span class="req-url" title="\${esc(r.url)}">\${esc(r.url)}</span>\n <span class="req-meta">\n <span class="req-status-row">\n <span class="source-letter \${sLetterCls}">\${sLetter}</span>\n <span class="status-badge \${sClass}">\${r.statusCode || '…'}</span>\n </span>\n <span>\${dur}</span>\n <span>\${size}</span>\n </span>\n </div>\`;\n }).join('');\n\n listEl.innerHTML = html;\n\n if (atBottom) {\n listEl.scrollTop = listEl.scrollHeight;\n }\n}\n\n// ── Select request ────────────────────────────────────────────────────────────\nasync function selectRequest(id) {\n selectedId = id;\n\n renderList();\n\n detailEmpty.style.display = 'none';\n detailPanel.style.display = 'flex';\n detailPanel.style.flexDirection = 'column';\n detailPanel.style.overflow = 'hidden';\n detailScroll.innerHTML = '<div style="padding:20px;color:var(--text3)">Loading…</div>';\n\n try {\n const r = await fetch('/@api/requests/' + encodeURIComponent(id));\n\n if (!r.ok) {\n showDetailEmpty();\n\n return;\n }\n\n const entry = await r.json();\n\n renderDetail(entry);\n } catch(e) {\n showDetailEmpty();\n }\n}\nwindow.selectRequest = selectRequest;\n\nfunction showDetailEmpty() {\n detailPanel.style.display = 'none';\n detailEmpty.style.display = 'flex';\n}\n\nfunction renderDetail(e) {\n currentEntry = e;\n\n const sClass = statusClass(e.statusCode);\n const src = sourceInfo(e.source);\n\n detailInfo.innerHTML = \`\n <div class="info-item"><div class="info-label">Method</div><div class="info-value"><span class="method-badge \${methodClass(e.method)}">\${esc(e.method)}</span></div></div>\n <div class="info-item"><div class="info-label">Status</div><div class="info-value"><span class="status-badge \${sClass}">\${e.statusCode || '—'}</span></div></div>\n <div class="info-item"><div class="info-label">Source</div><div class="info-value"><span class="source-chip \${src.cls}">\${src.title}</span></div></div>\n <div class="info-item"><div class="info-label">Duration</div><div class="info-value">\${e.duration != null ? formatDur(e.duration) : '—'}</div></div>\n <div class="info-item"><div class="info-label">Request</div><div class="info-value">\${formatBytes(e.requestBodySize||0)}</div></div>\n <div class="info-item"><div class="info-label">Response</div><div class="info-value">\${formatBytes(e.responseBodySize||0)}</div></div>\n <div class="info-item"><div class="info-label">Time</div><div class="info-value" style="font-size:11px">\${new Date(e.timestamp).toLocaleTimeString()}</div></div>\n \`;\n\n detailUrl.innerHTML = \`<span class="info-url-text" title="\${esc(e.url)}">\${esc(e.url)}</span><button class="section-btn" onclick="copyUrl(this)">Copy link</button>\`;\n\n const sections = [\n makeHeadersSection('Request Headers', e.requestHeaders, 'request'),\n makeBodySection('Request Body', e.requestBody, e.requestContentType, e.requestBodyTruncated, e.requestBodySize, 'request'),\n makeHeadersSection('Response Headers', e.responseHeaders, 'response'),\n makeBodySection('Response Body', e.responseBody, e.responseContentType, e.responseBodyTruncated, e.responseBodySize, 'response'),\n ];\n\n detailScroll.innerHTML = sections.join('');\n}\n\nfunction makeHeadersSection(title, headers, type) {\n const entries = Object.entries(headers || {}).filter(([,v]) => v != null);\n const rows = entries.map(([k,v]) => \`<tr><td>\${esc(k)}</td><td>\${esc(Array.isArray(v) ? v.join(', ') : String(v))}</td></tr>\`).join('');\n const actionsHtml = entries.length ? \`<div class="section-actions"><button class="section-btn" onclick="event.stopPropagation();copyHeaders('\${type}',this)">Copy</button></div>\` : '';\n\n return \`<div class="detail-section">\n <div class="detail-section-hdr" onclick="toggleSection(this)">\n <span class="toggle">▾</span>\n <span class="section-title">\${title}</span>\n <span class="section-badge">\${entries.length}</span>\n \${actionsHtml}\n </div>\n <div class="detail-section-body">\n <table class="kv-table">\${rows}</table>\n </div>\n </div>\`;\n}\n\nfunction makeBodySection(title, b64, contentType, truncated, size, type) {\n let bodyHtml = '';\n const ct = (contentType || '').toLowerCase().split(';')[0].trim();\n const isText = ct.includes('json') || ct.startsWith('text/') || ct.includes('/javascript') || ct === 'application/xml' || ct === 'application/x-www-form-urlencoded';\n\n if (!b64 && !truncated) {\n bodyHtml = '<div class="body-binary">No body</div>';\n } else if (truncated) {\n bodyHtml = \`<div class="body-binary">Body too large to capture (\${formatBytes(size)})</div>\`;\n } else if (b64) {\n if (ct.includes('json') || ct.includes('/javascript') || ct === 'text/plain') {\n const text = decodeTextBody(b64);\n\n if (ct.includes('json')) {\n try {\n bodyHtml = \`<pre class="body-content json">\${syntaxHighlightJson(text)}</pre>\`;\n } catch {\n bodyHtml = \`<pre class="body-content">\${esc(text)}</pre>\`;\n }\n } else {\n bodyHtml = \`<pre class="body-content">\${esc(text)}</pre>\`;\n }\n } else if (ct.startsWith('text/')) {\n bodyHtml = \`<pre class="body-content">\${esc(decodeTextBody(b64))}</pre>\`;\n } else if (ct.startsWith('image/') && size < 1024*1024) {\n bodyHtml = \`<img class="body-image" src="data:\${esc(ct)};base64,\${b64}" alt="image"/>\`;\n } else if (ct === 'application/x-www-form-urlencoded') {\n const params = new URLSearchParams(decodeTextBody(b64));\n const rows = [...params.entries()].map(([k,v]) => \`<tr><td>\${esc(k)}</td><td>\${esc(v)}</td></tr>\`).join('');\n\n bodyHtml = \`<table class="kv-table">\${rows}</table>\`;\n } else {\n bodyHtml = \`<div class="body-binary">Binary data — \${formatBytes(size)} (\${esc(ct || 'unknown type')})</div>\`;\n }\n }\n\n const copyBtn = b64 && isText ? \`<button class="section-btn" onclick="event.stopPropagation();copyBody('\${type}',this)">Copy</button>\` : '';\n const saveBtn = b64 ? \`<button class="section-btn" onclick="event.stopPropagation();saveBody('\${type}')">Save</button>\` : '';\n const actionsHtml = (copyBtn || saveBtn) ? \`<div class="section-actions">\${copyBtn}\${saveBtn}</div>\` : '';\n const note = truncated ? \`<div class="truncated-note">⚠ Body was larger than the capture limit (\${formatBytes(CAPTURE_LIMIT)})</div>\` : '';\n\n return \`<div class="detail-section">\n <div class="detail-section-hdr" onclick="toggleSection(this)">\n <span class="toggle">▾</span>\n <span class="section-title">\${title}</span>\n \${size ? \`<span class="section-badge">\${formatBytes(size)}</span>\` : ''}\n \${actionsHtml}\n </div>\n <div class="detail-section-body">\${bodyHtml}\${note}</div>\n </div>\`;\n}\n\nwindow.toggleSection = function(hdr) {\n const body = hdr.nextElementSibling;\n const toggle = hdr.querySelector('.toggle');\n\n if (body.classList.toggle('collapsed')) {\n toggle.textContent = '▸';\n } else {\n toggle.textContent = '▾';\n }\n};\n\nasync function clearAll() {\n await fetch('/@api/requests', { method: 'DELETE' });\n\n allRequests = [];\n filteredRequests = [];\n selectedId = null;\n\n showDetailEmpty();\n renderList();\n updateStats({ total: 0, memoryUsage: 0, maxMemory: 1024*1024*1024 });\n}\nwindow.clearAll = clearAll;\n\n// ── JSON syntax highlighting ──────────────────────────────────────────────────\nfunction syntaxHighlightJson(str) {\n try {\n const parsed = JSON.parse(str);\n\n str = JSON.stringify(parsed, null, 2);\n } catch { /* use as-is */ }\n\n return str.replace(/("(\\\\u[a-zA-Z0-9]{4}|\\\\[^u]|[^\\\\"])*"\\s*:?|\\b(true|false|null)\\b|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?)/g, function(m) {\n if (/^"/.test(m)) {\n if (/:$/.test(m)) {\n return '<span class="body-json-key">' + esc(m) + '</span>';\n }\n\n return '<span class="body-json-str">' + esc(m) + '</span>';\n }\n\n if (/true|false/.test(m)) {\n return '<span class="body-json-bool">' + m + '</span>';\n }\n\n if (/null/.test(m)) {\n return '<span class="body-json-null">' + m + '</span>';\n }\n\n return '<span class="body-json-num">' + m + '</span>';\n });\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\nfunction esc(s) {\n return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');\n}\n\nfunction decodeTextBody(b64) {\n const bytes = Uint8Array.from(atob(b64), function(c) { return c.charCodeAt(0); });\n\n return new TextDecoder('utf-8').decode(bytes);\n}\n\nfunction sourceInfo(source) {\n if (source === 'proxy') {\n return { cls: 'src-proxy', title: 'Backend (live)' };\n }\n\n if (source === 'proxy-cache') {\n return { cls: 'src-proxy-cache', title: 'Backend (cached)' };\n }\n\n return { cls: 'src-local', title: 'Local (dev server)' };\n}\n\nfunction sourceLetter(source) {\n if (source === 'proxy') {\n return 'P';\n }\n\n if (source === 'proxy-cache') {\n return 'C';\n }\n\n return 'L';\n}\n\n// ── Copy / Save ───────────────────────────────────────────────────────────────\nfunction clipboard(text, btn) {\n navigator.clipboard.writeText(text).then(() => {\n if (!btn) {\n return;\n }\n\n const orig = btn.textContent;\n\n btn.textContent = '✓';\n btn.style.color = 'var(--green)';\n setTimeout(() => {\n btn.textContent = orig;\n btn.style.color = '';\n }, 1500);\n }).catch(() => {});\n}\n\nfunction copyUrl(btn) {\n clipboard(currentEntry ? currentEntry.url : '', btn);\n}\nwindow.copyUrl = copyUrl;\n\nfunction copyHeaders(type, btn) {\n const h = type === 'request' ? (currentEntry && currentEntry.requestHeaders) : (currentEntry && currentEntry.responseHeaders);\n\n if (!h) {\n return;\n }\n\n const text = Object.entries(h).filter(([,v]) => v != null).map(([k,v]) => k + ': ' + (Array.isArray(v) ? v.join(', ') : v)).join('\\n');\n\n clipboard(text, btn);\n}\nwindow.copyHeaders = copyHeaders;\n\nfunction copyBody(type, btn) {\n const b64 = type === 'request' ? (currentEntry && currentEntry.requestBody) : (currentEntry && currentEntry.responseBody);\n\n if (!b64) {\n return;\n }\n\n try {\n clipboard(decodeTextBody(b64), btn);\n } catch(e) {}\n}\nwindow.copyBody = copyBody;\n\nfunction saveBody(type) {\n const b64 = type === 'request' ? (currentEntry && currentEntry.requestBody) : (currentEntry && currentEntry.responseBody);\n const ct = type === 'request' ? (currentEntry && currentEntry.requestContentType) : (currentEntry && currentEntry.responseContentType);\n\n if (!b64) {\n return;\n }\n\n downloadB64(b64, type + '-body.' + fileExt(ct));\n}\nwindow.saveBody = saveBody;\n\nfunction downloadB64(b64, filename) {\n try {\n const bytes = Uint8Array.from(atob(b64), function(c) { return c.charCodeAt(0); });\n const blob = new Blob([bytes]);\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n\n a.href = url;\n a.download = filename;\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n setTimeout(function() { URL.revokeObjectURL(url); }, 1000);\n } catch(e) {}\n}\n\nfunction fileExt(ct) {\n ct = ((ct || '').split(';')[0]).trim().toLowerCase();\n\n var map = {\n 'application/json': 'json', 'text/html': 'html', 'text/css': 'css',\n 'text/javascript': 'js', 'application/javascript': 'js', 'text/plain': 'txt',\n 'text/xml': 'xml', 'application/xml': 'xml', 'image/png': 'png',\n 'image/jpeg': 'jpg', 'image/gif': 'gif', 'image/svg+xml': 'svg',\n 'image/webp': 'webp', 'application/pdf': 'pdf', 'application/zip': 'zip',\n 'application/x-www-form-urlencoded': 'txt', 'application/octet-stream': 'bin',\n };\n\n return map[ct] || 'bin';\n}\n\nfunction formatBytes(b) {\n b = b || 0;\n\n if (b < 1024) {\n return b + ' B';\n }\n\n if (b < 1024*1024) {\n return (b/1024).toFixed(1) + ' KB';\n }\n\n if (b < 1024*1024*1024) {\n return (b/(1024*1024)).toFixed(1) + ' MB';\n }\n\n return (b/(1024*1024*1024)).toFixed(2) + ' GB';\n}\n\nfunction formatDur(ms) {\n if (ms < 1000) {\n return ms + 'ms';\n }\n\n return (ms/1000).toFixed(2) + 's';\n}\n\nfunction methodClass(m) {\n const map = { GET:'m-GET', POST:'m-POST', PUT:'m-PUT', PATCH:'m-PATCH', DELETE:'m-DELETE', HEAD:'m-HEAD', OPTIONS:'m-OPTIONS' };\n\n return map[m] || 'm-other';\n}\n\nfunction statusClass(s) {\n if (!s) {\n return 's-pending';\n }\n\n const c = Math.floor(s / 100);\n\n return ['','','s-2xx','s-3xx','s-4xx','s-5xx'][c] || '';\n}\n\n// ── Polling ───────────────────────────────────────────────────────────────────\nfetchList();\nsetInterval(fetchList, 2000);\n\n})();\n<\/script>\n</body>\n</html>`}(it.captureLimit))}))}function ct(e){return Array.isArray(e)?e[0]??"":String(e??"")}function lt(e){return e<1024?`${e} B`:e<1048576?`${(e/1024).toFixed(1)} KB`:e<1073741824?`${(e/1048576).toFixed(1)} MB`:`${(e/1073741824).toFixed(2)} GB`}exports.AuthProvider=Ue,exports.ClientService=Pe,exports.DistService=Re,exports.INSPECTOR_PATH=rt,exports.MiAPI=fe,exports.PACKAGE_NAME=z,exports.PATH_PAGE_PREFIX="/p",exports.PATH_TEMPLATE_LOCAL_PREFIX="/pl",exports.PATH_TEMPLATE_PREFIX="/pt",exports.PP_DEV_CLIENT_ENTRY=O,exports.PP_DEV_CONFIG_NAMES=[".pp-dev.config.js",".pp-dev.config.cjs",".pp-dev.config.mjs",".pp-dev.config.ts",".pp-dev.config.cts",".pp-dev.config.mts",".pp-dev.config.json","pp-dev.config.js","pp-dev.config.cjs","pp-dev.config.mjs","pp-dev.config.ts","pp-dev.config.cts","pp-dev.config.mts","pp-dev.config.json"],exports.PP_DEV_HMR_WS_PATH="/@pp-dev-hmr",exports.PP_DEV_PACKAGE_DIR=D,exports.RequestStore=et,exports.VERSION=_,exports.authProvider=_e,exports.colors=J,exports.createLogger=W,exports.createRequestCaptureMiddleware=st,exports.initLoadPPData=Je,exports.initPPRedirect=Me,exports.initProxy=ee,exports.initProxyCache=De,exports.initRewriteResponse=Oe,exports.internalServer=Qe,exports.normalizePPDevConfig=function(e,t){const{mi:n={},app:s={},proxy:r={},build:i={},sync:a={},inspector:o={}}=e,c=n.mode??"standalone",l=s.type??"template",d=n.apiVersion??7,p="standalone"===c;let u=!1;"top-bar"===n.include?u=!0:"shared-components"===n.include&&(u={addSharedComponentsScripts:!0,addRootElement:!1});const h="page"===l,f=s.name??t,m="VERSION-v{packageversion}-{currentDate}.json";let g;const y=i.zip;let b;g=!1!==y&&(!0===y||void 0===y?{outFileName:`${f}.zip`,outDir:"dist-zip"}:{outFileName:y.fileName??`${f}.zip`,outDir:y.outDir??"dist-zip",...y.inDir?{inDir:y.inDir}:{}});const v=i.versionFile;let x;b=!1!==v&&(!0===v||void 0===v?{versionFileTemplate:m,enabled:!0}:{versionFileTemplate:v.fileNameTemplate??m,enabled:v.enabled??!0});const w=i.imageOptimisations;return x=!1!==w&&(!0===w||void 0===w?{}:w),{templateName:f,backendBaseURL:n.url??process.env.MI_BACKEND_URL,appId:s.id,templateLess:h,miHudLess:p,integrateMiTopBar:u,enableProxyCache:r.cache??!0,proxyCacheTTL:r.cacheTtl??6e5,disableSSLValidation:r.tls?.allowSelfSigned??!1,imageOptimizer:x,outDir:i.outDir??"dist",distZip:g,versionPlugin:b,syncBackupsDir:a.backupsDir??"backups",v7Features:d>=7,personalAccessToken:n.token??process.env.MI_ACCESS_TOKEN,inspectorEnabled:o.enabled??!0,inspectorMaxMemory:o.maxMemory??1073741824,inspectorCaptureLimit:o.captureLimit??10485760}},exports.registerInspectorRoutes=ot,exports.urlReplacer=G,exports.validatePPDevConfig=function(e,t){const n=e.mi?.mode??"standalone",s=e.app?.type??"template",r=e.mi?.apiVersion??7,i=e.mi?.url??process.env.MI_BACKEND_URL;if(!t)throw new Error("[pp-dev] app.name is required (or set package.json#name)");if(e.mi?.include&&"standalone"!==n)throw console.warn('[pp-dev] mi.include is only valid when mi.mode is "standalone"'),new Error('[pp-dev] mi.include requires mi.mode to be "standalone"');if(!i){if("embedding"===n||"template"===s)throw new Error('[pp-dev] mi.url is required when mi.mode is "embedding" or app.type is "template"');"standalone"===n&&"page"===s&&console.warn("[pp-dev] mi.url is not set — dev server will run without a backend proxy")}if("template"===s&&!e.app?.id)throw new Error('[pp-dev] app.id is required when app.type is "template"');if("page"===s&&i&&("standalone"===n||r>=7)&&!e.app?.id)throw new Error('[pp-dev] app.id is required when backend page data is loaded for app.type "page"')},exports.vitePPDev=function(e){const{templateName:t,templateLess:n,backendBaseURL:s,miHudLess:r,appId:i,enableProxyCache:a,proxyCacheTTL:o,disableSSLValidation:c,distZip:l,versionPlugin:d,syncBackupsDir:p,v7Features:u,personalAccessToken:h,inspectorEnabled:f,inspectorMaxMemory:m,inspectorCaptureLimit:g}=e||{};let y=!0;return process.cwd(),{name:"vite-pp-dev",apply:"serve",config:e=>(e.clientInjectionPlugin={backendBaseURL:s,appId:i,templateLess:n,v7Features:u},u&&(e.base=`/pl/${t}`),e),transformIndexHtml:async(e,t)=>{const n={html:e,tags:[]};return y&&(y=!1,n.tags.push({tag:"script",injectTo:"body",children:`${Math.random()}`})),n},configureServer:y=>{let b=y.config.base;b.endsWith("/")||(b+="/");const v=b.substring(0,b.lastIndexOf("/"));if(y.middlewares.use(Me(b,t)),!1!==f){const e=new et(m);y.middlewares.use(st(e,g));const t=Ye();ot(t,e,g),y.middlewares.use(t)}if(s){const f=new URL(s).host,m={headers:{host:f,referer:s,origin:s.replace(/^(https?:\/\/)([^/]+)(\/.*)?$/i,"$1$2")},appId:i,templateLess:n,disableSSLValidation:c,v7Features:u,personalAccessToken:h??process.env.MI_ACCESS_TOKEN},g=new fe(s,m);if(a){let e=+o;(!e||Number.isNaN(e)||e<0)&&(e=6e5),y.middlewares.use(De({devServer:y,ttl:e}))}const x=new RegExp(`^((${b})|/)$`);y.middlewares.use(Je(x,g,Object.assign({},e,{appId:i,appBase:b}))),y.middlewares.use(ee({baseURL:s,proxyIgnore:["/@vite","/@metricinsights","/@",v],disableSSLValidation:c,miAPI:g,templateName:t}));const w=(e,t,n,s,r)=>{const i={error:n};s&&(i.details=s),r&&(i.code=r),e.status(t).json(i).end()},k=(e,t,n)=>{y.config.logger.error(`${n} token validation error:`,t);const s=pe(t);if(t.tokenErrorInfo){const n=t.tokenErrorInfo;w(e,n.status||500,n.userFriendlyMessage,n.message,n.code)}else{let t,r,i;switch(s.code){case"SESSION_EXPIRED":t=412,r="personal"===n?"Personal access token expired":"Session expired",i="personal"===n?"PAT_EXPIRED":"SESSION_EXPIRED";break;case"UNAUTHORIZED":t=401,r="Unauthorized",i="UNAUTHORIZED";break;case"FORBIDDEN":t=403,r="Access forbidden",i="FORBIDDEN";break;default:t=500,r="Internal server error",i=s.code}const a="personal"===n&&"PAT_EXPIRED"===i?"Your personal access token has expired. Please generate a new token from the portal.":"regular"===n&&"SESSION_EXPIRED"===i?"Your portal session has expired. Please refresh your token or re-authenticate.":s.userFriendlyMessage;w(e,t,r,a,i)}return null},E=Ye();E.post("/@api/login",async(e,t,n)=>{const{token:s,tokenType:r}=e.body;if(s)if(y.config.logger.info(`Attempting to validate ${r} token...`),"personal"===r){if(!await g.get("/data/page/index/auth/info",{"Content-Type":"application/json",Accept:"application/json",Authorization:`Bearer ${s}`},!0).then(async e=>"number"==typeof e.data?.user?.user_id?(g.personalAccessToken=s,y.config.logger.info(`Personal access token validated successfully for user ${e.data.user.user_id}`),e):(w(t,400,"Token expired or invalid"),null)).catch(e=>k(t,e,"personal")))return;K(t,"/",302)}else if("regular"===r){if(!await g.get("/api/user",{"Content-Type":"application/json",Accept:"application/json",Token:s},!0).then(e=>e.data?.users?.length?(g.personalAccessToken=void 0,y.config.logger.info(`Regular token validated successfully for ${e.data.users.length} user(s)`),t.setHeader("set-cookie",e.headers["set-cookie"]??""),e):(w(t,400,"Token expired or invalid"),null)).catch(e=>k(t,e,"regular")))return;K(t,"/",302)}else w(t,400,"Invalid tokenType",'tokenType must be "personal" or "regular".',"INVALID_TOKEN_TYPE");else w(t,400,"Token is required")}),y.middlewares.use(E);const T=!1!==l?new Re(t,Object.assign({backupDir:p},"object"==typeof l?{distZipFolder:l.outDir,distZipFilename:l.outFileName}:void 0,"object"==typeof d?{versionFileTemplate:d.versionFileTemplate}:void 0)):void 0;return new Pe(y,{distService:T,miAPI:g}),()=>{y.middlewares.use(Oe(e=>e.split("?")[0].endsWith("index.html"),(e,t)=>Buffer.from(G(f,t.headers.host??"",g.buildPage(e,r)))))}}}}},exports.writeBuildVersionManifest=xe;
2
- //# sourceMappingURL=plugin-34c8_AjI.js.map
1
+ "use strict";const e=require("http-proxy-middleware"),t=require("url"),n=require("vite"),s=require("picocolors"),r=require("axios"),i=require("node:http"),a=require("node:https"),o=require("jsdom"),c=require("path"),l=require("fs"),d=require("crypto"),p=require("process"),u=require("child_process"),h=require("os"),f=require("module"),m=require("extract-zip"),g=require("jszip"),y=require("memory-cache"),v=require("zlib"),b=require("express"),x=require("node:crypto");var w="undefined"!=typeof document?document.currentScript:null;const k=e=>e&&"object"==typeof e&&"default"in e?e.default:e;function E(e){if(e&&"object"==typeof e&&"default"in e)return e;const t=Object.create(null);if(e)for(const n in e)t[n]=e[n];return t.default=e,Object.freeze(t)}const T=k(r),S=k(i),$=k(a),B=k(c),A=E(c),I=E(d),P=E(p),R=E(u),C=E(h),L=k(m),F=k(g),j=E(y),H=k(b),q=c.resolve("undefined"!=typeof __filename&&__filename||t.fileURLToPath("undefined"==typeof document?require("url").pathToFileURL(__filename).href:w&&"SCRIPT"===w.tagName.toUpperCase()&&w.src||new URL("plugin-Cm9vRLCu.js",document.baseURI).href),"../../.."),N=c.resolve("undefined"!=typeof __filename&&__filename||t.fileURLToPath("undefined"==typeof document?require("url").pathToFileURL(__filename).href:w&&"SCRIPT"===w.tagName.toUpperCase()&&w.src||new URL("plugin-Cm9vRLCu.js",document.baseURI).href),"../.."),D=l.existsSync(c.resolve(q,"package.json"))?q:N,O=c.resolve(D,"dist/client/client.js"),{version:M,name:U}=JSON.parse(l.readFileSync(c.resolve(D,"package.json")).toString()),_=M,z=U;let V=new Map;const Z="default",W=(e="info",t=Z)=>{if(V&&"function"==typeof V.has||(V=new Map),V.has(t))return V.get(t);if(t===Z){const s=n.createLogger(e);return V.set(t,s),s}const s=n.createLogger(e);return V.set(t,s),s},J=s.createColors(),G=(e,t,n)=>{const s=new RegExp(`(!!)?(https?(:(\\\\)?/(\\\\)?/)${e})`,"gi");return n.replace(s,(e,...n)=>e.startsWith("!!")?n[1]:`http${n[2]}${t}`)},K=(e,t,n)=>{e.setHeader("location",t),e.statusCode=n,e.end()};const X=function(){const e={personal:{placeholder:"API Token",caption:`You can get the API Token\n <a href="https://${host}/api-token" target="_blank" \n style="color: #007bff; text-decoration: none;">here</a>`,endpoint:"/@api/login"},regular:{placeholder:"Legacy Token",caption:`You can get the Legacy Token\n <a href="https://${host}/api/get_token" target="_blank" \n style="color: #007bff; text-decoration: none;">here</a>`,endpoint:"/@api/login"}};function t(t,n,s){const r=e[t];r&&(n.placeholder=r.placeholder,s.innerHTML=r.caption)}function n(e,t){t.textContent=e,t.classList.add("show")}function s(e){e.classList.remove("show")}function r(e,t,r,i){r.addEventListener("click",async r=>{if(r.preventDefault(),s(i),!t.value.trim())return n(`${t.placeholder} is required`,i),void t.focus();const a=document.getElementById("token-type-switcher"),o=a?.querySelector(".token-type-option.active"),c=o?.dataset.value||"personal";await async function(e,t,s,r){try{s.classList.add("loading");const i=await fetch("/@api/login",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:e,tokenType:t}),redirect:"follow"}),a=i.url&&new URL(i.url,window.location.origin);if(!a||a.pathname.startsWith("/login")||a.pathname.startsWith("/@api/"))try{const e=await i.json();n(`Login failed: ${e?.error||"Unknown error occurred"}`,r)}catch(e){n("Login failed: Unable to parse response",r)}else window.location.href=i.url}catch(e){n("Login failed: Network error. Please check your connection.",r)}finally{s.classList.remove("loading")}}(t.value.trim(),c,e,i)}),t.addEventListener("keydown",e=>{"Enter"===e.key&&(e.preventDefault(),r.click())}),t.addEventListener("input",()=>{s(i)})}function i(e){const n=e.firstChild;if(!n)return;const s=n.lastChild?.cloneNode(!0);if(!s)return;s.innerHTML="";const i=s.cloneNode(!0);i.innerText="OR",i.setAttribute("role","separator"),i.setAttribute("aria-label","Alternative login method");const a=s.cloneNode(!0),o=document.createElement("div"),c=document.createElement("style");c.textContent="\n.helper-login-wrapper {\n font-family: Arial, sans-serif;\n color: #222;\n padding: 8px;\n background-color: #f8f9fa;\n border-radius: 8px;\n border: 1px solid #ddd;\n width: 100%;\n max-width: 400px;\n margin: 0 auto;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\n}\n\n.helper-login-wrapper .title {\n font-weight: bold;\n font-size: 18px;\n margin-bottom: 12px;\n text-align: center;\n}\n\n.helper-login-wrapper .control {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.helper-login-wrapper .control input {\n border: 1px solid #ddd;\n border-radius: 4px;\n width: 100%;\n box-sizing: border-box;\n text-align: center;\n height: 26px;\n padding: 0 8px;\n font-size: 14px;\n}\n\n.helper-login-wrapper .control input:focus {\n outline: none;\n border-color: #007bff;\n box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.helper-login-wrapper .control .caption {\n font-size: 12px;\n color: #666;\n text-align: center;\n}\n\n.helper-login-wrapper .footer {\n margin-top: 16px;\n text-align: center;\n}\n\n.helper-login-wrapper .footer .btn.submit {\n background-color: #007bff;\n color: white;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n width: 100%;\n font-size: 14px;\n transition: background-color 0.3s;\n height: 26px;\n font-weight: 500;\n}\n\n.helper-login-wrapper .footer .btn.submit:hover {\n background-color: #0056b3;\n}\n\n.helper-login-wrapper .footer .btn.submit:disabled {\n background-color: #6c757d;\n cursor: not-allowed;\n}\n\n.helper-login-wrapper .token-type-switcher {\n display: flex;\n border: 1px solid #ccc;\n border-radius: 4px;\n overflow: hidden;\n margin-bottom: 12px;\n width: 100%;\n}\n\n.helper-login-wrapper .token-type-option {\n padding: 4px 8px;\n cursor: pointer;\n background-color: #f8f9fa;\n color: #666;\n font-size: 12px;\n text-align: center;\n transition: background-color 0.2s, color 0.2s;\n border-right: 1px solid #ccc;\n flex: 1;\n user-select: none;\n}\n\n.helper-login-wrapper .token-type-option:last-child {\n border-right: none;\n}\n\n.helper-login-wrapper .token-type-option.active {\n background-color: #007bff;\n color: white;\n font-weight: bold;\n}\n\n.helper-login-wrapper .token-type-option:not(.active):hover {\n background-color: #e9ecef;\n}\n\n.helper-login-wrapper .error-message {\n color: #dc3545;\n font-size: 12px;\n text-align: center;\n margin-top: 8px;\n display: none;\n}\n\n.helper-login-wrapper .error-message.show {\n display: block;\n}\n\n.helper-login-wrapper .loading {\n opacity: 0.6;\n pointer-events: none;\n}\n",document.head.appendChild(c),o.innerHTML=`\n<div class="helper-login-wrapper" role="form" aria-label="PP Dev Helper Login">\n <div class="title" role="heading" aria-level="2">PP Dev Helper</div>\n\n <div class="token-type-switcher" id="token-type-switcher" role="tablist" aria-label="Token type selection">\n <div class="token-type-option active" data-value="personal" role="tab" aria-selected="true" tabindex="0">\n API Token\n </div>\n <div class="token-type-option" data-value="regular" role="tab" aria-selected="false" tabindex="0">\n Legacy Token\n </div>\n </div>\n\n <div class="control">\n <input \n type="password" \n id="helper-token" \n placeholder="API Token"\n aria-label="Token input"\n aria-describedby="token-caption"\n >\n <span class="caption" id="token-caption">\n You can get the API Token\n <a href="https://${host}/api-token" target="_blank" style="color: #007bff; text-decoration: none;">here</a>\n </span>\n </div>\n\n <div class="error-message" id="error-message" role="alert" aria-live="polite"></div>\n\n <div class="footer">\n <button id="helper-token-submit" class="btn submit" type="button">\n Token Login\n </button>\n </div>\n</div>`,a.appendChild(o),n.appendChild(i),n.appendChild(a);const l=document.getElementById("token-type-switcher"),d=document.getElementById("helper-token"),p=document.getElementById("token-caption"),u=document.getElementById("helper-token-submit"),h=document.getElementById("error-message"),f=o.querySelector(".helper-login-wrapper");l&&d&&p&&u&&h&&f?(!function(e,n,s){const r=e.querySelectorAll(".token-type-option"),i=e=>{const i=e.dataset.value;r.forEach(e=>{e.classList.remove("active"),e.setAttribute("aria-selected","false"),e.setAttribute("tabindex","-1")}),e.classList.add("active"),e.setAttribute("aria-selected","true"),e.setAttribute("tabindex","0"),t(i,n,s)};r.forEach(e=>{e.addEventListener("click",e=>{const t=e.currentTarget;i(t)}),e.addEventListener("keydown",t=>{"Enter"!==t.key&&" "!==t.key||(t.preventDefault(),i(e))})})}(l,d,p),r(f,d,u,h),t("personal",d,p)):console.error("Failed to find required form elements")}const a='#mi-react-root form:not([id]):has(input[name="login"]):has(input[name="password"])',o=new MutationObserver(e=>{for(const t of e)if("childList"===t.type){const e=document.querySelector(a);if(e){o.disconnect(),i(e);break}}});o.observe(document.body,{childList:!0,subtree:!0});const c=document.querySelector(a);c&&(o.disconnect(),i(c))},Y=/^(https?:\/\/)([^/]+)(\/.*)?$/i,Q="X-PP-Proxy";function ee(n){const{rewritePath:s=/^\/(?!p[tl]).*/i,baseURL:r="",disableSSLValidation:i=!1,miAPI:a,templateName:o}=n;if(!r)throw new Error("Base url is required");const c=r.replace(Y,"$2"),l=r.replace(Y,"$1$2"),d=import("file-type"),p=W();return e.createProxyMiddleware({selfHandleResponse:!0,pathFilter:(e,t)=>!(n.proxyIgnore||[]).some(t=>"string"==typeof t?e.startsWith(t):"function"!=typeof t.test||t.test(e))&&("string"==typeof s?e.startsWith(s):Array.isArray(s)?s.some(t=>e.startsWith(t)):"function"==typeof s.test&&s.test(e)),target:r,changeOrigin:!0,autoRewrite:!0,cookieDomainRewrite:{[c]:"localhost"},logger:{info:()=>{},warn:()=>{},error:()=>{}},secure:!i,headers:{host:c,origin:l},on:{proxyReq(e,n,s){const i=n.headers.host,c=e.getHeader("referer");i&&c&&"string"==typeof c&&e.setHeader("referer",c.replace(new RegExp(`https?://${i}`),r)),a.personalAccessToken&&e.setHeader("Authorization",`Bearer ${a.personalAccessToken}`);const l=n.url??"/",d=function(e,n,s,r){if(!n||!s||!r||s===r)return e;try{const n=new t.URL(e,"http://localhost"),i=n.pathname.match(/^\/data\/page\/([^/]+)(\/.*)?$/);if(!i)return e;let a;try{a=decodeURIComponent(i[1])}catch{a=i[1]}if(a!==s)return e;return`/data/page/${r}${i[2]??""}`+n.search+n.hash}catch{return e}}(l,a.v7Features,o,a.internalPageName);return d!==l&&(n.url=d,e.path=d,console.info(`${J.cyan("Rewrites data page to correct internal page name")}: ${J.yellow(l)} ${J.blue("→")} ${J.green(d)}`)),p.info(`${J.blue("Proxies request:")} ${J.green(n.method)} ${n.url} -> ${J.green(e.method)} ${e.protocol}//${e.host}${e.path}`),n.socket.setMaxListeners(0),n.socket.once("close",()=>{setTimeout(()=>{e.destroyed||e.destroy()},200)}),e},proxyRes:(t,n,s)=>{if(t.headers["content-type"]?.includes("text/event-stream")||t.headers["transfer-encoding"]?.includes("chunked")&&"no"===t.headers["x-accel-buffering"]){p.info(`${J.blue("Start streaming for request:")} ${J.green(n.method)} ${n.url}`);const e=async(e,t,n)=>{n.setHeader(Q,1),n.setHeaders(new Map(Object.entries(e.headers))),e.pipe(n)};return e(t,n,s)}const r=e.responseInterceptor(async(e,t,n,s)=>{s.setHeader(Q,1);if(await(await d).fileTypeFromBuffer(e))return e;{let t=e.toString("utf8");try{const e=new URL(n.url??"",`http://${c}`);if(e.searchParams&&e.searchParams.has("proxyRedirect")){const e=function(){const e="pp-dev::redirectCount",t="pp-dev::lastRedirect";let n=+(localStorage.getItem(e)??0);localStorage.getItem(t);Number.isNaN(n)&&(n=0);let s=window.location.href;const r=function(){const i=new URLSearchParams(window.location.search);if(!i.has("proxyRedirect"))return console.debug("No proxyRedirect param. Cleaning up."),void localStorage.removeItem(e);s!==window.location.href?(s=window.location.href,setTimeout(r,n<3?3e3:5e3)):window.location.href=i.get("proxyRedirect"),localStorage.setItem(e,""+ ++n),localStorage.setItem(t,(new Date).toISOString())};setTimeout(r,3e3)};t+=`<script>(${e.toString()})()<\/script>`}e.pathname.startsWith("/login")&&(s.setHeader("Cache-Control","no-cache, no-store, must-revalidate"),s.setHeader("Pragma","no-cache"),s.setHeader("Expires","0"));const r=t.slice(0,100);(r.includes("<html")||r.includes("<body")||r.includes("<head")||r.includes("html>"))&&(t+=`<script>const host = "${c}";\n(${X.toString()})()<\/script>`)}catch{}const r=n.headers.host??"";return((e,t,n)=>{const s=new RegExp(`${e.replace(/\\*\//gi,"\\\\/")}`,"gi"),r=new RegExp(`${e}`,"gi"),i=n.replace(s,t);return i===n?i.replace(r,t):i})("/auth/saml/login","/login",G(c,r,t))}});return r(t,n,s)},error(e,t,n){const s=`Proxy error: "${e.message}" when trying to "${t.method} ${t.url}"\n\n${e.stack}`;p.error(s),n.writable&&n.writeHead(500,{"Content-Type":"text/plain"}),n.end(s)}}})}class te{axios;constructor(e){this.axios=e}async checkAuth(e){return this.axios.get("/data/page/index/auth/info",{headers:Object.assign({},e,{accept:"text/html"}),maxRedirects:0}).then(()=>!0).catch(()=>!1)}}class ne extends te{formdataModulePromise=import("formdata-node");constructor(e){super(e)}getDownloadUrl(e){return`/admin/page/downloadassets/id/${e}`}getDownloadTemplateUrl(e){return`/admin/pagetemplate/downloadassets/id/${e}`}getUploadUrl(e){return`/admin/page/uploadassets/id/${e}`}getUploadTemplateUrl(e){return`/admin/pagetemplate/uploadassets/id/${e}`}async downloadPageAssets(e,t){return this.axios.get(this.getDownloadUrl(e),{withCredentials:!0,headers:Object.assign({},t,{accept:"*/*"}),responseType:"arraybuffer"}).then(e=>e.data)}async uploadPageAssets(e,t,n){const s=new(await this.formdataModulePromise).FormData,{File:r}=await this.formdataModulePromise,i=new r([t],"file.zip",{type:"application/zip"});s.append("file",i);const a=this.getUploadUrl(e);return this.axios.post(a,s,{withCredentials:!0,headers:Object.assign({},n,{accept:"application/json","Content-Type":"multipart/form-data",Referer:this.axios.getUri({url:a})})}).then(e=>e.data)}async downloadTemplateAssets(e,t){return this.axios.get(this.getDownloadTemplateUrl(e),{withCredentials:!0,headers:Object.assign({},t,{accept:"*/*"}),responseType:"arraybuffer"}).then(e=>e.data)}async uploadTemplateAssets(e,t,n){const s=new(await this.formdataModulePromise).FormData,{File:r}=await this.formdataModulePromise,i=new r([t],"file.zip",{type:"application/zip"});s.append("file",i,"file.zip");const a=this.getUploadTemplateUrl(e);return this.axios.post(a,s,{withCredentials:!0,headers:Object.assign({},n,{accept:"application/json","Content-Type":"multipart/form-data",Referer:this.axios.getUri({url:a})})}).then(e=>e.data)}}class se extends ne{getDownloadUrl(e){return`/api/page/id/${e}/asset/download`}getDownloadTemplateUrl(e){return`/api/page_template/id/${e}/asset/download`}getUploadUrl(e){return`/api/page/id/${e}/asset/upload`}getUploadTemplateUrl(e){return`/api/page_template/id/${e}/asset/upload`}}const re="Something went wrong when fetching page data",ie="Something went wrong when fetching page template data",ae=" The instance may be down or in maintenance mode. Please try again later.";function oe(){return new Error(`${ie}.${ae}`)}function ce(e){return e instanceof Error&&(e.message.includes(re)||e.message.includes(ie))}class le extends te{async getAll(e){return(await this.axios.get("/api/page",{withCredentials:!0,headers:Object.assign({},e,{Accept:"application/json","Content-Type":"application/json","Cache-Control":"no-cache",Pragma:"no-cache",Expires:"0"})})).data.pages}async get(e,t){const n=(await this.axios.get(`/api/page/id/${e}`,{withCredentials:!0,headers:Object.assign({},t,{accept:"application/json","content-type":"application/json"})})).data;if("object"!=typeof n||null===n)throw new Error(`${re}.${ae}`);return n.page}async getPageContent(e,t){return(await this.axios.get(`/p/${e}`,{withCredentials:!0,headers:Object.assign({},t,{accept:"text/html","content-type":"application/json"})})).data}async create(e,t){return(await this.axios.post("/api/page",e,{withCredentials:!0,headers:Object.assign({},t,{accept:"application/json","content-type":"application/json"})})).data.page}}class de extends te{async getAll(e,t){const n=(await this.axios.get("/api/page_template",{withCredentials:!0,headers:Object.assign({},t,{Accept:"application/json","Content-Type":"application/json","Cache-Control":"no-cache",Pragma:"no-cache",Expires:"0"}),params:{internal_name:e}})).data;if("object"!=typeof n||null===n||!Array.isArray(n.page_templates))throw oe();return n.page_templates}async get(e,t){const n=(await this.axios.get(`/api/page_template/id/${e}`,{withCredentials:!0,headers:Object.assign({},t,{accept:"application/json","content-type":"application/json"})})).data;if("object"!=typeof n||null===n||"object"!=typeof n.page_template||null===n.page_template)throw oe();return n.page_template}}function pe(e){const t=e.response?.status||0,n=e.response?.data?.message||e.message||"Unknown error";switch(t){case 412:const e=n.toLowerCase();return e.includes("session expired")||e.includes("session has expired")?{status:t,message:n,code:"SESSION_EXPIRED",userFriendlyMessage:"Your session has expired",suggestions:["Refresh your token in the portal","Re-authenticate with the portal","Check if your token has been revoked","Ensure your token has the correct permissions"]}:{status:t,message:n,code:"AUTH_FAILED",userFriendlyMessage:"Authentication failed",suggestions:["Verify your token is correct","Check token permissions","Ensure the token hasn't expired","Try generating a new token"]};case 401:return{status:t,message:n,code:"UNAUTHORIZED",userFriendlyMessage:"Unauthorized access",suggestions:["Check if your token is valid","Verify you have the required permissions","Ensure the token hasn't been revoked","Try logging in again"]};case 403:return{status:t,message:n,code:"FORBIDDEN",userFriendlyMessage:"Access forbidden",suggestions:["Check your user permissions","Verify the portal page ID is correct","Ensure your token has the right scope","Contact your administrator"]};default:return{status:t,message:n,code:"UNKNOWN_ERROR",userFriendlyMessage:"An unexpected error occurred",suggestions:["Check your network connection","Verify the portal URL is correct","Try again in a few moments","Check the portal status"]}}}const ue="[DEV PAGE. DO NOT DELETE]",he=new Map;class fe{#e;#t;#n=null;#s;#r;#i;#a;#o;#c;#l;internalPageName;appId;templateLess;assetsApi;pageApi;pageTemplateApi;logger;constructor(e,t){const{headers:n={},appId:s,templateLess:r=!0,disableSSLValidation:i=!1,v7Features:a=!1,personalAccessToken:o}=t||{};this.#e=n,this.#r=[],this.#s="",this.#i=a,this.#a=o,this.#o=!1,this.#c=Promise.resolve(!1);const c=`${e}:${i}`;if(he.has(c))this.#t=he.get(c);else{i&&(process.env.NODE_TLS_REJECT_UNAUTHORIZED="0");const t=new $.Agent({keepAlive:!1,...i?{rejectUnauthorized:!1}:{}});this.#t=T.create({baseURL:e,headers:n,timeout:3e4,maxRedirects:5,httpAgent:new S.Agent({keepAlive:!1}),httpsAgent:t}),i&&(T.defaults.httpsAgent=t),he.set(c,this.#t)}this.appId=s,this.templateLess=r,this.assetsApi=new(a?se:ne)(this.#t),this.pageApi=new le(this.#t),this.pageTemplateApi=new de(this.#t),this.logger=W()}async isTemplateLoaded(){return this.#c}get isV710OrHigher(){return this.#o}get v7Features(){return this.#i}#d(e){const t=Object.assign({},e,{host:void 0,referer:void 0});return Object.keys(t).forEach(e=>void 0===t[e]&&delete t[e]),this.#e=t,!t.authorization&&this.#a&&(t.authorization=`Bearer ${this.#a}`),t}#p(e){return e.replace(/[.*+?^${}()|[\]\\\/]/g,"\\$&")}get personalAccessToken(){return this.#a}set personalAccessToken(e){this.#a=e}updateHeaders(e){this.#d(e)}get localTemplateHTML(){return'<!DOCTYPE html>\n <html lang="en">\n <head>\n <meta charset="UTF-8" />\n <title>%%PAGE TITLE%%</title>\n <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />\n <meta name="theme-color" content="rgba(255, 255, 255, 1)" />\n <link rel="shortcut icon" type="image/x-icon" sizes="any" href="/img/favicon/favicon.ico" />\n <link rel="icon" type="image/png" sizes="16x16" href="/img/favicon/favicon-16x16.png" />\n <link rel="icon" type="image/png" sizes="32x32" href="/img/favicon/favicon-32x32.png" />\n <link rel="icon" type="image/png" sizes="48x48" href="/img/favicon/favicon-48x48.png" />\n <meta name="msapplication-config" content="/auth/browserconfig.xml" />\n <link rel="apple-touch-icon" sizes="180x180" href="/img/favicon/apple-touch-icon.png" />\n <link rel="manifest" href="/auth/site.webmanifest" />\n <link rel="manifest" href="/auth/manifest.json" />\n <link rel="stylesheet" type="text/css" href="/auth/theme-vars.css" />\n <link rel="icon" type="image/svg+xml" href="/favicon.ico" />\n <meta name="viewport" content="width=device-width, initial-scale=1.0" />\n <script src="/js/libs/underscore-latest.min.js" charset="utf-8"><\/script>\n <script src="/js/jquery/jquery-latest.min.js" charset="utf-8"><\/script>\n <script src="/js/application/rating_component.js" charset="utf-8"><\/script>\n </head>\n <body>\n <div id="mi-react-root"></div>\n <script src="/auth/info.js"><\/script>\n <script src="/js/main.js" defer><\/script>\n <link rel="stylesheet" href="/css/main.css" />\n </body>\n </html>'}async getPageTemplate(e){if(await this.#c||this.#n||(this.#c=new Promise(e=>{this.#l=e})),this.#n)return Promise.resolve(this.#n);if(void 0===this.templateLess&&(this.templateLess=!1),this.#i){const t=await this.pageApi.get(this.appId,this.#d(e)).then(e=>(this.logger.info(J.green("Page fetched")),e)).catch(async t=>{if(this.#l(!1),ce(t))throw t;throw await this.pageApi.checkAuth(this.#d(e))?(this.logger.error(J.red(`Error fetching page data: ${t.message}\n${t.stack}`)),new Error("The current user does not have access to this page. Check your configuration to ensure app.id is correct.")):t});return void 0!==t.template_id&&(this.#o=!0),this.internalPageName=t.internal_name,this.#n=this.localTemplateHTML,this.#n=this.#n.replace(/%%PAGE TITLE%%/g,t.name||"Local template"),this.logger.info(J.green("Local page template fetched")),this.#l(!0),this.#n}let t=(await this.pageApi.getAll(this.#d(e)).then(e=>(this.logger.info(J.green("Page list fetched")),e)).catch(async t=>{throw this.#l(!1),await this.pageApi.checkAuth(this.#d(e))?(this.logger.error(J.red(`Error fetching page list: ${t.message}\n${t.stack}`)),new Error("Current user does not have access to page list")):t})).find(e=>e.name===ue);return t||(this.logger.warn(J.yellow("Creating dev page template...")),t=await this.pageApi.create({enabled:"Y",name:ue,internal_name:"dev-page-template",visible_in_homepage:"Y"},this.#d(e)).then(e=>(this.logger.info(J.green("Dev page created")),e)).catch(e=>{throw this.logger.error(J.red(`Error creating dev page: ${e.message}`)),this.#l(!1),new Error(`Error when creating dev page.\n That can be caused by missing permissions or page with name "${ue}" already exists`)})),await this.pageApi.getPageContent(t.internal_name,this.#d(e)).then(e=>(this.#n=e,e)).then(e=>(this.#l(!0),this.logger.info(J.green("Page template fetched")),e)).catch(e=>{throw this.#l(!1),this.logger.error(J.red(`Error fetching page template: ${e.message}`)),new Error("Error fetching page template")})}async getPageVariables(e,t){return void 0===this.appId&&(this.appId=e,this.templateLess=!1),this.#n=await this.getPageTemplate(t),await this.pageApi.get(e,this.#d(t)).then(e=>{const{tags:t="[]",name:n,template:s}=e;if(s&&t){const e=JSON.parse(t);return this.#r=e,this.#s=n,e}return this.#r=[],this.#s=n,[]}).catch(t=>{if(this.logger.error(J.red(`Error fetching page variables: ${t.message}`)),404===t.response?.status)throw new Error(`Portal Page with id "${e}" not found on instance ${this.#t.getUri()}`);if(401===t.response?.status)throw this.logger.warn(`Unauthorized error: ${t}`),new Error(`Current user does not have access to page with id "${e}" on instance ${this.#t.getUri()}`);throw t})}async getPageInfo(e,t){void 0===this.appId&&(this.appId=e);const n=this.#d(t),s=this.appId;try{return this.#i?await this.#u(s,n):await this.#h(s,n)}catch(e){this.#f(e,s)}}async#u(e,t){const n=await this.pageApi.get(e,t);return this.logger.info(J.green("Page fetched")),void 0!==n.template_id&&(this.#o=!0),this.internalPageName=n.internal_name,n}async#h(e,t){return await this.pageApi.get(e,t)}#f(e,t){if(ce(e))throw e;if(this.#i&&e.message?.includes("access"))throw new Error("The current user does not have access to this page. Check your configuration to ensure app.id is correct.");if(404===e.response?.status)throw new Error(`Portal Page with id "${t}" not found on instance ${this.#t.getUri()}`);throw 401===e.response?.status&&this.logger.error(J.red(`Current user does not have access to page with id "${t}" on instance ${this.#t.getUri()}`)),e}buildPage(e,t=!1){let n="string"==typeof e?e:e.toString("utf-8");for(const e of this.#r){const t=this.#p(e.name),s=new RegExp(`\\[${t}\\]`,"g");n=n.replace(s,()=>e.value)}const s=new o.JSDOM(t?n:this.#n),r="%%PLACEHOLDER%%";if(!t){const e=s.window.document.createElement("div");e.innerHTML=r;const t=s.window.document.querySelector(".main-side");if(t){const n=t.querySelectorAll("script");n.length?t.insertBefore(e,n.item(n.length-1)):t.append(e)}else{const t=s.window.document.createElement("div");t.append(e),s.window.document.body.append(t)}const n=s.window.document.querySelector("head"),i=n.querySelector("title");if(i)i.text=this.#s;else{const e=s.window.document.createElement("title");e.textContent=this.#s,n.appendChild(e)}}return s.serialize().replace(new RegExp(`<div>\\s*${r}\\s*<\\/div>`,"i"),n)}async getAssets(){if(this.appId){if(this.templateLess)return await this.assetsApi.downloadPageAssets(this.appId,this.#e);{const e=await this.pageApi.get(this.appId,this.#e);if(this.#o){const t=await this.pageTemplateApi.get(e.template_id,this.#e);return await this.assetsApi.downloadTemplateAssets(t.id,this.#e)}if(e.template)return await this.assetsApi.downloadTemplateAssets(e.template,this.#e)}}}async updateAssets(e){if(this.appId){if(this.templateLess)return await this.assetsApi.uploadPageAssets(this.appId,e,this.#e);{const t=await this.pageApi.get(this.appId,this.#e);if(this.#o){const n=await this.pageTemplateApi.get(t.template_id,this.#e);return await this.assetsApi.uploadTemplateAssets(n.id,e,this.#e)}if(t.template)return await this.assetsApi.uploadTemplateAssets(t.template,e,this.#e)}}}async validateCredentials(e){try{const t=e||this.#e;return await this.get("/api/user",t,!0),{isValid:!0}}catch(e){const t=pe(e);return{isValid:!1,error:t.userFriendlyMessage,code:t.code}}}async get(e,t,n=!1){const s=n?t:Object.assign({},this.#d(this.#e),t);try{return await this.#t.get(e,{headers:s})}catch(e){if("UNKNOWN_ERROR"!==pe(e).code){const t=pe(e);throw this.logger.error(J.red(`API request failed: ${t.userFriendlyMessage}`)),function(e,t,n){const s=pe(t),r=`[${n}] `;e.error(J.red(`${r}${s.userFriendlyMessage}`)),e.error(J.red(`Status: ${s.status} (${s.code})`)),e.error(J.red(`Details: ${s.message}`)),s.suggestions.length>0&&(e.info(J.yellow("Suggestions:")),s.suggestions.forEach((t,n)=>{e.info(J.yellow(` ${n+1}. ${t}`))}))}(this.logger,e,"API Request"),e.response||(e.response={status:t.status,data:{message:t.message}}),e.tokenErrorInfo=t,e}throw e}}}const me="BUILD-MANIFEST.json";function ge(e,t,n=new Set){const s=l.readdirSync(e,{withFileTypes:!0}),r=[];for(const i of s){const s=B.join(e,i.name),a=B.relative(t,s).replace(/\\/g,"/");i.isDirectory()?r.push(...ge(s,t,n)):i.isFile()&&!n.has(a)&&r.push(s)}return r}function ye(e){const t=l.readFileSync(e);return d.createHash("sha256").update(t).digest("hex")}function ve(e){return e.replace(/^([a-z][a-z\d+.-]*:\/\/)(?:[^/?#@]+(?::[^/?#@]*)?@)/i,"$1")}function be(e){const t=e.trim().replace(/^git\+/,"").replace(/\.git$/i,""),n=t.match(/^([^@]+)@([^:]+):(.+)$/);if(n){const[,,e,t]=n;return ve(`https://${e}/${t.replace(/^\/+/,"")}`)}const s=t.match(/^ssh:\/\/(?:[^@]+@)?([^/:]+)(?::\d+)?\/(.+)$/i);if(s){const[,e,t]=s;return ve(`https://${e}/${t.replace(/^\/+/,"")}`)}const r=t.match(/^git:\/\/([^/]+)\/(.+)$/i);if(r){const[,e,t]=r;return ve(`https://${e}/${t.replace(/^\/+/,"")}`)}return ve(t)}function xe(e){if(!1===e.enabled)return;const{outDir:n,versionFileTemplate:s,packageVersion:r}=e,i=B.resolve(process.cwd(),n);if(!l.statSync(i,{throwIfNoEntry:!1})?.isDirectory())return;const a=(new Date).toISOString(),o=s??"VERSION-v{packageversion}-{currentDate}.json",c=function(e,t,n){return e.replace(/\{packageversion\}/g,t).replace(/\{currentDate\}/g,n)}(o,r,a.replace(/:/g,"-")),p=ge(i,i,new Set([c,me])),h={};for(const e of p){h[B.relative(i,e).replace(/\\/g,"/")]=ye(e)}const f=function(e){const t=Object.keys(e).sort().map(t=>e[t]).join("");return d.createHash("sha256").update(t).digest("hex")}(h),m=function(e){const t=Object.keys(e).sort().map(t=>`${t}:${e[t]}`).join("\n");return d.createHash("sha256").update(t,"utf-8").digest("hex")}(h),g=function(){const e=t.fileURLToPath("undefined"==typeof document?require("url").pathToFileURL(__filename).href:w&&"SCRIPT"===w.tagName.toUpperCase()&&w.src||new URL("plugin-Cm9vRLCu.js",document.baseURI).href);let n=B.dirname(e);for(;;){const e=B.join(n,"package.json"),t=l.statSync(e,{throwIfNoEntry:!1});if(t?.isFile())try{const t=JSON.parse(l.readFileSync(e,"utf-8"));if("string"==typeof t.version)return t.version}catch{}const s=B.dirname(n);if(s===n)return"unknown";n=s}}(),y=function(e){if("string"==typeof e&&e.trim())return e.trim();const t=["GITHUB_HEAD_REF","GITHUB_REF_NAME","CI_COMMIT_REF_NAME","BITBUCKET_BRANCH","BUILD_SOURCEBRANCHNAME","BRANCH_NAME","TRAVIS_BRANCH","CIRCLE_BRANCH","BUILDKITE_BRANCH","VERCEL_GIT_COMMIT_REF"];for(const e of t){const t=process.env[e];if("string"==typeof t&&t.trim())return t.trim()}try{const e=u.execSync("git rev-parse --abbrev-ref HEAD",{encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim();if(e&&"HEAD"!==e)return e}catch{}}(e.packageBranchName),v={schemaVersion:1,version:`v${r}`,date:a,checksum:f,files:h,repositoryUrl:e.packageRepositoryUrl?be(e.packageRepositoryUrl):void 0,branchName:y,helperVersion:g},b=B.join(i,c),x=B.join(i,me),k={schemaVersion:1,manifestType:"pp-dev-build-manifest",generatedAt:a,versionFile:c,versionFileTemplate:o,versionFileSchemaVersion:1,buildFingerprint:m,compat:{versionFileRequired:!0}};l.writeFileSync(b,JSON.stringify(v,null,2),"utf-8"),l.writeFileSync(x,JSON.stringify(k,null,2),"utf-8")}const we="pageName",ke="date",Ee=A.dirname("undefined"!=typeof __filename&&__filename||t.fileURLToPath("undefined"==typeof document?require("url").pathToFileURL(__filename).href:w&&"SCRIPT"===w.tagName.toUpperCase()&&w.src||new URL("plugin-Cm9vRLCu.js",document.baseURI).href)),Te=A.resolve(Ee,"..",".."),Se=A.resolve(Te,"..",".."),$e=A.resolve(Se,".pp-dev-meta"),Be=A.resolve($e,"sync-service.meta.json"),Ae="BUILD-MANIFEST.json",Ie="__template_variables.json";class Pe{backupFolder;backupNameTemplate;dateFormat;pageName;currentMeta=null;distZipFolder;distZipFilename;versionFileTemplate;nextBuild;logger;constructor(e,t){this.pageName=e;const{backupFolder:n=A.resolve(P.cwd(),"backups"),distZipFolder:s=A.resolve(P.cwd(),"dist-zip"),distZipFilename:r=`${this.pageName}.zip`,backupNameTemplate:i=`{${we}}-{${ke}}.zip`,versionFileTemplate:a="VERSION-v{packageversion}-{currentDate}.json",dateFormat:o=e=>e.toISOString().replace(/:/g,"-").replace(/\..*$/,""),nextBuild:c}=t||{};this.backupFolder=n,this.backupNameTemplate=i,this.dateFormat=o,this.distZipFolder=s,this.distZipFilename=r,this.versionFileTemplate=a,this.nextBuild=c,this.syncMeta(),this.logger=W()}async checkMeta(){try{await l.promises.stat($e)}catch{await l.promises.mkdir($e)}try{await l.promises.stat(this.backupFolder)}catch{await l.promises.mkdir(this.backupFolder)}}async readMetaFile(){return await this.checkMeta(),await l.promises.readFile(Be,{encoding:"utf-8"}).catch(()=>"{}")}async writeMetaFile(e){return await this.checkMeta(),await l.promises.writeFile(Be,JSON.stringify(e,null,2),{encoding:"utf-8"})}async syncMeta(){this.currentMeta?await this.writeMetaFile(this.currentMeta):this.currentMeta=JSON.parse(await this.readMetaFile())}async getLatestSavedBackup(){const{lastBackupName:e}=this.currentMeta;if(!e)return null;const t=A.resolve(this.backupFolder,e);try{return await l.promises.stat(t),t}catch{return null}}backupName(e,t=new Date){return this.backupNameTemplate.replace(`{${we}}`,e).replace(`{${ke}}`,this.dateFormat(t))}getBackupMeta(){return this.currentMeta}pathToPosix(e){return e.replace(/\\/g,"/")}async normalizeExtractedRootDir(e){let t=e;for(;;){const e=await l.promises.readdir(t,{withFileTypes:!0});if(1!==e.length||!e[0]?.isDirectory())return t;t=A.join(t,e[0].name)}}async listFilesRecursive(e){const t=await l.promises.readdir(e,{withFileTypes:!0}),n=[];for(const s of t){const t=A.join(e,s.name);s.isDirectory()?n.push(...await this.listFilesRecursive(t)):s.isFile()&&n.push(t)}return n}buildManifestChecksum(e){const t=Object.keys(e).sort().map(t=>e[t]).join("");return I.createHash("sha256").update(t).digest("hex")}buildBackupFingerprint(e){const t=Object.keys(e).sort().map(t=>`${t}:${e[t]}`).join("\n");return I.createHash("sha256").update(t,"utf-8").digest("hex")}escapeRegExp(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}versionFileTemplateMatcher(){const e=/\{[^}]+\}/g,t=[];let n=0,s=null;for(;s=e.exec(this.versionFileTemplate),s;){const[e]=s,r=this.versionFileTemplate.slice(n,s.index);t.push(this.escapeRegExp(r)),t.push("[^/]+"),n=s.index+e.length}const r=this.versionFileTemplate.slice(n);return t.push(this.escapeRegExp(r)),new RegExp(`^${t.join("")}$`,"i")}isTemplateVariablesBasename(e){return e===Ie}async pickTemplateVariablesFromBackupZip(e,t){const n=e.find(e=>A.basename(e)===Ie);if(!n)return null;const s=A.join(t,n),r=await l.promises.readFile(s);return{relativePath:n,content:r,actualHash:I.createHash("sha256").update(r).digest("hex"),manifestHash:null}}async resolveBackupAnalysisFromZip(e){const t=await l.promises.mkdtemp(A.join(C.tmpdir(),"pp-dev-backup-")),n=A.join(t,"backup.zip"),s=A.join(t,"extracted");try{await l.promises.mkdir(s,{recursive:!0}),await l.promises.writeFile(n,e),await L(n,{dir:s});const t=await this.normalizeExtractedRootDir(s),r=(await this.listFilesRecursive(t)).map(e=>this.pathToPosix(A.relative(t,e))),i=r.filter(e=>A.basename(e)===Ae);if(i.length>1)throw new Error(`Expected at most one ${Ae} in backup ZIP, found ${i.length}`);const a=1===i.length?i[0]:null;let o=null;if(a){const e=A.join(t,a);if(o=JSON.parse(await l.promises.readFile(e,"utf-8")),!(o&&"pp-dev-build-manifest"===o.manifestType&&"string"==typeof o.versionFile&&o.versionFile.trim()&&"string"==typeof o.versionFileTemplate&&o.versionFileTemplate.trim()&&"string"==typeof o.buildFingerprint&&o.buildFingerprint.trim()))throw new Error("Build manifest is missing required fields")}let c=null;if(o){const e=this.pathToPosix(o.versionFile.trim()),t=r.filter(t=>t===e),n=r.filter(t=>A.basename(t)===A.basename(e)),s=t.length>0?t:n;if(s.length>1)throw new Error(`Build manifest version file "${o.versionFile}" must resolve to exactly one file in backup ZIP, found ${s.length}`);if(1===s.length&&(c=s[0]),!0===o.compat?.versionFileRequired&&null===c)throw new Error(`Build manifest requires version file "${o.versionFile}", but it is missing in backup ZIP`)}else{const e=this.versionFileTemplateMatcher(),t=r.filter(t=>e.test(A.basename(t)));if(t.length>1)throw new Error(`Expected exactly one version file matching template "${this.versionFileTemplate}" in backup ZIP, found ${t.length}`);1===t.length&&(c=t[0])}const d={};let p=null,u=null;const h={};let f=[];if(c){const e=A.join(t,c),n=JSON.parse(await l.promises.readFile(e,"utf-8"));if(!n?.files||"object"!=typeof n.files)throw new Error("VERSION manifest files map is missing or invalid");if("string"!=typeof n.checksum||!n.checksum)throw new Error("VERSION manifest checksum is missing or invalid");u=n.checksum;for(const[e,t]of Object.entries(n.files)){if("string"!=typeof t||!t.trim())throw new Error(`VERSION manifest contains invalid hash for file: ${e}`);d[this.pathToPosix(e)]=t}if(this.buildManifestChecksum(d)!==n.checksum)throw new Error("VERSION manifest checksum does not match its files map");for(const[e,n]of Object.entries(d)){const s=A.join(t,e),r=await l.promises.readFile(s).catch(()=>null);if(!r)throw new Error(`VERSION manifest references missing file: ${e}`);const i=I.createHash("sha256").update(r).digest("hex");h[e]=i,i!==n&&f.push({relativePath:e,expected:n,actual:i}),this.isTemplateVariablesBasename(A.basename(e))&&(p={relativePath:e,content:r,actualHash:i,manifestHash:n})}f.length>0&&this.logger.warn(J.yellow(`[DistService] VERSION manifest hash mismatch for ${f.length} file(s) (confirm in UI): ${f.map(e=>e.relativePath).join(", ")}`))}const m=new Set(f.map(e=>e.relativePath)),g={},y=[];for(const e of r){if(e===a)continue;if(c&&e===c)continue;if(c&&Object.prototype.hasOwnProperty.call(g,e))continue;const n=A.join(t,e),s=await l.promises.readFile(n),r=I.createHash("sha256").update(s).digest("hex");c&&Object.prototype.hasOwnProperty.call(d,e)?g[e]=m.has(e)?h[e]:d[e]:c?(g[e]="__NOT_IN_VERSION_FILE__",y.push(e)):g[e]=r}p||(p=await this.pickTemplateVariablesFromBackupZip(r,t));const v=this.buildBackupFingerprint(g);let b=null;return o&&c&&v!==o.buildFingerprint&&(b={expected:o.buildFingerprint,actual:v},this.logger.warn(J.yellow(`[DistService] Build manifest fingerprint mismatch (confirm in UI): expected ${o.buildFingerprint}, got ${v}`))),{backupFingerprint:v,unknownFiles:y,templateVariables:p,mainHash:u,allFilesInMeta:Boolean(c&&0===y.length),buildManifestMismatch:b,versionManifestHashMismatches:f}}finally{await l.promises.rm(t,{recursive:!0,force:!0})}}async analyzeBackup(e){if("PK"!==e.toString("utf-8").slice(0,4))throw new Error("Backup file is not a ZIP file");return await this.resolveBackupAnalysisFromZip(e)}async getPublicTemplateVariablesHash(){const e=A.resolve(P.cwd(),"public",Ie),t=await l.promises.readFile(e).catch(()=>null);return t?I.createHash("sha256").update(t).digest("hex"):null}async saveTemplateVariablesFile(e){const t=A.resolve(P.cwd(),"public",Ie);return await l.promises.mkdir(A.dirname(t),{recursive:!0}),await l.promises.writeFile(t,e),t}async saveBackup(e,t){const n=t??await this.analyzeBackup(e),s=n.backupFingerprint;if(await this.getLatestSavedBackup()){const{lastBackupHash:e,lastMainHash:t}=this.currentMeta;if(e===s)return void this.logger.info(J.yellow(`[DistService] Backup skipped: same backup fingerprint as last saved (${s.slice(0,12)}...)`));if(n.allFilesInMeta&&n.mainHash&&t===n.mainHash)return void this.logger.info(J.yellow("[DistService] Backup skipped: same VERSION manifest checksum (main hash) and all files listed in VERSION"))}const r=new Date,i=this.backupName(this.pageName,r);return this.currentMeta.lastBackupName=i,this.currentMeta.lastBackupHash=s,this.currentMeta.lastBackupDate=r.toISOString(),n.mainHash?this.currentMeta.lastMainHash=n.mainHash:delete this.currentMeta.lastMainHash,await this.syncMeta(),await l.promises.writeFile(A.resolve(this.backupFolder,i),e).finally(()=>{this.logger.info(`Backup saved to ${i}`)})}async buildNewAssets(){if(this.nextBuild)return await this.buildNextAssets();const e=new Promise((e,t)=>{let n="";this.logger.info(J.cyan("[DistService] Build started"));const s=R.spawn("node",[A.resolve(Te,"./bin/pp-dev.js"),"build"],{cwd:P.cwd(),env:Object.assign({},P.env,{NODE_ENV:"production"}),stdio:"inherit"});s.on("message",e=>{n+=e}),s.on("close",s=>{0===s?e(n):t(new Error(`build command exited with code ${s}`))}),s.on("error",e=>{t(e)})});try{await e.finally(()=>{this.logger.info(J.cyan("[DistService] Build finished"))});const t=A.resolve(P.cwd(),this.distZipFolder,this.distZipFilename);if(!await l.promises.stat(t))throw new Error(`File ${t} not found`);return await l.promises.readFile(t)}catch(e){throw this.logger.error(e instanceof Error?e.stack??e.message:String(e)),e}}async saveBackupAndBuild(e){return await this.saveBackup(e),await this.buildNewAssets()}async buildNextAssets(){const{projectRoot:e,distDir:t}=this.nextBuild;this.logger.info(J.cyan("[DistService] Next.js build started")),await this.runNextBuild(e),this.logger.info(J.cyan("[DistService] Next.js build finished"));const n=A.resolve(e,t);if(!await this.isDirectory(n))throw new Error(`Next.js build output directory not found: ${n}`);return xe({outDir:n,packageVersion:this.nextBuild.packageVersion,versionFileTemplate:this.versionFileTemplate,packageRepositoryUrl:this.nextBuild.packageRepositoryUrl,packageBranchName:this.nextBuild.packageBranchName}),await this.zipDirectory(n)}runNextBuild(e){return new Promise((t,n)=>{let s;try{const t=f.createRequire(A.join(e,"package.json")).resolve("next/package.json");s=A.resolve(A.dirname(t),"dist/bin/next")}catch{return void n(new Error(`Unable to resolve the "next" binary from ${e}. Is Next.js installed?`))}const r=R.spawn(P.execPath,[s,"build"],{cwd:e,env:Object.assign({},P.env,{NODE_ENV:"production"}),stdio:"inherit"});r.on("close",e=>{0===e?t():n(new Error(`next build exited with code ${e}`))}),r.on("error",n)})}async zipDirectory(e){const t=new F,n=async s=>{const r=await l.promises.readdir(s,{withFileTypes:!0});for(const i of r){const r=A.join(s,i.name);if(i.isDirectory())await n(r);else if(i.isFile()){const n=A.relative(e,r).replace(/\\/g,"/");t.file(n,await l.promises.readFile(r))}}};return await n(e),await t.generateAsync({type:"nodebuffer",compression:"DEFLATE"})}async isDirectory(e){try{return(await l.promises.stat(e)).isDirectory()}catch{return!1}}}class Re{server;opts;eventMap;logger;syncActionResolvers=new Map;constructor(e,t){this.server=e,this.opts=t||{},this.eventMap=new Map,this.eventMap.set("info-data:request",this.onInfoDataRequest.bind(this)),this.eventMap.set("template:sync",this.onTemplateSync.bind(this)),this.eventMap.set("template:sync:action-response",this.onTemplateSyncActionResponse.bind(this)),this.logger=W(),this.init()}init(){const{ws:e}=this.server;for(const[t,n]of this.eventMap)e.on(t,n);e.on("close",()=>{this.clearAllPendingSyncActions(!1)}),e.on("error",()=>{this.clearAllPendingSyncActions(!1)})}resolveSyncAction(e,t){const n=this.syncActionResolvers.get(e);n&&(clearTimeout(n.timeoutId),this.syncActionResolvers.delete(e),n.resolve(t))}clearAllPendingSyncActions(e){for(const t of[...this.syncActionResolvers.keys()])this.resolveSyncAction(t,e)}onInfoDataRequest(e,t){t.send("info-data:response",{})}onTemplateSyncActionResponse(e){e&&"string"==typeof e.requestId&&"boolean"==typeof e.approved&&this.resolveSyncAction(e.requestId,e.approved)}async requestSyncAction(e,t){const n=d.randomUUID(),s=this.opts.syncActionTimeoutMs??12e4;return await new Promise(r=>{const i=setTimeout(()=>{this.resolveSyncAction(n,!1)},s);this.syncActionResolvers.set(n,{resolve:r,timeoutId:i}),t.send("template:sync:action-required",{...e,requestId:n})})}async onTemplateSync(e,t){if(!this.opts.distService||!this.opts.miAPI)return t.send("template:sync:response",{error:"Dist service or MiAPI is not defined"}),void this.logger.error(J.red("Dist service or MiAPI is not defined"));{const{distService:e,miAPI:n}=this.opts;try{if(this.server.config.clientInjectionPlugin?.v7Features){if(!n?.isV710OrHigher)return void t.send("template:sync:response",{error:"This feature is available only for MI v7.1.0 or higher",config:{canSync:!1}});t.send("client:config:update",{config:{canSync:!0}})}const s=await(n?.getAssets().catch(async e=>{if(r.isAxiosError(e)){if("SESSION_EXPIRED"===pe(e).code){this.logger.info(J.yellow("Session expired - attempting to validate credentials"));try{const e=await(n?.validateCredentials());e&&!e.isValid?(this.logger.error(J.red(`Authentication error: ${e.error}`)),t.send("template:sync:response",{error:e.error,code:e.code,refresh:!0})):(this.logger.info(J.yellow("Session expired")),t.send("template:sync:response",{error:"Session expired",code:"SESSION_EXPIRED",refresh:!0}))}catch(e){this.logger.info(J.yellow("Session expired")),t.send("template:sync:response",{error:"Session expired",code:"SESSION_EXPIRED",refresh:!0})}return e}if(e.cause instanceof Error&&("ECONNRESET"===e.cause.code||"ENOTFOUND"===e.cause.code))return this.logger.info(J.yellow("Server in maintenance mode, VPN connection is needed or no internet connection")),t.send("template:sync:response",{error:"Server in maintenance mode, VPN connection is needed or no internet connection",code:"CONNECTION_ERROR"}),e;const s=e.response?.status;if(502===s||503===s||504===s)return this.logger.info(J.yellow(`Server in maintenance mode or unreachable (HTTP ${s}); VPN may be required`)),t.send("template:sync:response",{error:"Server in maintenance mode, VPN connection is needed or no internet connection",code:"CONNECTION_ERROR"}),e}if(ce(e))return this.logger.info(J.yellow("Server in maintenance mode or non-JSON page / page-template API response (unavailable HTML body)")),t.send("template:sync:response",{error:"Server in maintenance mode, VPN connection is needed or no internet connection",code:"CONNECTION_ERROR"}),e;throw e}));if(s instanceof Error)return;let i;if(s){const n=await(e?.analyzeBackup(s).catch(e=>"Backup file is not a ZIP file"===e.message?(this.logger.error(J.red("Backup file is not a ZIP file")),e):e));if(n instanceof Error)i=n;else if(n){await(e?.saveBackup(s,n));const r=0===n.unknownFiles.length&&1===n.versionManifestHashMismatches.length&&A.basename(n.versionManifestHashMismatches[0].relativePath)===Ie,a=n.versionManifestHashMismatches,o=a.filter(e=>A.basename(e.relativePath)===Ie),c=a.filter(e=>A.basename(e.relativePath)!==Ie),l=!r&&o.length>0&&c.length>0,d=r?[]:c.map(e=>e.relativePath),p=async()=>{if(!n.templateVariables||!e)return;const{templateVariables:s}=n;if(await e.getPublicTemplateVariablesHash()!==s.actualHash){await this.requestSyncAction({title:"Template variables (server backup)",content:"The server backup includes __template_variables.json. It differs from your local public/__template_variables.json (or that file is missing). Replace your project copy with the server backup?",confirmText:"Replace from server",cancelText:"Keep local"},t)&&await e.saveTemplateVariablesFile(s.content)}};if(n.unknownFiles.length>0){if(!await this.requestSyncAction({title:"Unknown files found in backup",content:`Backup contains files not listed in VERSION: ${n.unknownFiles.join(", ")}. Continue sync or cancel?`,confirmText:"Continue sync",cancelText:"Cancel sync"},t))return t.send("template:sync:response",{cancelled:!0,message:"Sync cancelled by user. Backup was saved."}),void this.logger.info(J.yellow("Sync cancelled by user"))}if(l&&await p(),!r&&d.length>0){const e=40,n=d,s=n.slice(0,e),r=n.length-s.length,i=s.map(e=>`• ${e}`).join("\n"),a=r>0?`\n... and ${r} more file${1===r?"":"s"}`:"";if(!await this.requestSyncAction({title:"VERSION manifest out of date",content:`These files no longer match the hashes recorded in the VERSION manifest:\n\n${i}${a}\n\nCancel sync, or override and continue using the files on disk?`,confirmText:"Override and continue",cancelText:"Cancel sync"},t))return t.send("template:sync:response",{cancelled:!0,message:"Sync cancelled by user. Backup was saved."}),void this.logger.info(J.yellow("Sync cancelled by user"))}if(!r&&n.buildManifestMismatch&&0===n.versionManifestHashMismatches.length){const{expected:e,actual:s}=n.buildManifestMismatch;if(!await this.requestSyncAction({title:"Build manifest fingerprint mismatch",content:`The recomputed backup fingerprint does not match BUILD-MANIFEST.json.\nManifest: ${e.slice(0,12)}...\nComputed: ${s.slice(0,12)}...\nContinue sync anyway?`,confirmText:"Continue sync",cancelText:"Cancel sync"},t))return t.send("template:sync:response",{cancelled:!0,message:"Sync cancelled by user. Backup was saved."}),void this.logger.info(J.yellow("Sync cancelled by user"))}l||await p(),i=await(e?.buildNewAssets())}}else i=await(e?.buildNewAssets());if(!(i&&i instanceof Buffer))return i instanceof Error?(t.send("template:sync:response",{error:i.message}),void this.logger.error(J.red(i.message))):(t.send("template:sync:response",{error:"Failed to build new assets"}),void this.logger.error(J.red("Failed to build new assets")));{const s=await(n?.updateAssets(i));if("OK"===s?.status){const n=e?.getBackupMeta(),{lastBackupName:s,lastBackupHash:r,lastBackupDate:i}=n||{lastBackupName:"",lastBackupHash:"",lastBackupDate:(new Date).toISOString()};t.send("template:sync:response",{syncedAt:new Date(i),currentHash:r,backupFilename:s}),this.logger.info(J.green("Template synced"))}else t.send("template:sync:response",{error:"Failed to update assets"}),this.logger.error(J.red("Failed to update assets"))}}catch(e){const n=e instanceof Error?e.message:"Template sync failed";return this.logger.error(J.red(`Template sync failed: ${n}`)),void t.send("template:sync:response",{error:n,code:"SYNC_FAILED"})}}}}const Ce=/\.(?:[a-z0-9]+)$/i,Le=6e5,Fe=104857600,je=1e3;class He extends j.Cache{totalSize=0;maxSize;maxItems;constructor(e,t){super(),this.maxSize=e,this.maxItems=t}put(e,t,n){const s=this.get(e);s&&(this.totalSize-=s.size);const r=super.put(e,t,n);return this.totalSize+=t.size,this.cleanup(),r}cleanup(){const e=this.keys();if(e.length>this.maxItems){const t=e.length-this.maxItems,n=e.sort((e,t)=>{const n=this.get(e),s=this.get(t);return(n?.timestamp||0)-(s?.timestamp||0)});for(let e=0;e<t;e++){const t=n[e],s=this.get(t);s&&(this.totalSize-=s.size,this.del(t))}}for(;this.totalSize>this.maxSize;){const e=this.keys();if(0===e.length)break;const t=e.sort((e,t)=>{const n=this.get(e),s=this.get(t);return(n?.timestamp||0)-(s?.timestamp||0)})[0],n=this.get(t);if(!n)break;this.totalSize-=n.size,this.del(t)}}getTotalSize(){return this.totalSize}getItemCount(){return this.keys().length}}const qe=new He(Fe,je);function Ne(e){if(0===e.length)return Buffer.alloc(0);if(1===e.length)return e[0];const t=e.reduce((e,t)=>Buffer.byteLength(t)+e,0);return Buffer.concat(e,t)}function De(e){const{devServer:t,ttl:n=Le,maxSize:s=Fe,maxItems:r=je}=e,i=W();return s===Fe&&r===je||(qe.maxSize=s,qe.maxItems=r),t.cache=qe,(e,t,s)=>{const r=e.originalUrl||e.url||"",a=function(e){const t=e.split("?")[0];return Ce.test(t)?t.includes("/auth/info.js")?"":e:""}(r);if(!a)return s();const o=qe.get(a);if(o)return i.info(`${J.blue("Proxies request:")} ${J.green(e.method)} ${r} -> ${J.blue("Cache")} ${a}`),function(e,t){try{for(const[n,s]of Object.entries(t))null!=s&&e.setHeader(n,s)}catch(e){W().warn(`Failed to set some response headers: ${e instanceof Error?e.message:String(e)}`)}}(t,o.headers),t.setHeader("X-PP-Cache","hit"),t.write(o.content),void t.end();const c=t.end,l=t.write,d=[];t.write=function(e,n,s){if(!t.hasHeader(Q))return l.call(this,e,n,s);if("string"==typeof e){const t=n||"utf8";d.push(Buffer.from(e,t))}else Buffer.isBuffer(e)?d.push(e):null!=e&&d.push(Buffer.from(String(e),"utf8"));return s?.(),!0},t.end=function(e,s,r){if(!t.hasHeader(Q))return c.call(this,e,s,r);if(null!=e)if("string"==typeof e){const t=s||"utf8";d.push(Buffer.from(e,t))}else Buffer.isBuffer(e)?d.push(e):d.push(Buffer.from(String(e),"utf8"));let o;try{o=Ne(d);const e=Buffer.byteLength(o);if(e>0&&e<10485760){const s={headers:t.getHeaders(),content:o,timestamp:Date.now(),size:e};qe.put(a,s,n),i.info(`${J.blue("[Cached]")} ${a} (${(e/1024).toFixed(1)}KB)`)}}catch(e){i.error(`Failed to cache response for ${a}: ${e}`),o=Ne(d)}const l=s||"utf8";return c.call(this,o,l,r)},s()}}function Oe(e,t){return async(n,s,r)=>{if(await e(n.url??"",n,s)){W().info(`Rewrite response for ${n.url}`);const e=s.end,r=[];s.write=function(e){return r.push(e),!0},s.end=function(i,a,o){"string"==typeof i&&r.push(Buffer.from(i));const c=s.getHeader("content-encoding"),l=function(e,t){switch(t){case"gzip":return v.unzipSync(e);case"br":return v.brotliDecompressSync(e);case"deflate":return v.inflateSync(e);default:return e}}(Buffer.from(Buffer.concat(r)),c),d="function"!=typeof a&&a?a:"utf-8",p="function"==typeof a?a:o;return e.call(this,function(e,t){switch(t){case"gzip":return v.gzipSync(e);case"br":return v.brotliCompressSync(e);case"deflate":return v.deflateSync(e);default:return e}}(t(l,n,s),c),d,p),this}}r()}}function Me(e,n){const s=(e=(e=e.startsWith("/")?e:`/${e}`).endsWith("/")?e:`${e}/`).endsWith("/")?e.slice(0,-1):e,r=W();return(i,a,o)=>{const c=new t.URL(i.url??"","http://localhost"),{pathname:l,search:d}=c,p=["/",s];if(n&&(p.push(`/pt/${n}`),p.push(`/pl/${n}`)),p.includes(l)){const t=`${e}${d}`;return r.info(J.yellow(`Redirecting to: ${t}`)),K(a,t,302)}o()}}class Ue{state={isAuthenticated:!1,isRedirected:!1,lastChecked:0};listeners=new Set;getState(){return{...this.state}}isAuthenticated(){return this.state.isAuthenticated}isRedirected(){return this.state.isRedirected}setAuthenticated(e){this.state.isAuthenticated=e,this.state.lastChecked=Date.now(),this.notifyListeners()}setRedirected(e){this.state.isRedirected=e,this.notifyListeners()}updateState(e){void 0!==e.isAuthenticated&&(this.state.isAuthenticated=e.isAuthenticated),void 0!==e.isRedirected&&(this.state.isRedirected=e.isRedirected),this.state.lastChecked=Date.now(),this.notifyListeners()}reset(){this.state={isAuthenticated:!1,isRedirected:!1,lastChecked:0},this.notifyListeners()}subscribe(e){return this.listeners.add(e),()=>{this.listeners.delete(e)}}notifyListeners(){this.listeners.forEach(e=>{try{e(this.getState())}catch(e){console.error("Error in auth state listener:",e)}})}}const _e=new Ue,ze=new Map;function Ve(e){const t=ze.get(e);return t?Date.now()-t.timestamp>18e4?(ze.delete(e),null):t.data:null}function Ze(e,t){ze.set(e,{timestamp:Date.now(),data:t})}const We="/home?proxyRedirect=";function Je(e,t,n){const{templateLess:s=!1,miHudLess:r=!1,appId:i,base:a,appBase:o,v7Features:c}=n,l=W();if(s&&r&&void 0===i)throw new Error("Custom App ID is required when both templateLess and miHudLess are true");return async(n,d,p)=>{try{const u=!(s&&r),h=function(e){return e.split("?")[0]}(n.url??""),f=e.test(h),m=!f&&!h.startsWith("/home")&&function(e,t){if(!t||"/"===t)return!0;const n=t.endsWith("/")?t:`${t}/`;return e===n||e===n.slice(0,-1)||e.startsWith(n)}(h,o)&&function(e){const t=e.headers["sec-fetch-dest"];if("string"==typeof t)return"document"===t;const n=e.headers.accept;return"string"==typeof n&&n.includes("text/html")}(n),g=_e.getState();if(!g.isAuthenticated&&!g.isRedirected&&n.url?.startsWith("/home")&&!n.url?.startsWith(We)){l.info(J.blue("Trying to authenticate and redirect to base"));try{return u?await Ke(t,{templateLess:s,miHudLess:r,appId:i,redirectUrl:c&&a?`${a}`:`${We}${encodeURIComponent("/")}`},n,d,()=>{},l):await Ge(t,{templateLess:s,miHudLess:r,appId:i,redirectUrl:c&&a?`${a}`:`${We}${encodeURIComponent("/")}`},n,d,()=>{},l),_e.setRedirected(!0),l.info(J.blue("Successfully authenticated. Redirecting to base")),K(d,a??"/",302)}catch(e){return p()}}if(!f&&!m)return p();if(!u){return await Ge(t,{templateLess:s,miHudLess:r,appId:i,redirectUrl:c&&a?`${a}`:`${We}${encodeURIComponent("/")}`},n,d,p,l)}return await Ke(t,{templateLess:s,miHudLess:r,appId:i,redirectUrl:c&&a?`${a}`:`${We}${encodeURIComponent("/")}`},n,d,p,l)}catch(e){return l.error(J.red(`Unexpected error in load-pp-data middleware: ${e instanceof Error?e.message:String(e)}`)),_e.reset(),p(e)}}}async function Ge(e,t,n,s,r,i){const{appId:a,redirectOnAuthFailure:o,redirectUrl:c}=t;if(void 0===a){const e=new Error("Custom App ID is required for page info only mode");return i.error(J.red(e.message)),r(e)}const l=n.headers??{};i.info(J.green("Start loading page info"));try{const t=`pageInfo:${a}`;return Ve(t)?(i.info(J.green("Page info loaded from cache")),r()):(await e.getPageInfo(a,l),i.info(J.blue("Clearing proxy cache after successful login")),qe.clear(),Ze(t,{success:!0}),_e.updateState({isAuthenticated:!0}),i.info(J.green("Page info loaded successfully")),r())}catch(e){return _e.reset(),Xe(e,o??!0,c||`${We}${encodeURIComponent("/")}`,s,r,i,"Page info")}}async function Ke(e,t,n,s,r,i){const{templateLess:a,appId:o,redirectOnAuthFailure:c,redirectUrl:l}=t,d=n.headers??{};i.info(J.green("Start loading page data"));try{const t=`pageData:${a}:${o}`;if(Ve(t))return i.info(J.green("Page data loaded from cache")),r();const n=a||void 0===o?e.getPageTemplate(d):e.getPageVariables(o,d);return await n,i.info(J.blue("Clearing proxy cache after successful login")),qe.clear(),Ze(t,{success:!0}),i.info(J.green("Page data loaded successfully")),r()}catch(e){return _e.reset(),Xe(e,c??!0,l||`${We}${encodeURIComponent("/")}`,s,r,i,"Page data")}}function Xe(e,t,n,s,r,i,a){if(function(e){return null!==e&&"object"==typeof e&&"response"in e&&void 0!==e.response}(e)&&(i.info(J.red(`${a} loading failed. Not authorized`)),t)){const e=`${n}`;return i.info(J.yellow(`Redirecting to: ${e}`)),K(s,e,302)}const o=e instanceof Error?e.message:String(e);return i.info(J.red(`${a} loading failed. Error: ${o}`)),r(e)}function Ye(){const e=H();return e.use(H.json()),e.use(H.urlencoded({extended:!0})),e}const Qe=Ye();class et{entries=new Map;insertOrder=[];totalSize=0;maxMemory;constructor(e=1073741824){this.maxMemory=e}allocateId(){return x.randomUUID()}add(e){let t=e,n=(e.requestBody?.byteLength??0)+(e.responseBody?.byteLength??0);n>this.maxMemory&&(t={...e,requestBody:null,responseBody:null,requestBodyTruncated:e.requestBodyTruncated||null!==e.requestBody,responseBodyTruncated:e.responseBodyTruncated||null!==e.responseBody},n=0);const s=n+512;for(;this.totalSize+s>this.maxMemory&&this.insertOrder.length>0;){const e=this.insertOrder.shift(),t=this.entries.get(e);t&&(this.totalSize-=(t.requestBody?.byteLength??0)+(t.responseBody?.byteLength??0)+512,this.entries.delete(e))}this.entries.set(t.id,t),this.insertOrder.push(t.id),this.totalSize+=s}get(e){return this.entries.get(e)}list(e){let t=this.insertOrder.slice().map(e=>{const t=this.entries.get(e);return{id:t.id,timestamp:t.timestamp,method:t.method,url:t.url,statusCode:t.statusCode,duration:t.duration,source:t.source,requestSize:t.requestBody?.byteLength??0,responseSize:t.responseBody?.byteLength??0,requestBodyTruncated:t.requestBodyTruncated,responseBodyTruncated:t.responseBodyTruncated}});if(e?.method&&(t=t.filter(t=>t.method===e.method.toUpperCase())),e?.search){const n=e.search.toLowerCase();t=t.filter(e=>e.url.toLowerCase().includes(n))}const n=Number.isFinite(e?.limit)?e.limit:t.length;if(void 0===e?.offset)return t.slice(Math.max(0,t.length-n));const s=Number.isFinite(e.offset)?e.offset:0;return t.slice(s,s+n)}clear(){this.entries.clear(),this.insertOrder=[],this.totalSize=0}get size(){return this.entries.size}get memoryUsage(){return this.totalSize}}const tt=["/@pp-dev/","/@api/","/_next/","/__nextjs_","/@vite","/@metricinsights","/@"],nt=new Set(["/favicon.ico","/installHook.js.map"]);function st(e,t=10485760){return(n,s,r)=>{const i=n.url||"/",a=i.split("?")[0];if(tt.some(e=>i.startsWith(e))||nt.has(a))return void r();const o=e.allocateId(),c=Date.now(),l=[];let d=0,p=!1;const u=n.emit.bind(n);n.emit=(e,...n)=>{if("data"===e&&!p){const e=n[0],s=Buffer.isBuffer(e)?e:Buffer.from(String(e));d+=s.byteLength,d<=t?l.push(s):(p=!0,l.length=0)}return u(e,...n)};const h=[];let f=0,m=!1;const g=s.write.bind(s),y=s.end.bind(s);function v(e,n){if(!m&&null!=e)try{const s=Buffer.isBuffer(e)?e:"string"==typeof e?Buffer.from(e,n):ArrayBuffer.isView(e)?Buffer.from(e.buffer,e.byteOffset,e.byteLength):e instanceof ArrayBuffer?Buffer.from(e):Buffer.from(String(e));f+=s.byteLength,f<=t?h.push(s):(m=!0,h.length=0)}catch{}}function b(e){return"string"==typeof e[0]?e[0]:void 0}function x(){const e=s.getHeaders();return e["x-pp-proxy"]?"hit"===e["x-pp-cache"]?"proxy-cache":"proxy":"local"}let w=!1;s.write=function(e,...t){return v(e,b(t)),g(e,...t)},s.end=function(t,...r){return"function"!=typeof t&&v(t,b(r)),function(){if(w)return;w=!0;const t=!p&&l.length>0?Buffer.concat(l):null,r=!m&&h.length>0?Buffer.concat(h):null;e.add({id:o,timestamp:c,method:n.method||"GET",url:i,statusCode:s.statusCode??null,duration:Date.now()-c,source:x(),requestHeaders:n.headers,requestBody:t,requestBodyTruncated:p,responseHeaders:s.getHeaders(),responseBody:r,responseBodyTruncated:m})}(),y(t,...r)},r()}}const rt="/@pp-dev/inspector";let it,at=!1;function ot(e,t,n=10485760){it={store:t,captureLimit:n},at||(at=!0,e.get("/@api/requests",(e,t)=>{const{store:n}=it,{limit:s,offset:r,method:i,search:a}=e.query,o=void 0!==s?parseInt(s,10):void 0,c=n.list({limit:Number.isFinite(o)?o:500,offset:void 0!==r&&Number.isFinite(parseInt(r,10))?parseInt(r,10):void 0,method:i,search:a});t.json({requests:c,total:n.size,memoryUsage:n.memoryUsage,maxMemory:n.maxMemory})}),e.get("/@api/requests/stats",(e,t)=>{const{store:n}=it;t.json({total:n.size,memoryUsage:n.memoryUsage,maxMemory:n.maxMemory,memoryUsageFormatted:lt(n.memoryUsage),maxMemoryFormatted:lt(n.maxMemory)})}),e.get("/@api/requests/:id",(e,t)=>{const n=it.store.get(e.params.id);if(!n)return void t.status(404).json({error:"Not found",id:e.params.id});const s=ct(n.requestHeaders["content-type"]),r=ct(n.responseHeaders["content-type"]);t.json({id:n.id,timestamp:n.timestamp,method:n.method,url:n.url,statusCode:n.statusCode,duration:n.duration,source:n.source,requestHeaders:n.requestHeaders,requestBody:n.requestBody?n.requestBody.toString("base64"):null,requestBodySize:n.requestBody?.byteLength??0,requestBodyTruncated:n.requestBodyTruncated,requestContentType:s,responseHeaders:n.responseHeaders,responseBody:n.responseBody?n.responseBody.toString("base64"):null,responseBodySize:n.responseBody?.byteLength??0,responseBodyTruncated:n.responseBodyTruncated,responseContentType:r})}),e.delete("/@api/requests",(e,t)=>{it.store.clear(),t.json({ok:!0})}),e.get(rt,(e,t)=>{t.setHeader("Content-Type","text/html; charset=utf-8"),t.setHeader("Cache-Control","no-cache"),t.end(function(e){return`<!DOCTYPE html>\n<html lang="en">\n<head>\n<meta charset="utf-8">\n<meta name="viewport" content="width=device-width,initial-scale=1">\n<title>Request Inspector — pp-dev</title>\n<style>\n*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}\n:root{\n --bg:#1a1a1e;--bg2:#222228;--bg3:#2a2a32;--bg4:#32323c;\n --border:#3a3a46;--border2:#4a4a58;\n --text:#e0e0f0;--text2:#a0a0b8;--text3:#606078;\n --accent:#6e8efb;--accent2:#a78bfa;\n --green:#4ade80;--red:#f87171;--yellow:#fbbf24;--blue:#60a5fa;--orange:#fb923c;--purple:#c084fc;\n --font-mono:'Cascadia Code','Fira Code','JetBrains Mono',Consolas,monospace;\n --font-ui:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;\n}\nhtml,body{height:100%;overflow:hidden;background:var(--bg);color:var(--text);font-family:var(--font-ui);font-size:13px;line-height:1.5}\n\n/* ── Layout ── */\n.app{display:flex;flex-direction:column;height:100vh}\n.toolbar{display:flex;align-items:center;gap:8px;padding:8px 12px;background:var(--bg2);border-bottom:1px solid var(--border);flex-shrink:0;flex-wrap:wrap}\n.toolbar-title{font-weight:600;font-size:14px;color:var(--text);margin-right:4px}\n.toolbar-badge{font-size:11px;padding:2px 7px;border-radius:10px;background:var(--bg4);color:var(--text2)}\n.toolbar-spacer{flex:1}\n.mem-bar-wrap{display:flex;align-items:center;gap:6px;font-size:11px;color:var(--text2)}\n.mem-bar{width:80px;height:6px;background:var(--bg4);border-radius:3px;overflow:hidden}\n.mem-bar-fill{height:100%;background:var(--accent);transition:width .4s}\n\n.split{display:flex;flex:1;overflow:hidden}\n.pane-list{width:380px;min-width:200px;max-width:60%;border-right:1px solid var(--border);display:flex;flex-direction:column;flex-shrink:0}\n.pane-detail{flex:1;display:flex;flex-direction:column;overflow:hidden}\n\n/* ── Filter bar ── */\n.filter-bar{display:flex;flex-wrap:wrap;gap:6px;padding:8px 10px;background:var(--bg2);border-bottom:1px solid var(--border);flex-shrink:0}\n.filter-input{flex:1 1 120px;min-width:0;background:var(--bg3);border:1px solid var(--border);color:var(--text);padding:4px 8px;border-radius:4px;font-size:12px;outline:none}\n.filter-input:focus{border-color:var(--accent)}\n.filter-selects{display:flex;gap:6px;flex-shrink:0}\n.filter-select{background:var(--bg3);border:1px solid var(--border);color:var(--text);padding:4px 6px;border-radius:4px;font-size:12px;outline:none;cursor:pointer;max-width:90px}\n.filter-select:focus{border-color:var(--accent)}\n\n/* ── Request list ── */\n.req-list{flex:1;overflow-y:auto;overflow-x:hidden}\n.req-list::-webkit-scrollbar{width:6px}\n.req-list::-webkit-scrollbar-track{background:var(--bg)}\n.req-list::-webkit-scrollbar-thumb{background:var(--bg4);border-radius:3px}\n.req-item{display:flex;align-items:center;gap:8px;padding:7px 10px 7px 9px;border-bottom:1px solid var(--border);border-left:3px solid transparent;cursor:pointer;user-select:none;transition:background .1s,border-left-color .1s}\n.req-item:hover{background:var(--bg3)}\n.req-item.selected{background:var(--bg4);border-left-color:var(--accent) !important}\n.req-item.src-proxy{border-left-color:#6366f1}\n.req-item.src-proxy-cache{border-left-color:#d97706}\n.req-item.src-local{border-left-color:transparent}\n\n.method-badge{font-size:10px;font-weight:700;padding:2px 5px;border-radius:3px;min-width:38px;text-align:center;letter-spacing:.4px;flex-shrink:0}\n.m-GET{background:#1a3a2a;color:var(--green)}\n.m-POST{background:#1a2a3a;color:var(--blue)}\n.m-PUT{background:#3a2a1a;color:var(--orange)}\n.m-PATCH{background:#2a2a1a;color:var(--yellow)}\n.m-DELETE{background:#3a1a1a;color:var(--red)}\n.m-HEAD,.m-OPTIONS{background:#2a1a3a;color:var(--purple)}\n.m-other{background:var(--bg4);color:var(--text2)}\n\n.req-url{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;color:var(--text)}\n.req-meta{display:flex;flex-direction:column;align-items:flex-end;gap:1px;flex-shrink:0;font-size:10px;color:var(--text3)}\n.req-status-row{display:flex;align-items:center;gap:4px}\n.source-letter{font-size:9px;font-weight:700;width:15px;height:15px;display:flex;align-items:center;justify-content:center;border-radius:2px;flex-shrink:0}\n.src-letter-proxy{background:#1e1e3a;color:#6366f1}\n.src-letter-proxy-cache{background:#2a2010;color:#d97706}\n.src-letter-local{background:var(--bg4);color:var(--text3)}\n.status-badge{font-size:10px;font-weight:600;padding:1px 4px;border-radius:2px}\n.s-2xx{color:var(--green)}\n.s-3xx{color:var(--blue)}\n.s-4xx{color:var(--yellow)}\n.s-5xx{color:var(--red)}\n.s-pending{color:var(--text3)}\n\n.source-chip{display:inline-flex;align-items:center;gap:4px;font-size:11px;font-weight:500;padding:2px 7px;border-radius:10px}\n.src-local{background:#1e2a1e;color:#86efac}\n.src-proxy{background:#1e1e3a;color:#a5b4fc}\n.src-proxy-cache{background:#2a2010;color:#fcd34d}\n\n.empty-state{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;color:var(--text3);gap:8px;padding:40px}\n.empty-icon{font-size:40px;opacity:.5}\n\n/* ── Detail pane ── */\n.detail-empty{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;color:var(--text3);gap:8px}\n.detail-scroll{flex:1;overflow-y:auto;overflow-x:hidden;padding:0}\n.detail-scroll::-webkit-scrollbar{width:6px}\n.detail-scroll::-webkit-scrollbar-track{background:var(--bg)}\n.detail-scroll::-webkit-scrollbar-thumb{background:var(--bg4);border-radius:3px}\n\n.detail-section{border-bottom:1px solid var(--border)}\n.detail-section-hdr{display:flex;align-items:center;gap:8px;padding:8px 12px;background:var(--bg2);cursor:pointer;user-select:none;position:sticky;top:0;z-index:1}\n.detail-section-hdr:hover{background:var(--bg3)}\n.detail-section-hdr .toggle{font-size:10px;color:var(--text3)}\n.section-title{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.6px;color:var(--text2)}\n.section-badge{font-size:10px;padding:1px 5px;border-radius:8px;background:var(--bg4);color:var(--text3)}\n.section-actions{display:flex;gap:4px;margin-left:auto;align-items:center}\n.section-btn{font-size:10px;padding:1px 7px;border-radius:3px;border:1px solid var(--border);background:transparent;color:var(--text3);cursor:pointer;line-height:1.6;transition:all .1s;font-family:var(--font-ui)}\n.section-btn:hover{background:var(--bg4);color:var(--text);border-color:var(--border2)}\n.detail-section-body{padding:8px 12px;font-family:var(--font-mono);font-size:12px}\n.detail-section-body.collapsed{display:none}\n\n.kv-table{width:100%;border-collapse:collapse}\n.kv-table tr:hover td{background:var(--bg3)}\n.kv-table td{padding:2px 0;vertical-align:top}\n.kv-table td:first-child{color:var(--text2);white-space:nowrap;padding-right:12px;min-width:140px;max-width:220px;overflow:hidden;text-overflow:ellipsis}\n.kv-table td:last-child{color:var(--text);word-break:break-all}\n\n.body-content{white-space:pre-wrap;word-break:break-all;color:var(--text);line-height:1.6;max-height:400px;overflow:auto}\n.body-content.json{color:#c9d1d9}\n.body-json-key{color:#79b8ff}\n.body-json-str{color:#9ecbff}\n.body-json-num{color:#f8c555}\n.body-json-bool{color:#f97583}\n.body-json-null{color:#f97583;font-style:italic}\n.body-binary{color:var(--text3);font-style:italic;padding:8px 0}\n.body-image{max-width:100%;max-height:300px;object-fit:contain;margin-top:4px;border:1px solid var(--border);border-radius:4px}\n.truncated-note{font-size:11px;color:var(--yellow);margin-top:4px;font-family:var(--font-ui)}\n\n.detail-info-bar{display:flex;gap:16px;padding:10px 12px;background:var(--bg2);border-bottom:1px solid var(--border);flex-shrink:0;flex-wrap:wrap}\n.info-item{display:flex;flex-direction:column;gap:2px}\n.info-label{font-size:10px;color:var(--text3);text-transform:uppercase;letter-spacing:.5px}\n.info-value{font-size:13px;font-weight:600;color:var(--text)}\n.info-url{display:flex;align-items:center;gap:8px;font-size:11px;font-family:var(--font-mono);color:var(--text2);padding:6px 12px;border-bottom:1px solid var(--border);background:var(--bg)}\n.info-url-text{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n\n/* ── Buttons ── */\n.btn{padding:4px 10px;border-radius:4px;border:1px solid var(--border);background:var(--bg3);color:var(--text);font-size:12px;cursor:pointer;transition:background .1s}\n.btn:hover{background:var(--bg4);border-color:var(--border2)}\n.btn-danger:hover{background:#3a1a1a;border-color:#6a2a2a;color:var(--red)}\n.btn-sm{padding:2px 7px;font-size:11px}\n\n/* ── Live indicator ── */\n.live-dot{width:7px;height:7px;border-radius:50%;background:var(--green);animation:pulse 2s infinite}\n@keyframes pulse{0%,100%{opacity:1}50%{opacity:.3}}\n\n/* ── Scrollbar ── */\n::-webkit-scrollbar{width:6px;height:6px}\n::-webkit-scrollbar-track{background:var(--bg)}\n::-webkit-scrollbar-thumb{background:var(--bg4);border-radius:3px}\n</style>\n</head>\n<body>\n<div class="app">\n <div class="toolbar">\n <span class="toolbar-title">🔍 Request Inspector</span>\n <span class="live-dot" title="Live"></span>\n <span class="toolbar-badge" id="req-count">0 requests</span>\n <div class="mem-bar-wrap">\n <div class="mem-bar"><div class="mem-bar-fill" id="mem-fill" style="width:0%"></div></div>\n <span id="mem-text">0 B / 1 GB</span>\n </div>\n <div class="toolbar-spacer"></div>\n <button class="btn btn-sm btn-danger" onclick="clearAll()">Clear</button>\n </div>\n <div class="split">\n <div class="pane-list">\n <div class="filter-bar">\n <input class="filter-input" id="search-input" placeholder="Filter by URL…" oninput="applyFilter()" />\n <div class="filter-selects">\n <select class="filter-select" id="method-select" onchange="applyFilter()">\n <option value="">All</option>\n <option>GET</option><option>POST</option><option>PUT</option>\n <option>PATCH</option><option>DELETE</option><option>HEAD</option>\n </select>\n <select class="filter-select" id="status-select" onchange="applyFilter()">\n <option value="">All</option>\n <option value="2">2xx</option><option value="3">3xx</option>\n <option value="4">4xx</option><option value="5">5xx</option>\n </select>\n <select class="filter-select" id="source-select" onchange="applyFilter()">\n <option value="">All</option>\n <option value="local">Local</option>\n <option value="proxy">Proxy</option>\n <option value="proxy-cache">Cached</option>\n </select>\n </div>\n </div>\n <div class="req-list" id="req-list">\n <div class="empty-state">\n <div class="empty-icon">📡</div>\n <div>No requests captured yet</div>\n <div style="font-size:11px">Requests to the dev server will appear here</div>\n </div>\n </div>\n </div>\n <div class="pane-detail">\n <div class="detail-empty" id="detail-empty">\n <div style="font-size:32px;opacity:.3">←</div>\n <div>Select a request to inspect</div>\n </div>\n <div id="detail-panel" style="display:none;flex:1;flex-direction:column;overflow:hidden">\n <div class="detail-info-bar" id="detail-info"></div>\n <div class="info-url" id="detail-url"></div>\n <div class="detail-scroll" id="detail-scroll"></div>\n </div>\n </div>\n </div>\n</div>\n<script>\n(function(){\n'use strict';\n\nconst CAPTURE_LIMIT = ${e};\n\nlet allRequests = [];\nlet filteredRequests = [];\nlet selectedId = null;\nlet currentEntry = null;\n\nconst listEl = document.getElementById('req-list');\nconst detailEmpty = document.getElementById('detail-empty');\nconst detailPanel = document.getElementById('detail-panel');\nconst detailInfo = document.getElementById('detail-info');\nconst detailUrl = document.getElementById('detail-url');\nconst detailScroll = document.getElementById('detail-scroll');\n\n// ── Fetch request list ────────────────────────────────────────────────────────\nasync function fetchList() {\n try {\n const r = await fetch('/@api/requests');\n const data = await r.json();\n\n allRequests = data.requests || [];\n updateStats(data);\n applyFilter();\n\n if (selectedId && !allRequests.find(r => r.id === selectedId)) {\n selectedId = null;\n showDetailEmpty();\n }\n } catch(e) {}\n}\n\nfunction updateStats(data) {\n const pct = data.maxMemory > 0 ? Math.min(100, (data.memoryUsage / data.maxMemory) * 100) : 0;\n\n document.getElementById('req-count').textContent = data.total + ' request' + (data.total !== 1 ? 's' : '');\n document.getElementById('mem-fill').style.width = pct + '%';\n document.getElementById('mem-text').textContent = formatBytes(data.memoryUsage) + ' / ' + formatBytes(data.maxMemory);\n}\n\n// ── Filter ────────────────────────────────────────────────────────────────────\nfunction applyFilter() {\n const search = document.getElementById('search-input').value.toLowerCase();\n const method = document.getElementById('method-select').value;\n const status = document.getElementById('status-select').value;\n const source = document.getElementById('source-select').value;\n\n filteredRequests = allRequests.filter(r => {\n if (method && r.method !== method) {\n return false;\n }\n\n if (status && !String(r.statusCode || '').startsWith(status)) {\n return false;\n }\n\n if (source && r.source !== source) {\n return false;\n }\n\n if (search && !r.url.toLowerCase().includes(search)) {\n return false;\n }\n\n return true;\n });\n\n renderList();\n}\n\n// ── Render list ───────────────────────────────────────────────────────────────\nfunction isAtBottom() {\n return listEl.scrollTop + listEl.clientHeight >= listEl.scrollHeight - 16;\n}\n\nfunction renderList() {\n const atBottom = isAtBottom();\n\n if (!filteredRequests.length) {\n listEl.innerHTML = '<div class="empty-state"><div class="empty-icon">🔍</div><div>No matching requests</div></div>';\n\n return;\n }\n\n const html = filteredRequests.map(r => {\n const sel = r.id === selectedId ? ' selected' : '';\n const mClass = methodClass(r.method);\n const sClass = statusClass(r.statusCode);\n const dur = r.duration != null ? formatDur(r.duration) : '…';\n const size = formatBytes((r.responseSize || 0));\n const srcCls = 'src-' + (r.source || 'local');\n const sLetter = sourceLetter(r.source);\n const sLetterCls = 'src-letter-' + (r.source || 'local').replace(/-/g, '-');\n\n return \`<div class="req-item \${srcCls}\${sel}" data-id="\${esc(r.id)}" onclick="selectRequest('\${esc(r.id)}')">\n <span class="method-badge \${mClass}">\${esc(r.method)}</span>\n <span class="req-url" title="\${esc(r.url)}">\${esc(r.url)}</span>\n <span class="req-meta">\n <span class="req-status-row">\n <span class="source-letter \${sLetterCls}">\${sLetter}</span>\n <span class="status-badge \${sClass}">\${r.statusCode || '…'}</span>\n </span>\n <span>\${dur}</span>\n <span>\${size}</span>\n </span>\n </div>\`;\n }).join('');\n\n listEl.innerHTML = html;\n\n if (atBottom) {\n listEl.scrollTop = listEl.scrollHeight;\n }\n}\n\n// ── Select request ────────────────────────────────────────────────────────────\nasync function selectRequest(id) {\n selectedId = id;\n\n renderList();\n\n detailEmpty.style.display = 'none';\n detailPanel.style.display = 'flex';\n detailPanel.style.flexDirection = 'column';\n detailPanel.style.overflow = 'hidden';\n detailScroll.innerHTML = '<div style="padding:20px;color:var(--text3)">Loading…</div>';\n\n try {\n const r = await fetch('/@api/requests/' + encodeURIComponent(id));\n\n if (!r.ok) {\n showDetailEmpty();\n\n return;\n }\n\n const entry = await r.json();\n\n renderDetail(entry);\n } catch(e) {\n showDetailEmpty();\n }\n}\nwindow.selectRequest = selectRequest;\n\nfunction showDetailEmpty() {\n detailPanel.style.display = 'none';\n detailEmpty.style.display = 'flex';\n}\n\nfunction renderDetail(e) {\n currentEntry = e;\n\n const sClass = statusClass(e.statusCode);\n const src = sourceInfo(e.source);\n\n detailInfo.innerHTML = \`\n <div class="info-item"><div class="info-label">Method</div><div class="info-value"><span class="method-badge \${methodClass(e.method)}">\${esc(e.method)}</span></div></div>\n <div class="info-item"><div class="info-label">Status</div><div class="info-value"><span class="status-badge \${sClass}">\${e.statusCode || '—'}</span></div></div>\n <div class="info-item"><div class="info-label">Source</div><div class="info-value"><span class="source-chip \${src.cls}">\${src.title}</span></div></div>\n <div class="info-item"><div class="info-label">Duration</div><div class="info-value">\${e.duration != null ? formatDur(e.duration) : '—'}</div></div>\n <div class="info-item"><div class="info-label">Request</div><div class="info-value">\${formatBytes(e.requestBodySize||0)}</div></div>\n <div class="info-item"><div class="info-label">Response</div><div class="info-value">\${formatBytes(e.responseBodySize||0)}</div></div>\n <div class="info-item"><div class="info-label">Time</div><div class="info-value" style="font-size:11px">\${new Date(e.timestamp).toLocaleTimeString()}</div></div>\n \`;\n\n detailUrl.innerHTML = \`<span class="info-url-text" title="\${esc(e.url)}">\${esc(e.url)}</span><button class="section-btn" onclick="copyUrl(this)">Copy link</button>\`;\n\n const sections = [\n makeHeadersSection('Request Headers', e.requestHeaders, 'request'),\n makeBodySection('Request Body', e.requestBody, e.requestContentType, e.requestBodyTruncated, e.requestBodySize, 'request'),\n makeHeadersSection('Response Headers', e.responseHeaders, 'response'),\n makeBodySection('Response Body', e.responseBody, e.responseContentType, e.responseBodyTruncated, e.responseBodySize, 'response'),\n ];\n\n detailScroll.innerHTML = sections.join('');\n}\n\nfunction makeHeadersSection(title, headers, type) {\n const entries = Object.entries(headers || {}).filter(([,v]) => v != null);\n const rows = entries.map(([k,v]) => \`<tr><td>\${esc(k)}</td><td>\${esc(Array.isArray(v) ? v.join(', ') : String(v))}</td></tr>\`).join('');\n const actionsHtml = entries.length ? \`<div class="section-actions"><button class="section-btn" onclick="event.stopPropagation();copyHeaders('\${type}',this)">Copy</button></div>\` : '';\n\n return \`<div class="detail-section">\n <div class="detail-section-hdr" onclick="toggleSection(this)">\n <span class="toggle">▾</span>\n <span class="section-title">\${title}</span>\n <span class="section-badge">\${entries.length}</span>\n \${actionsHtml}\n </div>\n <div class="detail-section-body">\n <table class="kv-table">\${rows}</table>\n </div>\n </div>\`;\n}\n\nfunction makeBodySection(title, b64, contentType, truncated, size, type) {\n let bodyHtml = '';\n const ct = (contentType || '').toLowerCase().split(';')[0].trim();\n const isText = ct.includes('json') || ct.startsWith('text/') || ct.includes('/javascript') || ct === 'application/xml' || ct === 'application/x-www-form-urlencoded';\n\n if (!b64 && !truncated) {\n bodyHtml = '<div class="body-binary">No body</div>';\n } else if (truncated) {\n bodyHtml = \`<div class="body-binary">Body too large to capture (\${formatBytes(size)})</div>\`;\n } else if (b64) {\n if (ct.includes('json') || ct.includes('/javascript') || ct === 'text/plain') {\n const text = decodeTextBody(b64);\n\n if (ct.includes('json')) {\n try {\n bodyHtml = \`<pre class="body-content json">\${syntaxHighlightJson(text)}</pre>\`;\n } catch {\n bodyHtml = \`<pre class="body-content">\${esc(text)}</pre>\`;\n }\n } else {\n bodyHtml = \`<pre class="body-content">\${esc(text)}</pre>\`;\n }\n } else if (ct.startsWith('text/')) {\n bodyHtml = \`<pre class="body-content">\${esc(decodeTextBody(b64))}</pre>\`;\n } else if (ct.startsWith('image/') && size < 1024*1024) {\n bodyHtml = \`<img class="body-image" src="data:\${esc(ct)};base64,\${b64}" alt="image"/>\`;\n } else if (ct === 'application/x-www-form-urlencoded') {\n const params = new URLSearchParams(decodeTextBody(b64));\n const rows = [...params.entries()].map(([k,v]) => \`<tr><td>\${esc(k)}</td><td>\${esc(v)}</td></tr>\`).join('');\n\n bodyHtml = \`<table class="kv-table">\${rows}</table>\`;\n } else {\n bodyHtml = \`<div class="body-binary">Binary data — \${formatBytes(size)} (\${esc(ct || 'unknown type')})</div>\`;\n }\n }\n\n const copyBtn = b64 && isText ? \`<button class="section-btn" onclick="event.stopPropagation();copyBody('\${type}',this)">Copy</button>\` : '';\n const saveBtn = b64 ? \`<button class="section-btn" onclick="event.stopPropagation();saveBody('\${type}')">Save</button>\` : '';\n const actionsHtml = (copyBtn || saveBtn) ? \`<div class="section-actions">\${copyBtn}\${saveBtn}</div>\` : '';\n const note = truncated ? \`<div class="truncated-note">⚠ Body was larger than the capture limit (\${formatBytes(CAPTURE_LIMIT)})</div>\` : '';\n\n return \`<div class="detail-section">\n <div class="detail-section-hdr" onclick="toggleSection(this)">\n <span class="toggle">▾</span>\n <span class="section-title">\${title}</span>\n \${size ? \`<span class="section-badge">\${formatBytes(size)}</span>\` : ''}\n \${actionsHtml}\n </div>\n <div class="detail-section-body">\${bodyHtml}\${note}</div>\n </div>\`;\n}\n\nwindow.toggleSection = function(hdr) {\n const body = hdr.nextElementSibling;\n const toggle = hdr.querySelector('.toggle');\n\n if (body.classList.toggle('collapsed')) {\n toggle.textContent = '▸';\n } else {\n toggle.textContent = '▾';\n }\n};\n\nasync function clearAll() {\n await fetch('/@api/requests', { method: 'DELETE' });\n\n allRequests = [];\n filteredRequests = [];\n selectedId = null;\n\n showDetailEmpty();\n renderList();\n updateStats({ total: 0, memoryUsage: 0, maxMemory: 1024*1024*1024 });\n}\nwindow.clearAll = clearAll;\n\n// ── JSON syntax highlighting ──────────────────────────────────────────────────\nfunction syntaxHighlightJson(str) {\n try {\n const parsed = JSON.parse(str);\n\n str = JSON.stringify(parsed, null, 2);\n } catch { /* use as-is */ }\n\n return str.replace(/("(\\\\u[a-zA-Z0-9]{4}|\\\\[^u]|[^\\\\"])*"\\s*:?|\\b(true|false|null)\\b|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?)/g, function(m) {\n if (/^"/.test(m)) {\n if (/:$/.test(m)) {\n return '<span class="body-json-key">' + esc(m) + '</span>';\n }\n\n return '<span class="body-json-str">' + esc(m) + '</span>';\n }\n\n if (/true|false/.test(m)) {\n return '<span class="body-json-bool">' + m + '</span>';\n }\n\n if (/null/.test(m)) {\n return '<span class="body-json-null">' + m + '</span>';\n }\n\n return '<span class="body-json-num">' + m + '</span>';\n });\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\nfunction esc(s) {\n return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');\n}\n\nfunction decodeTextBody(b64) {\n const bytes = Uint8Array.from(atob(b64), function(c) { return c.charCodeAt(0); });\n\n return new TextDecoder('utf-8').decode(bytes);\n}\n\nfunction sourceInfo(source) {\n if (source === 'proxy') {\n return { cls: 'src-proxy', title: 'Backend (live)' };\n }\n\n if (source === 'proxy-cache') {\n return { cls: 'src-proxy-cache', title: 'Backend (cached)' };\n }\n\n return { cls: 'src-local', title: 'Local (dev server)' };\n}\n\nfunction sourceLetter(source) {\n if (source === 'proxy') {\n return 'P';\n }\n\n if (source === 'proxy-cache') {\n return 'C';\n }\n\n return 'L';\n}\n\n// ── Copy / Save ───────────────────────────────────────────────────────────────\nfunction clipboard(text, btn) {\n navigator.clipboard.writeText(text).then(() => {\n if (!btn) {\n return;\n }\n\n const orig = btn.textContent;\n\n btn.textContent = '✓';\n btn.style.color = 'var(--green)';\n setTimeout(() => {\n btn.textContent = orig;\n btn.style.color = '';\n }, 1500);\n }).catch(() => {});\n}\n\nfunction copyUrl(btn) {\n clipboard(currentEntry ? currentEntry.url : '', btn);\n}\nwindow.copyUrl = copyUrl;\n\nfunction copyHeaders(type, btn) {\n const h = type === 'request' ? (currentEntry && currentEntry.requestHeaders) : (currentEntry && currentEntry.responseHeaders);\n\n if (!h) {\n return;\n }\n\n const text = Object.entries(h).filter(([,v]) => v != null).map(([k,v]) => k + ': ' + (Array.isArray(v) ? v.join(', ') : v)).join('\\n');\n\n clipboard(text, btn);\n}\nwindow.copyHeaders = copyHeaders;\n\nfunction copyBody(type, btn) {\n const b64 = type === 'request' ? (currentEntry && currentEntry.requestBody) : (currentEntry && currentEntry.responseBody);\n\n if (!b64) {\n return;\n }\n\n try {\n clipboard(decodeTextBody(b64), btn);\n } catch(e) {}\n}\nwindow.copyBody = copyBody;\n\nfunction saveBody(type) {\n const b64 = type === 'request' ? (currentEntry && currentEntry.requestBody) : (currentEntry && currentEntry.responseBody);\n const ct = type === 'request' ? (currentEntry && currentEntry.requestContentType) : (currentEntry && currentEntry.responseContentType);\n\n if (!b64) {\n return;\n }\n\n downloadB64(b64, type + '-body.' + fileExt(ct));\n}\nwindow.saveBody = saveBody;\n\nfunction downloadB64(b64, filename) {\n try {\n const bytes = Uint8Array.from(atob(b64), function(c) { return c.charCodeAt(0); });\n const blob = new Blob([bytes]);\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n\n a.href = url;\n a.download = filename;\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n setTimeout(function() { URL.revokeObjectURL(url); }, 1000);\n } catch(e) {}\n}\n\nfunction fileExt(ct) {\n ct = ((ct || '').split(';')[0]).trim().toLowerCase();\n\n var map = {\n 'application/json': 'json', 'text/html': 'html', 'text/css': 'css',\n 'text/javascript': 'js', 'application/javascript': 'js', 'text/plain': 'txt',\n 'text/xml': 'xml', 'application/xml': 'xml', 'image/png': 'png',\n 'image/jpeg': 'jpg', 'image/gif': 'gif', 'image/svg+xml': 'svg',\n 'image/webp': 'webp', 'application/pdf': 'pdf', 'application/zip': 'zip',\n 'application/x-www-form-urlencoded': 'txt', 'application/octet-stream': 'bin',\n };\n\n return map[ct] || 'bin';\n}\n\nfunction formatBytes(b) {\n b = b || 0;\n\n if (b < 1024) {\n return b + ' B';\n }\n\n if (b < 1024*1024) {\n return (b/1024).toFixed(1) + ' KB';\n }\n\n if (b < 1024*1024*1024) {\n return (b/(1024*1024)).toFixed(1) + ' MB';\n }\n\n return (b/(1024*1024*1024)).toFixed(2) + ' GB';\n}\n\nfunction formatDur(ms) {\n if (ms < 1000) {\n return ms + 'ms';\n }\n\n return (ms/1000).toFixed(2) + 's';\n}\n\nfunction methodClass(m) {\n const map = { GET:'m-GET', POST:'m-POST', PUT:'m-PUT', PATCH:'m-PATCH', DELETE:'m-DELETE', HEAD:'m-HEAD', OPTIONS:'m-OPTIONS' };\n\n return map[m] || 'm-other';\n}\n\nfunction statusClass(s) {\n if (!s) {\n return 's-pending';\n }\n\n const c = Math.floor(s / 100);\n\n return ['','','s-2xx','s-3xx','s-4xx','s-5xx'][c] || '';\n}\n\n// ── Polling ───────────────────────────────────────────────────────────────────\nfetchList();\nsetInterval(fetchList, 2000);\n\n})();\n<\/script>\n</body>\n</html>`}(it.captureLimit))}))}function ct(e){return Array.isArray(e)?e[0]??"":String(e??"")}function lt(e){return e<1024?`${e} B`:e<1048576?`${(e/1024).toFixed(1)} KB`:e<1073741824?`${(e/1048576).toFixed(1)} MB`:`${(e/1073741824).toFixed(2)} GB`}const dt=["top-left","top-right","bottom-left","bottom-right"];exports.AuthProvider=Ue,exports.ClientService=Re,exports.DistService=Pe,exports.INSPECTOR_PATH=rt,exports.MiAPI=fe,exports.PACKAGE_NAME=z,exports.PATH_PAGE_PREFIX="/p",exports.PATH_TEMPLATE_LOCAL_PREFIX="/pl",exports.PATH_TEMPLATE_PREFIX="/pt",exports.PP_DEV_CLIENT_ENTRY=O,exports.PP_DEV_CONFIG_NAMES=[".pp-dev.config.js",".pp-dev.config.cjs",".pp-dev.config.mjs",".pp-dev.config.ts",".pp-dev.config.cts",".pp-dev.config.mts",".pp-dev.config.json","pp-dev.config.js","pp-dev.config.cjs","pp-dev.config.mjs","pp-dev.config.ts","pp-dev.config.cts","pp-dev.config.mts","pp-dev.config.json"],exports.PP_DEV_HMR_WS_PATH="/@pp-dev-hmr",exports.PP_DEV_PACKAGE_DIR=D,exports.RequestStore=et,exports.VERSION=_,exports.authProvider=_e,exports.colors=J,exports.createLogger=W,exports.createRequestCaptureMiddleware=st,exports.initLoadPPData=Je,exports.initPPRedirect=Me,exports.initProxy=ee,exports.initProxyCache=De,exports.initRewriteResponse=Oe,exports.internalServer=Qe,exports.normalizePPDevConfig=function(e,t){const{mi:n={},app:s={},proxy:r={},build:i={},sync:a={},inspector:o={},devPanel:c={}}=e,l=n.mode??"standalone",d=s.type??"template",p=n.apiVersion??7,u="standalone"===l;let h=!1;"top-bar"===n.include?h=!0:"shared-components"===n.include&&(h={addSharedComponentsScripts:!0,addRootElement:!1});const f="page"===d,m=s.name??t,g="VERSION-v{packageversion}-{currentDate}.json";let y;const v=i.zip;let b;y=!1!==v&&(!0===v||void 0===v?{outFileName:`${m}.zip`,outDir:"dist-zip"}:{outFileName:v.fileName??`${m}.zip`,outDir:v.outDir??"dist-zip",...v.inDir?{inDir:v.inDir}:{}});const x=i.versionFile;let w;b=!1!==x&&(!0===x||void 0===x?{versionFileTemplate:g,enabled:!0}:{versionFileTemplate:x.fileNameTemplate??g,enabled:x.enabled??!0});const k=i.imageOptimisations;return w=!1!==k&&(!0===k||void 0===k?{}:k),{templateName:m,backendBaseURL:n.url??process.env.MI_BACKEND_URL,appId:s.id,templateLess:f,miHudLess:u,integrateMiTopBar:h,enableProxyCache:r.cache??!0,proxyCacheTTL:r.cacheTtl??6e5,disableSSLValidation:r.tls?.allowSelfSigned??!1,imageOptimizer:w,outDir:i.outDir??"dist",distZip:y,versionPlugin:b,syncBackupsDir:a.backupsDir??"backups",v7Features:p>=7,personalAccessToken:n.token??process.env.MI_ACCESS_TOKEN,inspectorEnabled:o.enabled??!0,inspectorMaxMemory:o.maxMemory??1073741824,inspectorCaptureLimit:o.captureLimit??10485760,devPanelPosition:c.position??"bottom-right",devPanelHidden:c.hidden??!1,devPanelAutoHide:c.autoHide??!1}},exports.registerInspectorRoutes=ot,exports.urlReplacer=G,exports.validatePPDevConfig=function(e,t){const n=e.mi?.mode??"standalone",s=e.app?.type??"template",r=e.mi?.apiVersion??7,i=e.mi?.url??process.env.MI_BACKEND_URL;if(!t)throw new Error("[pp-dev] app.name is required (or set package.json#name)");if(e.mi?.include&&"standalone"!==n)throw console.warn('[pp-dev] mi.include is only valid when mi.mode is "standalone"'),new Error('[pp-dev] mi.include requires mi.mode to be "standalone"');if(!i){if("embedding"===n||"template"===s)throw new Error('[pp-dev] mi.url is required when mi.mode is "embedding" or app.type is "template"');"standalone"===n&&"page"===s&&console.warn("[pp-dev] mi.url is not set — dev server will run without a backend proxy")}if("template"===s&&!e.app?.id)throw new Error('[pp-dev] app.id is required when app.type is "template"');if("page"===s&&i&&("standalone"===n||r>=7)&&!e.app?.id)throw new Error('[pp-dev] app.id is required when backend page data is loaded for app.type "page"');if(void 0!==e.devPanel?.position&&!dt.includes(e.devPanel.position))throw new Error(`[pp-dev] devPanel.position must be one of: ${dt.join(", ")}`)},exports.vitePPDev=function(e){const{templateName:t,templateLess:n,backendBaseURL:s,miHudLess:r,appId:i,enableProxyCache:a,proxyCacheTTL:o,disableSSLValidation:c,distZip:l,versionPlugin:d,syncBackupsDir:p,v7Features:u,personalAccessToken:h,inspectorEnabled:f,inspectorMaxMemory:m,inspectorCaptureLimit:g,devPanelPosition:y,devPanelHidden:v,devPanelAutoHide:b}=e||{};let x=!0;return process.cwd(),{name:"vite-pp-dev",apply:"serve",config:e=>(e.clientInjectionPlugin={backendBaseURL:s,appId:i,templateLess:n,v7Features:u,devPanelPosition:y,devPanelHidden:v,devPanelAutoHide:b},u&&(e.base=`/pl/${t}`),e),transformIndexHtml:async(e,t)=>{const n={html:e,tags:[]};return x&&(x=!1,n.tags.push({tag:"script",injectTo:"body",children:`${Math.random()}`})),n},configureServer:y=>{let v=y.config.base;v.endsWith("/")||(v+="/");const b=v.substring(0,v.lastIndexOf("/"));if(y.middlewares.use(Me(v,t)),!1!==f){const e=new et(m);y.middlewares.use(st(e,g));const t=Ye();ot(t,e,g),y.middlewares.use((e,n,s)=>{e.url?.startsWith("/@api/")||e.url?.startsWith(rt)?t(e,n,s):s()})}if(s){const f=new URL(s).host,m={headers:{host:f,referer:s,origin:s.replace(/^(https?:\/\/)([^/]+)(\/.*)?$/i,"$1$2")},appId:i,templateLess:n,disableSSLValidation:c,v7Features:u,personalAccessToken:h??process.env.MI_ACCESS_TOKEN},g=new fe(s,m);if(a){let e=+o;(!e||Number.isNaN(e)||e<0)&&(e=6e5),y.middlewares.use(De({devServer:y,ttl:e}))}const x=new RegExp(`^((${v})|/)$`);y.middlewares.use(Je(x,g,Object.assign({},e,{appId:i,appBase:v}))),y.middlewares.use(ee({baseURL:s,proxyIgnore:["/@vite","/@metricinsights","/@",b],disableSSLValidation:c,miAPI:g,templateName:t}));const w=(e,t,n,s,r)=>{const i={error:n};s&&(i.details=s),r&&(i.code=r),e.status(t).json(i).end()},k=(e,t,n)=>{y.config.logger.error(`${n} token validation error:`,t);const s=pe(t);if(t.tokenErrorInfo){const n=t.tokenErrorInfo;w(e,n.status||500,n.userFriendlyMessage,n.message,n.code)}else{let t,r,i;switch(s.code){case"SESSION_EXPIRED":t=412,r="personal"===n?"Personal access token expired":"Session expired",i="personal"===n?"PAT_EXPIRED":"SESSION_EXPIRED";break;case"UNAUTHORIZED":t=401,r="Unauthorized",i="UNAUTHORIZED";break;case"FORBIDDEN":t=403,r="Access forbidden",i="FORBIDDEN";break;default:t=500,r="Internal server error",i=s.code}const a="personal"===n&&"PAT_EXPIRED"===i?"Your personal access token has expired. Please generate a new token from the portal.":"regular"===n&&"SESSION_EXPIRED"===i?"Your portal session has expired. Please refresh your token or re-authenticate.":s.userFriendlyMessage;w(e,t,r,a,i)}return null},E=Ye();E.post("/@api/login",async(e,t,n)=>{const{token:s,tokenType:r}=e.body;if(s)if(y.config.logger.info(`Attempting to validate ${r} token...`),"personal"===r){if(!await g.get("/data/page/index/auth/info",{"Content-Type":"application/json",Accept:"application/json",Authorization:`Bearer ${s}`},!0).then(async e=>"number"==typeof e.data?.user?.user_id?(g.personalAccessToken=s,y.config.logger.info(`Personal access token validated successfully for user ${e.data.user.user_id}`),e):(w(t,400,"Token expired or invalid"),null)).catch(e=>k(t,e,"personal")))return;K(t,"/",302)}else if("regular"===r){if(!await g.get("/api/user",{"Content-Type":"application/json",Accept:"application/json",Token:s},!0).then(e=>e.data?.users?.length?(g.personalAccessToken=void 0,y.config.logger.info(`Regular token validated successfully for ${e.data.users.length} user(s)`),t.setHeader("set-cookie",e.headers["set-cookie"]??""),e):(w(t,400,"Token expired or invalid"),null)).catch(e=>k(t,e,"regular")))return;K(t,"/",302)}else w(t,400,"Invalid tokenType",'tokenType must be "personal" or "regular".',"INVALID_TOKEN_TYPE");else w(t,400,"Token is required")}),y.middlewares.use(E);const T=!1!==l?new Pe(t,Object.assign({backupDir:p},"object"==typeof l?{distZipFolder:l.outDir,distZipFilename:l.outFileName}:void 0,"object"==typeof d?{versionFileTemplate:d.versionFileTemplate}:void 0)):void 0;return new Re(y,{distService:T,miAPI:g}),()=>{y.middlewares.use(Oe(e=>e.split("?")[0].endsWith("index.html"),(e,t)=>Buffer.from(G(f,t.headers.host??"",g.buildPage(e,r)))))}}}}},exports.writeBuildVersionManifest=xe;
2
+ //# sourceMappingURL=plugin-Cm9vRLCu.js.map