start bootstrapper

This commit is contained in:
Avad3 2024-07-30 22:09:17 -04:00
parent 034c8e1c6d
commit 17b440ea37
15 changed files with 166 additions and 131 deletions

View file

@ -1,27 +0,0 @@
import { defineConfig } from "@farmfe/core";
// import { RsdoctorRspackPlugin } from "@rsdoctor/rspack-plugin";
import { join } from "path";
import sj from "scramjet-farm-plugin";
export default defineConfig({
plugins: [sj()],
compilation: {
presetEnv: false,
mode: "development",
sourcemap: false,
input: {
worker: "src/worker/index.ts",
thread: "src/thread/thread.ts",
client: "src/client/index.ts",
config: "src/scramjet.config.ts",
},
output: {
path: "dist",
format: "cjs",
targetEnv: "browser-esnext",
entryFilename: "scramjet.[entryName].js",
filename: "scramjet.split.[name].js",
},
},
});

View file

@ -1,39 +0,0 @@
import typescript from "rollup-plugin-typescript2";
import { join } from "node:path";
import fs from "node:fs";
import { fileURLToPath } from "node:url";
import { nodeResolve } from "@rollup/plugin-node-resolve";
// check if its
const production = !process.env.ROLLUP_WATCH;
console.log(production);
fs.rmSync(join(fileURLToPath(new URL(".", import.meta.url)), "./dist"), {
recursive: true,
force: true,
});
const commonPlugins = () => [
typescript({
tsconfig: "tsconfig.json",
}),
nodeResolve(),
];
export default {
plugins: commonPlugins(),
input: {
client: "./src/client/index.ts",
worker: "./src/worker/index.ts",
config: "./src/scramjet.config.ts",
},
output: {
entryFileNames: "scramjet.[name].js",
dir: "./dist",
format: "system",
bundle: true,
minify: production,
sourcemap: true,
treeshake: "recommended",
},
};

View file

