This commit is contained in:
velzie 2024-10-13 10:21:54 -04:00
commit b6bdb674fb
No known key found for this signature in database
GPG key ID: AA51AEFB0A1F3820
20 changed files with 61 additions and 197 deletions

View file

@ -5,6 +5,11 @@ on:
pull_request: pull_request:
workflow_dispatch: workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -41,6 +46,13 @@ jobs:
dist/*.js.map dist/*.js.map
- name: build statics - name: build statics
run: bash buildstatic.sh 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: upload:
name: Upload release name: Upload release
@ -78,16 +90,3 @@ jobs:
body: ${{ github.event.head_commit.message }} body: ${{ github.event.head_commit.message }}
artifacts: "scramjet.zip" artifacts: "scramjet.zip"
prerelease: true 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

View file

@ -46,6 +46,7 @@ export default [
"no-unreachable": "warn", "no-unreachable": "warn",
"no-undef": "off", "no-undef": "off",
"no-empty": "off", "no-empty": "off",
"no-debugger": "off",
"@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/ban-ts-comment": "off", "@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/ban-types": "off", "@typescript-eslint/ban-types": "off",

View file

@ -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 // 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", "window",
"self", "self",
"globalThis", "globalThis",

View file

@ -10,7 +10,6 @@ const __dirname = fileURLToPath(new URL(".", import.meta.url));
const packagemeta = JSON.parse(await readFile("package.json")); const packagemeta = JSON.parse(await readFile("package.json"));
export default defineConfig({ export default defineConfig({
// change to production when needed
mode: "development", mode: "development",
entry: { entry: {
shared: join(__dirname, "src/shared/index.ts"), shared: join(__dirname, "src/shared/index.ts"),

View file

@ -7,11 +7,11 @@ import { getOwnPropertyDescriptorHandler } from "../helpers";
export const order = 2; export const order = 2;
export const enabled = () => config.flags.serviceworkers; 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"); Reflect.deleteProperty(Navigator.prototype, "serviceWorker");
} }
export default function (client: ScramjetClient, self: Self) { export default function (client: ScramjetClient, _self: Self) {
let registration; let registration;
client.Proxy("EventTarget.prototype.addEventListener", { client.Proxy("EventTarget.prototype.addEventListener", {
@ -45,7 +45,7 @@ export default function (client: ScramjetClient, self: Self) {
}); });
client.Trap("navigator.serviceWorker.ready", { client.Trap("navigator.serviceWorker.ready", {
get(ctx) { get(_ctx) {
console.log(registration); console.log(registration);
return new Promise((resolve) => resolve(registration)); return new Promise((resolve) => resolve(registration));
@ -53,7 +53,7 @@ export default function (client: ScramjetClient, self: Self) {
}); });
client.Trap("navigator.serviceWorker.controller", { client.Trap("navigator.serviceWorker.controller", {
get(ctx) { get(_ctx) {
return registration?.active; return registration?.active;
}, },
}); });

View file

@ -63,7 +63,9 @@ export default function (client: ScramjetClient, self: typeof window) {
ownKeys(target) { ownKeys(target) {
return Reflect.ownKeys(target) return Reflect.ownKeys(target)
.filter((f) => typeof f === "string" && f.startsWith(client.url.host)) .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) { getOwnPropertyDescriptor(target, property) {

View file

@ -1,8 +1,8 @@
// import { encodeUrl } from "../shared"; // import { encodeUrl } from "../shared";
import { ScramjetClient } from "./client"; import { ScramjetClient } from "./client";
import { indirectEval } from "./shared/eval";
// import { config } from "../shared"; // import { config } from "../shared";
import { getOwnPropertyDescriptorHandler } from "./helpers"; import { getOwnPropertyDescriptorHandler } from "./helpers";
import { indirectEval } from "./shared/eval";
export const UNSAFE_GLOBALS = [ export const UNSAFE_GLOBALS = [
"window", "window",
@ -21,10 +21,13 @@ export function createGlobalProxy(
): typeof globalThis { ): typeof globalThis {
return new Proxy(self, { return new Proxy(self, {
get(target, prop) { get(target, prop) {
const value = Reflect.get(target, prop); if (prop === "location") return client.locationProxy;
if (typeof prop === "string" && UNSAFE_GLOBALS.includes(prop)) 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; return value;
}, },

View file

@ -2,27 +2,26 @@ export function getOwnPropertyDescriptorHandler(target, prop) {
const realDescriptor = Reflect.getOwnPropertyDescriptor(target, prop); const realDescriptor = Reflect.getOwnPropertyDescriptor(target, prop);
return realDescriptor; 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) // if (realDescriptor.get) {
d.enumerable = realDescriptor.enumerable; // d.get = () => this.get(target, prop);
if (realDescriptor.configurable !== undefined) // }
d.configurable = realDescriptor.configurable;
if (realDescriptor.writable !== undefined)
d.writable = realDescriptor.writable;
if (realDescriptor.get) { // if (realDescriptor.set) {
d.get = () => this.get(target, prop); // d.set = (value) => this.set(target, prop, value);
} // }
if (realDescriptor.set) { // if (realDescriptor.value) {
d.set = (value) => this.set(target, prop, value); // d.value = this.get(target, prop);
} // }
if (realDescriptor.value) { // return d;
d.value = this.get(target, prop);
}
return d;
} }

View file

@ -3,9 +3,9 @@
import { isemulatedsw } from "../.."; import { isemulatedsw } from "../..";
import { unrewriteUrl } from "../../../shared"; import { unrewriteUrl } from "../../../shared";
import { ScramjetClient } from "../../client"; 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", { client.Proxy("fetch", {
apply(ctx) { apply(ctx) {
if (typeof ctx.args[0] === "string" || ctx.args[0] instanceof URL) { if (typeof ctx.args[0] === "string" || ctx.args[0] instanceof URL) {

View file

@ -59,7 +59,7 @@ export default function (client: ScramjetClient, self: typeof globalThis) {
fakeWebSocket.dispatchEvent(fakeev); fakeWebSocket.dispatchEvent(fakeev);
} }
barews.addEventListener("open", (ev) => { barews.addEventListener("open", () => {
fakeEventSend(new Event("open")); fakeEventSend(new Event("open"));
}); });
barews.addEventListener("close", (ev) => { barews.addEventListener("close", (ev) => {
@ -76,7 +76,7 @@ export default function (client: ScramjetClient, self: typeof globalThis) {
fakeEventSend(fakeev); fakeEventSend(fakeev);
}); });
barews.addEventListener("error", (ev) => { barews.addEventListener("error", () => {
fakeEventSend(new Event("error")); fakeEventSend(new Event("error"));
}); });

View file

@ -51,17 +51,17 @@ export default function (client: ScramjetClient, self: typeof window) {
if (desc.get) { if (desc.get) {
client.RawProxy(desc, "get", { client.RawProxy(desc, "get", {
apply(ctx) { apply(getCtx) {
// value of this in the getter needs to be corrected // value of this in the getter needs to be corrected
unproxy(ctx, client); unproxy(getCtx, client);
}, },
}); });
} }
if (desc.set) { if (desc.set) {
client.RawProxy(desc, "set", { client.RawProxy(desc, "set", {
apply(ctx) { apply(setCtx) {
unproxy(ctx, client); unproxy(setCtx, client);
}, },
}); });
} }

View file

@ -47,9 +47,6 @@ export function createWrapFn(client: ScramjetClient, self: typeof globalThis) {
} else if (isworker && identifier instanceof self.WorkerGlobalScope) { } else if (isworker && identifier instanceof self.WorkerGlobalScope) {
return client.globalProxy; return client.globalProxy;
} }
if (identifier == self.eval) {
return indirectEval.bind(client);
}
return identifier; return identifier;
}; };

View file

@ -10,7 +10,6 @@ export class ScramjetController {
// sane ish defaults // sane ish defaults
const defaultConfig: Partial<ScramjetConfig> = { const defaultConfig: Partial<ScramjetConfig> = {
prefix: "/scramjet/", prefix: "/scramjet/",
globals: { globals: {
wrapfn: "$scramjet$wrap", wrapfn: "$scramjet$wrap",
trysetfn: "$scramjet$tryset", trysetfn: "$scramjet$tryset",

View file

@ -137,6 +137,7 @@ export const htmlRules: {
// because they can't be fetch'd // because they can't be fetch'd
return unrewriteBlob(value); return unrewriteBlob(value);
} }
return rewriteUrl(value, meta); return rewriteUrl(value, meta);
}, },
src: ["video", "audio"], src: ["video", "audio"],

View file

@ -26,7 +26,7 @@ export function rewriteJs(js: string | ArrayBuffer, meta: URLMeta) {
return rewriteJsNaiive(text); return rewriteJsNaiive(text);
} }
const before = performance.now(); // const before = performance.now();
if (typeof js === "string") { if (typeof js === "string") {
js = new TextDecoder().decode(rewrite_js(js, meta.base.href, $scramjet)); js = new TextDecoder().decode(rewrite_js(js, meta.base.href, $scramjet));
} else { } else {
@ -36,7 +36,7 @@ export function rewriteJs(js: string | ArrayBuffer, meta: URLMeta) {
$scramjet $scramjet
); );
} }
const after = performance.now(); // const after = performance.now();
// dbg.debug("Rewrite took", Math.floor((after - before) * 10) / 10, "ms"); // dbg.debug("Rewrite took", Math.floor((after - before) * 10) / 10, "ms");

View file

@ -15,12 +15,14 @@ function tryCanParseURL(url: string, origin?: string | URL): URL | null {
} }
export function rewriteBlob(url: string, meta: URLMeta) { 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; return "blob:" + meta.origin.origin + blob.pathname;
} }
export function unrewriteBlob(url: string) { 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; return "blob:" + location.origin + blob.pathname;
} }

View file

@ -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), () => {});
}

View file

@ -58,7 +58,7 @@ export async function swfetch(
dataurl = unrewriteBlob(dataurl); dataurl = unrewriteBlob(dataurl);
} }
let response: Response = await fetch(dataurl, {}); const response: Response = await fetch(dataurl, {});
let body: BodyType; let body: BodyType;
@ -286,15 +286,10 @@ async function rewriteBody(
} else { } else {
return response.body; return response.body;
} }
break;
case "script": case "script":
return rewriteJs(await response.arrayBuffer(), meta); 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": case "style":
return rewriteCss(await response.text(), meta); return rewriteCss(await response.text(), meta);
break;
case "sharedworker": case "sharedworker":
case "worker": case "worker":
return rewriteWorkers(await response.arrayBuffer(), workertype, meta); return rewriteWorkers(await response.arrayBuffer(), workertype, meta);

View file

@ -1,6 +1,5 @@
import { FakeServiceWorker } from "./fakesw"; import { FakeServiceWorker } from "./fakesw";
import { swfetch } from "./fetch"; import { swfetch } from "./fetch";
import { ScramjetThreadpool } from "./threadpool";
import type BareClient from "@mercuryworkshop/bare-mux"; import type BareClient from "@mercuryworkshop/bare-mux";
import { ScramjetConfig } from "../types"; import { ScramjetConfig } from "../types";
import { $scramjet, loadCodecs } from "../scramjet"; import { $scramjet, loadCodecs } from "../scramjet";
@ -8,7 +7,6 @@ import { $scramjet, loadCodecs } from "../scramjet";
export class ScramjetServiceWorker extends EventTarget { export class ScramjetServiceWorker extends EventTarget {
client: BareClient; client: BareClient;
config: ScramjetConfig; config: ScramjetConfig;
threadpool: ScramjetThreadpool;
syncPool: Record<number, (val?: any) => void> = {}; syncPool: Record<number, (val?: any) => void> = {};
synctoken = 0; synctoken = 0;
@ -21,8 +19,6 @@ export class ScramjetServiceWorker extends EventTarget {
super(); super();
this.client = new $scramjet.shared.util.BareClient(); this.client = new $scramjet.shared.util.BareClient();
this.threadpool = new ScramjetThreadpool();
addEventListener("message", ({ data }: { data: MessageC2W }) => { addEventListener("message", ({ data }: { data: MessageC2W }) => {
if (!("scramjet$type" in data)) return; if (!("scramjet$type" in data)) return;

View file

@ -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]);
}
}