bug fixes

Co-authored-by: Avad3 <Avad3@users.noreply.github.com>
This commit is contained in:
Percs 2024-07-13 19:42:12 -05:00
parent a39a2657c6
commit 47b59945a9
23 changed files with 385 additions and 327 deletions

View file

@ -19,7 +19,7 @@ export default {
treeshake: "recommended", treeshake: "recommended",
input: { input: {
client: "./src/client/index.ts", client: "./src/client/index.ts",
bundle: "./src/bundle/index.ts", shared: "./src/shared/index.ts",
worker: "./src/worker/index.ts", worker: "./src/worker/index.ts",
codecs: "./src/codecs/index.ts", codecs: "./src/codecs/index.ts",
config: "./src/scramjet.config.ts" config: "./src/scramjet.config.ts"

View file

@ -1,4 +1,4 @@
import { encodeUrl } from "../bundle"; import { encodeUrl } from "../shared";
navigator.sendBeacon = new Proxy(navigator.sendBeacon, { navigator.sendBeacon = new Proxy(navigator.sendBeacon, {
apply(target, thisArg, argArray) { apply(target, thisArg, argArray) {

View file

@ -1,4 +1,4 @@
import { rewriteCss } from "../bundle"; import { rewriteCss } from "../shared";
const cssProperties = ["background", "background-image", "mask", "mask-image", "list-style", "list-style-image", "border-image", "border-image-source", "cursor"]; const cssProperties = ["background", "background-image", "mask", "mask-image", "list-style", "list-style-image", "border-image", "border-image-source", "cursor"];
const jsProperties = ["background", "backgroundImage", "mask", "maskImage", "listStyle", "listStyleImage", "borderImage", "borderImageSource", "cursor"]; const jsProperties = ["background", "backgroundImage", "mask", "maskImage", "listStyle", "listStyleImage", "borderImage", "borderImageSource", "cursor"];

View file

@ -1,4 +1,4 @@
import { encodeUrl, rewriteCss, rewriteHtml, rewriteJs, rewriteSrcset } from "../bundle"; import { encodeUrl, rewriteCss, rewriteHtml, rewriteJs, rewriteSrcset } from "../shared";
const attrObject = { const attrObject = {
"nonce": [HTMLElement], "nonce": [HTMLElement],

10
src/client/event.ts Normal file
View file

@ -0,0 +1,10 @@
// idk what shit has to be done on here but it has to be done
// i'm going to temporarily disable rewriting if a MemberExpression detects addEventListener
// window.addEventListener = new Proxy(window.addEventListener, {
// apply (target, thisArg, argArray) {
// //
// return Reflect.apply(target, thisArg, argArray);
// }
// })

View file

@ -1 +1,19 @@
// forgot aobut this api
import { encodeUrl } from "../shared";
window.history.pushState = new Proxy(window.history.pushState, {
apply(target, thisArg, argArray) {
argArray[3] = encodeUrl(argArray[3]);
return Reflect.apply(target, thisArg, argArray);
},
});
window.history.replaceState = new Proxy(window.history.replaceState, {
apply(target, thisArg, argArray) {
argArray[3] = encodeUrl(argArray[3]);
return Reflect.apply(target, thisArg, argArray);
},
});

View file

@ -1,4 +1,5 @@
import "./window.ts"; import "./window.ts";
import "./event.ts";
import "./native/eval.ts"; import "./native/eval.ts";
import "./location.ts"; import "./location.ts";
import "./trustedTypes.ts"; import "./trustedTypes.ts";
@ -8,11 +9,15 @@ import "./requests/websocket.ts"
import "./element.ts"; import "./element.ts";
import "./storage.ts"; import "./storage.ts";
import "./css.ts"; import "./css.ts";
import "./history.ts"
import "./worker.ts"; import "./worker.ts";
import "./scope.ts";
declare global { declare global {
interface Window { interface Window {
__location: Location; __location: Location;
__window: Window; __window: Window;
//@ts-ignore scope function cant be typed
__s: any;
} }
} }

View file

@ -1,5 +1,5 @@
// @ts-nocheck // @ts-nocheck
import { encodeUrl, decodeUrl } from "../bundle"; import { encodeUrl, decodeUrl } from "../shared";
function urlLocation() { function urlLocation() {
const loc = new URL(decodeUrl(location.href)); const loc = new URL(decodeUrl(location.href));

View file

@ -1,4 +1,4 @@
import { rewriteJs } from "../../bundle"; import { rewriteJs } from "../../shared";
const FunctionProxy = new Proxy(Function, { const FunctionProxy = new Proxy(Function, {
construct(target, argArray) { construct(target, argArray) {

View file

@ -1,6 +1,6 @@
// ts throws an error if you dont do window.fetch // ts throws an error if you dont do window.fetch
import { encodeUrl, rewriteHeaders } from "../../bundle"; import { encodeUrl, rewriteHeaders } from "../../shared";
window.fetch = new Proxy(window.fetch, { window.fetch = new Proxy(window.fetch, {
apply(target, thisArg, argArray) { apply(target, thisArg, argArray) {

View file

@ -1,4 +1,4 @@
import { encodeUrl, rewriteHeaders } from "../../bundle"; import { encodeUrl, rewriteHeaders } from "../../shared";
XMLHttpRequest.prototype.open = new Proxy(XMLHttpRequest.prototype.open, { XMLHttpRequest.prototype.open = new Proxy(XMLHttpRequest.prototype.open, {
apply(target, thisArg, argArray) { apply(target, thisArg, argArray) {

12
src/client/scope.ts Normal file
View file

@ -0,0 +1,12 @@
function scope(identifier: any) {
if (identifier instanceof Window) {
return window.__window;
} else if (identifier instanceof Location) {
return window.__location;
}
return identifier;
}
// shorthand because this can get out of hand reall quickly
window.__s = scope;

View file

@ -1,4 +1,4 @@
import { rewriteHtml, rewriteJs, encodeUrl } from "../bundle"; import { rewriteHtml, rewriteJs, encodeUrl } from "../shared";
// @ts-expect-error // @ts-expect-error
trustedTypes.createPolicy = new Proxy(trustedTypes.createPolicy, { trustedTypes.createPolicy = new Proxy(trustedTypes.createPolicy, {

View file

@ -10,8 +10,13 @@ const windowProxy = new Proxy(window, {
return target[prop]; return target[prop];
}, },
set(target, p, newValue, receiver) { set(target, prop, newValue) {
return Reflect.set(target, p, newValue, receiver); // ensures that no apis are overwritten
if (typeof prop === "string" && ["window", "top", "parent", "self", "globalThis", "location"].includes(prop)) {
return false;
}
return Reflect.set(target, prop, newValue);
}, },
}); });

View file

@ -1,7 +1,13 @@
import { encodeUrl } from "../bundle"; import { encodeUrl } from "../shared";
const RealWorker = Worker
Worker = new Proxy(Worker, { Worker = new Proxy(Worker, {
construct(_target, args) { construct(target, argArray) {
return new RealWorker(encodeUrl(args[0]), args[1]) argArray[0] = encodeUrl(argArray[0]);
// target is a reference to the object that you are proxying
// Reflect.construct is just a wrapper for calling target
// you could do new target(...argArray) and it would work the same effectively
return Reflect.construct(target, argArray);
} }
}) })

View file

@ -6,7 +6,7 @@ declare global {
prefix: string; prefix: string;
codec: Codec codec: Codec
config: string; config: string;
bundle: string; shared: string;
worker: string; worker: string;
client: string; client: string;
codecs: string; codecs: string;
@ -18,7 +18,7 @@ self.__scramjet$config = {
prefix: "/scramjet/", prefix: "/scramjet/",
codec: self.__scramjet$codecs.plain, codec: self.__scramjet$codecs.plain,
config: "/scram/scramjet.config.js", config: "/scram/scramjet.config.js",
bundle: "/scram/scramjet.bundle.js", shared: "/scram/scramjet.shared.js",
worker: "/scram/scramjet.worker.js", worker: "/scram/scramjet.worker.js",
client: "/scram/scramjet.client.js", client: "/scram/scramjet.client.js",
codecs: "/scram/scramjet.codecs.js" codecs: "/scram/scramjet.codecs.js"

View file

@ -1,14 +1,15 @@
export { encodeUrl, decodeUrl } from "./rewriters/url"; export { encodeUrl, decodeUrl } from "./rewriters/url";
export { rewriteCss } from "./rewriters/css"; export { rewriteCss } from "./rewriters/css";
export { rewriteHtml, rewriteSrcset } from "./rewriters/html"; export { rewriteHtml, rewriteSrcset } from "./rewriters/html";
export { rewriteJs } from "./rewriters/js"; export { rewriteJs } from "./rewriters/js";
export { rewriteHeaders } from "./rewriters/headers"; export { rewriteHeaders } from "./rewriters/headers";
export { BareClient } from "@mercuryworkshop/bare-mux"
export function isScramjetFile(src: string) {
let bool = false; export function isScramjetFile(src: string) {
["codecs", "client", "bundle", "worker", "config"].forEach((file) => { let bool = false;
if (src === self.__scramjet$config[file]) bool = true; ["codecs", "client", "shared", "worker", "config"].forEach((file) => {
}); if (src === self.__scramjet$config[file]) bool = true;
});
return bool;
return bool;
} }

View file

@ -1,35 +1,35 @@
// This CSS rewriter uses code from Meteor // This CSS rewriter uses code from Meteor
// You can find the original source code at https://github.com/MeteorProxy/Meteor // You can find the original source code at https://github.com/MeteorProxy/Meteor
import { encodeUrl } from "./url"; import { encodeUrl } from "./url";
export function rewriteCss(css: string, origin?: URL) { export function rewriteCss(css: string, origin?: URL) {
const regex = const regex =
/(@import\s+(?!url\())?\s*url\(\s*(['"]?)([^'")]+)\2\s*\)|@import\s+(['"])([^'"]+)\4/g /(@import\s+(?!url\())?\s*url\(\s*(['"]?)([^'")]+)\2\s*\)|@import\s+(['"])([^'"]+)\4/g
return css.replace( return css.replace(
regex, regex,
( (
match, match,
importStatement, importStatement,
urlQuote, urlQuote,
urlContent, urlContent,
importQuote, importQuote,
importContent importContent
) => { ) => {
const url = urlContent || importContent const url = urlContent || importContent
const encodedUrl = encodeUrl(url.trim(), origin) const encodedUrl = encodeUrl(url.trim(), origin)
if (importStatement) { if (importStatement) {
return `@import url(${urlQuote}${encodedUrl}${urlQuote})` return `@import url(${urlQuote}${encodedUrl}${urlQuote})`
} }
if (importQuote) { if (importQuote) {
return `@import ${importQuote}${encodedUrl}${importQuote}` return `@import ${importQuote}${encodedUrl}${importQuote}`
} }
return `url(${urlQuote}${encodedUrl}${urlQuote})` return `url(${urlQuote}${encodedUrl}${urlQuote})`
} }
) )
} }

View file

@ -1,52 +1,52 @@
import { encodeUrl } from "./url"; import { encodeUrl } from "./url";
import { BareHeaders } from "@mercuryworkshop/bare-mux"; import { BareHeaders } from "@mercuryworkshop/bare-mux";
const cspHeaders = [ const cspHeaders = [
"cross-origin-embedder-policy", "cross-origin-embedder-policy",
"cross-origin-opener-policy", "cross-origin-opener-policy",
"cross-origin-resource-policy", "cross-origin-resource-policy",
"content-security-policy", "content-security-policy",
"content-security-policy-report-only", "content-security-policy-report-only",
"expect-ct", "expect-ct",
"feature-policy", "feature-policy",
"origin-isolation", "origin-isolation",
"strict-transport-security", "strict-transport-security",
"upgrade-insecure-requests", "upgrade-insecure-requests",
"x-content-type-options", "x-content-type-options",
"x-download-options", "x-download-options",
"x-frame-options", "x-frame-options",
"x-permitted-cross-domain-policies", "x-permitted-cross-domain-policies",
"x-powered-by", "x-powered-by",
"x-xss-protection", "x-xss-protection",
// This needs to be emulated, but for right now it isn't that important of a feature to be worried about // This needs to be emulated, but for right now it isn't that important of a feature to be worried about
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data
"clear-site-data" "clear-site-data"
]; ];
const urlHeaders = [ const urlHeaders = [
"location", "location",
"content-location", "content-location",
"referer" "referer"
]; ];
export function rewriteHeaders(rawHeaders: BareHeaders, origin?: URL) { export function rewriteHeaders(rawHeaders: BareHeaders, origin?: URL) {
const headers = {}; const headers = {};
for (const key in rawHeaders) { for (const key in rawHeaders) {
headers[key.toLowerCase()] = rawHeaders[key]; headers[key.toLowerCase()] = rawHeaders[key];
} }
cspHeaders.forEach((header) => { cspHeaders.forEach((header) => {
delete headers[header]; delete headers[header];
}); });
urlHeaders.forEach((header) => { urlHeaders.forEach((header) => {
if (headers[header]) if (headers[header])
headers[header] = encodeUrl(headers[header] as string, origin); headers[header] = encodeUrl(headers[header] as string, origin);
}); });
if (headers["link"]) { if (headers["link"]) {
headers["link"] = headers["link"].replace(/<(.*?)>/gi, (match) => encodeUrl(match, origin)); headers["link"] = headers["link"].replace(/<(.*?)>/gi, (match) => encodeUrl(match, origin));
} }
return headers; return headers;
} }

View file

@ -1,97 +1,97 @@
import { Parser } from "htmlparser2"; import { Parser } from "htmlparser2";
import { DomHandler, Element } from "domhandler"; import { DomHandler, Element } from "domhandler";
import { hasAttrib } from "domutils"; import { hasAttrib } from "domutils";
import render from "dom-serializer"; import render from "dom-serializer";
import { encodeUrl } from "./url"; import { encodeUrl } from "./url";
import { rewriteCss } from "./css"; import { rewriteCss } from "./css";
import { rewriteJs } from "./js"; import { rewriteJs } from "./js";
import { isScramjetFile } from "../"; import { isScramjetFile } from "..";
export function rewriteHtml(html: string, origin?: URL) { export function rewriteHtml(html: string, origin?: URL) {
const handler = new DomHandler((err, dom) => dom); const handler = new DomHandler((err, dom) => dom);
const parser = new Parser(handler); const parser = new Parser(handler);
parser.write(html); parser.write(html);
parser.end(); parser.end();
return render(traverseParsedHtml(handler.root, origin)); return render(traverseParsedHtml(handler.root, origin));
} }
// i need to add the attributes in during rewriting // i need to add the attributes in during rewriting
function traverseParsedHtml(node, origin?: URL) { function traverseParsedHtml(node, origin?: URL) {
/* csp attributes */ /* csp attributes */
for (const cspAttr of ["nonce", "integrity", "csp"]) { for (const cspAttr of ["nonce", "integrity", "csp"]) {
if (hasAttrib(node, cspAttr)) { if (hasAttrib(node, cspAttr)) {
node.attribs[`data-${cspAttr}`] = node.attribs[cspAttr]; node.attribs[`data-${cspAttr}`] = node.attribs[cspAttr];
delete node.attribs[cspAttr]; delete node.attribs[cspAttr];
} }
} }
/* url attributes */ /* url attributes */
for (const urlAttr of ["src", "href", "data", "action", "formaction"]) { for (const urlAttr of ["src", "href", "data", "action", "formaction"]) {
if (hasAttrib(node, urlAttr) && !isScramjetFile(node.attribs[urlAttr])) { if (hasAttrib(node, urlAttr) && !isScramjetFile(node.attribs[urlAttr])) {
const value = node.attribs[urlAttr]; const value = node.attribs[urlAttr];
node.attribs[`data-${urlAttr}`] = value; node.attribs[`data-${urlAttr}`] = value;
node.attribs[urlAttr] = encodeUrl(value, origin); node.attribs[urlAttr] = encodeUrl(value, origin);
} }
} }
/* other */ /* other */
for (const srcsetAttr of ["srcset", "imagesrcset"]) { for (const srcsetAttr of ["srcset", "imagesrcset"]) {
if (hasAttrib(node, srcsetAttr)) { if (hasAttrib(node, srcsetAttr)) {
const value = node.attribs[srcsetAttr]; const value = node.attribs[srcsetAttr];
node.attribs[`data-${srcsetAttr}`] = value; node.attribs[`data-${srcsetAttr}`] = value;
node.attribs[srcsetAttr] = rewriteSrcset(value, origin); node.attribs[srcsetAttr] = rewriteSrcset(value, origin);
} }
} }
if (hasAttrib(node, "srcdoc")) node.attribs.srcdoc = rewriteHtml(node.attribs.srcdoc, origin); if (hasAttrib(node, "srcdoc")) node.attribs.srcdoc = rewriteHtml(node.attribs.srcdoc, origin);
if (hasAttrib(node, "style")) node.attribs.style = rewriteCss(node.attribs.style, origin); if (hasAttrib(node, "style")) node.attribs.style = rewriteCss(node.attribs.style, origin);
if (node.name === "style" && node.children[0] !== undefined) node.children[0].data = rewriteCss(node.children[0].data, origin); if (node.name === "style" && node.children[0] !== undefined) node.children[0].data = rewriteCss(node.children[0].data, origin);
if (node.name === "script" && /(application|text)\/javascript|importmap|undefined/.test(node.attribs.type) && node.children[0] !== undefined) node.children[0].data = rewriteJs(node.children[0].data, origin); if (node.name === "script" && /(application|text)\/javascript|importmap|undefined/.test(node.attribs.type) && node.children[0] !== undefined) node.children[0].data = rewriteJs(node.children[0].data, origin);
if (node.name === "meta" && hasAttrib(node, "http-equiv")) { if (node.name === "meta" && hasAttrib(node, "http-equiv")) {
if (node.attribs["http-equiv"] === "content-security-policy") { if (node.attribs["http-equiv"] === "content-security-policy") {
node = {}; node = {};
} else if (node.attribs["http-equiv"] === "refresh" && node.attribs.content.includes("url")) { } else if (node.attribs["http-equiv"] === "refresh" && node.attribs.content.includes("url")) {
const contentArray = node.attribs.content.split("url="); const contentArray = node.attribs.content.split("url=");
contentArray[1] = encodeUrl(contentArray[1].trim(), origin); contentArray[1] = encodeUrl(contentArray[1].trim(), origin);
node.attribs.content = contentArray.join("url="); node.attribs.content = contentArray.join("url=");
} }
} }
if (node.name === "head") { if (node.name === "head") {
const scramjetScripts = []; const scramjetScripts = [];
["codecs", "config", "bundle", "client"].forEach((script) => { ["codecs", "config", "shared", "client"].forEach((script) => {
scramjetScripts.push(new Element("script", { scramjetScripts.push(new Element("script", {
src: self.__scramjet$config[script], src: self.__scramjet$config[script],
type: "module" type: "module"
})); }));
}); });
node.children.unshift(...scramjetScripts); node.children.unshift(...scramjetScripts);
} }
if (node.childNodes) { if (node.childNodes) {
for (const childNode in node.childNodes) { for (const childNode in node.childNodes) {
node.childNodes[childNode] = traverseParsedHtml(node.childNodes[childNode], origin); node.childNodes[childNode] = traverseParsedHtml(node.childNodes[childNode], origin);
} }
} }
return node; return node;
} }
export function rewriteSrcset(srcset: string, origin?: URL) { export function rewriteSrcset(srcset: string, origin?: URL) {
const urls = srcset.split(/ [0-9]+x,? ?/g); const urls = srcset.split(/ [0-9]+x,? ?/g);
if (!urls) return ""; if (!urls) return "";
const sufixes = srcset.match(/ [0-9]+x,? ?/g); const sufixes = srcset.match(/ [0-9]+x,? ?/g);
if (!sufixes) return ""; if (!sufixes) return "";
const rewrittenUrls = urls.map((url, i) => { const rewrittenUrls = urls.map((url, i) => {
if (url && sufixes[i]) { if (url && sufixes[i]) {
return encodeUrl(url, origin) + sufixes[i]; return encodeUrl(url, origin) + sufixes[i];
} }
}); });
return rewrittenUrls.join(""); return rewrittenUrls.join("");
} }

View file

@ -1,78 +1,79 @@
import { parseModule } from "meriyah"; import { parseModule } from "meriyah";
import { generate } from "astring"; import { generate } from "astring";
import { makeTraveler } from "astravel"; import { makeTraveler } from "astravel";
import { encodeUrl } from "./url"; import { encodeUrl } from "./url";
import * as ESTree from "estree"; import * as ESTree from "estree";
// i am a cat. i like to be petted. i like to be fed. i like to be // i am a cat. i like to be petted. i like to be fed. i like to be
// js rewiter is NOT finished // js rewiter is NOT finished
// location // location
// window // window
// self // self
// globalThis // globalThis
// this // this
// top // top
// parent // parent
export function rewriteJs(js: string, origin?: URL) { export function rewriteJs(js: string, origin?: URL) {
try { try {
const ast = parseModule(js, { const ast = parseModule(js, {
module: true, module: true,
webcompat: true webcompat: true
}); });
const identifierList = [ const identifierList = [
"window", "window",
"self", "self",
"globalThis", "globalThis",
"this", "this",
"parent", "parent",
"top", "top",
"location" "this",
] "location"
]
const customTraveler = makeTraveler({
ImportDeclaration: (node: ESTree.ImportDeclaration) => { const customTraveler = makeTraveler({
node.source.value = encodeUrl(node.source.value as string, origin); ImportDeclaration: (node: ESTree.ImportDeclaration) => {
}, node.source.value = encodeUrl(node.source.value as string, origin);
},
ImportExpression: (node: ESTree.ImportExpression) => {
if (node.source.type === "Literal") { ImportExpression: (node: ESTree.ImportExpression) => {
node.source.value = encodeUrl(node.source.value as string, origin); if (node.source.type === "Literal") {
} else if (node.source.type === "Identifier") { node.source.value = encodeUrl(node.source.value as string, origin);
// this is for things that import something like } else if (node.source.type === "Identifier") {
// const moduleName = "name"; // this is for things that import something like
// await import(moduleName); // const moduleName = "name";
node.source.name = `__wrapImport(${node.source.name})`; // await import(moduleName);
} node.source.name = `__wrapImport(${node.source.name})`;
}, }
},
ExportAllDeclaration: (node: ESTree.ExportAllDeclaration) => {
node.source.value = encodeUrl(node.source.value as string, origin); ExportAllDeclaration: (node: ESTree.ExportAllDeclaration) => {
}, node.source.value = encodeUrl(node.source.value as string, origin);
},
ExportNamedDeclaration: (node: ESTree.ExportNamedDeclaration) => {
// strings are Literals in ESTree syntax but these will always be strings ExportNamedDeclaration: (node: ESTree.ExportNamedDeclaration) => {
if (node.source) node.source.value = encodeUrl(node.source.value as string, origin); // strings are Literals in ESTree syntax but these will always be strings
}, if (node.source) node.source.value = encodeUrl(node.source.value as string, origin);
},
// js rweriting notrdone
MemberExpression: (node: ESTree.MemberExpression) => { // js rweriting notrdone
if (node.object.type === "Identifier" && identifierList.includes(node.object.name)) { MemberExpression: (node: ESTree.MemberExpression) => {
node.object.name = "__" + node.object.name; if (node.object.type === "Identifier" && identifierList.includes(node.object.name)) {
} node.object.name = `__s${node.object.name}`;
} }
}); }
});
customTraveler.go(ast);
customTraveler.go(ast);
return generate(ast);
} catch { return generate(ast);
console.log(js); } catch {
console.log(js);
return js;
} return js;
}
} }

View file

@ -1,37 +1,37 @@
import { rewriteJs } from "./js"; import { rewriteJs } from "./js";
function canParseUrl(url: string, origin?: URL) { function canParseUrl(url: string, origin?: URL) {
try { try {
new URL(url, origin); new URL(url, origin);
return true; return true;
} catch { } catch {
return false; return false;
} }
} }
// something is broken with this but i didn't debug it // something is broken with this but i didn't debug it
export function encodeUrl(url: string, origin?: URL) { export function encodeUrl(url: string, origin?: URL) {
if (!origin) { if (!origin) {
origin = new URL(self.__scramjet$config.codec.decode(location.href.slice((location.origin + self.__scramjet$config.prefix).length))); origin = new URL(self.__scramjet$config.codec.decode(location.href.slice((location.origin + self.__scramjet$config.prefix).length)));
} }
if (url.startsWith("javascript:")) { if (url.startsWith("javascript:")) {
return "javascript:" + rewriteJs(url.slice("javascript:".length)); return "javascript:" + rewriteJs(url.slice("javascript:".length));
} else if (/^(#|mailto|about|data)/.test(url)) { } else if (/^(#|mailto|about|data)/.test(url)) {
return url; return url;
} else if (canParseUrl(url, origin)) { } else if (canParseUrl(url, origin)) {
return location.origin + self.__scramjet$config.prefix + self.__scramjet$config.codec.encode(new URL(url, origin).href); return location.origin + self.__scramjet$config.prefix + self.__scramjet$config.codec.encode(new URL(url, origin).href);
} }
} }
// something is also broken with this but i didn't debug it // something is also broken with this but i didn't debug it
export function decodeUrl(url: string) { export function decodeUrl(url: string) {
if (/^(#|about|data|mailto|javascript)/.test(url)) { if (/^(#|about|data|mailto|javascript)/.test(url)) {
return url; return url;
} else if (canParseUrl(url)) { } else if (canParseUrl(url)) {
return self.__scramjet$config.codec.decode(url.slice((location.origin + self.__scramjet$config.prefix).length)) return self.__scramjet$config.codec.decode(url.slice((location.origin + self.__scramjet$config.prefix).length))
} else { } else {
return url; return url;
} }
} }

View file

@ -1,6 +1,6 @@
import { BareClient } from "@mercuryworkshop/bare-mux"; import { BareClient } from "@mercuryworkshop/bare-mux";
import { BareResponseFetch } from "@mercuryworkshop/bare-mux"; import { BareResponseFetch } from "@mercuryworkshop/bare-mux";
import { encodeUrl, decodeUrl, rewriteCss, rewriteHeaders, rewriteHtml, rewriteJs } from "../bundle"; import { encodeUrl, decodeUrl, rewriteCss, rewriteHeaders, rewriteHtml, rewriteJs } from "../shared";
declare global { declare global {
interface Window { interface Window {