feat: added scramjet. it is broken. but it will soon be not

This commit is contained in:
QuiteAFancyEmerald 2024-12-18 23:22:45 -08:00
parent 9fd91834dc
commit 214716e28c
No known key found for this signature in database
GPG key ID: 2C9730062CD48870
20 changed files with 577 additions and 96 deletions

View file

@ -122,6 +122,7 @@ commands: for (let i = 2; i < process.argv.length; i++)
await build({ await build({
entryPoints: [ entryPoints: [
'./views/uv/**/*.js', './views/uv/**/*.js',
'./views/scram/**/*.js',
'./views/assets/js/**/*.js', './views/assets/js/**/*.js',
'./views/assets/css/**/*.css', './views/assets/css/**/*.css',
], ],

View file

@ -36,6 +36,7 @@ NOT entire folders check src/routes.mjs and add it manually. */
'retro-games': 'pages/nav/emulibrary.html', 'retro-games': 'pages/nav/emulibrary.html',
/* Proxies */ /* Proxies */
ultraviolet: 'pages/proxnav/ultraviolet.html', ultraviolet: 'pages/proxnav/ultraviolet.html',
scramjet: 'pages/proxnav/scramjet.html',
uverror: 'pages/proxnav/ultraviolet-error.html', uverror: 'pages/proxnav/ultraviolet-error.html',
rammerhead: 'pages/proxnav/rammerhead.html', rammerhead: 'pages/proxnav/rammerhead.html',
/* Proxy Presets */ /* Proxy Presets */

View file

@ -203,6 +203,23 @@ app.register(fastifyStatic, {
decorateReply: false, decorateReply: false,
}); });
// This combines scripts from the official scramjet repository with local scramjet scripts into
// one directory path. Local versions of files override the official versions.
app.register(fastifyStatic, {
root: [
fileURLToPath(
new URL(
// Use the pre-compiled, minified scripts instead, if enabled in config.
config.minifyScripts ? '../views/dist/scram' : '../views/scram',
import.meta.url
)
),
uvPath,
],
prefix: '/scram/',
decorateReply: false,
});
// Register proxy paths to the website. // Register proxy paths to the website.
app.register(fastifyStatic, { app.register(fastifyStatic, {
root: epoxyPath, root: epoxyPath,

View file

@ -63,7 +63,7 @@ const setAuthCookie = (s, lax) => {
// Search engine is set to Bing. Intended to work just like the usual // Search engine is set to Bing. Intended to work just like the usual
// bar at the top of a browser. // bar at the top of a browser.
const sx = 'bing.com' + '/search?q=', const sx = 'startpage.com/sp' + '/search?query=',
/* /*
omnibox = url => omnibox = url =>
(url.indexOf("http") (url.indexOf("http")
@ -107,6 +107,19 @@ const sx = 'bing.com' + '/search?q=',
url = search(url); url = search(url);
} }
return url; return url;
},
// Parse a URL to use with Scramjet.
sjUrl = (url) => {
try {
url =
location.origin +
"/scram/service/" +
search(url);
} catch (e) {
// This is for cases where the SJ scripts have not been loaded.
url = search(url);
}
return url;
}; };
/* RAMMERHEAD CONFIGURATION */ /* RAMMERHEAD CONFIGURATION */
@ -364,6 +377,8 @@ addEventListener('DOMContentLoaded', async () => {
// setAuthCookie("__cor_auth=1", false); // setAuthCookie("__cor_auth=1", false);
ultraviolet: urlHandler(uvUrl), ultraviolet: urlHandler(uvUrl),
scramjet: urlHandler(sjUrl),
rammerhead: asyncUrlHandler( rammerhead: asyncUrlHandler(
async (url) => location.origin + (await RammerheadEncode(search(url))) async (url) => location.origin + (await RammerheadEncode(search(url)))
), ),
@ -451,6 +466,7 @@ addEventListener('DOMContentLoaded', async () => {
}; };
prSet('pr-uv', 'ultraviolet'); prSet('pr-uv', 'ultraviolet');
prSet('pr-sj', 'scramjet');
prSet('pr-rh', 'rammerhead'); prSet('pr-rh', 'rammerhead');
prSet('pr-yt', 'youtube'); prSet('pr-yt', 'youtube');
prSet('pr-rh-dc', 'discordRH'); prSet('pr-rh-dc', 'discordRH');

View file

@ -1,98 +1,99 @@
// Encase everything in a new scope so that variables are not accidentally // Encase everything in a new scope so that variables are not accidentally
// attached to the global scope. // attached to the global scope.
(() => { (() => {
const stockSW = '/uv/sw.js', const stockSW = '/uv/sw.js',
blacklistSW = '/uv/sw-blacklist.js', blacklistSW = '/uv/sw-blacklist.js',
swAllowedHostnames = ['localhost', '127.0.0.1'], swAllowedHostnames = ['localhost', '127.0.0.1'],
connection = new BareMux.BareMuxConnection('/baremux/worker.js'), connection = new BareMux.BareMuxConnection('/baremux/worker.js'),
wispUrl = wispUrl =
(location.protocol === 'https:' ? 'wss' : 'ws') + (location.protocol === 'https:' ? 'wss' : 'ws') +
'://' + '://' +
location.host + location.host +
'/wisp/', '/wisp/',
// Proxy configuration scramjet = new ScramjetController({
proxyUrl = 'socks5h://localhost:9050', // Replace with your proxy URL prefix: "/scram/service/",
transports = { files: {
epoxy: '/epoxy/index.mjs', wasm: "/scram/scramjet.wasm.js",
libcurl: '/libcurl/index.mjs', worker: "/scram/scramjet.worker.js",
bare: '/baremux/index.mjs', client: "/scram/scramjet.client.js",
}, shared: "/scram/scramjet.shared.js",
// The following two variables are copied and pasted here from csel.js. sync: "/scram/scramjet.sync.js"
readCookie = async (name) => { },
// Get the first cookie that has the same name. flags: {
for (let cookie of document.cookie.split('; ')) serviceworkers: true,
if (!cookie.indexOf(name + '=')) syncxhr: true,
// Return the cookie's stored content. scramitize: true,
return decodeURIComponent(cookie.slice(name.length + 1)); },
}, }),
// Sets the default transport mode based on the browser. Firefox is not // Proxy configuration
// supported by epoxy yet, which is why this is implemented. proxyUrl = 'socks5h://localhost:9050', // Replace with your TOR proxy URL (or any)
defaultMode = /(?:Chrome|AppleWebKit)\//.test(navigator.userAgent) transports = {
? 'epoxy' epoxy: '/epoxy/index.mjs',
: 'libcurl'; libcurl: '/libcurl/index.mjs',
bare: '/baremux/index.mjs',
},
// The following two variables are copied and pasted here from csel.js.
readCookie = async (name) => {
// Get the first cookie that has the same name.
for (let cookie of document.cookie.split('; '))
if (!cookie.indexOf(name + '='))
// Return the cookie's stored content.
return decodeURIComponent(cookie.slice(name.length + 1));
},
// Sets the default transport mode based on the browser. Firefox is not
// supported by epoxy yet, which is why this is implemented.
defaultMode = /(?:Chrome|AppleWebKit)\//.test(navigator.userAgent)
? 'epoxy'
: 'libcurl';
transports.default = transports[defaultMode]; transports.default = transports[defaultMode];
// Prevent the transports object from accidentally being edited. // Prevent the transports object from accidentally being edited.
Object.freeze(transports); Object.freeze(transports);
const registerSW = async () => { const registerSW = async () => {
if (!navigator.serviceWorker) { if (!navigator.serviceWorker) {
if ( if (
location.protocol !== 'https:' && location.protocol !== 'https:' &&
!swAllowedHostnames.includes(location.hostname) !swAllowedHostnames.includes(location.hostname)
) )
throw new Error('Service workers cannot be registered without https.'); throw new Error('Service workers cannot be registered without https.');
throw new Error("Your browser doesn't support service workers."); throw new Error("Your browser doesn't support service workers.");
} }
// If the user has changed the transport mode, use that over the default. // If the user has changed the transport mode, use that over the default.
const transportMode = const transportMode =
transports[await readCookie('HBTransport')] || transports.default; transports[await readCookie('HBTransport')] || transports.default;
let transportOptions = { wisp: wispUrl }; let transportOptions = { wisp: wispUrl };
// Only use Tor with the proxy if the user has enabled it in settings. // Only use Tor with the proxy if the user has enabled it in settings.
if ((await readCookie('HBUseOnion')) === 'true') if ((await readCookie('HBUseOnion')) === 'true')
transportOptions.proxy = proxyUrl; transportOptions.proxy = proxyUrl;
await connection.setTransport(transportMode, [transportOptions]); await connection.setTransport(transportMode, [transportOptions]);
/* Choose a service worker to register based on whether or not the user /* Choose a service worker to register based on whether or not the user
* has ads enabled. If the user changes this setting, this script needs * has adblocking enabled. If the user changes this setting, this script needs
* to be reloaded for this to update, such as by refreshing the page. * to be reloaded for this to update, such as by refreshing the page.
*/ */
const registrations = await navigator.serviceWorker.getRegistrations(), const registrations = await navigator.serviceWorker.getRegistrations(),
usedSW = usedSW =
(await readCookie('HBHideAds')) !== 'false' ? blacklistSW : stockSW; (await readCookie('HBHideAds')) !== 'false' ? blacklistSW : stockSW;
// Unregister a service worker if it isn't the one being used. // Unregister a service worker if it isn't the one being used.
for (const registration of registrations) for (const registration of registrations)
if ( if (
registration.active && registration.active &&
new URL(registration.active.scriptURL).pathname !== new URL(registration.active.scriptURL).pathname !==
new URL(usedSW, location.origin).pathname new URL(usedSW, location.origin).pathname
) )
await registration.unregister(); await registration.unregister();
await navigator.serviceWorker.register(usedSW); await navigator.serviceWorker.register(usedSW);
}; };
/* // Register the service worker
registerSW();
Commented out upon discovering that a duplicate BareMux connection may be scramjet.init("/scram/scramjet.sw.js");
unnecessary; previously thought to have prevented issues with refreshing.
async function setupTransportOnLoad() {
const conn = new BareMux.BareMuxConnection("/baremux/worker.js");
if (await conn.getTransport() !== "/baremux/module.js") {
await conn.setTransport("/libcurl/index.mjs", [{ wisp: wispUrl, proxy: proxyUrl }]);
}
}
// Run transport setup on page load.
setupTransportOnLoad();
*/
registerSW();
})(); })();

View file

@ -0,0 +1,172 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, shrink-to-fit=no"
/>
<title>Holy Unblocker LTS | Scramjet Proxy</title>
<meta
name="description"
content="The new highly innovative proxy of Titanium Network using technologies such as service workers and sophisticated rewriting techniques with CAPTCHA support. Ultraviolet focuses on speed with YouTube, now.gg, Spotify, CoolMathGames and various .io sites!"
/>
<meta
name="theme-color"
media="(prefers-color-scheme: dark)"
content="#434c5e"
/>
<link
rel="apple-touch-icon"
sizes="57x57"
href="assets/ico/apple-icon-57x57.png"
/>
<link
rel="apple-touch-icon"
sizes="60x60"
href="assets/ico/apple-icon-60x60.png"
/>
<link
rel="apple-touch-icon"
sizes="72x72"
href="assets/ico/apple-icon-72x72.png"
/>
<link
rel="apple-touch-icon"
sizes="76x76"
href="assets/ico/apple-icon-76x76.png"
/>
<link
rel="apple-touch-icon"
sizes="114x114"
href="assets/ico/apple-icon-114x114.png"
/>
<link
rel="apple-touch-icon"
sizes="120x120"
href="assets/ico/apple-icon-120x120.png"
/>
<link
rel="apple-touch-icon"
sizes="144x144"
href="assets/ico/apple-icon-144x144.png"
/>
<link
rel="apple-touch-icon"
sizes="152x152"
href="assets/ico/apple-icon-152x152.png"
/>
<link
rel="apple-touch-icon"
sizes="180x180"
href="assets/ico/apple-icon-180x180.png"
/>
<link
rel="icon"
type="image/png"
sizes="192x192"
href="assets/ico/android-icon-192x192.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="assets/ico/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="96x96"
href="assets/ico/favicon-96x96.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="assets/ico/favicon-16x16.png"
/>
<link rel="icon" href="assets/ico/favicon.ico" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="dns-prefetch" href="https://fonts.googleapis.com" />
<link rel="stylesheet" href="assets/css/styles.css" />
<script src="/baremux/index.js" defer></script>
<script src="/uv/uv.bundle.js" defer></script>
<script src="/uv/uv.config.js" defer></script>
<link rel="prefetch" href="/scram/scramjet.worker.js" />
<link rel="prefetch" href="/scram/scramjet.shared.js" />
<script src="/assets/js/register-sw.js" defer></script>
<script src="
https://cdn.jsdelivr.net/npm/tsparticles@3.5/tsparticles.bundle.min.js
"></script>
</head>
<body>
<!-- IMPORTANT-HUCOOKINGINSERT-DONOTDELETE -->
<div id="header" class="fullwidth"><!--HEADER--></div>
<div id="particles-js" class="fullwidth"></div>
<div id="mainbody" class="fullwidth">
<div class="ad" id="ad-left"></div>
<div class="ad" id="ad-right"></div>
<!-- IMPORTANT-HUCOOKINGINSERT-DONOTDELETE -->
<div class="box box-medium text-center textm">
<h1 class="bigtitle">Scramjet (Beta)</h1>
<p>
Scramjet is an experimental interception based web proxy that aims to
be the successor to Ultraviolet.<br>
</p>
<div id="pr-sj" class="pr-form">
<input
type="text"
spellcheck="false"
autocomplete="off"
placeholder="Search or enter in a target site!"
/>
<a href="#" style="display: none" class="pr-button glowbutton pr-go1"
>Stea&#173;lth</a
>
<a href="#" class="pr-button glowbutton pr-go2">Stea&#173;lth</a>
</div>
<h3>
N<wbr />etwo<wbr />rk fi<wbr />x i<wbr />f erro<wbr />rs occ<wbr />urs
or bla<wbr />nk pa<wbr />ge:
</h3>
<button id="reload" class="homebutton glowbutton">
Ref<wbr />re<wbr />sh
</button>
<br /><br />
<h3>More Information:</h3>
<div class="font3">
<p class="accented">
Scramjet works with almost all sites including Google, Youtube, Spotify, Discord,
Reddit, GeForce NOW, and now.gg!
<br><br />Scramjet is highly recommended due
to security, developer friendliness, and performance along enhanced
support for almost every site making it one of the most advanced web
proxies. <br />
</p>
<p class="accented">
Common Errors with Solutions:
<br />- Having issues with CAPTCHAs? It will take trial and error
but try to go slow when it comes to solving them. CAPTCHA is a given
but will take a few attempts.<br />- You may not be able to login
normally into a number of sites. Phone verification on a select
number of sites may occur also with no real soluion.
</p>
<p>
GitHub:&nbsp;<a class="bluelink" id="tnlink"
><strong>https://github.com/MercuryWorkshop/scramjet</strong></a
>
</p>
</div>
</div>
</div>
<div id="footer" class="fullwidth"><!--FOOTER--></div>
<!-- IMPORTANT-HUCOOKINGINSERT-DONOTDELETE -->
<script src="/scram/scramjet.controller.js" defer></script>
<script src="/assets/js/register-sw.js" defer></script>
<script src="assets/js/common.js"></script>
<script src="assets/js/csel.js"></script>
<script src="{{src}}"></script>
<script defer="defer" src="assets/js/particlesjs/particles.js"></script>
</body>
</html>

View file

@ -51,7 +51,7 @@
<div class="ad" id="ad-right"></div> <div class="ad" id="ad-right"></div>
<!-- IMPORTANT-HUCOOKINGINSERT-DONOTDELETE --> <!-- IMPORTANT-HUCOOKINGINSERT-DONOTDELETE -->
<div class="box box-medium text-center textm"> <div class="box box-medium text-center textm">
<h1 class="bigtitle">Ultraviolet (Beta)</h1> <h1 class="bigtitle">Ultraviolet</h1>
<p> <p>
The highly innovative proxy of Titanium Network using technologies The highly innovative proxy of Titanium Network using technologies
such as service workers and sophisticated rewriting techniques with such as service workers and sophisticated rewriting techniques with

View file

@ -60,6 +60,7 @@
advanced security measures. advanced security measures.
</h2> </h2>
<div class="box-button-container"> <div class="box-button-container">
<a href="/scramjet" class="box-button">Scramjet (Beta)</a>
<a href="/ultraviolet" class="box-button">Ultraviolet</a> <a href="/ultraviolet" class="box-button">Ultraviolet</a>
<a href="/rammerhead" class="box-button">Rammerhead</a> <a href="/rammerhead" class="box-button">Rammerhead</a>
</div> </div>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,11 @@
(()=>{"use strict";var e={1762:function(e,r,t){t.d(r,{Z:function(){return o}});let o={fmt:function(e,r,...t){let o=Error.prepareStackTrace;Error.prepareStackTrace=(e,r)=>{r.shift(),r.shift(),r.shift();let t="";for(let e=1;e<Math.min(2,r.length);e++)r[e].getFunctionName()&&(t+=`${r[e].getFunctionName()} -> `+t);return t+=r[0].getFunctionName()||"Anonymous"};let n=function(){try{throw Error()}catch(e){return e.stack}}();Error.prepareStackTrace=o;let c=console[e]||console.log;c(`%c${n}%c ${r}`,`
background-color: ${{log:"#000",warn:"#f80",error:"#f00",debug:"transparent"}[e]};
color: ${{log:"#fff",warn:"#fff",error:"#fff",debug:"gray"}[e]};
padding: ${{log:2,warn:4,error:4,debug:0}[e]}px;
font-weight: bold;
font-family: monospace;
font-size: 0.9em;
`,`${"debug"===e?"color: gray":""}`,...t)},log:function(e,...r){this.fmt("log",e,...r)},warn:function(e,...r){this.fmt("warn",e,...r)},error:function(e,...r){this.fmt("error",e,...r)},debug:function(e,...r){this.fmt("debug",e,...r)}}}},r={};function t(o){var n=r[o];if(void 0!==n)return n.exports;var c=r[o]={exports:{}};return e[o](c,c.exports,t),c.exports}t.d=function(e,r){for(var o in r)t.o(r,o)&&!t.o(e,o)&&Object.defineProperty(e,o,{enumerable:!0,get:r[o]})},t.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)};let o=Symbol.for("scramjet client global"),n=Symbol.for("scramjet frame handle");var c=t(1762).Z;class s extends EventTarget{controller;frame;constructor(e,r){super(),this.controller=e,this.frame=r,r[n]=this}get client(){return this.frame.contentWindow.window[o]}get url(){return this.client.url}go(e){e instanceof URL&&(e=e.toString()),c.log("navigated to",e),this.frame.src=this.controller.encodeUrl(e)}back(){this.frame.contentWindow?.history.back()}forward(){this.frame.contentWindow?.history.forward()}reload(){this.frame.contentWindow?.location.reload()}}!("$scramjet"in self)&&(self.$scramjet={version:{build:"1efcf85",version:"1.0.2-dev"},codec:{},flagEnabled:function(e,r){let t=i.config.flags[e];for(let t in i.config.siteFlags){let o=i.config.siteFlags[t];if(new RegExp(t).test(r.href)&&e in o)return o[e]}return t}});let i=self.$scramjet,a=Function;function f(){i.codec.encode=a("url",i.config.codec.encode),i.codec.decode=a("url",i.config.codec.decode)}var l=t(1762).Z;window.ScramjetController=class e{db;constructor(e){let r={prefix:"/scramjet/",globals:{wrapfn:"$scramjet$wrap",wrapthisfn:"$scramjet$wrapthis",trysetfn:"$scramjet$tryset",importfn:"$scramjet$import",rewritefn:"$scramjet$rewrite",metafn:"$scramjet$meta",setrealmfn:"$scramjet$setrealm",pushsourcemapfn:"$scramjet$pushsourcemap"},files:{wasm:"/scramjet.wasm.js",shared:"/scramjet.shared.js",worker:"/scramjet.worker.js",client:"/scramjet.client.js",sync:"/scramjet.sync.js"},flags:{serviceworkers:!1,syncxhr:!1,naiiveRewriter:!1,strictRewrites:!0,rewriterLogs:!0,captureErrors:!0,cleanErrors:!1,scramitize:!1,sourcemaps:!1},siteFlags:{},codec:{encode:`if (!url) return url;
return encodeURIComponent(url);`,decode:`if (!url) return url;
return decodeURIComponent(url);`}},t=(e,r)=>{for(let o in r)r[o]instanceof Object&&o in e&&Object.assign(r[o],t(e[o],r[o]));return Object.assign(e||{},r)};i.config=t(r,e)}async init(e){f(),await this.openIDB();let r=await navigator.serviceWorker.register(e);return l.log("service worker registered"),r}createFrame(e){return!e&&(e=document.createElement("iframe")),new s(this,e)}encodeUrl(e){return e instanceof URL&&(e=e.toString()),i.config.prefix+i.codec.encode(e)}decodeUrl(e){return e instanceof URL&&(e=e.toString()),i.codec.decode(e)}async openIDB(){let e=indexedDB.open("$scramjet",1);return new Promise((r,t)=>{e.onsuccess=async()=>{this.db=e.result,await this.#e(),r(e.result)},e.onupgradeneeded=()=>{let r=e.result;!r.objectStoreNames.contains("config")&&r.createObjectStore("config"),!r.objectStoreNames.contains("cookies")&&r.createObjectStore("cookies")},e.onerror=()=>t(e.error)})}async #e(){if(!this.db){console.error("Store not ready!");return}let e=this.db.transaction("config","readwrite").objectStore("config").put(i.config,"config");return new Promise((r,t)=>{e.onsuccess=r,e.onerror=t})}async modifyConfig(e){i.config=Object.assign({},i.config,e),f(),await this.#e()}}})();
//# sourceMappingURL=scramjet.controller.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,26 @@
importScripts(
"/scram/scramjet.wasm.js",
"/scram/scramjet.shared.js",
"/scram/scramjet.worker.js"
);
const scramjet = new ScramjetServiceWorker();
async function handleRequest(event) {
await scramjet.loadConfig();
if (scramjet.route(event)) {
return scramjet.fetch(event);
}
return fetch(event.request);
}
self.addEventListener("fetch", (event) => {
event.respondWith(handleRequest(event));
});
let playgroundData;
self.addEventListener("message", ({ data }) => {
if (data.type === "playgroundData") {
playgroundData = data;
}
});

View file

@ -0,0 +1,2 @@
addEventListener("message",({data:{sab:e,args:[t,n,s,r,o],body:a,headers:g}})=>{let i=new DataView(e),l=new Uint8Array(e),d=new XMLHttpRequest;if(d.responseType="arraybuffer",d.open(t,n,!0,r,o),g)for(let[e,t]of Object.entries(g))d.setRequestHeader(e,t);d.send(a),d.onload=()=>{let t=1;i.setUint16(t,d.status),t+=2;let n=d.getAllResponseHeaders();i.setUint32(t,n.length),t+=4,e.byteLength<t+n.length&&e.grow(t+n.length),l.set(new TextEncoder().encode(n),t),t+=n.length,i.setUint32(t,d.response.byteLength),t+=4,e.byteLength<t+d.response.byteLength&&e.grow(t+d.response.byteLength),l.set(new Uint8Array(d.response),t),i.setUint8(0,1)},d.ontimeout=d.onerror=d.onabort=()=>{console.error("xhr failed"),i.setUint8(0,1)}});
//# sourceMappingURL=scramjet.sync.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"scramjet.sync.js","sources":["webpack://@mercuryworkshop/scramjet/./src/sync.ts"],"sourcesContent":["addEventListener(\n\t\"message\",\n\t({\n\t\tdata: {\n\t\t\tsab,\n\t\t\targs: [method, url, _, username, password],\n\t\t\tbody,\n\t\t\theaders,\n\t\t},\n\t}) => {\n\t\tconst view = new DataView(sab);\n\t\tconst u8view = new Uint8Array(sab);\n\n\t\tconst xhr = new XMLHttpRequest();\n\t\txhr.responseType = \"arraybuffer\";\n\n\t\t// force async since we need it to resolve to the sw\n\t\txhr.open(method, url, true, username, password);\n\n\t\tif (headers)\n\t\t\tfor (const [k, v] of Object.entries(headers)) {\n\t\t\t\txhr.setRequestHeader(k, v as string);\n\t\t\t}\n\n\t\txhr.send(body);\n\n\t\txhr.onload = () => {\n\t\t\tlet cursor = 1; // first byte is the lock\n\n\t\t\tview.setUint16(cursor, xhr.status);\n\t\t\tcursor += 2;\n\n\t\t\t// next write the header string\n\t\t\tconst headers = xhr.getAllResponseHeaders();\n\t\t\tview.setUint32(cursor, headers.length);\n\t\t\tcursor += 4;\n\n\t\t\tif (sab.byteLength < cursor + headers.length)\n\t\t\t\tsab.grow(cursor + headers.length);\n\t\t\tu8view.set(new TextEncoder().encode(headers), cursor);\n\t\t\tcursor += headers.length;\n\n\t\t\tview.setUint32(cursor, xhr.response.byteLength);\n\t\t\tcursor += 4;\n\n\t\t\tif (sab.byteLength < cursor + xhr.response.byteLength)\n\t\t\t\tsab.grow(cursor + xhr.response.byteLength);\n\t\t\tu8view.set(new Uint8Array(xhr.response), cursor);\n\n\t\t\t// release the lock, main thread will stop spinning now\n\t\t\tview.setUint8(0, 1);\n\t\t};\n\t\txhr.ontimeout =\n\t\t\txhr.onerror =\n\t\t\txhr.onabort =\n\t\t\t\t() => {\n\t\t\t\t\tconsole.error(\"xhr failed\");\n\t\t\t\t\tview.setUint8(0, 1);\n\t\t\t\t};\n\t}\n);\n"],"names":["addEventListener","sab","method","url","_","username","password","body","headers","view","DataView","u8view","Uint8Array","xhr","XMLHttpRequest","k","v","Object","cursor","TextEncoder","console"],"mappings":"AAAAA,iBACC,UACA,CAAC,CACA,KAAM,CACLC,IAAAA,CAAG,CACH,KAAM,CAACC,EAAQC,EAAKC,EAAGC,EAAUC,EAAS,CAC1CC,KAAAA,CAAI,CACJC,QAAAA,CAAO,CACP,CACD,IACA,IAAMC,EAAO,IAAIC,SAAST,GACpBU,EAAS,IAAIC,WAAWX,GAExBY,EAAM,IAAIC,eAMhB,GALAD,EAAI,YAAY,CAAG,cAGnBA,EAAI,IAAI,CAACX,EAAQC,EAAK,GAAME,EAAUC,GAElCE,EACH,IAAK,GAAM,CAACO,EAAGC,EAAE,GAAIC,OAAO,OAAO,CAACT,GACnCK,EAAI,gBAAgB,CAACE,EAAGC,GAG1BH,EAAI,IAAI,CAACN,GAETM,EAAI,MAAM,CAAG,KACZ,IAAIK,EAAS,EAEbT,EAAK,SAAS,CAACS,EAAQL,EAAI,MAAM,EACjCK,GAAU,EAGV,IAAMV,EAAUK,EAAI,qBAAqB,GACzCJ,EAAK,SAAS,CAACS,EAAQV,EAAQ,MAAM,EACrCU,GAAU,EAENjB,EAAI,UAAU,CAAGiB,EAASV,EAAQ,MAAM,EAC3CP,EAAI,IAAI,CAACiB,EAASV,EAAQ,MAAM,EACjCG,EAAO,GAAG,CAAC,IAAIQ,cAAc,MAAM,CAACX,GAAUU,GAC9CA,GAAUV,EAAQ,MAAM,CAExBC,EAAK,SAAS,CAACS,EAAQL,EAAI,QAAQ,CAAC,UAAU,EAC9CK,GAAU,EAENjB,EAAI,UAAU,CAAGiB,EAASL,EAAI,QAAQ,CAAC,UAAU,EACpDZ,EAAI,IAAI,CAACiB,EAASL,EAAI,QAAQ,CAAC,UAAU,EAC1CF,EAAO,GAAG,CAAC,IAAIC,WAAWC,EAAI,QAAQ,EAAGK,GAGzCT,EAAK,QAAQ,CAAC,EAAG,EAClB,EACAI,EAAI,SAAS,CACZA,EAAI,OAAO,CACXA,EAAI,OAAO,CACV,KACCO,QAAQ,KAAK,CAAC,cACdX,EAAK,QAAQ,CAAC,EAAG,EAClB,CACH"}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,179 @@
(()=>{"use strict";var e={1762:function(e,t,r){r.d(t,{Z:function(){return o}});let o={fmt:function(e,t,...r){let o=Error.prepareStackTrace;Error.prepareStackTrace=(e,t)=>{t.shift(),t.shift(),t.shift();let r="";for(let e=1;e<Math.min(2,t.length);e++)t[e].getFunctionName()&&(r+=`${t[e].getFunctionName()} -> `+r);return r+=t[0].getFunctionName()||"Anonymous"};let s=function(){try{throw Error()}catch(e){return e.stack}}();Error.prepareStackTrace=o;let n=console[e]||console.log;n(`%c${s}%c ${t}`,`
background-color: ${{log:"#000",warn:"#f80",error:"#f00",debug:"transparent"}[e]};
color: ${{log:"#fff",warn:"#fff",error:"#fff",debug:"gray"}[e]};
padding: ${{log:2,warn:4,error:4,debug:0}[e]}px;
font-weight: bold;
font-family: monospace;
font-size: 0.9em;
`,`${"debug"===e?"color: gray":""}`,...r)},log:function(e,...t){this.fmt("log",e,...t)},warn:function(e,...t){this.fmt("warn",e,...t)},error:function(e,...t){this.fmt("error",e,...t)},debug:function(e,...t){this.fmt("debug",e,...t)}}}},t={};function r(o){var s=t[o];if(void 0!==s)return s.exports;var n=t[o]={exports:{}};return e[o](n,n.exports,r),n.exports}r.d=function(e,t){for(var o in t)r.o(t,o)&&!r.o(e,o)&&Object.defineProperty(e,o,{enumerable:!0,get:t[o]})},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)};class o{handle;origin;syncToken;promises;messageChannel;connected;constructor(e,t){this.handle=e,this.origin=t,this.syncToken=0,this.promises={},this.messageChannel=new MessageChannel,this.connected=!1,this.messageChannel.port1.addEventListener("message",e=>{"scramjet$type"in e.data&&("init"===e.data.scramjet$type?this.connected=!0:this.handleMessage(e.data))}),this.messageChannel.port1.start(),this.handle.postMessage({scramjet$type:"init",scramjet$port:this.messageChannel.port2},[this.messageChannel.port2])}handleMessage(e){let t=this.promises[e.scramjet$token];t&&(t(e),delete this.promises[e.scramjet$token])}async fetch(e){let t=this.syncToken++,r={scramjet$type:"fetch",scramjet$token:t,scramjet$request:{url:e.url,body:e.body,headers:Array.from(e.headers.entries()),method:e.method,mode:e.mode,destinitation:e.destination}},o=e.body?[e.body]:[];this.handle.postMessage(r,o);let{scramjet$response:s}=await new Promise(e=>{this.promises[t]=e});return!!s&&new Response(s.body,{headers:s.headers,status:s.status,statusText:s.statusText})}}!("$scramjet"in self)&&(self.$scramjet={version:{build:"1efcf85",version:"1.0.2-dev"},codec:{},flagEnabled:function(e,t){let r=s.config.flags[e];for(let r in s.config.siteFlags){let o=s.config.siteFlags[r];if(new RegExp(r).test(t.href)&&e in o)return o[e]}return r}});let s=self.$scramjet,n=Function,{util:{BareClient:i,ScramjetHeaders:a,BareMuxConnection:c},url:{rewriteUrl:l,unrewriteUrl:d,rewriteBlob:u,unrewriteBlob:h},rewrite:{rewriteCss:p,unrewriteCss:f,rewriteHtml:m,unrewriteHtml:g,rewriteSrcset:y,rewriteJs:b,rewriteHeaders:w,rewriteWorkers:v,htmlRules:k},CookieStore:x}=s.shared;function R(e){return{origin:e,base:e}}async function S(e,t){let r=new URLSearchParams(new URL(e.url).search);if(r.has("url"))return Response.redirect(l(r.get("url"),R(new URL(r.get("url")))));try{let o=new URL(e.url),n="";if(o.searchParams.has("type")&&(n=o.searchParams.get("type"),o.searchParams.delete("type")),o.searchParams.has("dest")&&o.searchParams.delete("dest"),o.pathname.startsWith(this.config.prefix+"blob:")||o.pathname.startsWith(this.config.prefix+"data:")){let r,s=o.pathname.substring(this.config.prefix.length);s.startsWith("blob:")&&(s=h(s));let i=await fetch(s,{}),a=s.startsWith("blob:")?s:"(data url)";i.finalURL=a,i.body&&(r=await $(i,t?{base:new URL(new URL(t.url).origin),origin:new URL(new URL(t.url).origin)}:R(new URL(d(e.referrer))),e.destination,n,this.cookieStore));let c=Object.fromEntries(i.headers.entries());return crossOriginIsolated&&(c["Cross-Origin-Opener-Policy"]="same-origin",c["Cross-Origin-Embedder-Policy"]="require-corp"),new Response(r,{status:i.status,statusText:i.statusText,headers:c})}let i=new URL(d(o)),c=this.serviceWorkers.find(e=>e.origin===i.origin);if(c&&c.connected&&"swruntime"!==r.get("from")){let t=await c.fetch(e);if(t)return t}if(i.origin==new URL(e.url).origin)throw Error("attempted to fetch from same origin - this means the site has obtained a reference to the real origin, aborting");let l=new a;for(let[t,r]of e.headers.entries())l.set(t,r);if(t&&new URL(t.url).pathname.startsWith(s.config.prefix)){let e=new URL(d(t.url));e.toString().includes("youtube.com")||(l.set("Referer",e.toString()),l.set("Origin",e.origin?`${e.protocol}//${e.host}`:"null"))}let u=this.cookieStore.getCookies(i,!1);u.length&&l.set("Cookie",u),l.set("Sec-Fetch-Dest",e.destination),l.set("Sec-Fetch-Site","same-origin"),l.set("Sec-Fetch-Mode","cors"===e.mode?e.mode:"same-origin");let p=new E(i,e.body,e.method,e.destination,t,l.headers);this.dispatchEvent(p);let f=p.response||await this.client.fetch(p.url,{method:p.method,body:p.body,headers:p.requestHeaders,credentials:"omit",mode:"cors"===e.mode?e.mode:"same-origin",cache:e.cache,redirect:"manual",duplex:"half"});return await C(i,n,e.destination,f,this.cookieStore,t,this)}catch(r){let t={message:r.message,url:e.url,destination:e.destination,timestamp:new Date().toISOString()};if(r.stack&&(t.stack=r.stack),console.error("ERROR FROM SERVICE WORKER FETCH: ",t),!["document","iframe"].includes(e.destination))return new Response(void 0,{status:500});return function(e,t){let r={"content-type":"text/html"};return crossOriginIsolated&&(r["Cross-Origin-Embedder-Policy"]="require-corp"),new Response(function(e,t){let r=`
errorTrace.value = ${JSON.stringify(e)};
fetchedURL.textContent = ${JSON.stringify(t)};
for (const node of document.querySelectorAll("#hostname")) node.textContent = ${JSON.stringify(location.hostname)};
reload.addEventListener("click", () => location.reload());
version.textContent = ${JSON.stringify(s.version.version)};
build.textContent = ${JSON.stringify(s.version.build)};
document.getElementById('copy-button').addEventListener('click', async () => {
const text = document.getElementById('errorTrace').value;
await navigator.clipboard.writeText(text);
const btn = document.getElementById('copy-button');
btn.textContent = 'Copied!';
setTimeout(() => btn.textContent = 'Copy', 2000);
});
`;return`<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Scramjet</title>
<style>
:root {
--deep: #080602;
--shallow: #181412;
--beach: #f1e8e1;
--shore: #b1a8a1;
--accent: #ffa938;
--font-sans: -apple-system, system-ui, BlinkMacSystemFont, sans-serif;
--font-monospace: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
}
*:not(div,p,span,ul,li,i,span) {
background-color: var(--deep);
color: var(--beach);
font-family: var(--font-sans);
}
textarea,
button {
background-color: var(--shallow);
border-radius: 0.6em;
padding: 0.6em;
border: none;
appearance: none;
font-family: var(--font-sans);
color: var(--beach);
}
button.primary {
background-color: var(--accent);
color: var(--deep);
font-weight: bold;
}
textarea {
resize: none;
height: 20em;
text-align: left;
font-family: var(--font-monospace);
}
body {
width: 100vw;
height: 100vh;
justify-content: center;
align-items: center;
}
body,
html,
#inner {
display: flex;
align-items: center;
flex-direction: column;
gap: 0.5em;
overflow: hidden;
}
#inner {
z-index: 100;
}
#cover {
position: absolute;
width: 100%;
height: 100%;
background-color: color-mix(in srgb, var(--deep) 70%, transparent);
z-index: 99;
}
#info {
display: flex;
flex-direction: row;
align-items: flex-start;
gap: 1em;
}
#version-wrapper {
width: auto;
text-align: right;
position: absolute;
top: 0.5rem;
right: 0.5rem;
font-size: 0.8rem;
color: var(--shore)!important;
i {
background-color: color-mix(in srgb, var(--deep), transparent 50%);
border-radius: 9999px;
padding: 0.2em 0.5em;
}
z-index: 101;
}
#errorTrace-wrapper {
position: relative;
width: fit-content;
}
#copy-button {
position: absolute;
top: 0.5em;
right: 0.5em;
padding: 0.23em;
cursor: pointer;
opacity: 0;
transition: opacity 0.4s;
font-size: 0.9em;
}
#errorTrace-wrapper:hover #copy-button {
opacity: 1;
}
</style>
</head>
<body>
<div id="cover"></div>
<div id="inner">
<h1 id="errorTitle">Uh oh!</h1>
<p>There was an error loading <b id="fetchedURL"></b></p>
<!-- <p id="errorMessage">Internal Server Error</p> -->
<div id="info">
<div id="errorTrace-wrapper">
<textarea id="errorTrace" cols="40" rows="10" readonly></textarea>
<button id="copy-button" class="primary">Copy</button>
</div>
<div id="troubleshooting">
<p>Try:</p>
<ul>
<li>Checking your internet connection</li>
<li>Verifying you entered the correct address</li>
<li>Clearing the site data</li>
<li>Contacting <b id="hostname"></b>'s administrator</li>
<li>Verify the server isn't censored</li>
</ul>
<p>If you're the administrator of <b id="hostname"></b>, try:</p>
<ul>
<li>Restarting your server</li>
<li>Updating Scramjet</li>
<li>Troubleshooting the error on the <a href="https://github.com/MercuryWorkshop/scramjet" target="_blank">GitHub repository</a></li>
</ul>
</div>
</div>
<br>
<button id="reload" class="primary">Reload</button>
</div>
<p id="version-wrapper"><i>Scramjet v<span id="version"></span> (build <span id="build"></span>)</i></p>
<script src="${"data:application/javascript,"+encodeURIComponent(r)}"></script>
</body>
</html>
`}(String(e),t),{status:500,headers:r})}(Object.entries(t).map(([e,t])=>`${e.charAt(0).toUpperCase()+e.slice(1)}: ${t}`).join("\n\n"),d(e.url))}}async function C(e,t,r,o,s,n,i){let a;let c=w(o.rawHeaders,R(e)),l=c["set-cookie"]||[];for(let t in l)n&&n.postMessage({scramjet$type:"cookie",cookie:t,url:e.href});for(let t in await s.setCookies(l instanceof Array?l:[l],e),c)Array.isArray(c[t])&&(c[t]=c[t][0]);if(o.body&&(a=await $(o,R(e),r,t,s)),["document","iframe"].includes(r)){let e=c["content-disposition"];if(!/\s*?((inline|attachment);\s*?)filename=/i.test(e)){let t=/^\s*?attachment/i.test(e)?"attachment":"inline",[r]=new URL(o.finalURL).pathname.split("/").slice(-1);c["content-disposition"]=`${t}; filename=${JSON.stringify(r)}`}}"text/event-stream"===c.accept&&(c["content-type"]="text/event-stream"),delete c["permissions-policy"],crossOriginIsolated&&["document","iframe","worker","sharedworker","style","script"].includes(r)&&(c["Cross-Origin-Embedder-Policy"]="require-corp",c["Cross-Origin-Opener-Policy"]="same-origin");let d=new j(a,c,o.status,o.statusText,r,e,o,n);return i.dispatchEvent(d),new Response(d.responseBody,{headers:d.responseHeaders,status:d.status,statusText:d.statusText})}async function $(e,t,r,o,s){switch(r){case"iframe":case"document":if(e.headers.get("content-type")?.startsWith("text/html"))return m(await e.text(),s,t,!0);return e.body;case"script":return b(await e.arrayBuffer(),e.finalURL,t);case"style":return p(await e.text(),t);case"sharedworker":case"worker":return v(await e.arrayBuffer(),o,e.finalURL,t);default:return e.body}}s.config;class j extends Event{responseBody;responseHeaders;status;statusText;destination;url;rawResponse;client;constructor(e,t,r,o,s,n,i,a){super("handleResponse"),this.responseBody=e,this.responseHeaders=t,this.status=r,this.statusText=o,this.destination=s,this.url=n,this.rawResponse=i,this.client=a}}class E extends Event{url;body;method;destination;client;requestHeaders;constructor(e,t,r,o,s,n){super("request"),this.url=e,this.body=t,this.method=r,this.destination=o,this.client=s,this.requestHeaders=n}response}var T=r(1762).Z;class O extends EventTarget{client;config;syncPool={};synctoken=0;cookieStore=new s.shared.CookieStore;serviceWorkers=[];constructor(){super(),this.client=new s.shared.util.BareClient;let e=indexedDB.open("$scramjet",1);e.onsuccess=()=>{let t=e.result.transaction("cookies","readonly").objectStore("cookies").get("cookies");t.onsuccess=()=>{t.result&&(this.cookieStore.load(t.result),T.log("Loaded cookies from IDB!"))}},addEventListener("message",async({data:t})=>{if("scramjet$type"in t){if("registerServiceWorker"===t.scramjet$type){this.serviceWorkers.push(new o(t.port,t.origin));return}"cookie"===t.scramjet$type&&(this.cookieStore.setCookies([t.cookie],new URL(t.url)),e.result.transaction("cookies","readwrite").objectStore("cookies").put(JSON.parse(this.cookieStore.dump()),"cookies"))}})}async loadConfig(){if(this.config)return;let e=indexedDB.open("$scramjet",1);return new Promise((t,r)=>{e.onsuccess=async()=>{let o=e.result.transaction("config","readonly").objectStore("config").get("config");o.onsuccess=()=>{this.config=o.result,s.config=o.result,s.codec.encode=n("url",s.config.codec.encode),s.codec.decode=n("url",s.config.codec.decode),t()},o.onerror=()=>r(o.error)},e.onerror=()=>r(e.error)})}route({request:e}){return!!e.url.startsWith(location.origin+this.config.prefix)||!1}async fetch({request:e,clientId:t}){let r=await self.clients.get(t);return S.call(this,e,r)}}self.ScramjetServiceWorker=O})();
//# sourceMappingURL=scramjet.worker.js.map

File diff suppressed because one or more lines are too long