@ -16,6 +16,7 @@ export default defineConfig({
client: join(__dirname, "src/client/index.ts"), client: join(__dirname, "src/client/index.ts"),
config: join(__dirname, "src/scramjet.config.ts"), config: join(__dirname, "src/scramjet.config.ts"),
codecs: join(__dirname, "src/codecs/index.ts"), codecs: join(__dirname, "src/codecs/index.ts"),
bootstrapper: join(__dirname, "src/bootsrapper/index.ts")
}, },
resolve: { resolve: {
extensions: [".ts", ".js"], extensions: [".ts", ".js"],
@ -44,6 +45,11 @@ export default defineConfig({
type: "javascript/auto", type: "javascript/auto",
}, },
], ],
parser: {
javascript: {
dynamicImportMode: "eager"
}
}
}, },
output: { output: {
filename: "scramjet.[name].js", filename: "scramjet.[name].js",

View file

@ -76,7 +76,7 @@ fastify.register(fastifyStatic, {
}); });
fastify.listen({ fastify.listen({
port: process.env.PORT || 1337, port: process.env.PORT || 1337,
host: "0.0.0.0", // host: "0.0.0.0",
}); });
writeFileSync( writeFileSync(

70
src/bootsrapper/index.ts Normal file
View file

@ -0,0 +1,70 @@
import IDBMap from "idb-map-entries";
import { ScramjetConfig } from "../types";
import { Codec } from "../codecs";
export class ScramjetBootstrapper {
config: ScramjetConfig;
private store: IDBMap;
codec: Codec;
constructor(config: ScramjetConfig) {
const defaultConfig = {
prefix: "/scramjet/",
codec: "plain",
wrapfn: "$scramjet$wrap",
trysetfn: "$scramjet$tryset",
importfn: "$scramjet$import",
rewritefn: "$scramjet$rewrite",
shared: "/scramjet.shared.js",
worker: "/scramjet.worker.js",
thread: "/scramjet.thread.js",
client: "/scramjet.client.js",
codecs: "/scramjet.codecs.js",
}
this.config = Object.assign({}, defaultConfig, config);
// rspack won't let me use a dynamic import
fetch(config.codecs).then(async (response) => {
eval(await response.text());
self.$scramjet.codec = self.$scramjet.codecs[this.config.codec];
self.$scramjet.config = this.config;
});
console.log(this.config);
this.store = new IDBMap("config", {
prefix: "scramjet"
});
this.saveConfig();
}
registerSw(serviceWorkerPath: string) {
if ("serviceWorker" in navigator) {
navigator.serviceWorker
.register(serviceWorkerPath, {
scope: this.config.prefix
})
.then((registration) => {
console.log("ServiceWorker registration successful with scope: ", registration.scope);
})
.catch((err) => {
console.log("ServiceWorker registration failed: ", err);
});
}
}
saveConfig() {
this.store.set("config", this.config).then(() => {
console.log("scramjet config saved");
});
}
modifyConfig(config: ScramjetConfig) {
this.config = Object.assign({}, this.config, config);
this.saveConfig();
}
}
window.ScramjetBootstrapper = ScramjetBootstrapper;

View file

@ -1,18 +1,18 @@
if (!self.$scramjet) { // if (!self.$scramjet) {
//@ts-expect-error really dumb workaround // //@ts-expect-error really dumb workaround
self.$scramjet = {}; // self.$scramjet = {};
} // }
self.$scramjet.config = { // self.$scramjet.config = {
prefix: "/scramjet/", // prefix: "/scramjet/",
codec: self.$scramjet.codecs.plain, // codec: self.$scramjet.codecs.plain,
wrapfn: "$scramjet$wrap", // wrapfn: "$scramjet$wrap",
trysetfn: "$scramjet$tryset", // trysetfn: "$scramjet$tryset",
importfn: "$scramjet$import", // importfn: "$scramjet$import",
rewritefn: "$scramjet$rewrite", // rewritefn: "$scramjet$rewrite",
config: "/scram/scramjet.config.js", // config: "/scram/scramjet.config.js",
shared: "/scram/scramjet.shared.js", // shared: "/scram/scramjet.shared.js",
worker: "/scram/scramjet.worker.js", // worker: "/scram/scramjet.worker.js",
thread: "/scram/scramjet.thread.js", // thread: "/scram/scramjet.thread.js",
client: "/scram/scramjet.client.js", // client: "/scram/scramjet.client.js",
codecs: "/scram/scramjet.codecs.js", // codecs: "/scram/scramjet.codecs.js",
}; // };

View file

@ -1,5 +1,5 @@
import { Parser } from "htmlparser2"; import { ElementType, Parser } from "htmlparser2";
import { DomHandler, Element } from "domhandler"; import { DomHandler, Element, Text } 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";
@ -8,7 +8,7 @@ import { rewriteJs } from "./js";
export function isScramjetFile(src: string) { export function isScramjetFile(src: string) {
let bool = false; let bool = false;
["codecs", "client", "shared", "worker", "config"].forEach((file) => { ["codecs", "client", "shared", "worker"].forEach((file) => {
if (src === self.$scramjet.config[file]) bool = true; if (src === self.$scramjet.config[file]) bool = true;
}); });
@ -96,11 +96,13 @@ function traverseParsedHtml(node, origin?: URL) {
node.attribs.type node.attribs.type
) && ) &&
node.children[0] !== undefined node.children[0] !== undefined
&& !(node.attribs["data-scramjet"])
) { ) {
let js = node.children[0].data; let js = node.children[0].data;
const htmlcomment = /<!--[\s\S]*?-->/g; const htmlcomment = /<!--[\s\S]*?-->/g;
js = js.replace(htmlcomment, ""); js = js.replace(htmlcomment, "");
node.children[0].data = rewriteJs(js, origin); node.children[0].data = rewriteJs(js, origin);
console.log(node.children)
} }
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") {
@ -117,17 +119,19 @@ function traverseParsedHtml(node, origin?: URL) {
} }
if (node.name === "head") { if (node.name === "head") {
const scramjetScripts = []; const scripts = [];
["codecs", "config", "shared", "client"].forEach((script) => {
scramjetScripts.push(
new Element("script", {
src: self.$scramjet.config[script],
"data-scramjet": "",
})
);
});
node.children.unshift(...scramjetScripts); const codecs = new Element("script", { src: self.$scramjet.config["codecs"], "data-scramjet": "true" });
const config = new Element("script", { "data-scramjet": "true" }, [], ElementType.Script);
config.children[0] = new Text(`self.$scramjet.config = ${JSON.stringify(self.$scramjet.config)};
self.$scramjet.codec = self.$scramjet.codecs[self.$scramjet.config.codec];`);
console.log(config.children[0]);
const shared = new Element("script", { src: self.$scramjet.config["shared"], "data-scramjet": "true" });
const client = new Element("script", { src: self.$scramjet.config["client"], "data-scramjet": "true" });
scripts.push(codecs, config, shared, client);
node.children.unshift(...scripts);
} }
if (node.childNodes) { if (node.childNodes) {

View file

@ -26,13 +26,13 @@ export function rewriteJs(js: string | ArrayBuffer, origin?: URL) {
const before = performance.now(); const before = performance.now();
if (typeof js === "string") { if (typeof js === "string") {
js = new TextDecoder().decode( js = new TextDecoder().decode(
rewrite_js(js, origin.toString(), self.$scramjet.config) rewrite_js(js, origin.toString(), self.$scramjet)
); );
} else { } else {
js = rewrite_js_from_arraybuffer( js = rewrite_js_from_arraybuffer(
new Uint8Array(js), new Uint8Array(js),
origin.toString(), origin.toString(),
self.$scramjet.config self.$scramjet
); );
} }
const after = performance.now(); const after = performance.now();

View file

@ -21,7 +21,7 @@ export function encodeUrl(url: string | URL, origin?: URL) {
origin = new URL(new URL(location.href).searchParams.get("origin")); origin = new URL(new URL(location.href).searchParams.get("origin"));
} else } else
origin = new URL( origin = new URL(
self.$scramjet.config.codec.decode( self.$scramjet.codec.decode(
location.href.slice( location.href.slice(
(location.origin + self.$scramjet.config.prefix).length (location.origin + self.$scramjet.config.prefix).length
) )
@ -40,7 +40,7 @@ export function encodeUrl(url: string | URL, origin?: URL) {
return ( return (
location.origin + location.origin +
self.$scramjet.config.prefix + self.$scramjet.config.prefix +
self.$scramjet.config.codec.encode(new URL(url, origin).href) self.$scramjet.codec.encode(new URL(url, origin).href)
); );
} }
} }
@ -55,13 +55,13 @@ export function decodeUrl(url: string | URL) {
URL.canParse(url) && URL.canParse(url) &&
new URL(url).pathname.startsWith(self.$scramjet.config.prefix + "worker") new URL(url).pathname.startsWith(self.$scramjet.config.prefix + "worker")
) { ) {
return new URL(new URL(url).searchParams.get("origin")); return new URL(new URL(url).searchParams.get("origin")).href;
} }
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( return self.$scramjet.codec.decode(
url.slice((location.origin + self.$scramjet.config.prefix).length) url.slice((location.origin + self.$scramjet.config.prefix).length)
); );
} else { } else {

32
src/types.d.ts vendored
View file

@ -1,3 +1,4 @@
import { ScramjetBootstrapper } from "./bootsrapper/index";
import { encodeUrl, decodeUrl } from "./shared/rewriters/url"; import { encodeUrl, decodeUrl } from "./shared/rewriters/url";
import { rewriteCss } from "./shared/rewriters/css"; import { rewriteCss } from "./shared/rewriters/css";
import { rewriteHtml, rewriteSrcset } from "./shared/rewriters/html"; import { rewriteHtml, rewriteSrcset } from "./shared/rewriters/html";
@ -9,6 +10,20 @@ import type { Codec } from "./codecs";
import { BareClient } from "@mercuryworkshop/bare-mux"; import { BareClient } from "@mercuryworkshop/bare-mux";
import { parseDomain } from "parse-domain"; import { parseDomain } from "parse-domain";
interface ScramjetConfig {
prefix: string;
codec: string;
wrapfn: string;
trysetfn: string;
importfn: string;
rewritefn: string;
shared: string;
worker: string;
thread: string;
client: string;
codecs: string;
}
declare global { declare global {
interface Window { interface Window {
$scramjet: { $scramjet: {
@ -31,27 +46,16 @@ declare global {
parseDomain: typeof parseDomain; parseDomain: typeof parseDomain;
}; };
}; };
config: { config: ScramjetConfig;
prefix: string;
codec: Codec;
wrapfn: string;
trysetfn: string;
importfn: string;
rewritefn: string;
config: string;
shared: string;
worker: string;
thread: string;
client: string;
codecs: string;
};
codecs: { codecs: {
none: Codec; none: Codec;
plain: Codec; plain: Codec;
base64: Codec; base64: Codec;
xor: Codec; xor: Codec;
}; };
codec: Codec;
}; };
WASM: string; WASM: string;
ScramjetBootstrapper: typeof ScramjetBootstrapper;
} }
} }

