import { BareResponseFetch } from "@mercuryworkshop/bare-mux"; import IDBMap from "@webreflection/idb-map"; declare global { interface Window { ScramjetServiceWorker; } } self.ScramjetServiceWorker = class ScramjetServiceWorker { client: typeof self.$scramjet.shared.BareClient.prototype; config: typeof self.$scramjet.config; constructor(config = self.$scramjet.config) { this.client = new self.$scramjet.shared.BareClient(); if (!config.prefix) config.prefix = "/scramjet/"; this.config = config; } route({ request }: FetchEvent) { if (request.url.startsWith(location.origin + this.config.prefix)) return true; else return false; } async fetch({ request }: FetchEvent) { const urlParam = new URLSearchParams(new URL(request.url).search); const { encodeUrl, decodeUrl, rewriteHeaders, rewriteHtml, rewriteJs, rewriteCss, rewriteWorkers } = self.$scramjet.shared; if (urlParam.has("url")) { return Response.redirect(encodeUrl(urlParam.get("url"), new URL(urlParam.get("url")))) } try { const url = new URL(decodeUrl(request.url)); const cookieStore = new IDBMap(url.origin, { durability: "relaxed", prefix: "Cookies" }); const response: BareResponseFetch = await this.client.fetch(url, { method: request.method, body: request.body, headers: request.headers, credentials: "omit", mode: request.mode === "cors" ? request.mode : "same-origin", cache: request.cache, redirect: request.redirect, //@ts-ignore why the fuck is this not typed mircosoft duplex: "half", }); let responseBody; const responseHeaders = rewriteHeaders(response.rawHeaders, url); for (const cookie of (responseHeaders["set-cookie"] || []) as string[]) { let cookieParsed = cookie.split(";").map(x=>x.trim().split("=")); let [key, value] = cookieParsed.shift(); value = value.replace("\"", ""); cookieStore.set(key, { value: value, args: cookieParsed }); } if (response.body) { switch (request.destination) { case "iframe": case "document": if (responseHeaders["content-type"].toString().startsWith("text/html")) { responseBody = rewriteHtml(await response.text(), url); } else { responseBody = response.body; } break; case "script": responseBody = rewriteJs(await response.text(), url); break; case "style": responseBody = rewriteCss(await response.text(), url); break; case "sharedworker": case "worker": responseBody = rewriteWorkers(await response.text(), url); break; default: responseBody = response.body; break; } } // downloads if (["document", "iframe"].includes(request.destination)) { const header = responseHeaders["content-disposition"]; // validate header and test for filename if (!/\s*?((inline|attachment);\s*?)filename=/i.test(header)) { // if filename= wasn"t specified then maybe the remote specified to download this as an attachment? // if it"s invalid then we can still possibly test for the attachment/inline type const type = /^\s*?attachment/i.test(header) ? "attachment" : "inline"; // set the filename const [filename] = new URL(response.finalURL).pathname .split("/") .slice(-1); responseHeaders[ "content-disposition" ] = `${type}; filename=${JSON.stringify(filename)}`; } } if (responseHeaders["accept"] === "text/event-stream") { responseHeaders["content-type"] = "text/event-stream"; } if (crossOriginIsolated) { responseHeaders["Cross-Origin-Embedder-Policy"] = "require-corp"; } return new Response(responseBody, { headers: responseHeaders as HeadersInit, status: response.status, statusText: response.statusText }) } catch (err) { if (!["document", "iframe"].includes(request.destination)) return new Response(undefined, { status: 500 }); console.error(err); return renderError(err, decodeUrl(request.url)); } } } function errorTemplate( trace: string, fetchedURL: string, ) { // turn script into a data URI so we don"t have to escape any HTML values const script = ` errorTrace.value = ${JSON.stringify(trace)}; fetchedURL.textContent = ${JSON.stringify(fetchedURL)}; for (const node of document.querySelectorAll("#hostname")) node.textContent = ${JSON.stringify( location.hostname )}; reload.addEventListener("click", () => location.reload()); version.textContent = "0.0.1"; ` return ( ` Error

Error processing your request


Failed to load

Internal Server Error

Try:

If you"re the administrator of , try:


Scramjet v

` ); } /** * * @param {unknown} err * @param {string} fetchedURL */ function renderError(err, fetchedURL) { const headers = { "content-type": "text/html", }; if (crossOriginIsolated) { headers["Cross-Origin-Embedd'er-Policy"] = "require-corp"; } return new Response( errorTemplate( String(err), fetchedURL ), { status: 500, headers: headers } ); }