mirror of
https://github.com/MercuryWorkshop/scramjet.git
synced 2025-05-15 07:20:02 -04:00
start client api
This commit is contained in:
parent
9f2ad08edf
commit
7433e0a7ff
7 changed files with 131 additions and 10 deletions
|
@ -37,6 +37,7 @@
|
|||
"esbuild-server": "^0.3.0",
|
||||
"fastify": "^4.26.2",
|
||||
"htmlparser2": "^9.1.0",
|
||||
"idb-keyval": "^6.2.1",
|
||||
"meriyah": "^4.4.2"
|
||||
}
|
||||
}
|
||||
|
|
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
|
@ -35,6 +35,9 @@ importers:
|
|||
htmlparser2:
|
||||
specifier: ^9.1.0
|
||||
version: 9.1.0
|
||||
idb-keyval:
|
||||
specifier: ^6.2.1
|
||||
version: 6.2.1
|
||||
meriyah:
|
||||
specifier: ^4.4.2
|
||||
version: 4.4.2
|
||||
|
@ -809,6 +812,9 @@ packages:
|
|||
resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
idb-keyval@6.2.1:
|
||||
resolution: {integrity: sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==}
|
||||
|
||||
ieee754@1.2.1:
|
||||
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
|
||||
|
||||
|
@ -2076,6 +2082,8 @@ snapshots:
|
|||
statuses: 2.0.1
|
||||
toidentifier: 1.0.1
|
||||
|
||||
idb-keyval@6.2.1: {}
|
||||
|
||||
ieee754@1.2.1: {}
|
||||
|
||||
ignore@5.3.1: {}
|
||||
|
|
|
@ -3,6 +3,16 @@ import { rewriteCss } from "./rewriters/css";
|
|||
import { rewriteHtml, rewriteSrcset } from "./rewriters/html";
|
||||
import { rewriteJs } from "./rewriters/js";
|
||||
import { rewriteHeaders } from "./rewriters/headers";
|
||||
import * as idb from "idb-keyval";
|
||||
|
||||
export function isScramjetFile(src: string) {
|
||||
let bool = false;
|
||||
["codecs", "client", "bundle", "worker", "config"].forEach((file) => {
|
||||
if (src === self.__scramjet$config[file]) bool = true;
|
||||
});
|
||||
|
||||
return bool;
|
||||
}
|
||||
|
||||
const bundle = {
|
||||
rewriters: {
|
||||
|
@ -14,7 +24,9 @@ const bundle = {
|
|||
rewriteSrcset,
|
||||
rewriteJs,
|
||||
rewriteHeaders
|
||||
}
|
||||
},
|
||||
idb,
|
||||
isScramjetFile
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
import { Parser } from "htmlparser2";
|
||||
import { DomHandler } from "domhandler";
|
||||
import { hasAttrib } from "domutils";
|
||||
import { DomHandler, Element } from "domhandler";
|
||||
import { hasAttrib, prependChild } from "domutils";
|
||||
import render from "dom-serializer";
|
||||
import { encodeUrl } from "./url";
|
||||
import { rewriteCss } from "./css";
|
||||
import { rewriteJs } from "./js";
|
||||
|
||||
// html nodes to rewrite
|
||||
// meta
|
||||
import { isScramjetFile } from "../";
|
||||
|
||||
export function rewriteHtml(html: string, origin?: URL) {
|
||||
const handler = new DomHandler((err, dom) => dom);
|
||||
|
@ -26,9 +24,10 @@ function traverseParsedHtml(node, origin?: URL) {
|
|||
if (hasAttrib(node, "csp")) delete node.attribs.csp;
|
||||
|
||||
/* url attributes */
|
||||
if (hasAttrib(node, "src")) node.attribs.src = encodeUrl(node.attribs.src, origin);
|
||||
if (hasAttrib(node, "src") && !isScramjetFile(node.attribs.src)) node.attribs.src = encodeUrl(node.attribs.src, origin);
|
||||
if (hasAttrib(node, "href")) node.attribs.href = encodeUrl(node.attribs.href, origin);
|
||||
if (hasAttrib(node, "data")) node.attribs.data = encodeUrl(node.attribs.data, origin);
|
||||
if (hasAttrib(node, "action")) node.attribs.action = encodeUrl(node.attribs.action, origin);
|
||||
if (hasAttrib(node, "formaction")) node.attribs.formaction = encodeUrl(node.attribs.formaction, origin);
|
||||
if (hasAttrib(node, "form")) node.attribs.action = encodeUrl(node.attribs.action, origin);
|
||||
|
||||
|
@ -41,12 +40,22 @@ function traverseParsedHtml(node, origin?: URL) {
|
|||
if (node.name === "script" && /(application|text)\/javascript|importmap|undefined/.test(node.attribs.type) && node.children[0] !== undefined) node.children[0].data = rewriteJs(node.children[0].data, origin);
|
||||
if (node.name === "meta" && hasAttrib(node, "http-equiv")) {
|
||||
if (node.attribs["http-equiv"] === "content-security-policy") {
|
||||
return;
|
||||
node = {};
|
||||
} else if (node.attribs["http-equiv"] === "refresh") {
|
||||
node.attribs.content = node.attribs.content.split(";url=").map((elem, index) => index === 1 ? encodeUrl(elem) : elem).join(";url=");
|
||||
const contentArray = node.attribs.content.split(";url=");
|
||||
contentArray[1] = encodeUrl(contentArray[1], origin);
|
||||
node.attribs.content = contentArray.join(";url=");
|
||||
}
|
||||
}
|
||||
|
||||
if (node.name === "head") {
|
||||
["codecs", "config", "bundle", "client"].forEach((script) => {
|
||||
prependChild(node, new Element("script", {
|
||||
src: self.__scramjet$config[script]
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
if (node.childNodes) {
|
||||
for (const childNode in node.childNodes) {
|
||||
node.childNodes[childNode] = traverseParsedHtml(node.childNodes[childNode], origin);
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
import "./location";
|
||||
import "./storage";
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
__location: Location;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
function urlLocation() {
|
||||
let loc = new URL(self.__scramjet$bundle.rewriters.url.decodeUrl(location.href));
|
||||
loc.assign = (url: string) => location.assign(self.__scramjet$bundle.rewriters.url.encodeUrl(url));
|
||||
loc.reload = () => location.reload();
|
||||
loc.replace = (url: string) => location.replace(self.__scramjet$bundle.rewriters.url.encodeUrl(url));
|
||||
loc.toString = () => loc.href;
|
||||
|
||||
return loc;
|
||||
}
|
||||
|
||||
export function locationProxy() {
|
||||
const loc = urlLocation();
|
||||
|
||||
return new Proxy(Location, {
|
||||
get(target, prop) {
|
||||
return loc[prop];
|
||||
},
|
||||
set(obj, prop, value) {
|
||||
if (prop === "href") {
|
||||
location.href = self.__scramjet$bundle.rewriters.url.encodeUrl(value);
|
||||
} else {
|
||||
loc[prop] = value;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
window.__location = locationProxy();
|
53
src/client/storage.ts
Normal file
53
src/client/storage.ts
Normal file
|
@ -0,0 +1,53 @@
|
|||
function storageProxy(scope: Storage): Storage {
|
||||
// sessionStorage isn't properly implemented currently, since everything is being stored in IDB
|
||||
|
||||
const { set, get, keys, del, createStore } = self.__scramjet$bundle.idb;
|
||||
const store = createStore(window.__location.host, "store");
|
||||
|
||||
return new Proxy(scope, {
|
||||
get(target, prop) {
|
||||
switch (prop) {
|
||||
case "getItem":
|
||||
return async function getItem(key: string) {
|
||||
return await get(key, store);
|
||||
}
|
||||
|
||||
case "setItem":
|
||||
return async function setItem(key: string, value: string) {
|
||||
await set(key, value, store);
|
||||
}
|
||||
|
||||
case "removeItem":
|
||||
return async function removeItem(key: string) {
|
||||
await del(key, store);
|
||||
}
|
||||
|
||||
case "clear":
|
||||
return async function clear() {
|
||||
// clear can't be used because the names are the exact same
|
||||
await self.__scramjet$bundle.idb.clear(store);
|
||||
}
|
||||
|
||||
case "key":
|
||||
return async function key(index: number) {
|
||||
// supposed to be key but key is the name of the function
|
||||
return (await keys(store))[index];
|
||||
}
|
||||
|
||||
case "length":
|
||||
return (async ()=>{
|
||||
return (await keys(store)).length;
|
||||
})();
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const localStorageProxy = storageProxy(window.localStorage);
|
||||
const sessionStorageProxy = storageProxy(window.sessionStorage);
|
||||
|
||||
delete window.localStorage;
|
||||
delete window.sessionStorage;
|
||||
|
||||
window.localStorage = localStorageProxy;
|
||||
window.sessionStorage = sessionStorageProxy;
|
Loading…
Add table
Add a link
Reference in a new issue