mirror of
https://github.com/MercuryWorkshop/scramjet.git
synced 2025-05-12 22:10:01 -04:00
merge
This commit is contained in:
commit
b6bdb674fb
20 changed files with 61 additions and 197 deletions
25
.github/workflows/main.yml
vendored
25
.github/workflows/main.yml
vendored
|
@ -5,6 +5,11 @@ on:
|
|||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -41,6 +46,13 @@ jobs:
|
|||
dist/*.js.map
|
||||
- name: build statics
|
||||
run: bash buildstatic.sh
|
||||
- name: upload pages artifact
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
path: "./staticbuild"
|
||||
- name: deploy to github
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
|
||||
upload:
|
||||
name: Upload release
|
||||
|
@ -78,16 +90,3 @@ jobs:
|
|||
body: ${{ github.event.head_commit.message }}
|
||||
artifacts: "scramjet.zip"
|
||||
prerelease: true
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build # Deploy after the build job finishes
|
||||
permissions:
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
with:
|
||||
github_token: ${{ github.token }}
|
||||
publish_dir: ./staticbuild
|
||||
|
|
|
@ -46,6 +46,7 @@ export default [
|
|||
"no-unreachable": "warn",
|
||||
"no-undef": "off",
|
||||
"no-empty": "off",
|
||||
"no-debugger": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"@typescript-eslint/ban-types": "off",
|
||||
|
|
|
@ -419,7 +419,7 @@ fn expression_span(e: &Expression) -> Span {
|
|||
}
|
||||
|
||||
// js MUST not be able to get a reference to any of these because sbx
|
||||
const UNSAFE_GLOBALS: [&str; 10] = [
|
||||
const UNSAFE_GLOBALS: &[&str] = &[
|
||||
"window",
|
||||
"self",
|
||||
"globalThis",
|
||||
|
|
|
@ -10,7 +10,6 @@ const __dirname = fileURLToPath(new URL(".", import.meta.url));
|
|||
const packagemeta = JSON.parse(await readFile("package.json"));
|
||||
|
||||
export default defineConfig({
|
||||
// change to production when needed
|
||||
mode: "development",
|
||||
entry: {
|
||||
shared: join(__dirname, "src/shared/index.ts"),
|
||||
|
|
|
@ -7,11 +7,11 @@ import { getOwnPropertyDescriptorHandler } from "../helpers";
|
|||
export const order = 2;
|
||||
|
||||
export const enabled = () => config.flags.serviceworkers;
|
||||
export function disabled(client: ScramjetClient, self: Self) {
|
||||
export function disabled(_client: ScramjetClient, _self: Self) {
|
||||
Reflect.deleteProperty(Navigator.prototype, "serviceWorker");
|
||||
}
|
||||
|
||||
export default function (client: ScramjetClient, self: Self) {
|
||||
export default function (client: ScramjetClient, _self: Self) {
|
||||
let registration;
|
||||
|
||||
client.Proxy("EventTarget.prototype.addEventListener", {
|
||||
|
@ -45,7 +45,7 @@ export default function (client: ScramjetClient, self: Self) {
|
|||
});
|
||||
|
||||
client.Trap("navigator.serviceWorker.ready", {
|
||||
get(ctx) {
|
||||
get(_ctx) {
|
||||
console.log(registration);
|
||||
|
||||
return new Promise((resolve) => resolve(registration));
|
||||
|
@ -53,7 +53,7 @@ export default function (client: ScramjetClient, self: Self) {
|
|||
});
|
||||
|
||||
client.Trap("navigator.serviceWorker.controller", {
|
||||
get(ctx) {
|
||||
get(_ctx) {
|
||||
return registration?.active;
|
||||
},
|
||||
});
|
||||
|
|
|
@ -63,7 +63,9 @@ export default function (client: ScramjetClient, self: typeof window) {
|
|||
ownKeys(target) {
|
||||
return Reflect.ownKeys(target)
|
||||
.filter((f) => typeof f === "string" && f.startsWith(client.url.host))
|
||||
.map((f) => f.substring(client.url.host.length + 1));
|
||||
.map((f) =>
|
||||
typeof f === "string" ? f.substring(client.url.host.length + 1) : f
|
||||
);
|
||||
},
|
||||
|
||||
getOwnPropertyDescriptor(target, property) {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
// import { encodeUrl } from "../shared";
|
||||
import { ScramjetClient } from "./client";
|
||||
import { indirectEval } from "./shared/eval";
|
||||
// import { config } from "../shared";
|
||||
import { getOwnPropertyDescriptorHandler } from "./helpers";
|
||||
import { indirectEval } from "./shared/eval";
|
||||
|
||||
export const UNSAFE_GLOBALS = [
|
||||
"window",
|
||||
|
@ -21,10 +21,13 @@ export function createGlobalProxy(
|
|||
): typeof globalThis {
|
||||
return new Proxy(self, {
|
||||
get(target, prop) {
|
||||
const value = Reflect.get(target, prop);
|
||||
|
||||
if (prop === "location") return client.locationProxy;
|
||||
if (typeof prop === "string" && UNSAFE_GLOBALS.includes(prop))
|
||||
return client.wrapfn(value);
|
||||
return client.wrapfn(self[prop]);
|
||||
if (prop === "$scramjet") return;
|
||||
if (prop === "eval") return indirectEval.bind(client);
|
||||
|
||||
const value = Reflect.get(target, prop);
|
||||
|
||||
return value;
|
||||
},
|
||||
|
|
|
@ -2,27 +2,26 @@ export function getOwnPropertyDescriptorHandler(target, prop) {
|
|||
const realDescriptor = Reflect.getOwnPropertyDescriptor(target, prop);
|
||||
|
||||
return realDescriptor;
|
||||
// const d: PropertyDescriptor = {};
|
||||
|
||||
const d: PropertyDescriptor = {};
|
||||
// if (realDescriptor.enumerable !== undefined)
|
||||
// d.enumerable = realDescriptor.enumerable;
|
||||
// if (realDescriptor.configurable !== undefined)
|
||||
// d.configurable = realDescriptor.configurable;
|
||||
// if (realDescriptor.writable !== undefined)
|
||||
// d.writable = realDescriptor.writable;
|
||||
|
||||
if (realDescriptor.enumerable !== undefined)
|
||||
d.enumerable = realDescriptor.enumerable;
|
||||
if (realDescriptor.configurable !== undefined)
|
||||
d.configurable = realDescriptor.configurable;
|
||||
if (realDescriptor.writable !== undefined)
|
||||
d.writable = realDescriptor.writable;
|
||||
// if (realDescriptor.get) {
|
||||
// d.get = () => this.get(target, prop);
|
||||
// }
|
||||
|
||||
if (realDescriptor.get) {
|
||||
d.get = () => this.get(target, prop);
|
||||
}
|
||||
// if (realDescriptor.set) {
|
||||
// d.set = (value) => this.set(target, prop, value);
|
||||
// }
|
||||
|
||||
if (realDescriptor.set) {
|
||||
d.set = (value) => this.set(target, prop, value);
|
||||
}
|
||||
// if (realDescriptor.value) {
|
||||
// d.value = this.get(target, prop);
|
||||
// }
|
||||
|
||||
if (realDescriptor.value) {
|
||||
d.value = this.get(target, prop);
|
||||
}
|
||||
|
||||
return d;
|
||||
// return d;
|
||||
}
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
import { isemulatedsw } from "../..";
|
||||
import { unrewriteUrl } from "../../../shared";
|
||||
import { ScramjetClient } from "../../client";
|
||||
import { rewriteUrl, rewriteHeaders } from "../../../shared";
|
||||
import { rewriteUrl } from "../../../shared";
|
||||
|
||||
export default function (client: ScramjetClient, self: typeof globalThis) {
|
||||
export default function (client: ScramjetClient, _self: typeof globalThis) {
|
||||
client.Proxy("fetch", {
|
||||
apply(ctx) {
|
||||
if (typeof ctx.args[0] === "string" || ctx.args[0] instanceof URL) {
|
||||
|
|
|
@ -59,7 +59,7 @@ export default function (client: ScramjetClient, self: typeof globalThis) {
|
|||
fakeWebSocket.dispatchEvent(fakeev);
|
||||
}
|
||||
|
||||
barews.addEventListener("open", (ev) => {
|
||||
barews.addEventListener("open", () => {
|
||||
fakeEventSend(new Event("open"));
|
||||
});
|
||||
barews.addEventListener("close", (ev) => {
|
||||
|
@ -76,7 +76,7 @@ export default function (client: ScramjetClient, self: typeof globalThis) {
|
|||
|
||||
fakeEventSend(fakeev);
|
||||
});
|
||||
barews.addEventListener("error", (ev) => {
|
||||
barews.addEventListener("error", () => {
|
||||
fakeEventSend(new Event("error"));
|
||||
});
|
||||
|
||||
|
|
|
@ -51,17 +51,17 @@ export default function (client: ScramjetClient, self: typeof window) {
|
|||
|
||||
if (desc.get) {
|
||||
client.RawProxy(desc, "get", {
|
||||
apply(ctx) {
|
||||
apply(getCtx) {
|
||||
// value of this in the getter needs to be corrected
|
||||
unproxy(ctx, client);
|
||||
unproxy(getCtx, client);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (desc.set) {
|
||||
client.RawProxy(desc, "set", {
|
||||
apply(ctx) {
|
||||
unproxy(ctx, client);
|
||||
apply(setCtx) {
|
||||
unproxy(setCtx, client);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
@ -47,9 +47,6 @@ export function createWrapFn(client: ScramjetClient, self: typeof globalThis) {
|
|||
} else if (isworker && identifier instanceof self.WorkerGlobalScope) {
|
||||
return client.globalProxy;
|
||||
}
|
||||
if (identifier == self.eval) {
|
||||
return indirectEval.bind(client);
|
||||
}
|
||||
|
||||
return identifier;
|
||||
};
|
||||
|
|
|
@ -10,7 +10,6 @@ export class ScramjetController {
|
|||
// sane ish defaults
|
||||
const defaultConfig: Partial<ScramjetConfig> = {
|
||||
prefix: "/scramjet/",
|
||||
|
||||
globals: {
|
||||
wrapfn: "$scramjet$wrap",
|
||||
trysetfn: "$scramjet$tryset",
|
||||
|
|
|
@ -137,6 +137,7 @@ export const htmlRules: {
|
|||
// because they can't be fetch'd
|
||||
return unrewriteBlob(value);
|
||||
}
|
||||
|
||||
return rewriteUrl(value, meta);
|
||||
},
|
||||
src: ["video", "audio"],
|
||||
|
|
|
@ -26,7 +26,7 @@ export function rewriteJs(js: string | ArrayBuffer, meta: URLMeta) {
|
|||
return rewriteJsNaiive(text);
|
||||
}
|
||||
|
||||
const before = performance.now();
|
||||
// const before = performance.now();
|
||||
if (typeof js === "string") {
|
||||
js = new TextDecoder().decode(rewrite_js(js, meta.base.href, $scramjet));
|
||||
} else {
|
||||
|
@ -36,7 +36,7 @@ export function rewriteJs(js: string | ArrayBuffer, meta: URLMeta) {
|
|||
$scramjet
|
||||
);
|
||||
}
|
||||
const after = performance.now();
|
||||
// const after = performance.now();
|
||||
|
||||
// dbg.debug("Rewrite took", Math.floor((after - before) * 10) / 10, "ms");
|
||||
|
||||
|
|
|
@ -15,12 +15,14 @@ function tryCanParseURL(url: string, origin?: string | URL): URL | null {
|
|||
}
|
||||
|
||||
export function rewriteBlob(url: string, meta: URLMeta) {
|
||||
let blob = new URL(url.substring("blob:".length));
|
||||
const blob = new URL(url.substring("blob:".length));
|
||||
|
||||
return "blob:" + meta.origin.origin + blob.pathname;
|
||||
}
|
||||
|
||||
export function unrewriteBlob(url: string) {
|
||||
let blob = new URL(url.substring("blob:".length));
|
||||
const blob = new URL(url.substring("blob:".length));
|
||||
|
||||
return "blob:" + location.origin + blob.pathname;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
import { rewriteJs } from "../shared/rewriters/js";
|
||||
|
||||
// @ts-ignore
|
||||
onconnect = (e) => {
|
||||
const port = e.ports[0];
|
||||
|
||||
console.log("thread: connected to port", port);
|
||||
port.postMessage("ready");
|
||||
|
||||
let syncToken = 0;
|
||||
port.onmessage = ({ data }) => {
|
||||
console.log("thread: received message", data);
|
||||
const [task, ...args] = data;
|
||||
const token = syncToken++;
|
||||
|
||||
try {
|
||||
const res = tasks[task](...args);
|
||||
console.log("thread: task", task, "completed with token", token);
|
||||
port.postMessage({
|
||||
token,
|
||||
result: res,
|
||||
});
|
||||
} catch (e) {
|
||||
port.postMessage({
|
||||
token,
|
||||
error: e.message,
|
||||
});
|
||||
}
|
||||
|
||||
port.postMessage("idle");
|
||||
};
|
||||
};
|
||||
|
||||
const tasks = {
|
||||
rewriteJs: taskRewriteJs,
|
||||
};
|
||||
|
||||
function taskRewriteJs(js: ArrayBuffer, origin: string): string {
|
||||
// idk how to get the codec from here
|
||||
return rewriteJs(js, new URL(origin), () => {});
|
||||
}
|
|
@ -58,7 +58,7 @@ export async function swfetch(
|
|||
dataurl = unrewriteBlob(dataurl);
|
||||
}
|
||||
|
||||
let response: Response = await fetch(dataurl, {});
|
||||
const response: Response = await fetch(dataurl, {});
|
||||
|
||||
let body: BodyType;
|
||||
|
||||
|
@ -286,15 +286,10 @@ async function rewriteBody(
|
|||
} else {
|
||||
return response.body;
|
||||
}
|
||||
break;
|
||||
case "script":
|
||||
return rewriteJs(await response.arrayBuffer(), meta);
|
||||
// Disable threading for now, it's causing issues.
|
||||
// responseBody = await this.threadpool.rewriteJs(await responseBody.arrayBuffer(), url.toString());
|
||||
break;
|
||||
case "style":
|
||||
return rewriteCss(await response.text(), meta);
|
||||
break;
|
||||
case "sharedworker":
|
||||
case "worker":
|
||||
return rewriteWorkers(await response.arrayBuffer(), workertype, meta);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { FakeServiceWorker } from "./fakesw";
|
||||
import { swfetch } from "./fetch";
|
||||
import { ScramjetThreadpool } from "./threadpool";
|
||||
import type BareClient from "@mercuryworkshop/bare-mux";
|
||||
import { ScramjetConfig } from "../types";
|
||||
import { $scramjet, loadCodecs } from "../scramjet";
|
||||
|
@ -8,7 +7,6 @@ import { $scramjet, loadCodecs } from "../scramjet";
|
|||
export class ScramjetServiceWorker extends EventTarget {
|
||||
client: BareClient;
|
||||
config: ScramjetConfig;
|
||||
threadpool: ScramjetThreadpool;
|
||||
|
||||
syncPool: Record<number, (val?: any) => void> = {};
|
||||
synctoken = 0;
|
||||
|
@ -21,8 +19,6 @@ export class ScramjetServiceWorker extends EventTarget {
|
|||
super();
|
||||
this.client = new $scramjet.shared.util.BareClient();
|
||||
|
||||
this.threadpool = new ScramjetThreadpool();
|
||||
|
||||
addEventListener("message", ({ data }: { data: MessageC2W }) => {
|
||||
if (!("scramjet$type" in data)) return;
|
||||
|
||||
|
|
|
@ -1,88 +0,0 @@
|
|||
type Thread = {
|
||||
handle: MessagePort;
|
||||
ready: boolean;
|
||||
busy: boolean;
|
||||
syncToken: number;
|
||||
promises: Map<number, { resolve; reject }>;
|
||||
};
|
||||
|
||||
export class ScramjetThreadpool {
|
||||
threads: Thread[] = [];
|
||||
constructor() {
|
||||
self.addEventListener("message", ({ data }) => {
|
||||
if (data.scramjet$type == "add") {
|
||||
this.spawn(data.handle);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
spawn(handle) {
|
||||
const thread = {
|
||||
handle,
|
||||
ready: false,
|
||||
busy: false,
|
||||
syncToken: 0,
|
||||
promises: new Map(),
|
||||
};
|
||||
|
||||
this.threads.push(thread);
|
||||
|
||||
thread.handle.onmessage = (e) => {
|
||||
if (e.data === "ready") {
|
||||
thread.ready = true;
|
||||
|
||||
return;
|
||||
}
|
||||
if (e.data === "idle") {
|
||||
thread.busy = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const { token, result, error } = e.data;
|
||||
const { resolve, reject } = thread.promises.get(token);
|
||||
thread.promises.delete(token);
|
||||
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
};
|
||||
|
||||
thread.handle.start();
|
||||
}
|
||||
|
||||
pick(): Thread | undefined {
|
||||
const alive = this.threads.filter((t) => t.ready);
|
||||
const idle = alive.filter((t) => !t.busy);
|
||||
|
||||
// no threads
|
||||
if (!alive.length) return;
|
||||
|
||||
// there is a thread, but it's busy
|
||||
if (!idle.length) return alive[Math.floor(Math.random() * alive.length)];
|
||||
|
||||
// there's an open thread
|
||||
return idle[Math.floor(Math.random() * idle.length)];
|
||||
}
|
||||
|
||||
run(task: string, args: any[], transferrable: any[]): Promise<any> {
|
||||
const thread = this.pick();
|
||||
if (!thread) throw new Error("No threads available");
|
||||
thread.busy = true;
|
||||
|
||||
const token = thread.syncToken++;
|
||||
|
||||
// console.log("runthread: dispatching task", task, "to thread", thread, "of token", token)
|
||||
return new Promise((resolve, reject) => {
|
||||
thread.promises.set(token, { resolve, reject });
|
||||
|
||||
thread.handle.postMessage([task, ...args], transferrable);
|
||||
});
|
||||
}
|
||||
|
||||
async rewriteJs(js: ArrayBuffer, origin: string): Promise<string> {
|
||||
return await this.run("rewriteJs", [js, origin], [js]);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue