mirror of
https://github.com/MercuryWorkshop/scramjet.git
synced 2025-05-12 14:00:01 -04:00
start bootstrapper
This commit is contained in:
parent
034c8e1c6d
commit
17b440ea37
15 changed files with 166 additions and 131 deletions
|
@ -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",
|
||||
},
|
||||
},
|
||||
});
|
|
@ -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",
|
||||
},
|
||||
};
|
|
@ -16,6 +16,7 @@ export default defineConfig({
|
|||
client: join(__dirname, "src/client/index.ts"),
|
||||
config: join(__dirname, "src/scramjet.config.ts"),
|
||||
codecs: join(__dirname, "src/codecs/index.ts"),
|
||||
bootstrapper: join(__dirname, "src/bootsrapper/index.ts")
|
||||
},
|
||||
resolve: {
|
||||
extensions: [".ts", ".js"],
|
||||
|
@ -44,6 +45,11 @@ export default defineConfig({
|
|||
type: "javascript/auto",
|
||||
},
|
||||
],
|
||||
parser: {
|
||||
javascript: {
|
||||
dynamicImportMode: "eager"
|
||||
}
|
||||
}
|
||||
},
|
||||
output: {
|
||||
filename: "scramjet.[name].js",
|
||||
|
|
|
@ -76,7 +76,7 @@ fastify.register(fastifyStatic, {
|
|||
});
|
||||
fastify.listen({
|
||||
port: process.env.PORT || 1337,
|
||||
host: "0.0.0.0",
|
||||
// host: "0.0.0.0",
|
||||
});
|
||||
|
||||
writeFileSync(
|
||||
|
|
70
src/bootsrapper/index.ts
Normal file
70
src/bootsrapper/index.ts
Normal 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;
|
|
@ -1,18 +1,18 @@
|
|||
if (!self.$scramjet) {
|
||||
//@ts-expect-error really dumb workaround
|
||||
self.$scramjet = {};
|
||||
}
|
||||
self.$scramjet.config = {
|
||||
prefix: "/scramjet/",
|
||||
codec: self.$scramjet.codecs.plain,
|
||||
wrapfn: "$scramjet$wrap",
|
||||
trysetfn: "$scramjet$tryset",
|
||||
importfn: "$scramjet$import",
|
||||
rewritefn: "$scramjet$rewrite",
|
||||
config: "/scram/scramjet.config.js",
|
||||
shared: "/scram/scramjet.shared.js",
|
||||
worker: "/scram/scramjet.worker.js",
|
||||
thread: "/scram/scramjet.thread.js",
|
||||
client: "/scram/scramjet.client.js",
|
||||
codecs: "/scram/scramjet.codecs.js",
|
||||
};
|
||||
// if (!self.$scramjet) {
|
||||
// //@ts-expect-error really dumb workaround
|
||||
// self.$scramjet = {};
|
||||
// }
|
||||
// self.$scramjet.config = {
|
||||
// prefix: "/scramjet/",
|
||||
// codec: self.$scramjet.codecs.plain,
|
||||
// wrapfn: "$scramjet$wrap",
|
||||
// trysetfn: "$scramjet$tryset",
|
||||
// importfn: "$scramjet$import",
|
||||
// rewritefn: "$scramjet$rewrite",
|
||||
// config: "/scram/scramjet.config.js",
|
||||
// shared: "/scram/scramjet.shared.js",
|
||||
// worker: "/scram/scramjet.worker.js",
|
||||
// thread: "/scram/scramjet.thread.js",
|
||||
// client: "/scram/scramjet.client.js",
|
||||
// codecs: "/scram/scramjet.codecs.js",
|
||||
// };
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Parser } from "htmlparser2";
|
||||
import { DomHandler, Element } from "domhandler";
|
||||
import { ElementType, Parser } from "htmlparser2";
|
||||
import { DomHandler, Element, Text } from "domhandler";
|
||||
import { hasAttrib } from "domutils";
|
||||
import render from "dom-serializer";
|
||||
import { encodeUrl } from "./url";
|
||||
|
@ -8,7 +8,7 @@ import { rewriteJs } from "./js";
|
|||
|
||||
export function isScramjetFile(src: string) {
|
||||
let bool = false;
|
||||
["codecs", "client", "shared", "worker", "config"].forEach((file) => {
|
||||
["codecs", "client", "shared", "worker"].forEach((file) => {
|
||||
if (src === self.$scramjet.config[file]) bool = true;
|
||||
});
|
||||
|
||||
|
@ -96,11 +96,13 @@ function traverseParsedHtml(node, origin?: URL) {
|
|||
node.attribs.type
|
||||
) &&
|
||||
node.children[0] !== undefined
|
||||
&& !(node.attribs["data-scramjet"])
|
||||
) {
|
||||
let js = node.children[0].data;
|
||||
const htmlcomment = /<!--[\s\S]*?-->/g;
|
||||
js = js.replace(htmlcomment, "");
|
||||
node.children[0].data = rewriteJs(js, origin);
|
||||
console.log(node.children)
|
||||
}
|
||||
if (node.name === "meta" && hasAttrib(node, "http-equiv")) {
|
||||
if (node.attribs["http-equiv"] === "content-security-policy") {
|
||||
|
@ -117,17 +119,19 @@ function traverseParsedHtml(node, origin?: URL) {
|
|||
}
|
||||
|
||||
if (node.name === "head") {
|
||||
const scramjetScripts = [];
|
||||
["codecs", "config", "shared", "client"].forEach((script) => {
|
||||
scramjetScripts.push(
|
||||
new Element("script", {
|
||||
src: self.$scramjet.config[script],
|
||||
"data-scramjet": "",
|
||||
})
|
||||
);
|
||||
});
|
||||
const scripts = [];
|
||||
|
||||
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) {
|
||||
|
|
|
@ -26,13 +26,13 @@ export function rewriteJs(js: string | ArrayBuffer, origin?: URL) {
|
|||
const before = performance.now();
|
||||
if (typeof js === "string") {
|
||||
js = new TextDecoder().decode(
|
||||
rewrite_js(js, origin.toString(), self.$scramjet.config)
|
||||
rewrite_js(js, origin.toString(), self.$scramjet)
|
||||
);
|
||||
} else {
|
||||
js = rewrite_js_from_arraybuffer(
|
||||
new Uint8Array(js),
|
||||
origin.toString(),
|
||||
self.$scramjet.config
|
||||
self.$scramjet
|
||||
);
|
||||
}
|
||||
const after = performance.now();
|
||||
|
|
|
@ -21,7 +21,7 @@ export function encodeUrl(url: string | URL, origin?: URL) {
|
|||
origin = new URL(new URL(location.href).searchParams.get("origin"));
|
||||
} else
|
||||
origin = new URL(
|
||||
self.$scramjet.config.codec.decode(
|
||||
self.$scramjet.codec.decode(
|
||||
location.href.slice(
|
||||
(location.origin + self.$scramjet.config.prefix).length
|
||||
)
|
||||
|
@ -40,7 +40,7 @@ export function encodeUrl(url: string | URL, origin?: URL) {
|
|||
return (
|
||||
location.origin +
|
||||
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) &&
|
||||
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)) {
|
||||
return 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)
|
||||
);
|
||||
} else {
|
||||
|
|
32
src/types.d.ts
vendored
32
src/types.d.ts
vendored
|
@ -1,3 +1,4 @@
|
|||
import { ScramjetBootstrapper } from "./bootsrapper/index";
|
||||
import { encodeUrl, decodeUrl } from "./shared/rewriters/url";
|
||||
import { rewriteCss } from "./shared/rewriters/css";
|
||||
import { rewriteHtml, rewriteSrcset } from "./shared/rewriters/html";
|
||||
|
@ -9,6 +10,20 @@ import type { Codec } from "./codecs";
|
|||
import { BareClient } from "@mercuryworkshop/bare-mux";
|
||||
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 {
|
||||
interface Window {
|
||||
$scramjet: {
|
||||
|
@ -31,27 +46,16 @@ declare global {
|
|||
parseDomain: typeof parseDomain;
|
||||
};
|
||||
};
|
||||
config: {
|
||||
prefix: string;
|
||||
codec: Codec;
|
||||
wrapfn: string;
|
||||
trysetfn: string;
|
||||
importfn: string;
|
||||
rewritefn: string;
|
||||
config: string;
|
||||
shared: string;
|
||||
worker: string;
|
||||
thread: string;
|
||||
client: string;
|
||||
codecs: string;
|
||||
};
|
||||
config: ScramjetConfig;
|
||||
codecs: {
|
||||
none: Codec;
|
||||
plain: Codec;
|
||||
base64: Codec;
|
||||
xor: Codec;
|
||||
};
|
||||
codec: Codec;
|
||||
};
|
||||
WASM: string;
|
||||
ScramjetBootstrapper: typeof ScramjetBootstrapper;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ export async function swfetch(
|
|||
try {
|
||||
const url = new URL(decodeUrl(request.url));
|
||||
|
||||
let headers = new Headers();
|
||||
const headers = new Headers();
|
||||
for (const [key, value] of request.headers.entries()) {
|
||||
headers.set(key, value);
|
||||
}
|
||||
|
@ -104,8 +104,7 @@ async function handleResponse(
|
|||
case "script":
|
||||
responseBody = rewriteJs(
|
||||
await response.arrayBuffer(),
|
||||
url,
|
||||
self.$scramjet.config.codec.encode
|
||||
url
|
||||
);
|
||||
// Disable threading for now, it's causing issues.
|
||||
// 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();
|
||||
if (!value) continue;
|
||||
value = value.replace('"', "");
|
||||
value = value.replace("\"", "");
|
||||
|
||||
const hostArg = cookieParsed.find((x) => x[0] === "Domain");
|
||||
cookieParsed = cookieParsed.filter((x) => x[0] !== "Domain");
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import IDBMap from "idb-map-entries";
|
||||
import { FakeServiceWorker } from "./fakesw";
|
||||
import { swfetch } from "./fetch";
|
||||
import { ScramjetThreadpool } from "./threadpool";
|
||||
|
@ -18,10 +19,9 @@ export class ScramjetServiceWorker {
|
|||
|
||||
serviceWorkers: FakeServiceWorker[] = [];
|
||||
|
||||
constructor(config = self.$scramjet.config) {
|
||||
constructor() {
|
||||
this.loadConfig();
|
||||
this.client = new self.$scramjet.shared.util.BareClient();
|
||||
if (!config.prefix) config.prefix = "/scramjet/";
|
||||
this.config = config;
|
||||
|
||||
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>> {
|
||||
let clients = await self.clients.matchAll();
|
||||
clients = clients.filter(
|
||||
|
|
|
@ -32,8 +32,7 @@
|
|||
<body>
|
||||
<script src="https://unpkg.com/dreamland"></script>
|
||||
<script src="/baremux/index.js" defer></script>
|
||||
<script src="/scram/scramjet.codecs.js" defer></script>
|
||||
<script src="/scram/scramjet.config.js" defer></script>
|
||||
<script src="/scram/scramjet.bootstrapper.js"></script>
|
||||
<script src="ui.js" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
importScripts(
|
||||
"/scram/scramjet.codecs.js",
|
||||
"/scram/scramjet.config.js",
|
||||
"/scram/scramjet.shared.js",
|
||||
"/scram/scramjet.worker.js"
|
||||
);
|
||||
|
|
12
static/ui.js
12
static/ui.js
|
@ -1,7 +1,13 @@
|
|||
navigator.serviceWorker.register("./sw.js").then((reg) => {
|
||||
reg.update();
|
||||
const bootstrapper = new ScramjetBootstrapper({
|
||||
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) => {
|
||||
for (let i = 0; i < 20; i++) {
|
||||
const thread = new SharedWorker($scramjet.config.thread, {
|
||||
|
@ -159,7 +165,7 @@ function App() {
|
|||
<button on:click=${() => window.open(this.urlencoded)}>open in fullscreen</button>
|
||||
</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>
|
||||
</div>
|
||||
`;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue