Repository navigation
Expand file tree
/
Copy pathsw.min.js
More file actions
1 lines (1 loc) · 14.1 KB
/
Copy pathsw.min.js
File metadata and controls
1 lines (1 loc) · 14.1 KB
1
const APP_SHELL_FILES=["./","./index.html","./main.min.js","sw.min.js"],NETWORK_ALLOWLIST_PREFIXES=[];let reloadOnRequest=!1,pendingNavData=null;const clientSessionStore=new Map,handleCache=new Map,manifestCache=new Map,compiledHeaderCache=new Map,ruleCache=new Map,MAX_REGEX_SIZE=10485760,dirHandleCache=new Map,MAX_DIR_CACHE_SIZE=1e3;function addToCache(e,t,a){e.size>=1e3&&e.delete(e.keys().next().value),e.set(t,a)}function getFromCache(e,t){const a=e.get(t);return void 0!==a&&(e.delete(t),e.set(t,a)),a}const RFS_PREFIX="rfs",SYSTEM_FILE="rfs_system.json",CACHE_NAME="fc",FULL_APP_SHELL_URLS=APP_SHELL_FILES.map(e=>new URL(e,self.location.href).href),STORE_ENTRY_TTL=6e5,basePath=new URL("./",self.location).pathname,virtualPathPrefix=basePath+"n/",MIME_TYPES={html:"text/html",htm:"text/html",css:"text/css",js:"text/javascript",mjs:"text/javascript",cjs:"text/javascript",jsx:"text/javascript",ts:"text/javascript",tsx:"text/javascript",json:"application/json",jsonld:"application/ld+json",xml:"application/xml",svg:"image/svg+xml",txt:"text/plain",md:"text/markdown",csv:"text/csv",webmanifest:"application/manifest+json",png:"image/png",jpg:"image/jpeg",jpeg:"image/jpeg",gif:"image/gif",webp:"image/webp",ico:"image/x-icon",avif:"image/avif",bmp:"image/bmp",woff:"font/woff",woff2:"font/woff2",ttf:"font/ttf",otf:"font/otf",eot:"application/vnd.ms-fontobject",mp3:"audio/mpeg",wav:"audio/wav",ogg:"audio/ogg",m4a:"audio/mp4",mp4:"video/mp4",webm:"video/webm",ogv:"video/ogg",vtt:"text/vtt",wasm:"application/wasm",pdf:"application/pdf",zip:"application/zip",rar:"application/x-rar-compressed","7z":"application/x-7z-compressed",tar:"application/x-tar",gz:"application/gzip",bin:"application/octet-stream",dat:"application/octet-stream"};function getMimeType(e){const t=e.split(".").pop().toLowerCase();return MIME_TYPES[t]||"text/plain"}let registryCache=null,_opfsRoot=null;async function getOpfsRoot(){return _opfsRoot||(_opfsRoot=await navigator.storage.getDirectory()),_opfsRoot}function cleanupExpiredStores(){const e=Date.now();for(const[t,a]of clientSessionStore.entries())e-a.timestamp>6e5&&clientSessionStore.delete(t)}setInterval(cleanupExpiredStores,6e4);const TEXT_DECODER=new TextDecoder,TEXT_ENCODER=new TextEncoder,STRICT_TEXT_DECODER=new TextDecoder("utf-8",{fatal:!0});function escapeRegex(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function compileRules(e){if(!e||!e.trim())return[];if(ruleCache.has(e))return ruleCache.get(e);const t=[],a=e.trim().split(/\r?\n/);for(const e of a){const a=e.split("->");if(a.length<2)continue;const n=a[0].trim();let s=a.slice(1).join("->").trim();const r=n.match(/^(.*?)\s+(\$|\$\$|\|\||\|)\s+(.*)$/s);if(!r)continue;const[,i,o,c]=r;try{const e=new RegExp("*"===i.trim()?".*":i.trim());let a;if("{{SCRIPT}}"===c)a=/(?:<!DOCTYPE\b[^>]*>)|(?:<head\b[^>]*>)|(?:<body\b[^>]*>)|(?:<html\b[^>]*>)|(?:^)/i,s=`$&<script>${s}<\/script>`;else switch(o){case"|":a=new RegExp(c,"g");break;case"$":a=new RegExp(escapeRegex(c),"g");break;case"||":a=new RegExp(c);break;case"$$":a=new RegExp(escapeRegex(c))}a&&e&&t.push({fileRegex:e,searchRegex:a,replacePart:s})}catch(t){console.warn("Rule compilation failed for line "+e+":",t);continue}}return ruleCache.size>50&&ruleCache.clear(),ruleCache.set(e,t),t}function applyRegexRules(e,t,a,n){if(!/^(text\/|application\/(javascript|json|xml|x-javascript|typescript))/.test(a))return t;if(t.byteLength>10485760)return t;try{let a=TEXT_DECODER.decode(t),s=!1;for(const t of n)if(t.fileRegex.test(e)){const e=a.replace(t.searchRegex,t.replacePart);e!==a&&(a=e,s=!0)}return s?TEXT_ENCODER.encode(a).buffer:t}catch(a){return console.error(`Error applying regex rules to ${e}:`,a),t}}async function getRegistry(){if(registryCache)return registryCache;try{const e=await getOpfsRoot(),t=await e.getFileHandle(SYSTEM_FILE),a=await t.getFile();registryCache=JSON.parse(await a.text())}catch(e){registryCache={}}return registryCache}async function getFileFromHandle(e,t,a,n){try{return await e.getFile()}catch(e){handleCache.delete(`${a}|${n}`);const s=await getCachedFileHandle(t,a,n);if(!s)return null;try{return await s.getFile()}catch(e){return handleCache.delete(`${a}|${n}`),null}}}async function getCachedFileHandle(e,t,a){const n=`${t}|${a}`,s=getFromCache(handleCache,n);if(s)return s;try{const s=a.split("/").filter(e=>e&&""!==e.trim()),r=decodeURIComponent(s.pop());let i,o=t;if(i=getFromCache(dirHandleCache,o),!i){const a=await e.getDirectoryHandle("rfs");i=await a.getDirectoryHandle(t),addToCache(dirHandleCache,o,i)}for(const e of s){const t=decodeURIComponent(e);o+=`/${t}`;const a=getFromCache(dirHandleCache,o);a?i=a:(i=await i.getDirectoryHandle(t),addToCache(dirHandleCache,o,i))}const c=await i.getFileHandle(r);return addToCache(handleCache,n,c),c}catch(e){return null}}function parseCustomHeaders(e){if(!e||!e.trim())return[];const t=[];return e.trim().split("\n").forEach(e=>{if(0===(e=e.trim()).length||e.startsWith("#"))return;let a="",n=!1;const s=e.indexOf("--\x3e"),r=e.indexOf("->");if(-1!==s&&s<r)a="--\x3e",n=!0;else{if(-1===r)return;a="->",n=!1}const i=e.indexOf(a),o=e.substring(0,i).trim(),c=e.substring(i+a.length).trim(),l=c.indexOf(":");if(-1===l)return;const p=c.substring(0,l).trim(),d=c.substring(l+1).trim().replace(/^['"]|['"]$/g,"");try{const e=n?o:"^"+o.replace(/\./g,"\\.").replace(/\*/g,".*")+"$";t.push({regex:new RegExp(e),header:p,value:d})}catch(t){console.error("Invalid pattern on line:",e)}}),t}function applyCustomHeaders(e,t,a){if(!a||!Array.isArray(a))return e;for(const n of a)n.regex.test(t)&&(e[n.header]=n.value);return e}function getCompiledHeaders(e,t){const a=compiledHeaderCache.get(e);if(a&&a.raw===t)return a.compiled;const n=parseCustomHeaders(t);return compiledHeaderCache.set(e,{raw:t,compiled:n}),n}async function handleEncryptedRequest(e,t,a,n,s,r){try{const i=4194304,o=28;let c;if(manifestCache.has(t))c=manifestCache.get(t);else{const a=await e.getDirectoryHandle("rfs"),s=await a.getDirectoryHandle(t),r=await s.getFileHandle("manifest.enc"),i=await(await r.getFile()).arrayBuffer(),o=i.slice(16,28),l=i.slice(28);try{const e=await crypto.subtle.decrypt({name:"AES-GCM",iv:o},n,l);if(c=JSON.parse((new TextDecoder).decode(e)),manifestCache.size>5){const e=manifestCache.keys().next().value;manifestCache.delete(e)}manifestCache.set(t,c)}catch(e){return new Response("Password Incorrect",{status:403})}}const l=c[a]||c[a+".html"]||c[a+"/index.html"];if(!l)return new Response("File not found: "+a,{status:404});const p=l.size;if(0===p)return new Response(new Uint8Array(0),{status:200,headers:{"Content-Type":l.type||getMimeType(a),"Content-Length":"0"}});const d=await getCachedFileHandle(e,t,"content/"+l.id);if(!d)return new Response("File data not found: "+a,{status:404});const h=await getFileFromHandle(d,e,t,"content/"+l.id);if(!h)return new Response("File data not found: "+a,{status:404});const u=s.headers.get("Range");let f=0,g=p-1;if(u){const e=u.replace(/bytes=/,"").split("-");f=parseInt(e[0],10),e[1]&&(g=parseInt(e[1],10)),isNaN(f)&&(f=0),isNaN(g)&&(g=p-1),g>=p&&(g=p-1)}if(f>=p)return new Response(null,{status:416,headers:{"Content-Range":`bytes */${p}`}});const m=Math.floor(f/i),w=Math.floor(g/i);let y=m;const R=new ReadableStream({async pull(e){if(y>w)e.close();else try{const t=y*(i+o),a=(y*i+i>=p&&p%i||i)+o,s=h.slice(t,t+a),r=await s.arrayBuffer();if(0===r.byteLength)return void e.close();const c=r.slice(0,12),l=r.slice(12),d=await crypto.subtle.decrypt({name:"AES-GCM",iv:c},n,l),u=new Uint8Array(d),m=y*i,w=Math.max(f,m),R=Math.min(g+1,m+u.length);w<R&&e.enqueue(u.subarray(w-m,R-m)),y++}catch(t){e.error(t)}}}),C={...r,"Content-Type":l.type||getMimeType(a),"Content-Length":(g-f+1).toString(),"Accept-Ranges":"bytes","Cache-Control":"no-store"};return u?(C["Content-Range"]=`bytes ${f}-${g}/${p}`,new Response(R,{status:206,headers:C})):new Response(R,{status:200,headers:C})}catch(e){return new Response("Internal Encryption Error",{status:500})}}self.addEventListener("install",e=>{e.waitUntil(async function(){const e=await caches.open("fc");await Promise.all(APP_SHELL_FILES.map(async t=>{try{const a=await fetch(t,{cache:"reload"});a.ok&&await e.put(t,a)}catch(e){console.warn("Failed to cache app shell file:",t)}})),await self.skipWaiting()}())}),self.addEventListener("activate",e=>{e.waitUntil(async function(){await self.clients.claim(),registryCache=null;try{await getOpfsRoot()}catch(e){}const e=await self.clients.matchAll({includeUncontrolled:!0});for(const t of e)t.postMessage({type:"SW_READY"})}())}),self.addEventListener("message",e=>{if(!e.data)return;const t=e.source?e.source.id:null;switch(e.data.type){case"SET_RULES":const{rules:a,headers:n,key:s,folderName:r}=e.data,i=compileRules(a),o=parseCustomHeaders(n);if(compiledHeaderCache.set(r,{raw:n,compiled:o}),pendingNavData||(pendingNavData={}),pendingNavData[r]={rules:a,compiledRules:i,headers:n,compiledHeaders:o,key:s,timestamp:Date.now()},t){const e=clientSessionStore.get(t)||{};e.rules=a,e.compiledRules=i,e.headers=n,e.compiledHeaders=o,s&&(e.key=s),e.timestamp=Date.now(),clientSessionStore.set(t,e)}e.ports&&e.ports[0]&&e.ports[0].postMessage("OK"),setTimeout(()=>{pendingNavData&&pendingNavData[r]&&Date.now()-pendingNavData[r].timestamp>5e3&&delete pendingNavData[r]},6e3);break;case"INVALIDATE_CACHE":registryCache=null,handleCache.clear(),manifestCache.clear(),dirHandleCache.clear(),e.waitUntil(async function(){const a=await self.clients.matchAll({includeUncontrolled:!0});for(const n of a)n.id!==t&&n.postMessage({type:"INVALIDATE_CACHE",folderName:e.data.folderName})}());break;case"PREPARE_FOR_IMPORT":registryCache=null,e.source&&e.source.postMessage({type:"IMPORT_READY"})}}),self.addEventListener("fetch",e=>{const{request:t,clientId:a}=e,n=new URL(t.url);if(NETWORK_ALLOWLIST_PREFIXES.some(e=>n.pathname.startsWith(e)))return;if(n.pathname.startsWith(virtualPathPrefix)&&!n.pathname.substring(virtualPathPrefix.length).includes("/"))return void e.respondWith(Response.redirect(n.href+"/",301));const s=n.origin+n.pathname;let r=null;if(t.referrer&&n.origin===self.location.origin)try{const e=new URL(t.referrer);if(e.pathname.startsWith(virtualPathPrefix)){const t=e.pathname.substring(virtualPathPrefix.length).split("/");t.length>0&&t[0]&&(r=t[0])}}catch(e){}if(r&&!n.pathname.startsWith(virtualPathPrefix)){const i=`${self.location.origin}${virtualPathPrefix}${r}${n.pathname}`;return void e.respondWith((async()=>{const e="navigate"===t.mode?new Request(i,{headers:t.headers,credentials:t.credentials,referrer:t.referrer}):new Request(i,t),n=await generateResponseForVirtualFile(e,a);return 404!==n.status?n:FULL_APP_SHELL_URLS.includes(s)?await caches.match(t,{ignoreSearch:!0})||fetch(t):n})())}FULL_APP_SHELL_URLS.includes(s)?e.respondWith((async()=>await caches.match(t,{ignoreSearch:!0})||fetch(t))()):n.pathname.startsWith(virtualPathPrefix)&&e.respondWith(generateResponseForVirtualFile(t,a,e.resultingClientId))});const TEXT_MIME_REGEX=/^(text\/|application\/(javascript|json|xml|x-javascript|typescript|x-sh|x-httpd-php|ld\+json|manifest\+json|svg\+xml))/i,TEXT_EXTENSIONS=/\.(txt|html|htm|js|mjs|css|json|md|xml|svg|sh|php|py|rb|c|cpp|h|ts|sql|ini|yaml|yml)$/i;function isLikelyText(e,t){return TEXT_MIME_REGEX.test(e)||TEXT_EXTENSIONS.test(t)}function isActuallyTextSniff(e){const t=new Uint8Array(e,0,Math.min(e.byteLength,4096));if(0===t.length)return!0;if(t.includes(0))return!1;if(239===t[0]&&187===t[1]&&191===t[2]||255===t[0]&&254===t[1]||254===t[0]&&255===t[1])return!0;try{return STRICT_TEXT_DECODER.decode(t),!0}catch(e){let a=0;for(let e=0;e<t.length;e++){const n=t[e];(n<7||n>14&&n<32)&&a++}return a/t.length<.05}}async function generateResponseForVirtualFile(e,t,a){try{const n=new URL(e.url),{mode:s}=e,r=n.pathname.substring(virtualPathPrefix.length).split("/").map(e=>{try{return decodeURIComponent(e)}catch(t){return e}}),i=r[0];let o=await getOpfsRoot();registryCache||await getRegistry();const c=(registryCache||{})[i]||{};if(reloadOnRequest&&"navigate"===s&&!n.searchParams.has("boot"))return n.searchParams.set("boot","1"),new Response(`<!DOCTYPE html><script>location.replace("${n.href}")<\/script>`,{headers:{"Content-Type":"text/html"}});const l=a||t;let p=clientSessionStore.get(l)||(t?clientSessionStore.get(t):null);!p&&pendingNavData&&pendingNavData[i]&&(p=pendingNavData[i]),p&&(p.timestamp=Date.now(),l&&clientSessionStore.set(l,p)),p=p||{};let d=r.slice(1).join("/");if(d&&!d.endsWith("/")||(d+="index.html"),"password"===c.encryptionType){if(!p.key)return new Response("Session locked",{status:403});const t=p.compiledHeaders||getCompiledHeaders(i,c.headers),a=applyCustomHeaders({"Content-Type":getMimeType(d),"Cache-Control":"no-store","Accept-Ranges":"bytes"},d,t);return await handleEncryptedRequest(o,i,d,p.key,e,a)}let h=await getCachedFileHandle(o,i,d);if(!h&&!d.includes(".")&&!d.endsWith("/")){const e=await getCachedFileHandle(o,i,d+".html");e&&(h=e,d+=".html")}let u=200;if(!h&&("navigate"===s||(e.headers.get("Accept")||"").includes("text/html"))){const e=await getCachedFileHandle(o,i,"404.html");if(e)h=e,d="404.html",u=404;else if(!d.endsWith("index.html")){const e=await getCachedFileHandle(o,i,"index.html");e&&(h=e,d="index.html")}}if(!h)return new Response("File not found: "+i+"/"+d,{status:404});const f=await getFileFromHandle(h,o,i,d);if(!f)return new Response("File not found: "+i+"/"+d,{status:404});const g=f.size,m=f.type||getMimeType(d)||"application/octet-stream",w=p.compiledHeaders||getCompiledHeaders(i,c.headers),y=p.compiledRules||compileRules(c.rules);let R=applyCustomHeaders({"Content-Type":m,"Cache-Control":"no-store","Accept-Ranges":"bytes"},d,w),C=f,E=g;if(y&&y.length>0&&g>0&&g<=10485760&&isLikelyText(m,d)&&isActuallyTextSniff(await f.slice(0,4096).arrayBuffer())){const e=await f.arrayBuffer(),t=applyRegexRules(d,e,m,y);t!==e?(C=t,E=t.byteLength):C=e}const x=e.headers.get("Range");if(x&&200===u){const e=x.replace(/bytes=/,"").split("-");let t=parseInt(e[0],10)||0,a=e[1]?parseInt(e[1],10):E-1;if(t>=E)return new Response(null,{status:416,headers:{"Content-Range":`bytes */${E}`}});a=Math.min(a,E-1);const n=a-t+1;R["Content-Range"]=`bytes ${t}-${a}/${E}`,R["Content-Length"]=n.toString();const s=C.slice(t,a+1);return new Response(s,{status:206,headers:R})}return R["Content-Length"]=E.toString(),new Response(C,{status:u,headers:R})}catch(e){return console.error("SW fetch error:",e),new Response("Internal server error",{status:500})}}