mirror of
https://github.com/MercuryWorkshop/scramjet.git
synced 2025-05-13 22:40:01 -04:00
bug fixes
Co-authored-by: Avad3 <Avad3@users.noreply.github.com>
This commit is contained in:
parent
a39a2657c6
commit
47b59945a9
23 changed files with 385 additions and 327 deletions
|
@ -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"
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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"];
|
||||||
|
|
|
@ -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
10
src/client/event.ts
Normal 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);
|
||||||
|
// }
|
||||||
|
// })
|
|
@ -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);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
12
src/client/scope.ts
Normal 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;
|
|
@ -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, {
|
||||||
|
|
|
@ -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);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
})
|
})
|
|
@ -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"
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
|
@ -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})`
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
|
@ -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;
|
||||||
}
|
}
|
|
@ -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("");
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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 {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue