|
import {
|
|
codecDecode,
|
|
codecEncode,
|
|
config,
|
|
loadCodecs,
|
|
setConfig,
|
|
} from "../shared/index";
|
|
import { ScramjetConfig, ScramjetInitConfig } from "../types";
|
|
import { ScramjetFrame } from "./frame";
|
|
|
|
export class ScramjetController {
|
|
private db: IDBDatabase;
|
|
|
|
constructor(config: Partial<ScramjetInitConfig>) {
|
|
|
|
const defaultConfig: ScramjetInitConfig = {
|
|
prefix: "/scramjet/",
|
|
globals: {
|
|
wrapfn: "$scramjet$wrap",
|
|
wrapthisfn: "$scramjet$wrapthis",
|
|
trysetfn: "$scramjet$tryset",
|
|
importfn: "$scramjet$import",
|
|
rewritefn: "$scramjet$rewrite",
|
|
metafn: "$scramjet$meta",
|
|
setrealmfn: "$scramjet$setrealm",
|
|
pushsourcemapfn: "$scramjet$pushsourcemap",
|
|
},
|
|
files: {
|
|
wasm: "/scramjet.wasm.wasm",
|
|
all: "/scramjet.all.js",
|
|
sync: "/scramjet.sync.js",
|
|
},
|
|
flags: {
|
|
serviceworkers: false,
|
|
syncxhr: false,
|
|
naiiveRewriter: false,
|
|
strictRewrites: true,
|
|
rewriterLogs: false,
|
|
captureErrors: true,
|
|
cleanErrors: false,
|
|
scramitize: false,
|
|
sourcemaps: true,
|
|
},
|
|
siteFlags: {},
|
|
codec: {
|
|
encode: (url: string) => {
|
|
if (!url) return url;
|
|
|
|
return encodeURIComponent(url);
|
|
},
|
|
decode: (url: string) => {
|
|
if (!url) return url;
|
|
|
|
return decodeURIComponent(url);
|
|
},
|
|
},
|
|
};
|
|
|
|
const deepMerge = (target: any, source: any): any => {
|
|
for (const key in source) {
|
|
if (source[key] instanceof Object && key in target) {
|
|
Object.assign(source[key], deepMerge(target[key], source[key]));
|
|
}
|
|
}
|
|
|
|
return Object.assign(target || {}, source);
|
|
};
|
|
|
|
const newConfig = deepMerge(defaultConfig, config);
|
|
newConfig.codec.encode = newConfig.codec.encode.toString();
|
|
newConfig.codec.decode = newConfig.codec.decode.toString();
|
|
setConfig(newConfig as ScramjetConfig);
|
|
}
|
|
|
|
async init(): Promise<void> {
|
|
loadCodecs();
|
|
|
|
await this.openIDB();
|
|
navigator.serviceWorker.controller?.postMessage({
|
|
scramjet$type: "loadConfig",
|
|
config,
|
|
});
|
|
dbg.log("config loaded");
|
|
}
|
|
|
|
createFrame(frame?: HTMLIFrameElement): ScramjetFrame {
|
|
if (!frame) {
|
|
frame = document.createElement("iframe");
|
|
}
|
|
|
|
return new ScramjetFrame(this, frame);
|
|
}
|
|
|
|
encodeUrl(url: string | URL): string {
|
|
if (url instanceof URL) url = url.toString();
|
|
|
|
return config.prefix + codecEncode(url);
|
|
}
|
|
|
|
decodeUrl(url: string | URL) {
|
|
if (url instanceof URL) url = url.toString();
|
|
const prefixed = location.origin + config.prefix;
|
|
|
|
return codecDecode(url.slice(prefixed.length));
|
|
}
|
|
|
|
async openIDB(): Promise<IDBDatabase> {
|
|
const db = indexedDB.open("$scramjet", 1);
|
|
|
|
return new Promise<IDBDatabase>((resolve, reject) => {
|
|
db.onsuccess = async () => {
|
|
this.db = db.result;
|
|
await this.#saveConfig();
|
|
resolve(db.result);
|
|
};
|
|
db.onupgradeneeded = () => {
|
|
const res = db.result;
|
|
if (!res.objectStoreNames.contains("config")) {
|
|
res.createObjectStore("config");
|
|
}
|
|
if (!res.objectStoreNames.contains("cookies")) {
|
|
res.createObjectStore("cookies");
|
|
}
|
|
if (!res.objectStoreNames.contains("redirectTrackers")) {
|
|
res.createObjectStore("redirectTrackers");
|
|
}
|
|
if (!res.objectStoreNames.contains("referrerPolicies")) {
|
|
res.createObjectStore("referrerPolicies");
|
|
}
|
|
if (!res.objectStoreNames.contains("publicSuffixList")) {
|
|
res.createObjectStore("publicSuffixList");
|
|
}
|
|
};
|
|
db.onerror = () => reject(db.error);
|
|
});
|
|
}
|
|
|
|
async #saveConfig() {
|
|
if (!this.db) {
|
|
console.error("Store not ready!");
|
|
|
|
return;
|
|
}
|
|
const tx = this.db.transaction("config", "readwrite");
|
|
const store = tx.objectStore("config");
|
|
const req = store.put(config, "config");
|
|
|
|
return new Promise((resolve, reject) => {
|
|
req.onsuccess = resolve;
|
|
req.onerror = reject;
|
|
});
|
|
}
|
|
|
|
async modifyConfig(newconfig: ScramjetConfig) {
|
|
setConfig(Object.assign({}, config, newconfig));
|
|
loadCodecs();
|
|
|
|
await this.#saveConfig();
|
|
}
|
|
}
|
|
|