View file

@ -46,7 +46,7 @@ export async function swfetch(
try { try {
const url = new URL(decodeUrl(request.url)); const url = new URL(decodeUrl(request.url));
let headers = new Headers(); const headers = new Headers();
for (const [key, value] of request.headers.entries()) { for (const [key, value] of request.headers.entries()) {
headers.set(key, value); headers.set(key, value);
} }
@ -104,8 +104,7 @@ async function handleResponse(
case "script": case "script":
responseBody = rewriteJs( responseBody = rewriteJs(
await response.arrayBuffer(), await response.arrayBuffer(),
url, url
self.$scramjet.config.codec.encode
); );
// Disable threading for now, it's causing issues. // Disable threading for now, it's causing issues.
// responseBody = await this.threadpool.rewriteJs(await responseBody.arrayBuffer(), url.toString()); // responseBody = await this.threadpool.rewriteJs(await responseBody.arrayBuffer(), url.toString());
@ -166,7 +165,7 @@ async function handleCookies(url: URL, headers: string[]) {
let [key, value] = cookieParsed.shift(); let [key, value] = cookieParsed.shift();
if (!value) continue; if (!value) continue;
value = value.replace('"', ""); value = value.replace("\"", "");
const hostArg = cookieParsed.find((x) => x[0] === "Domain"); const hostArg = cookieParsed.find((x) => x[0] === "Domain");
cookieParsed = cookieParsed.filter((x) => x[0] !== "Domain"); cookieParsed = cookieParsed.filter((x) => x[0] !== "Domain");

View file

@ -1,3 +1,4 @@
import IDBMap from "idb-map-entries";
import { FakeServiceWorker } from "./fakesw"; import { FakeServiceWorker } from "./fakesw";
import { swfetch } from "./fetch"; import { swfetch } from "./fetch";
import { ScramjetThreadpool } from "./threadpool"; import { ScramjetThreadpool } from "./threadpool";
@ -18,10 +19,9 @@ export class ScramjetServiceWorker {
serviceWorkers: FakeServiceWorker[] = []; serviceWorkers: FakeServiceWorker[] = [];
constructor(config = self.$scramjet.config) { constructor() {
this.loadConfig();
this.client = new self.$scramjet.shared.util.BareClient(); this.client = new self.$scramjet.shared.util.BareClient();
if (!config.prefix) config.prefix = "/scramjet/";
this.config = config;
this.threadpool = new ScramjetThreadpool(); this.threadpool = new ScramjetThreadpool();
@ -46,6 +46,20 @@ export class ScramjetServiceWorker {
}); });
} }
loadConfig() {
const store = new IDBMap("config", {
prefix: "scramjet"
});
if (store.has("config")) {
store.get("config").then((config) => {
this.config = config;
self.$scramjet.config = config;
self.$scramjet.codec = self.$scramjet.codecs[config.codec]
});
}
}
async getLocalStorage(): Promise<Record<string, string>> { async getLocalStorage(): Promise<Record<string, string>> {
let clients = await self.clients.matchAll(); let clients = await self.clients.matchAll();
clients = clients.filter( clients = clients.filter(

View file

@ -32,8 +32,7 @@
<body> <body>
<script src="https://unpkg.com/dreamland"></script> <script src="https://unpkg.com/dreamland"></script>
<script src="/baremux/index.js" defer></script> <script src="/baremux/index.js" defer></script>
<script src="/scram/scramjet.codecs.js" defer></script> <script src="/scram/scramjet.bootstrapper.js"></script>
<script src="/scram/scramjet.config.js" defer></script>
<script src="ui.js" defer></script> <script src="ui.js" defer></script>
</body> </body>
</html> </html>

View file

@ -1,6 +1,5 @@
importScripts( importScripts(
"/scram/scramjet.codecs.js", "/scram/scramjet.codecs.js",
"/scram/scramjet.config.js",
"/scram/scramjet.shared.js", "/scram/scramjet.shared.js",
"/scram/scramjet.worker.js" "/scram/scramjet.worker.js"
); );

View file

@ -1,7 +1,13 @@
navigator.serviceWorker.register("./sw.js").then((reg) => { const bootstrapper = new ScramjetBootstrapper({
reg.update(); codecs: "/scram/scramjet.codecs.js",
worker: "/scram/scramjet.worker.js",
thread: "/scram/scramjet.thread.js",
client: "/scram/scramjet.client.js",
shared: "/scram/scramjet.shared.js",
}); });
bootstrapper.registerSw("./sw.js");
navigator.serviceWorker.ready.then((reg) => { navigator.serviceWorker.ready.then((reg) => {
for (let i = 0; i < 20; i++) { for (let i = 0; i < 20; i++) {
const thread = new SharedWorker($scramjet.config.thread, { const thread = new SharedWorker($scramjet.config.thread, {
@ -159,7 +165,7 @@ function App() {
<button on:click=${() => window.open(this.urlencoded)}>open in fullscreen</button> <button on:click=${() => window.open(this.urlencoded)}>open in fullscreen</button>
</div> </div>
</div> </div>
<input class="bar" bind:value=${use(store.url)} on:input=${(e) => (store.url = e.target.value)} on:keyup=${(e) => e.keyCode == 13 && console.log((this.urlencoded = $scramjet.config.prefix + $scramjet.config.codec.encode(e.target.value)))}></input> <input class="bar" bind:value=${use(store.url)} on:input=${(e) => (store.url = e.target.value)} on:keyup=${(e) => e.keyCode == 13 && console.log((this.urlencoded = $scramjet.config.prefix + $scramjet.codec.encode(e.target.value)))}></input>
<iframe src=${use(this.urlencoded)}></iframe> <iframe src=${use(this.urlencoded)}></iframe>
</div> </div>
`; `;