change build system to rollup

This commit is contained in:
Avad3 2024-07-13 16:30:33 -04:00
parent 5122438c96
commit c3db610837
39 changed files with 2225 additions and 2451 deletions

View file

@ -1,27 +1,27 @@
{
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"rules": {
"no-await-in-loop": "warn",
"no-unused-labels": "error",
"no-unused-vars": "error",
"quotes": ["error", "double"],
"max-lines-per-function": ["error", {
"max": 200,
"skipComments": true
}],
"getter-return": "error",
"newline-before-return": "error",
"no-multiple-empty-lines": "error",
"no-var": "error",
"indent": ["warn", 4],
"no-this-before-super": "warn",
"no-useless-return": "error",
"no-shadow": "error",
"prefer-const": "warn",
"no-unreachable": "warn",
"no-undef": "off",
"@typescript-eslint/ban-ts-comment": "off"
}
{
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"rules": {
"no-await-in-loop": "warn",
"no-unused-labels": "error",
"no-unused-vars": "error",
"quotes": ["error", "double"],
"max-lines-per-function": ["error", {
"max": 200,
"skipComments": true
}],
"getter-return": "error",
"newline-before-return": "error",
"no-multiple-empty-lines": "error",
"no-var": "error",
"indent": ["warn", 4],
"no-this-before-super": "warn",
"no-useless-return": "error",
"no-shadow": "error",
"prefer-const": "warn",
"no-unreachable": "warn",
"no-undef": "off",
"@typescript-eslint/ban-ts-comment": "off"
}
}

18
.gitignore vendored
View file

@ -1,9 +1,9 @@
node_modules
dist
static/scramjet*
static/bare-mux.js
static/bare-client.js
static/curl-client.js
static/epoxy-client.js
static/bare-mux-worker.js
meta.json
node_modules
dist
static/scramjet*
static/bare-mux.js
static/bare-client.js
static/curl-client.js
static/epoxy-client.js
static/bare-mux-worker.js
meta.json

View file

@ -1,21 +1,21 @@
# Scramjet
Scramjet is an experimental web proxy that aims to be the successor to Ultraviolet.
It currently does not support most websites due to it being very early in the development stage.
The UI is not finalized and only used as a means to test the web proxy.
## How to build
Running `pnpm dev` will build Scramjet and start a dev server on localhost:1337. If you only want to build the proxy without using the dev server, run `pnpm build`.
## TODO
- Finish HTML rewriting
- `<script type="importmap"></script>` rewriting
- Make an array of all possible import values and pass the array onto the JS rewriter, then rewrite all the URLs inside of it
- Finish JS rewriting
- Only thing rewritten currently are imports and exports
- Check imports/exports for values contained in the `importmap` array, don't rewrite the node value if present
- Write client APIs
# Scramjet
Scramjet is an experimental web proxy that aims to be the successor to Ultraviolet.
It currently does not support most websites due to it being very early in the development stage.
The UI is not finalized and only used as a means to test the web proxy.
## How to build
Running `pnpm dev` will build Scramjet and start a dev server on localhost:1337. If you only want to build the proxy without using the dev server, run `pnpm build`.
## TODO
- Finish HTML rewriting
- `<script type="importmap"></script>` rewriting
- Make an array of all possible import values and pass the array onto the JS rewriter, then rewrite all the URLs inside of it
- Finish JS rewriting
- Only thing rewritten currently are imports and exports
- Check imports/exports for values contained in the `importmap` array, don't rewrite the node value if present
- Write client APIs

View file

@ -1,97 +0,0 @@
// import { createServer } from "esbuild-server";
import copy from "esbuild-plugin-copy";
import time from "esbuild-plugin-time";
import { createBareServer } from "@tomphttp/bare-server-node";
import Fastify from "fastify";
import { context } from "esbuild";
import { createServer } from "http";
import fastifyStatic from "@fastify/static";
import { join } from "path";
import { fileURLToPath } from "url";
import "dotenv/config";
const bare = createBareServer("/bare/", {
logErrors: true
});
const fastify = Fastify({
serverFactory: (handler) => {
return createServer()
.on("request", (req, res) => {
if (bare.shouldRoute(req)) {
bare.routeRequest(req, res);
} else {
handler(req, res);
}
}).on("upgrade", (req, socket, head) => {
if (bare.shouldRoute(req)) {
bare.routeUpgrade(req, socket, head);
} else {
socket.end();
}
})
}
});
fastify.register(fastifyStatic, {
root: join(fileURLToPath(new URL(".", import.meta.url)), "./static"),
decorateReply: false
});
//fastify.get("/", {
// })
fastify.listen({
port: 1337
});
const devServer = await context({
entryPoints: {
client: "./src/client/index.ts",
bundle: "./src/bundle/index.ts",
worker: "./src/worker/index.ts",
codecs: "./src/codecs/index.ts",
config: "./src/scramjet.config.ts",
},
entryNames: "scramjet.[name]",
outdir: "./dist",
bundle: true,
sourcemap: true,
logLevel: "info",
format: "esm",
plugins: [
copy({
resolveFrom: "cwd",
assets: [
{
from: ["./node_modules/@mercuryworkshop/bare-mux/dist/index.js"],
to: ["./static/bare-mux.js"],
},
{
from: ["./node_modules/@mercuryworkshop/bare-as-module3/dist/index.mjs"],
to: ["./static/bare-client.js"],
},
{
from: ["./node_modules/@mercuryworkshop/libcurl-transport/dist/index.mjs"],
to: ["./static/curl-client.js"],
},
{
from: ["./node_modules/@mercuryworkshop/epoxy-transport/dist/index.mjs"],
to: ["./static/epoxy-client.js"],
},
{
from: ["./node_modules/@mercuryworkshop/bare-mux/dist/worker.js"],
to: ["./static/bare-mux-worker.js"],
},
{
from: ["./dist/*"],
to: ["./static"]
},
],
}),
time()
],
});
await devServer.watch();

View file

@ -1,26 +0,0 @@
import { build } from "esbuild";
import time from "esbuild-plugin-time";
import { writeFileSync } from "fs"
const scramjetBuild = await build({
entryPoints: {
client: "./src/client/index.ts",
bundle: "./src/bundle/index.ts",
worker: "./src/worker/index.ts",
codecs: "./src/codecs/index.ts",
config: "./src/scramjet.config.ts",
},
entryNames: "scramjet.[name]",
outdir: "./dist",
bundle: true,
plugins: [
time()
],
logLevel: "info",
metafile: true,
treeShaking: true,
minify: true,
format: "esm"
});
writeFileSync("./meta.json", JSON.stringify(scramjetBuild.metafile));

View file

@ -1,7 +1,7 @@
"use strict";
const { resolve } = require("node:path");
const scramjetPath = resolve(__dirname, "..", "dist");
exports.scramjetPath = scramjetPath;
"use strict";
const { resolve } = require("node:path");
const scramjetPath = resolve(__dirname, "..", "dist");
exports.scramjetPath = scramjetPath;

6
lib/index.d.ts vendored
View file

@ -1,3 +1,3 @@
declare const scramjetPath: string;
export { scramjetPath };
declare const scramjetPath: string;
export { scramjetPath };

View file

@ -9,8 +9,8 @@
"url": "https://github.com/MercuryWorkshop/scramjet"
},
"scripts": {
"build": "node esbuild.js",
"dev": "node esbuild.dev.js",
"build": "rollup -c",
"dev": "node server.js",
"prepublish": "pnpm build",
"pub": "pnpm publish --no-git-checks --access public"
},
@ -26,18 +26,20 @@
"@mercuryworkshop/bare-as-module3": "^2.2.2",
"@mercuryworkshop/epoxy-transport": "^2.1.3",
"@mercuryworkshop/libcurl-transport": "^1.3.6",
"@rollup/plugin-inject": "^5.0.5",
"@rollup/plugin-replace": "^5.0.5",
"@tomphttp/bare-server-node": "^2.0.3",
"@types/eslint": "^8.56.10",
"@types/estraverse": "^5.1.7",
"@types/estree": "^1.0.5",
"@types/node": "^20.14.10",
"@types/serviceworker": "^0.0.85",
"@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0",
"dotenv": "^16.4.5",
"esbuild": "^0.20.2",
"esbuild-plugin-copy": "^2.1.1",
"esbuild-plugin-time": "^1.0.0",
"eslint": "^8.57.0",
"fastify": "^4.26.2",
"rollup": "^4.17.2",
"rollup-plugin-typescript2": "^0.36.0",
"tslib": "^2.6.2",
"typescript": "^5.4.5"
},
@ -45,11 +47,11 @@
"dependencies": {
"@mercuryworkshop/bare-mux": "^2.0.2",
"@webreflection/idb-map": "^0.3.1",
"astravel": "^0.6.1",
"astring": "^1.8.6",
"dom-serializer": "^2.0.0",
"domhandler": "^5.0.3",
"domutils": "^3.1.0",
"estraverse": "^5.3.0",
"htmlparser2": "^9.1.0",
"meriyah": "^4.4.2"
}

429
pnpm-lock.yaml generated
View file

@ -14,6 +14,9 @@ importers:
'@webreflection/idb-map':
specifier: ^0.3.1
version: 0.3.1
astravel:
specifier: ^0.6.1
version: 0.6.1
astring:
specifier: ^1.8.6
version: 1.8.6
@ -26,9 +29,6 @@ importers:
domutils:
specifier: ^3.1.0
version: 3.1.0
estraverse:
specifier: ^5.3.0
version: 5.3.0
htmlparser2:
specifier: ^9.1.0
version: 9.1.0
@ -48,15 +48,24 @@ importers:
'@mercuryworkshop/libcurl-transport':
specifier: ^1.3.6
version: 1.3.6(typescript@5.4.5)
'@rollup/plugin-inject':
specifier: ^5.0.5
version: 5.0.5(rollup@4.17.2)
'@rollup/plugin-replace':
specifier: ^5.0.5
version: 5.0.7(rollup@4.17.2)
'@tomphttp/bare-server-node':
specifier: ^2.0.3
version: 2.0.3
'@types/eslint':
specifier: ^8.56.10
version: 8.56.10
'@types/estraverse':
specifier: ^5.1.7
version: 5.1.7
'@types/estree':
specifier: ^1.0.5
version: 1.0.5
'@types/node':
specifier: ^20.14.10
version: 20.14.10
'@types/serviceworker':
specifier: ^0.0.85
version: 0.0.85
@ -69,21 +78,18 @@ importers:
dotenv:
specifier: ^16.4.5
version: 16.4.5
esbuild:
specifier: ^0.20.2
version: 0.20.2
esbuild-plugin-copy:
specifier: ^2.1.1
version: 2.1.1(esbuild@0.20.2)
esbuild-plugin-time:
specifier: ^1.0.0
version: 1.0.0
eslint:
specifier: ^8.57.0
version: 8.57.0
fastify:
specifier: ^4.26.2
version: 4.26.2
rollup:
specifier: ^4.17.2
version: 4.17.2
rollup-plugin-typescript2:
specifier: ^0.36.0
version: 0.36.0(rollup@4.17.2)(typescript@5.4.5)
tslib:
specifier: ^2.6.2
version: 2.6.2
@ -93,144 +99,6 @@ importers:
packages:
'@esbuild/aix-ppc64@0.20.2':
resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [aix]
'@esbuild/android-arm64@0.20.2':
resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==}
engines: {node: '>=12'}
cpu: [arm64]
os: [android]
'@esbuild/android-arm@0.20.2':
resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==}
engines: {node: '>=12'}
cpu: [arm]
os: [android]
'@esbuild/android-x64@0.20.2':
resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==}
engines: {node: '>=12'}
cpu: [x64]
os: [android]
'@esbuild/darwin-arm64@0.20.2':
resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==}
engines: {node: '>=12'}
cpu: [arm64]
os: [darwin]
'@esbuild/darwin-x64@0.20.2':
resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==}
engines: {node: '>=12'}
cpu: [x64]
os: [darwin]
'@esbuild/freebsd-arm64@0.20.2':
resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==}
engines: {node: '>=12'}
cpu: [arm64]
os: [freebsd]
'@esbuild/freebsd-x64@0.20.2':
resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==}
engines: {node: '>=12'}
cpu: [x64]
os: [freebsd]
'@esbuild/linux-arm64@0.20.2':
resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==}
engines: {node: '>=12'}
cpu: [arm64]
os: [linux]
'@esbuild/linux-arm@0.20.2':
resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==}
engines: {node: '>=12'}
cpu: [arm]
os: [linux]
'@esbuild/linux-ia32@0.20.2':
resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==}
engines: {node: '>=12'}
cpu: [ia32]
os: [linux]
'@esbuild/linux-loong64@0.20.2':
resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==}
engines: {node: '>=12'}
cpu: [loong64]
os: [linux]
'@esbuild/linux-mips64el@0.20.2':
resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==}
engines: {node: '>=12'}
cpu: [mips64el]
os: [linux]
'@esbuild/linux-ppc64@0.20.2':
resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [linux]
'@esbuild/linux-riscv64@0.20.2':
resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==}
engines: {node: '>=12'}
cpu: [riscv64]
os: [linux]
'@esbuild/linux-s390x@0.20.2':
resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==}
engines: {node: '>=12'}
cpu: [s390x]
os: [linux]
'@esbuild/linux-x64@0.20.2':
resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==}
engines: {node: '>=12'}
cpu: [x64]
os: [linux]
'@esbuild/netbsd-x64@0.20.2':
resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [netbsd]
'@esbuild/openbsd-x64@0.20.2':
resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [openbsd]
'@esbuild/sunos-x64@0.20.2':
resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==}
engines: {node: '>=12'}
cpu: [x64]
os: [sunos]
'@esbuild/win32-arm64@0.20.2':
resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==}
engines: {node: '>=12'}
cpu: [arm64]
os: [win32]
'@esbuild/win32-ia32@0.20.2':
resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==}
engines: {node: '>=12'}
cpu: [ia32]
os: [win32]
'@esbuild/win32-x64@0.20.2':
resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [win32]
'@eslint-community/eslint-utils@4.4.0':
resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@ -286,6 +154,9 @@ packages:
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
engines: {node: '>=12'}
'@jridgewell/sourcemap-codec@1.5.0':
resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
'@lukeed/ms@2.0.2':
resolution: {integrity: sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==}
engines: {node: '>=8'}
@ -321,10 +192,37 @@ packages:
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
'@rollup/plugin-inject@5.0.5':
resolution: {integrity: sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg==}
engines: {node: '>=14.0.0'}
peerDependencies:
rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
peerDependenciesMeta:
rollup:
optional: true
'@rollup/plugin-replace@5.0.7':
resolution: {integrity: sha512-PqxSfuorkHz/SPpyngLyg5GCEkOcee9M1bkxiVDr41Pd61mqP1PLOoDPbpl44SB2mQGKwV/In74gqQmGITOhEQ==}
engines: {node: '>=14.0.0'}
peerDependencies:
rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
peerDependenciesMeta:
rollup:
optional: true
'@rollup/pluginutils@4.2.1':
resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==}
engines: {node: '>= 8.0.0'}
'@rollup/pluginutils@5.1.0':
resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==}
engines: {node: '>=14.0.0'}
peerDependencies:
rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
peerDependenciesMeta:
rollup:
optional: true
'@rollup/rollup-android-arm-eabi@4.17.2':
resolution: {integrity: sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==}
cpu: [arm]
@ -413,17 +311,14 @@ packages:
'@types/eslint@8.56.10':
resolution: {integrity: sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==}
'@types/estraverse@5.1.7':
resolution: {integrity: sha512-JRVtdKYZz7VkNp7hMC/WKoiZ8DS3byw20ZGoMZ1R8eBrBPIY7iBaDAS1zcrnXQCwK44G4vbXkimeU7R0VLG8UQ==}
'@types/estree@1.0.5':
resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
'@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
'@types/node@20.12.11':
resolution: {integrity: sha512-vDg9PZ/zi+Nqp6boSOT7plNuthRugEKixDv5sFTIpkE89MmNtEArAShI4mxuX2+UrLEe9pxC1vm2cjm9YlWbJw==}
'@types/node@20.14.10':
resolution: {integrity: sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==}
'@types/resolve@0.0.8':
resolution: {integrity: sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==}
@ -553,10 +448,6 @@ packages:
resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
engines: {node: '>=12'}
anymatch@3.1.3:
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
engines: {node: '>= 8'}
archy@1.0.0:
resolution: {integrity: sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==}
@ -567,6 +458,9 @@ packages:
resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
engines: {node: '>=8'}
astravel@0.6.1:
resolution: {integrity: sha512-ZIkgWFIV0Yo423Vqalz7VcF+BAiISvSgplnkV2abPGACPFKofsWTcvr9SFyYM/t/vMZWqmdP/Eze6ATX7r84Dg==}
astring@1.8.6:
resolution: {integrity: sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==}
hasBin: true
@ -588,10 +482,6 @@ packages:
base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
binary-extensions@2.3.0:
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
engines: {node: '>=8'}
brace-expansion@1.1.11:
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
@ -620,10 +510,6 @@ packages:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'}
chokidar@3.6.0:
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
engines: {node: '>= 8.10.0'}
color-convert@2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
@ -707,22 +593,9 @@ packages:
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
engines: {node: '>=0.12'}
esbuild-plugin-copy@2.1.1:
resolution: {integrity: sha512-Bk66jpevTcV8KMFzZI1P7MZKZ+uDcrZm2G2egZ2jNIvVnivDpodZI+/KnpL3Jnap0PBdIHU7HwFGB8r+vV5CVw==}
peerDependencies:
esbuild: '>= 0.14.0'
esbuild-plugin-time@1.0.0:
resolution: {integrity: sha512-I4Shhi0fpgXxSoc34djTHIuhbEzkcBbKKiNtb3LhZe2JEE1vSxXilW+KE0M/KZ4kqORLGgdGCKuJh9CNUL55yw==}
esbuild-plugin-umd-wrapper@2.0.0:
resolution: {integrity: sha512-pcu2/lcm29S85VCnSJuValrQ8FqeFJs5VWEwfp7vBRsOHjxZypcxgwXjxDIxDRo17uOcENZIbgz2szjln029eQ==}
esbuild@0.20.2:
resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==}
engines: {node: '>=12'}
hasBin: true
escape-html@1.0.3:
resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
@ -946,10 +819,6 @@ packages:
resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==}
engines: {node: '>= 10'}
is-binary-path@2.1.0:
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
engines: {node: '>=8'}
is-core-module@2.13.1:
resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==}
@ -1037,6 +906,9 @@ packages:
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
engines: {node: '>=10'}
magic-string@0.30.10:
resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==}
make-dir@3.1.0:
resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==}
engines: {node: '>=8'}
@ -1075,10 +947,6 @@ packages:
natural-compare@1.4.0:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
normalize-path@3.0.0:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
engines: {node: '>=0.10.0'}
on-exit-leak-free@2.1.2:
resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==}
engines: {node: '>=14.0.0'}
@ -1184,10 +1052,6 @@ packages:
resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
readdirp@3.6.0:
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
engines: {node: '>=8.10.0'}
real-require@0.2.0:
resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==}
engines: {node: '>= 12.13.0'}
@ -1431,75 +1295,6 @@ packages:
snapshots:
'@esbuild/aix-ppc64@0.20.2':
optional: true
'@esbuild/android-arm64@0.20.2':
optional: true
'@esbuild/android-arm@0.20.2':
optional: true
'@esbuild/android-x64@0.20.2':
optional: true
'@esbuild/darwin-arm64@0.20.2':
optional: true
'@esbuild/darwin-x64@0.20.2':
optional: true
'@esbuild/freebsd-arm64@0.20.2':
optional: true
'@esbuild/freebsd-x64@0.20.2':
optional: true
'@esbuild/linux-arm64@0.20.2':
optional: true
'@esbuild/linux-arm@0.20.2':
optional: true
'@esbuild/linux-ia32@0.20.2':
optional: true
'@esbuild/linux-loong64@0.20.2':
optional: true
'@esbuild/linux-mips64el@0.20.2':
optional: true
'@esbuild/linux-ppc64@0.20.2':
optional: true
'@esbuild/linux-riscv64@0.20.2':
optional: true
'@esbuild/linux-s390x@0.20.2':
optional: true
'@esbuild/linux-x64@0.20.2':
optional: true
'@esbuild/netbsd-x64@0.20.2':
optional: true
'@esbuild/openbsd-x64@0.20.2':
optional: true
'@esbuild/sunos-x64@0.20.2':
optional: true
'@esbuild/win32-arm64@0.20.2':
optional: true
'@esbuild/win32-ia32@0.20.2':
optional: true
'@esbuild/win32-x64@0.20.2':
optional: true
'@eslint-community/eslint-utils@4.4.0(eslint@8.57.0)':
dependencies:
eslint: 8.57.0
@ -1579,6 +1374,8 @@ snapshots:
wrap-ansi: 8.1.0
wrap-ansi-cjs: wrap-ansi@7.0.0
'@jridgewell/sourcemap-codec@1.5.0': {}
'@lukeed/ms@2.0.2': {}
'@mercuryworkshop/bare-as-module3@2.2.2':
@ -1624,11 +1421,34 @@ snapshots:
'@pkgjs/parseargs@0.11.0':
optional: true
'@rollup/plugin-inject@5.0.5(rollup@4.17.2)':
dependencies:
'@rollup/pluginutils': 5.1.0(rollup@4.17.2)
estree-walker: 2.0.2
magic-string: 0.30.10
optionalDependencies:
rollup: 4.17.2
'@rollup/plugin-replace@5.0.7(rollup@4.17.2)':
dependencies:
'@rollup/pluginutils': 5.1.0(rollup@4.17.2)
magic-string: 0.30.10
optionalDependencies:
rollup: 4.17.2
'@rollup/pluginutils@4.2.1':
dependencies:
estree-walker: 2.0.2
picomatch: 2.3.1
'@rollup/pluginutils@5.1.0(rollup@4.17.2)':
dependencies:
'@types/estree': 1.0.5
estree-walker: 2.0.2
picomatch: 2.3.1
optionalDependencies:
rollup: 4.17.2
'@rollup/rollup-android-arm-eabi@4.17.2':
optional: true
@ -1695,21 +1515,17 @@ snapshots:
'@types/estree': 1.0.5
'@types/json-schema': 7.0.15
'@types/estraverse@5.1.7':
dependencies:
'@types/estree': 1.0.5
'@types/estree@1.0.5': {}
'@types/json-schema@7.0.15': {}
'@types/node@20.12.11':
'@types/node@20.14.10':
dependencies:
undici-types: 5.26.5
'@types/resolve@0.0.8':
dependencies:
'@types/node': 20.12.11
'@types/node': 20.14.10
'@types/semver@7.5.8': {}
@ -1849,17 +1665,14 @@ snapshots:
ansi-styles@6.2.1: {}
anymatch@3.1.3:
dependencies:
normalize-path: 3.0.0
picomatch: 2.3.1
archy@1.0.0: {}
argparse@2.0.1: {}
array-union@2.1.0: {}
astravel@0.6.1: {}
astring@1.8.6: {}
async-exit-hook@2.0.1: {}
@ -1879,8 +1692,6 @@ snapshots:
base64-js@1.5.1: {}
binary-extensions@2.3.0: {}
brace-expansion@1.1.11:
dependencies:
balanced-match: 1.0.2
@ -1910,18 +1721,6 @@ snapshots:
ansi-styles: 4.3.0
supports-color: 7.2.0
chokidar@3.6.0:
dependencies:
anymatch: 3.1.3
braces: 3.0.2
glob-parent: 5.1.2
is-binary-path: 2.1.0
is-glob: 4.0.3
normalize-path: 3.0.0
readdirp: 3.6.0
optionalDependencies:
fsevents: 2.3.3
color-convert@2.0.1:
dependencies:
color-name: 1.1.4
@ -1990,46 +1789,8 @@ snapshots:
entities@4.5.0: {}
esbuild-plugin-copy@2.1.1(esbuild@0.20.2):
dependencies:
chalk: 4.1.2
chokidar: 3.6.0
esbuild: 0.20.2
fs-extra: 10.1.0
globby: 11.1.0
esbuild-plugin-time@1.0.0:
dependencies:
chalk: 4.1.2
esbuild-plugin-umd-wrapper@2.0.0: {}
esbuild@0.20.2:
optionalDependencies:
'@esbuild/aix-ppc64': 0.20.2
'@esbuild/android-arm': 0.20.2
'@esbuild/android-arm64': 0.20.2
'@esbuild/android-x64': 0.20.2
'@esbuild/darwin-arm64': 0.20.2
'@esbuild/darwin-x64': 0.20.2
'@esbuild/freebsd-arm64': 0.20.2
'@esbuild/freebsd-x64': 0.20.2
'@esbuild/linux-arm': 0.20.2
'@esbuild/linux-arm64': 0.20.2
'@esbuild/linux-ia32': 0.20.2
'@esbuild/linux-loong64': 0.20.2
'@esbuild/linux-mips64el': 0.20.2
'@esbuild/linux-ppc64': 0.20.2
'@esbuild/linux-riscv64': 0.20.2
'@esbuild/linux-s390x': 0.20.2
'@esbuild/linux-x64': 0.20.2
'@esbuild/netbsd-x64': 0.20.2
'@esbuild/openbsd-x64': 0.20.2
'@esbuild/sunos-x64': 0.20.2
'@esbuild/win32-arm64': 0.20.2
'@esbuild/win32-ia32': 0.20.2
'@esbuild/win32-x64': 0.20.2
escape-html@1.0.3: {}
escape-string-regexp@4.0.0: {}
@ -2316,10 +2077,6 @@ snapshots:
ipaddr.js@2.2.0: {}
is-binary-path@2.1.0:
dependencies:
binary-extensions: 2.3.0
is-core-module@2.13.1:
dependencies:
hasown: 2.0.2
@ -2401,6 +2158,10 @@ snapshots:
dependencies:
yallist: 4.0.0
magic-string@0.30.10:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.0
make-dir@3.1.0:
dependencies:
semver: 6.3.1
@ -2430,8 +2191,6 @@ snapshots:
natural-compare@1.4.0: {}
normalize-path@3.0.0: {}
on-exit-leak-free@2.1.2: {}
once@1.4.0:
@ -2536,10 +2295,6 @@ snapshots:
process: 0.11.10
string_decoder: 1.3.0
readdirp@3.6.0:
dependencies:
picomatch: 2.3.1
real-require@0.2.0: {}
require-from-string@2.0.2: {}

35
rollup.config.js Normal file
View file

@ -0,0 +1,35 @@
import typescript from "rollup-plugin-typescript2";
import { join } from "node:path";
import fs from "node:fs"
import { fileURLToPath } from "node:url";
// 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",
}),
]
export default {
plugins: commonPlugins(),
input: {
client: "./src/client/index.ts",
bundle: "./src/bundle/index.ts",
worker: "./src/worker/index.ts",
codecs: "./src/codecs/index.ts",
config: "./src/scramjet.config.ts"
},
output: {
entryFileNames: "scramjet.[name].js",
dir: "./dist",
format: "esm",
bundle: true,
minify: production,
sourcemap: true,
treeshake: "recommended",
},
};

74
server.js Normal file
View file

@ -0,0 +1,74 @@
// Dev server imports
import { createBareServer } from "@tomphttp/bare-server-node";
import { createServer } from "http";
import Fastify from "fastify";
import fastifyStatic from "@fastify/static";
import { join } from "node:path";
import fs from "node:fs"
import { fileURLToPath } from "node:url";
import { watch } from "rollup"
import { loadConfigFile } from "rollup/loadConfigFile"
//transports
import { baremuxPath } from "@mercuryworkshop/bare-mux/node"
import { epoxyPath } from "@mercuryworkshop/epoxy-transport"
import { libcurlPath } from "@mercuryworkshop/libcurl-transport"
import { bareModulePath } from "@mercuryworkshop/bare-as-module3"
let watcher = watch()
const bare = createBareServer("/bare/", {
logErrors: true
});
const fastify = Fastify({
serverFactory: (handler) => {
return createServer()
.on("request", (req, res) => {
if (bare.shouldRoute(req)) {
bare.routeRequest(req, res);
} else {
handler(req, res);
}
}).on("upgrade", (req, socket, head) => {
if (bare.shouldRoute(req)) {
bare.routeUpgrade(req, socket, head);
} else {
socket.end();
}
})
}
});
fastify.register(fastifyStatic, {
root: join(fileURLToPath(new URL(".", import.meta.url)), "./static"),
decorateReply: false
});
fastify.register(fastifyStatic, {
root: join(fileURLToPath(new URL(".", import.meta.url)), "./dist"),
prefix: "/dist/",
decorateReply: false
})
fastify.register(fastifyStatic, {
root: baremuxPath,
prefix: "/baremux/",
decorateReply: false
})
fastify.register(fastifyStatic, {
root: epoxyPath,
prefix: "/epoxy/",
decorateReply: false
})
fastify.register(fastifyStatic, {
root: libcurlPath,
prefix: "/libcurl/",
decorateReply: false
})
fastify.register(fastifyStatic, {
root: bareModulePath,
prefix: "/baremod/",
decorateReply: false
})
fastify.listen({
port: process.env.PORT || 1337
});

View file

@ -1,14 +1,14 @@
export { encodeUrl, decodeUrl } from "./rewriters/url";
export { rewriteCss } from "./rewriters/css";
export { rewriteHtml, rewriteSrcset } from "./rewriters/html";
export { rewriteJs } from "./rewriters/js";
export { rewriteHeaders } from "./rewriters/headers";
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;
export { encodeUrl, decodeUrl } from "./rewriters/url";
export { rewriteCss } from "./rewriters/css";
export { rewriteHtml, rewriteSrcset } from "./rewriters/html";
export { rewriteJs } from "./rewriters/js";
export { rewriteHeaders } from "./rewriters/headers";
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;
}

View file

@ -1,35 +1,35 @@
// This CSS rewriter uses code from Meteor
// You can find the original source code at https://github.com/MeteorProxy/Meteor
import { encodeUrl } from "./url";
export function rewriteCss(css: string, origin?: URL) {
const regex =
/(@import\s+(?!url\())?\s*url\(\s*(['"]?)([^'")]+)\2\s*\)|@import\s+(['"])([^'"]+)\4/g
return css.replace(
regex,
(
match,
importStatement,
urlQuote,
urlContent,
importQuote,
importContent
) => {
const url = urlContent || importContent
const encodedUrl = encodeUrl(url.trim(), origin)
if (importStatement) {
return `@import url(${urlQuote}${encodedUrl}${urlQuote})`
}
if (importQuote) {
return `@import ${importQuote}${encodedUrl}${importQuote}`
}
return `url(${urlQuote}${encodedUrl}${urlQuote})`
}
)
}
// This CSS rewriter uses code from Meteor
// You can find the original source code at https://github.com/MeteorProxy/Meteor
import { encodeUrl } from "./url";
export function rewriteCss(css: string, origin?: URL) {
const regex =
/(@import\s+(?!url\())?\s*url\(\s*(['"]?)([^'")]+)\2\s*\)|@import\s+(['"])([^'"]+)\4/g
return css.replace(
regex,
(
match,
importStatement,
urlQuote,
urlContent,
importQuote,
importContent
) => {
const url = urlContent || importContent
const encodedUrl = encodeUrl(url.trim(), origin)
if (importStatement) {
return `@import url(${urlQuote}${encodedUrl}${urlQuote})`
}
if (importQuote) {
return `@import ${importQuote}${encodedUrl}${importQuote}`
}
return `url(${urlQuote}${encodedUrl}${urlQuote})`
}
)
}

View file

@ -1,53 +1,53 @@
import { encodeUrl } from "./url";
import { BareHeaders } from "@mercuryworkshop/bare-mux";
const cspHeaders = [
"cross-origin-embedder-policy",
"cross-origin-opener-policy",
"cross-origin-resource-policy",
"content-security-policy",
"content-security-policy-report-only",
"expect-ct",
"feature-policy",
"origin-isolation",
"strict-transport-security",
"upgrade-insecure-requests",
"x-content-type-options",
"x-download-options",
"x-frame-options",
"x-permitted-cross-domain-policies",
"x-powered-by",
"x-xss-protection",
// This needs to be emulated, but for right now it isn't that important of a feature to be worried about
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data
"clear-site-data"
];
const urlHeaders = [
"location",
"content-location",
"referer"
];
export function rewriteHeaders(rawHeaders: BareHeaders, origin?: URL) {
const headers = {};
for (const key in rawHeaders) {
headers[key.toLowerCase()] = rawHeaders[key];
}
cspHeaders.forEach((header) => {
delete headers[header];
});
urlHeaders.forEach((header) => {
if (headers[header])
headers[header] = encodeUrl(headers[header] as string, origin);
});
if (headers["link"]) {
headers["link"] = headers["link"].replace(/<(.*?)>/gi, (match) => encodeUrl(match, origin));
}
return headers;
import { encodeUrl } from "./url";
import { BareHeaders } from "@mercuryworkshop/bare-mux";
const cspHeaders = [
"cross-origin-embedder-policy",
"cross-origin-opener-policy",
"cross-origin-resource-policy",
"content-security-policy",
"content-security-policy-report-only",
"expect-ct",
"feature-policy",
"origin-isolation",
"strict-transport-security",
"upgrade-insecure-requests",
"x-content-type-options",
"x-download-options",
"x-frame-options",
"x-permitted-cross-domain-policies",
"x-powered-by",
"x-xss-protection",
// This needs to be emulated, but for right now it isn't that important of a feature to be worried about
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data
"clear-site-data"
];
const urlHeaders = [
"location",
"content-location",
"referer"
];
export function rewriteHeaders(rawHeaders: BareHeaders, origin?: URL) {
const headers = {};
for (const key in rawHeaders) {
headers[key.toLowerCase()] = rawHeaders[key];
}
cspHeaders.forEach((header) => {
delete headers[header];
});
urlHeaders.forEach((header) => {
if (headers[header])
headers[header] = encodeUrl(headers[header] as string, origin);
});
if (headers["link"]) {
headers["link"] = headers["link"].replace(/<(.*?)>/gi, (match) => encodeUrl(match, origin));
}
return headers;
}

View file

@ -1,97 +1,97 @@
import { Parser } from "htmlparser2";
import { DomHandler, Element } from "domhandler";
import { hasAttrib } from "domutils";
import render from "dom-serializer";
import { encodeUrl } from "./url";
import { rewriteCss } from "./css";
import { rewriteJs } from "./js";
import { isScramjetFile } from "../";
export function rewriteHtml(html: string, origin?: URL) {
const handler = new DomHandler((err, dom) => dom);
const parser = new Parser(handler);
parser.write(html);
parser.end();
return render(traverseParsedHtml(handler.root, origin));
}
// i need to add the attributes in during rewriting
function traverseParsedHtml(node, origin?: URL) {
/* csp attributes */
for (const cspAttr of ["nonce", "integrity", "csp"]) {
if (hasAttrib(node, cspAttr)) {
node.attribs[`data-${cspAttr}`] = node.attribs[cspAttr];
delete node.attribs[cspAttr];
}
}
/* url attributes */
for (const urlAttr of ["src", "href", "data", "action", "formaction"]) {
if (hasAttrib(node, urlAttr) && !isScramjetFile(node.attribs[urlAttr])) {
const value = node.attribs[urlAttr];
node.attribs[`data-${urlAttr}`] = value;
node.attribs[urlAttr] = encodeUrl(value, origin);
}
}
/* other */
for (const srcsetAttr of ["srcset", "imagesrcset"]) {
if (hasAttrib(node, srcsetAttr)) {
const value = node.attribs[srcsetAttr];
node.attribs[`data-${srcsetAttr}`] = value;
node.attribs[srcsetAttr] = rewriteSrcset(value, origin);
}
}
if (hasAttrib(node, "srcdoc")) node.attribs.srcdoc = rewriteHtml(node.attribs.srcdoc, origin);
if (hasAttrib(node, "style")) node.attribs.style = rewriteCss(node.attribs.style, origin);
if (node.name === "style" && node.children[0] !== undefined) node.children[0].data = rewriteCss(node.children[0].data, origin);
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") {
node = {};
} else if (node.attribs["http-equiv"] === "refresh" && node.attribs.content.includes("url")) {
const contentArray = node.attribs.content.split("url=");
contentArray[1] = encodeUrl(contentArray[1].trim(), origin);
node.attribs.content = contentArray.join("url=");
}
}
if (node.name === "head") {
const scramjetScripts = [];
["codecs", "config", "bundle", "client"].forEach((script) => {
scramjetScripts.push(new Element("script", {
src: self.__scramjet$config[script],
type: "module"
}));
});
node.children.unshift(...scramjetScripts);
}
if (node.childNodes) {
for (const childNode in node.childNodes) {
node.childNodes[childNode] = traverseParsedHtml(node.childNodes[childNode], origin);
}
}
return node;
}
export function rewriteSrcset(srcset: string, origin?: URL) {
const urls = srcset.split(/ [0-9]+x,? ?/g);
if (!urls) return "";
const sufixes = srcset.match(/ [0-9]+x,? ?/g);
if (!sufixes) return "";
const rewrittenUrls = urls.map((url, i) => {
if (url && sufixes[i]) {
return encodeUrl(url, origin) + sufixes[i];
}
});
return rewrittenUrls.join("");
import { Parser } from "htmlparser2";
import { DomHandler, Element } from "domhandler";
import { hasAttrib } from "domutils";
import render from "dom-serializer";
import { encodeUrl } from "./url";
import { rewriteCss } from "./css";
import { rewriteJs } from "./js";
import { isScramjetFile } from "../";
export function rewriteHtml(html: string, origin?: URL) {
const handler = new DomHandler((err, dom) => dom);
const parser = new Parser(handler);
parser.write(html);
parser.end();
return render(traverseParsedHtml(handler.root, origin));
}
// i need to add the attributes in during rewriting
function traverseParsedHtml(node, origin?: URL) {
/* csp attributes */
for (const cspAttr of ["nonce", "integrity", "csp"]) {
if (hasAttrib(node, cspAttr)) {
node.attribs[`data-${cspAttr}`] = node.attribs[cspAttr];
delete node.attribs[cspAttr];
}
}
/* url attributes */
for (const urlAttr of ["src", "href", "data", "action", "formaction"]) {
if (hasAttrib(node, urlAttr) && !isScramjetFile(node.attribs[urlAttr])) {
const value = node.attribs[urlAttr];
node.attribs[`data-${urlAttr}`] = value;
node.attribs[urlAttr] = encodeUrl(value, origin);
}
}
/* other */
for (const srcsetAttr of ["srcset", "imagesrcset"]) {
if (hasAttrib(node, srcsetAttr)) {
const value = node.attribs[srcsetAttr];
node.attribs[`data-${srcsetAttr}`] = value;
node.attribs[srcsetAttr] = rewriteSrcset(value, origin);
}
}
if (hasAttrib(node, "srcdoc")) node.attribs.srcdoc = rewriteHtml(node.attribs.srcdoc, origin);
if (hasAttrib(node, "style")) node.attribs.style = rewriteCss(node.attribs.style, origin);
if (node.name === "style" && node.children[0] !== undefined) node.children[0].data = rewriteCss(node.children[0].data, origin);
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") {
node = {};
} else if (node.attribs["http-equiv"] === "refresh" && node.attribs.content.includes("url")) {
const contentArray = node.attribs.content.split("url=");
contentArray[1] = encodeUrl(contentArray[1].trim(), origin);
node.attribs.content = contentArray.join("url=");
}
}
if (node.name === "head") {
const scramjetScripts = [];
["codecs", "config", "bundle", "client"].forEach((script) => {
scramjetScripts.push(new Element("script", {
src: self.__scramjet$config[script],
type: "module"
}));
});
node.children.unshift(...scramjetScripts);
}
if (node.childNodes) {
for (const childNode in node.childNodes) {
node.childNodes[childNode] = traverseParsedHtml(node.childNodes[childNode], origin);
}
}
return node;
}
export function rewriteSrcset(srcset: string, origin?: URL) {
const urls = srcset.split(/ [0-9]+x,? ?/g);
if (!urls) return "";
const sufixes = srcset.match(/ [0-9]+x,? ?/g);
if (!sufixes) return "";
const rewrittenUrls = urls.map((url, i) => {
if (url && sufixes[i]) {
return encodeUrl(url, origin) + sufixes[i];
}
});
return rewrittenUrls.join("");
}

View file

@ -1,49 +1,78 @@
import { parseModule } from "meriyah";
import { generate } from "astring";
import { encodeUrl } from "./url";
import { replace } from "estraverse";
// i am a cat. i like to be petted. i like to be fed. i like to be
// js rewiter is NOT finished
// location
// window
// self
// globalThis
// this
// top
// parent
export function rewriteJs(js: string, origin?: URL) {
try {
const ast = parseModule(js);
// const identifierList = [
// "window",
// "self",
// "globalThis",
// "parent",
// "top",
// "location",
// ""
// ]
replace(ast, {
enter: (node, parent) => {
if (["ImportDeclaration", "ImportExpression", "ExportAllDeclaration", "ExportNamedDeclaration"].includes(node.type) && node.source) {
node.source.value = encodeUrl(node.source.value, origin);
}
return node;
},
fallback: "iteration"
})
return generate(ast);
} catch (err) {
throw new Error(err);
}
import { parseModule } from "meriyah";
import { generate } from "astring";
import { makeTraveler } from "astravel";
import { encodeUrl } from "./url";
import * as ESTree from "estree";
// i am a cat. i like to be petted. i like to be fed. i like to be
// js rewiter is NOT finished
// location
// window
// self
// globalThis
// this
// top
// parent
export function rewriteJs(js: string, origin?: URL) {
try {
const ast = parseModule(js, {
module: true,
webcompat: true
});
const identifierList = [
"window",
"self",
"globalThis",
"this",
"parent",
"top",
"location"
]
const customTraveler = makeTraveler({
ImportDeclaration: (node: ESTree.ImportDeclaration) => {
node.source.value = encodeUrl(node.source.value as string, origin);
},
ImportExpression: (node: ESTree.ImportExpression) => {
if (node.source.type === "Literal") {
node.source.value = encodeUrl(node.source.value as string, origin);
} else if (node.source.type === "Identifier") {
// this is for things that import something like
// const moduleName = "name";
// await import(moduleName);
node.source.name = `__wrapImport(${node.source.name})`;
}
},
ExportAllDeclaration: (node: ESTree.ExportAllDeclaration) => {
node.source.value = encodeUrl(node.source.value as string, origin);
},
ExportNamedDeclaration: (node: ESTree.ExportNamedDeclaration) => {
// strings are Literals in ESTree syntax but these will always be strings
if (node.source) node.source.value = encodeUrl(node.source.value as string, origin);
},
// js rweriting notrdone
MemberExpression: (node: ESTree.MemberExpression) => {
if (node.object.type === "Identifier" && identifierList.includes(node.object.name)) {
node.object.name = "__" + node.object.name;
}
}
});
customTraveler.go(ast);
return generate(ast);
} catch {
console.log(js);
return js;
}
}

View file

@ -1,37 +1,37 @@
import { rewriteJs } from "./js";
function canParseUrl(url: string, origin?: URL) {
try {
new URL(url, origin);
return true;
} catch {
return false;
}
}
// something is broken with this but i didn't debug it
export function encodeUrl(url: string, origin?: URL) {
if (!origin) {
origin = new URL(self.__scramjet$config.codec.decode(location.href.slice((location.origin + self.__scramjet$config.prefix).length)));
}
if (url.startsWith("javascript:")) {
return "javascript:" + rewriteJs(url.slice("javascript:".length));
} else if (/^(#|mailto|about|data)/.test(url)) {
return url;
} else if (canParseUrl(url, origin)) {
return location.origin + self.__scramjet$config.prefix + self.__scramjet$config.codec.encode(new URL(url, origin).href);
}
}
// something is also broken with this but i didn't debug it
export function decodeUrl(url: string) {
if (/^(#|about|data|mailto|javascript)/.test(url)) {
return url;
} else if (canParseUrl(url)) {
return self.__scramjet$config.codec.decode(url.slice((location.origin + self.__scramjet$config.prefix).length))
} else {
return url;
}
import { rewriteJs } from "./js";
function canParseUrl(url: string, origin?: URL) {
try {
new URL(url, origin);
return true;
} catch {
return false;
}
}
// something is broken with this but i didn't debug it
export function encodeUrl(url: string, origin?: URL) {
if (!origin) {
origin = new URL(self.__scramjet$config.codec.decode(location.href.slice((location.origin + self.__scramjet$config.prefix).length)));
}
if (url.startsWith("javascript:")) {
return "javascript:" + rewriteJs(url.slice("javascript:".length));
} else if (/^(#|mailto|about|data)/.test(url)) {
return url;
} else if (canParseUrl(url, origin)) {
return location.origin + self.__scramjet$config.prefix + self.__scramjet$config.codec.encode(new URL(url, origin).href);
}
}
// something is also broken with this but i didn't debug it
export function decodeUrl(url: string) {
if (/^(#|about|data|mailto|javascript)/.test(url)) {
return url;
} else if (canParseUrl(url)) {
return self.__scramjet$config.codec.decode(url.slice((location.origin + self.__scramjet$config.prefix).length))
} else {
return url;
}
}

View file

@ -1,9 +1,9 @@
import { encodeUrl } from "../bundle";
navigator.sendBeacon = new Proxy(navigator.sendBeacon, {
apply(target, thisArg, argArray) {
argArray[0] = encodeUrl(argArray[0]);
return Reflect.apply(target, thisArg, argArray);
},
import { encodeUrl } from "../bundle";
navigator.sendBeacon = new Proxy(navigator.sendBeacon, {
apply(target, thisArg, argArray) {
argArray[0] = encodeUrl(argArray[0]);
return Reflect.apply(target, thisArg, argArray);
},
});

View file

@ -1,26 +1,26 @@
import { rewriteCss } from "../bundle";
const cssProperties = ["background", "background-image", "mask", "mask-image", "list-style", "list-style-image", "border-image", "border-image-source", "cursor"];
const jsProperties = ["background", "backgroundImage", "mask", "maskImage", "listStyle", "listStyleImage", "borderImage", "borderImageSource", "cursor"];
CSSStyleDeclaration.prototype.setProperty = new Proxy(CSSStyleDeclaration.prototype.setProperty, {
apply(target, thisArg, argArray) {
if (cssProperties.includes(argArray[0])) argArray[1] = rewriteCss(argArray[1]);
return Reflect.apply(target, thisArg, argArray);
},
});
jsProperties.forEach((prop) => {
const propDescriptor = Object.getOwnPropertyDescriptor(CSSStyleDeclaration.prototype, prop);
Object.defineProperty(CSSStyleDeclaration.prototype, prop, {
get() {
return propDescriptor.get.call(this);
},
set(v) {
return propDescriptor.set.call(this, rewriteCss(v));
},
})
import { rewriteCss } from "../bundle";
const cssProperties = ["background", "background-image", "mask", "mask-image", "list-style", "list-style-image", "border-image", "border-image-source", "cursor"];
const jsProperties = ["background", "backgroundImage", "mask", "maskImage", "listStyle", "listStyleImage", "borderImage", "borderImageSource", "cursor"];
CSSStyleDeclaration.prototype.setProperty = new Proxy(CSSStyleDeclaration.prototype.setProperty, {
apply(target, thisArg, argArray) {
if (cssProperties.includes(argArray[0])) argArray[1] = rewriteCss(argArray[1]);
return Reflect.apply(target, thisArg, argArray);
},
});
jsProperties.forEach((prop) => {
const propDescriptor = Object.getOwnPropertyDescriptor(CSSStyleDeclaration.prototype, prop);
Object.defineProperty(CSSStyleDeclaration.prototype, prop, {
get() {
return propDescriptor.get.call(this);
},
set(v) {
return propDescriptor.set.call(this, rewriteCss(v));
},
})
});

View file

@ -1,98 +1,98 @@
import { encodeUrl, rewriteCss, rewriteHtml, rewriteJs, rewriteSrcset } from "../bundle";
const attrObject = {
"nonce": [HTMLElement],
"integrity": [HTMLScriptElement, HTMLLinkElement],
"csp": [HTMLIFrameElement],
"src": [HTMLImageElement, HTMLMediaElement, HTMLIFrameElement, HTMLEmbedElement, HTMLScriptElement],
"href": [HTMLAnchorElement, HTMLLinkElement],
"data": [HTMLObjectElement],
"action": [HTMLFormElement],
"formaction": [HTMLButtonElement, HTMLInputElement],
"srcdoc": [HTMLIFrameElement],
"srcset": [HTMLImageElement, HTMLSourceElement],
"imagesrcset": [HTMLLinkElement]
}
const attrs = Object.keys(attrObject);
for (const attr of attrs) {
for (const element of attrObject[attr]) {
const descriptor = Object.getOwnPropertyDescriptor(element.prototype, attr);
Object.defineProperty(element.prototype, attr, {
get() {
return this.dataset[attr];
},
set(value) {
this.dataset[attr] = value;
if (/nonce|integrity|csp/.test(attr)) {
return;
} else if (/src|href|data|action|formaction/.test(attr)) {
// @ts-expect-error
if (value instanceof TrustedScriptURL) {
return;
}
value = encodeUrl(value);
} else if (attr === "srcdoc") {
value = rewriteHtml(value);
} else if (/(image)?srcset/.test(attr)) {
value = rewriteSrcset(value);
}
descriptor.set.call(this, value);
},
});
}
}
Element.prototype.getAttribute = new Proxy(Element.prototype.getAttribute, {
apply(target, thisArg, argArray) {
if (attrs.includes(argArray[0]) && thisArg.dataset[argArray[0]]) {
return thisArg.dataset[argArray[0]];
}
return Reflect.apply(target, thisArg, argArray);
},
});
Element.prototype.setAttribute = new Proxy(Element.prototype.setAttribute, {
apply(target, thisArg, argArray) {
if (attrs.includes(argArray[0])) {
thisArg.dataset[argArray[0]] = argArray[1];
if (/nonce|integrity|csp/.test(argArray[0])) {
return;
} else if (/src|href|data|action|formaction/.test(argArray[0])) {
console.log(thisArg);
argArray[1] = encodeUrl(argArray[1]);
} else if (argArray[0] === "srcdoc") {
argArray[1] = rewriteHtml(argArray[1]);
} else if (/(image)?srcset/.test(argArray[0])) {
argArray[1] = rewriteSrcset(argArray[1]);
} else if (argArray[1] === "style") {
argArray[1] = rewriteCss(argArray[1]);
}
}
return Reflect.apply(target, thisArg, argArray);
},
});
const innerHTML = Object.getOwnPropertyDescriptor(Element.prototype, "innerHTML");
Object.defineProperty(Element.prototype, "innerHTML", {
set(value) {
// @ts-expect-error
if (this instanceof HTMLScriptElement && !(value instanceof TrustedScript)) {
value = rewriteJs(value);
} else if (this instanceof HTMLStyleElement) {
value = rewriteCss(value);
// @ts-expect-error
} else if (!(value instanceof TrustedHTML)) {
value = rewriteHtml(value);
}
return innerHTML.set.call(this, value);
},
import { encodeUrl, rewriteCss, rewriteHtml, rewriteJs, rewriteSrcset } from "../bundle";
const attrObject = {
"nonce": [HTMLElement],
"integrity": [HTMLScriptElement, HTMLLinkElement],
"csp": [HTMLIFrameElement],
"src": [HTMLImageElement, HTMLMediaElement, HTMLIFrameElement, HTMLEmbedElement, HTMLScriptElement],
"href": [HTMLAnchorElement, HTMLLinkElement],
"data": [HTMLObjectElement],
"action": [HTMLFormElement],
"formaction": [HTMLButtonElement, HTMLInputElement],
"srcdoc": [HTMLIFrameElement],
"srcset": [HTMLImageElement, HTMLSourceElement],
"imagesrcset": [HTMLLinkElement]
}
const attrs = Object.keys(attrObject);
for (const attr of attrs) {
for (const element of attrObject[attr]) {
const descriptor = Object.getOwnPropertyDescriptor(element.prototype, attr);
Object.defineProperty(element.prototype, attr, {
get() {
return this.dataset[attr];
},
set(value) {
this.dataset[attr] = value;
if (/nonce|integrity|csp/.test(attr)) {
return;
} else if (/src|href|data|action|formaction/.test(attr)) {
// @ts-expect-error
if (value instanceof TrustedScriptURL) {
return;
}
value = encodeUrl(value);
} else if (attr === "srcdoc") {
value = rewriteHtml(value);
} else if (/(image)?srcset/.test(attr)) {
value = rewriteSrcset(value);
}
descriptor.set.call(this, value);
},
});
}
}
Element.prototype.getAttribute = new Proxy(Element.prototype.getAttribute, {
apply(target, thisArg, argArray) {
if (attrs.includes(argArray[0]) && thisArg.dataset[argArray[0]]) {
return thisArg.dataset[argArray[0]];
}
return Reflect.apply(target, thisArg, argArray);
},
});
Element.prototype.setAttribute = new Proxy(Element.prototype.setAttribute, {
apply(target, thisArg, argArray) {
if (attrs.includes(argArray[0])) {
thisArg.dataset[argArray[0]] = argArray[1];
if (/nonce|integrity|csp/.test(argArray[0])) {
return;
} else if (/src|href|data|action|formaction/.test(argArray[0])) {
console.log(thisArg);
argArray[1] = encodeUrl(argArray[1]);
} else if (argArray[0] === "srcdoc") {
argArray[1] = rewriteHtml(argArray[1]);
} else if (/(image)?srcset/.test(argArray[0])) {
argArray[1] = rewriteSrcset(argArray[1]);
} else if (argArray[1] === "style") {
argArray[1] = rewriteCss(argArray[1]);
}
}
return Reflect.apply(target, thisArg, argArray);
},
});
const innerHTML = Object.getOwnPropertyDescriptor(Element.prototype, "innerHTML");
Object.defineProperty(Element.prototype, "innerHTML", {
set(value) {
// @ts-expect-error
if (this instanceof HTMLScriptElement && !(value instanceof TrustedScript)) {
value = rewriteJs(value);
} else if (this instanceof HTMLStyleElement) {
value = rewriteCss(value);
// @ts-expect-error
} else if (!(value instanceof TrustedHTML)) {
value = rewriteHtml(value);
}
return innerHTML.set.call(this, value);
},
})

1
src/client/history.ts Normal file
View file

@ -0,0 +1 @@
// forgot aobut this api

View file

@ -1,16 +1,16 @@
import "./native/eval.ts";
import "./location.ts";
import "./trustedTypes.ts";
import "./requests/fetch.ts";
import "./requests/xmlhttprequest.ts";
import "./requests/websocket.ts"
import "./element.ts";
import "./storage.ts";
import "./css.ts";
import "./worker.ts";
declare global {
interface Window {
__location: Location;
}
}
import "./native/eval.ts";
import "./location.ts";
import "./trustedTypes.ts";
import "./requests/fetch.ts";
import "./requests/xmlhttprequest.ts";
import "./requests/websocket.ts"
import "./element.ts";
import "./storage.ts";
import "./css.ts";
import "./worker.ts";
declare global {
interface Window {
__location: Location;
}
}

View file

@ -1,34 +1,34 @@
// @ts-nocheck
import { encodeUrl, decodeUrl } from "../bundle";
function urlLocation() {
const loc = new URL(decodeUrl(location.href));
loc.assign = (url: string) => location.assign(encodeUrl(url));
loc.reload = () => location.reload();
loc.replace = (url: string) => location.replace(encodeUrl(url));
loc.toString = () => loc.href;
return loc;
}
export function LocationProxy() {
const loc = urlLocation();
return new Proxy(window.location, {
get(target, prop) {
return loc[prop];
},
set(obj, prop, value) {
if (prop === "href") {
location.href = encodeUrl(value);
} else {
loc[prop] = value;
}
return true;
}
})
}
// @ts-nocheck
import { encodeUrl, decodeUrl } from "../bundle";
function urlLocation() {
const loc = new URL(decodeUrl(location.href));
loc.assign = (url: string) => location.assign(encodeUrl(url));
loc.reload = () => location.reload();
loc.replace = (url: string) => location.replace(encodeUrl(url));
loc.toString = () => loc.href;
return loc;
}
export function LocationProxy() {
const loc = urlLocation();
return new Proxy(window.location, {
get(target, prop) {
return loc[prop];
},
set(obj, prop, value) {
if (prop === "href") {
location.href = encodeUrl(value);
} else {
loc[prop] = value;
}
return true;
}
})
}
window.__location = LocationProxy();

View file

@ -1,28 +1,28 @@
import { rewriteJs } from "../../bundle";
const FunctionProxy = new Proxy(Function, {
construct(target, argArray) {
if (argArray.length === 1) {
return Reflect.construct(target, rewriteJs(argArray[0]));
} else {
return Reflect.construct(target, rewriteJs(argArray[argArray.length - 1]))
}
},
apply(target, thisArg, argArray) {
if (argArray.length === 1) {
return Reflect.apply(target, undefined, rewriteJs(argArray[0]));
} else {
return Reflect.apply(target, undefined, [...argArray.map((x, index) => index === argArray.length - 1), rewriteJs(argArray[argArray.length - 1])])
}
},
});
delete window.Function;
window.Function = FunctionProxy;
delete window.eval;
// since the function proxy is already rewriting the js we can just reuse it for the eval proxy
import { rewriteJs } from "../../bundle";
const FunctionProxy = new Proxy(Function, {
construct(target, argArray) {
if (argArray.length === 1) {
return Reflect.construct(target, rewriteJs(argArray[0]));
} else {
return Reflect.construct(target, rewriteJs(argArray[argArray.length - 1]))
}
},
apply(target, thisArg, argArray) {
if (argArray.length === 1) {
return Reflect.apply(target, undefined, rewriteJs(argArray[0]));
} else {
return Reflect.apply(target, undefined, [...argArray.map((x, index) => index === argArray.length - 1), rewriteJs(argArray[argArray.length - 1])])
}
},
});
delete window.Function;
window.Function = FunctionProxy;
delete window.eval;
// since the function proxy is already rewriting the js we can just reuse it for the eval proxy
window.eval = (str: string) => window.Function(str);

View file

@ -1,35 +1,35 @@
// ts throws an error if you dont do window.fetch
import { encodeUrl, rewriteHeaders } from "../../bundle";
window.fetch = new Proxy(window.fetch, {
apply(target, thisArg, argArray) {
argArray[0] = encodeUrl(argArray[0]);
return Reflect.apply(target, thisArg, argArray);
},
});
Headers = new Proxy(Headers, {
construct(target, argArray, newTarget) {
argArray[0] = rewriteHeaders(argArray[0]);
return Reflect.construct(target, argArray, newTarget);
},
})
Request = new Proxy(Request, {
construct(target, argArray, newTarget) {
if (typeof argArray[0] === "string") argArray[0] = encodeUrl(argArray[0]);
return Reflect.construct(target, argArray, newTarget);
},
});
Response.redirect = new Proxy(Response.redirect, {
apply(target, thisArg, argArray) {
argArray[0] = encodeUrl(argArray[0]);
return Reflect.apply(target, thisArg, argArray);
},
// ts throws an error if you dont do window.fetch
import { encodeUrl, rewriteHeaders } from "../../bundle";
window.fetch = new Proxy(window.fetch, {
apply(target, thisArg, argArray) {
argArray[0] = encodeUrl(argArray[0]);
return Reflect.apply(target, thisArg, argArray);
},
});
Headers = new Proxy(Headers, {
construct(target, argArray, newTarget) {
argArray[0] = rewriteHeaders(argArray[0]);
return Reflect.construct(target, argArray, newTarget);
},
})
Request = new Proxy(Request, {
construct(target, argArray, newTarget) {
if (typeof argArray[0] === "string") argArray[0] = encodeUrl(argArray[0]);
return Reflect.construct(target, argArray, newTarget);
},
});
Response.redirect = new Proxy(Response.redirect, {
apply(target, thisArg, argArray) {
argArray[0] = encodeUrl(argArray[0]);
return Reflect.apply(target, thisArg, argArray);
},
});

View file

@ -1,17 +1,16 @@
import { BareClient } from "@mercuryworkshop/bare-mux"
const client = new BareClient()
const RealWebSocket = WebSocket
WebSocket = new Proxy(WebSocket, {
construct(_target, args) {
return client.createWebSocket(
args[0],
args[1],
RealWebSocket,
{
"User-Agent": navigator.userAgent
},
// @ts-expect-error
ArrayBuffer.prototype
)
}
import { BareClient } from "@mercuryworkshop/bare-mux"
const client = new BareClient()
const RealWebSocket = WebSocket
WebSocket = new Proxy(WebSocket, {
construct(_target, args) {
return client.createWebSocket(
args[0],
args[1],
RealWebSocket,
{
"User-Agent": navigator.userAgent
},
ArrayBuffer.prototype
)
}
})

View file

@ -1,20 +1,20 @@
import { encodeUrl, rewriteHeaders } from "../../bundle";
XMLHttpRequest.prototype.open = new Proxy(XMLHttpRequest.prototype.open, {
apply(target, thisArg, argArray) {
if (argArray[1]) argArray[1] = encodeUrl(argArray[1]);
return Reflect.apply(target, thisArg, argArray);
},
});
XMLHttpRequest.prototype.setRequestHeader = new Proxy(XMLHttpRequest.prototype.setRequestHeader, {
apply(target, thisArg, argArray) {
let headerObject = Object.fromEntries([argArray]);
headerObject = rewriteHeaders(headerObject);
argArray = Object.entries(headerObject)[0];
return Reflect.apply(target, thisArg, argArray);
},
});
import { encodeUrl, rewriteHeaders } from "../../bundle";
XMLHttpRequest.prototype.open = new Proxy(XMLHttpRequest.prototype.open, {
apply(target, thisArg, argArray) {
if (argArray[1]) argArray[1] = encodeUrl(argArray[1]);
return Reflect.apply(target, thisArg, argArray);
},
});
XMLHttpRequest.prototype.setRequestHeader = new Proxy(XMLHttpRequest.prototype.setRequestHeader, {
apply(target, thisArg, argArray) {
let headerObject = Object.fromEntries([argArray]);
headerObject = rewriteHeaders(headerObject);
argArray = Object.entries(headerObject)[0];
return Reflect.apply(target, thisArg, argArray);
},
});

View file

@ -1,68 +1,69 @@
import IDBMapSync from "@webreflection/idb-map/sync";
const store = new IDBMapSync(window.__location.host, {
prefix: "Storage",
durability: "relaxed"
});
await store.sync();
function storageProxy(scope: Storage): Storage {
return new Proxy(scope, {
get(target, prop) {
switch (prop) {
case "getItem":
return (key: string) => {
return store.get(key);
}
case "setItem":
return (key: string, value: string) => {
store.set(key, value);
store.sync();
}
case "removeItem":
return (key: string) => {
store.delete(key);
store.sync();
}
case "clear":
return () => {
store.clear();
store.sync();
}
case "key":
return (index: number) => {
store.keys()[index];
}
case "length":
return store.size;
default:
return store.get(prop);
}
},
set(target, prop, value) {
store.set(prop, value);
store.sync();
},
defineProperty(target, property, attributes) {
store.set(property as string, attributes.value);
return true;
},
})
}
const localStorageProxy = storageProxy(window.localStorage);
const sessionStorageProxy = storageProxy(window.sessionStorage);
delete window.localStorage;
delete window.sessionStorage;
window.localStorage = localStorageProxy;
window.sessionStorage = sessionStorageProxy;
import IDBMapSync from "@webreflection/idb-map/sync";
const store = new IDBMapSync(window.__location.host, {
prefix: "Storage",
durability: "relaxed"
});
await store.sync();
function storageProxy(scope: Storage): Storage {
return new Proxy(scope, {
get(target, prop) {
switch (prop) {
case "getItem":
return (key: string) => {
return store.get(key);
}
case "setItem":
return (key: string, value: string) => {
store.set(key, value);
store.sync();
}
case "removeItem":
return (key: string) => {
store.delete(key);
store.sync();
}
case "clear":
return () => {
store.clear();
store.sync();
}
case "key":
return (index: number) => {
store.keys()[index];
}
case "length":
return store.size;
default:
return store.get(prop);
}
},
//@ts-ignore
set(target, prop, value) {
store.set(prop, value);
store.sync();
},
defineProperty(target, property, attributes) {
store.set(property as string, attributes.value);
return true;
},
})
}
const localStorageProxy = storageProxy(window.localStorage);
const sessionStorageProxy = storageProxy(window.sessionStorage);
delete window.localStorage;
delete window.sessionStorage;
window.localStorage = localStorageProxy;
window.sessionStorage = sessionStorageProxy;

View file

@ -1,32 +1,32 @@
import { rewriteHtml, rewriteJs, encodeUrl } from "../bundle";
// @ts-expect-error
trustedTypes.createPolicy = new Proxy(trustedTypes.createPolicy, {
apply(target, thisArg, argArray) {
if (argArray[1].createHTML) {
argArray[1].createHTML = new Proxy(argArray[1].createHTML, {
apply(target1, thisArg1, argArray1) {
return rewriteHtml(target1(...argArray1));
},
});
}
if (argArray[1].createScript) {
argArray[1].createScript = new Proxy(argArray[1].createScript, {
apply(target1, thisArg1, argArray1) {
return rewriteJs(target1(...argArray1));
},
});
}
if (argArray[1].createScriptURL) {
argArray[1].createScriptURL = new Proxy(argArray[1].createScriptURL, {
apply(target1, thisArg1, argArray1) {
return encodeUrl(target1(...argArray1));
},
})
}
return Reflect.apply(target, thisArg, argArray);
},
import { rewriteHtml, rewriteJs, encodeUrl } from "../bundle";
// @ts-expect-error
trustedTypes.createPolicy = new Proxy(trustedTypes.createPolicy, {
apply(target, thisArg, argArray) {
if (argArray[1].createHTML) {
argArray[1].createHTML = new Proxy(argArray[1].createHTML, {
apply(target1, thisArg1, argArray1) {
return rewriteHtml(target1(...argArray1));
},
});
}
if (argArray[1].createScript) {
argArray[1].createScript = new Proxy(argArray[1].createScript, {
apply(target1, thisArg1, argArray1) {
return rewriteJs(target1(...argArray1));
},
});
}
if (argArray[1].createScriptURL) {
argArray[1].createScriptURL = new Proxy(argArray[1].createScriptURL, {
apply(target1, thisArg1, argArray1) {
return encodeUrl(target1(...argArray1));
},
})
}
return Reflect.apply(target, thisArg, argArray);
},
})

1
src/client/window.ts Normal file
View file

@ -0,0 +1 @@
// the DAMN WINDOW PROXY

View file

@ -1,7 +1,7 @@
import { encodeUrl } from "../bundle";
const RealWorker = Worker
Worker = new Proxy(Worker, {
construct(_target, args) {
return new RealWorker(encodeUrl(args[0]), args[1])
}
import { encodeUrl } from "../bundle";
const RealWorker = Worker
Worker = new Proxy(Worker, {
construct(_target, args) {
return new RealWorker(encodeUrl(args[0]), args[1])
}
})

File diff suppressed because it is too large Load diff

View file

@ -1,82 +1,83 @@
import { enc, dec } from "./aes.ts";
// for some reason eslint was parsing the type inside of the function params as a variable
export interface Codec {
// eslint-disable-next-line
encode: (str: string | undefined) => string;
// eslint-disable-next-line
decode: (str: string | undefined) => string;
}
const xor = {
encode: (str: string | undefined, key: number = 2) => {
if (!str) return str;
return encodeURIComponent(str.split("").map((e, i) => i % key ? String.fromCharCode(e.charCodeAt(0) ^ key) : e).join(""));
},
decode: (str: string | undefined, key: number = 2) => {
if (!str) return str;
return decodeURIComponent(str).split("").map((e, i) => i % key ? String.fromCharCode(e.charCodeAt(0) ^ key) : e).join("");
}
}
const plain = {
encode: (str: string | undefined) => {
if (!str) return str;
return encodeURIComponent(str);
},
decode: (str: string | undefined) => {
if (!str) return str;
return decodeURIComponent(str);
}
}
const aes = {
encode: (str: string | undefined) => {
if (!str) return str;
return encodeURIComponent(enc(str, "dynamic").substring(10));
},
decode: (str: string | undefined) => {
if (!str) return str;
return dec("U2FsdGVkX1" + decodeURIComponent(str), "dynamic");
}
}
const none = {
encode: (str: string | undefined) => str,
decode: (str: string | undefined) => str,
}
const base64 = {
encode: (str: string | undefined) => {
if (!str) return str;
return decodeURIComponent(btoa(str));
},
decode: (str: string | undefined) => {
if (!str) return str;
return atob(str);
}
}
declare global {
interface Window {
__scramjet$codecs: {
none: Codec;
plain: Codec;
base64: Codec;
xor: Codec;
aes: Codec;
}
}
}
self.__scramjet$codecs = {
none, plain, base64, xor, aes
import { enc, dec } from "./aes";
// for some reason eslint was parsing the type inside of the function params as a variable
export interface Codec {
// eslint-disable-next-line
encode: (str: string | undefined) => string;
// eslint-disable-next-line
decode: (str: string | undefined) => string;
}
const xor = {
encode: (str: string | undefined, key: number = 2) => {
if (!str) return str;
return encodeURIComponent(str.split("").map((e, i) => i % key ? String.fromCharCode(e.charCodeAt(0) ^ key) : e).join(""));
},
decode: (str: string | undefined, key: number = 2) => {
if (!str) return str;
return decodeURIComponent(str).split("").map((e, i) => i % key ? String.fromCharCode(e.charCodeAt(0) ^ key) : e).join("");
}
}
const plain = {
encode: (str: string | undefined) => {
if (!str) return str;
return encodeURIComponent(str);
},
decode: (str: string | undefined) => {
if (!str) return str;
return decodeURIComponent(str);
}
}
/*
const aes = {
encode: (str: string | undefined) => {
if (!str) return str;
return encodeURIComponent(enc(str, "dynamic").substring(10));
},
decode: (str: string | undefined) => {
if (!str) return str;
return dec("U2FsdGVkX1" + decodeURIComponent(str), "dynamic");
}
}
*/
const none = {
encode: (str: string | undefined) => str,
decode: (str: string | undefined) => str,
}
const base64 = {
encode: (str: string | undefined) => {
if (!str) return str;
return decodeURIComponent(btoa(str));
},
decode: (str: string | undefined) => {
if (!str) return str;
return atob(str);
}
}
declare global {
interface Window {
__scramjet$codecs: {
none: Codec;
plain: Codec;
base64: Codec;
xor: Codec;
}
}
}
self.__scramjet$codecs = {
none, plain, base64, xor
}

View file

@ -1,25 +1,25 @@
import { Codec } from "./codecs";
declare global {
interface Window {
__scramjet$config: {
prefix: string;
codec: Codec
config: string;
bundle: string;
worker: string;
client: string;
codecs: string;
}
}
}
self.__scramjet$config = {
prefix: "/scramjet/",
codec: self.__scramjet$codecs.plain,
config: "/scramjet.config.js",
bundle: "/scramjet.bundle.js",
worker: "/scramjet.worker.js",
client: "/scramjet.client.js",
codecs: "/scramjet.codecs.js"
import { Codec } from "./codecs";
declare global {
interface Window {
__scramjet$config: {
prefix: string;
codec: Codec
config: string;
bundle: string;
worker: string;
client: string;
codecs: string;
}
}
}
self.__scramjet$config = {
prefix: "/scramjet/",
codec: self.__scramjet$codecs.plain,
config: "/scramjet.config.js",
bundle: "/scramjet.bundle.js",
worker: "/scramjet.worker.js",
client: "/scramjet.client.js",
codecs: "/scramjet.codecs.js"
}

View file

@ -1,194 +1,194 @@
import { BareClient } from "@mercuryworkshop/bare-mux";
import { BareResponseFetch } from "@mercuryworkshop/bare-mux";
import { encodeUrl, decodeUrl, rewriteCss, rewriteHeaders, rewriteHtml, rewriteJs } from "../bundle";
declare global {
interface Window {
ScramjetServiceWorker;
}
}
export default class ScramjetServiceWorker {
client: typeof BareClient.prototype;
config: typeof self.__scramjet$config;
constructor(config = self.__scramjet$config) {
this.client = new BareClient();
if (!config.prefix) config.prefix = "/scramjet/";
this.config = config;
}
route({ request }: FetchEvent) {
if (request.url.startsWith(location.origin + this.config.prefix)) return true;
else return false;
}
async fetch({ request }: FetchEvent) {
const urlParam = new URLSearchParams(new URL(request.url).search);
if (urlParam.has("url")) {
return Response.redirect(encodeUrl(urlParam.get("url"), new URL(urlParam.get("url"))))
}
try {
const url = new URL(decodeUrl(request.url));
const response: BareResponseFetch = await this.client.fetch(url, {
method: request.method,
body: request.body,
headers: request.headers,
credentials: "omit",
mode: request.mode === "cors" ? request.mode : "same-origin",
cache: request.cache,
redirect: request.redirect,
});
let responseBody;
const responseHeaders = rewriteHeaders(response.rawHeaders, url);
if (response.body) {
switch (request.destination) {
case "iframe":
case "document":
responseBody = rewriteHtml(await response.text(), url);
break;
case "script":
responseBody = rewriteJs(await response.text(), url);
break;
case "style":
responseBody = rewriteCss(await response.text(), url);
break;
case "sharedworker":
case "worker":
break;
default:
responseBody = response.body;
break;
}
}
// downloads
if (["document", "iframe"].includes(request.destination)) {
const header = responseHeaders["content-disposition"];
// validate header and test for filename
if (!/\s*?((inline|attachment);\s*?)filename=/i.test(header)) {
// if filename= wasn"t specified then maybe the remote specified to download this as an attachment?
// if it"s invalid then we can still possibly test for the attachment/inline type
const type = /^\s*?attachment/i.test(header)
? "attachment"
: "inline";
// set the filename
const [filename] = new URL(response.finalURL).pathname
.split("/")
.slice(-1);
responseHeaders[
"content-disposition"
] = `${type}; filename=${JSON.stringify(filename)}`;
}
}
if (responseHeaders["accept"] === "text/event-stream") {
responseHeaders["content-type"] = "text/event-stream";
}
if (crossOriginIsolated) {
responseHeaders["Cross-Origin-Embedder-Policy"] = "require-corp";
}
return new Response(responseBody, {
headers: responseHeaders as HeadersInit,
status: response.status,
statusText: response.statusText
})
} catch (err) {
if (!["document", "iframe"].includes(request.destination))
return new Response(undefined, { status: 500 });
console.error(err);
return renderError(err, decodeUrl(request.url));
}
}
}
function errorTemplate(
trace: string,
fetchedURL: string,
) {
// turn script into a data URI so we don"t have to escape any HTML values
const script = `
errorTrace.value = ${JSON.stringify(trace)};
fetchedURL.textContent = ${JSON.stringify(fetchedURL)};
for (const node of document.querySelectorAll("#hostname")) node.textContent = ${JSON.stringify(
location.hostname
)};
reload.addEventListener("click", () => location.reload());
version.textContent = "0.0.1";
`
return (
`<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Error</title>
<style>
* { background-color: white }
</style>
</head>
<body>
<h1 id="errorTitle">Error processing your request</h1>
<hr />
<p>Failed to load <b id="fetchedURL"></b></p>
<p id="errorMessage">Internal Server Error</p>
<textarea id="errorTrace" cols="40" rows="10" readonly></textarea>
<p>Try:</p>
<ul>
<li>Checking your internet connection</li>
<li>Verifying you entered the correct address</li>
<li>Clearing the site data</li>
<li>Contacting <b id="hostname"></b>"s administrator</li>
<li>Verify the server isn"t censored</li>
</ul>
<p>If you"re the administrator of <b id="hostname"></b>, try:</p>
<ul>
<li>Restarting your server</li>
<li>Updating Scramjet</li>
<li>Troubleshooting the error on the <a href="https://github.com/MercuryWorkshop/scramjet" target="_blank">GitHub repository</a></li>
</ul>
<button id="reload">Reload</button>
<hr />
<p><i>Scramjet v<span id="version"></span></i></p>
<script src="${
"data:application/javascript," + encodeURIComponent(script)
}"></script>
</body>
</html>
`
);
}
/**
*
* @param {unknown} err
* @param {string} fetchedURL
*/
function renderError(err, fetchedURL) {
const headers = {
"content-type": "text/html",
};
if (crossOriginIsolated) {
headers["Cross-Origin-Embedd'er-Policy"] = "require-corp";
}
return new Response(
errorTemplate(
String(err),
fetchedURL
),
{
status: 500,
headers: headers
}
);
}
import { BareClient } from "@mercuryworkshop/bare-mux";
import { BareResponseFetch } from "@mercuryworkshop/bare-mux";
import { encodeUrl, decodeUrl, rewriteCss, rewriteHeaders, rewriteHtml, rewriteJs } from "../bundle";
declare global {
interface Window {
ScramjetServiceWorker;
}
}
export default class ScramjetServiceWorker {
client: typeof BareClient.prototype;
config: typeof self.__scramjet$config;
constructor(config = self.__scramjet$config) {
this.client = new BareClient();
if (!config.prefix) config.prefix = "/scramjet/";
this.config = config;
}
route({ request }: FetchEvent) {
if (request.url.startsWith(location.origin + this.config.prefix)) return true;
else return false;
}
async fetch({ request }: FetchEvent) {
const urlParam = new URLSearchParams(new URL(request.url).search);
if (urlParam.has("url")) {
return Response.redirect(encodeUrl(urlParam.get("url"), new URL(urlParam.get("url"))))
}
try {
const url = new URL(decodeUrl(request.url));
const response: BareResponseFetch = await this.client.fetch(url, {
method: request.method,
body: request.body,
headers: request.headers,
credentials: "omit",
mode: request.mode === "cors" ? request.mode : "same-origin",
cache: request.cache,
redirect: request.redirect,
});
let responseBody;
const responseHeaders = rewriteHeaders(response.rawHeaders, url);
if (response.body) {
switch (request.destination) {
case "iframe":
case "document":
responseBody = rewriteHtml(await response.text(), url);
break;
case "script":
responseBody = rewriteJs(await response.text(), url);
break;
case "style":
responseBody = rewriteCss(await response.text(), url);
break;
case "sharedworker":
case "worker":
break;
default:
responseBody = response.body;
break;
}
}
// downloads
if (["document", "iframe"].includes(request.destination)) {
const header = responseHeaders["content-disposition"];
// validate header and test for filename
if (!/\s*?((inline|attachment);\s*?)filename=/i.test(header)) {
// if filename= wasn"t specified then maybe the remote specified to download this as an attachment?
// if it"s invalid then we can still possibly test for the attachment/inline type
const type = /^\s*?attachment/i.test(header)
? "attachment"
: "inline";
// set the filename
const [filename] = new URL(response.finalURL).pathname
.split("/")
.slice(-1);
responseHeaders[
"content-disposition"
] = `${type}; filename=${JSON.stringify(filename)}`;
}
}
if (responseHeaders["accept"] === "text/event-stream") {
responseHeaders["content-type"] = "text/event-stream";
}
if (crossOriginIsolated) {
responseHeaders["Cross-Origin-Embedder-Policy"] = "require-corp";
}
return new Response(responseBody, {
headers: responseHeaders as HeadersInit,
status: response.status,
statusText: response.statusText
})
} catch (err) {
if (!["document", "iframe"].includes(request.destination))
return new Response(undefined, { status: 500 });
console.error(err);
return renderError(err, decodeUrl(request.url));
}
}
}
function errorTemplate(
trace: string,
fetchedURL: string,
) {
// turn script into a data URI so we don"t have to escape any HTML values
const script = `
errorTrace.value = ${JSON.stringify(trace)};
fetchedURL.textContent = ${JSON.stringify(fetchedURL)};
for (const node of document.querySelectorAll("#hostname")) node.textContent = ${JSON.stringify(
location.hostname
)};
reload.addEventListener("click", () => location.reload());
version.textContent = "0.0.1";
`
return (
`<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Error</title>
<style>
* { background-color: white }
</style>
</head>
<body>
<h1 id="errorTitle">Error processing your request</h1>
<hr />
<p>Failed to load <b id="fetchedURL"></b></p>
<p id="errorMessage">Internal Server Error</p>
<textarea id="errorTrace" cols="40" rows="10" readonly></textarea>
<p>Try:</p>
<ul>
<li>Checking your internet connection</li>
<li>Verifying you entered the correct address</li>
<li>Clearing the site data</li>
<li>Contacting <b id="hostname"></b>"s administrator</li>
<li>Verify the server isn"t censored</li>
</ul>
<p>If you"re the administrator of <b id="hostname"></b>, try:</p>
<ul>
<li>Restarting your server</li>
<li>Updating Scramjet</li>
<li>Troubleshooting the error on the <a href="https://github.com/MercuryWorkshop/scramjet" target="_blank">GitHub repository</a></li>
</ul>
<button id="reload">Reload</button>
<hr />
<p><i>Scramjet v<span id="version"></span></i></p>
<script src="${
"data:application/javascript," + encodeURIComponent(script)
}"></script>
</body>
</html>
`
);
}
/**
*
* @param {unknown} err
* @param {string} fetchedURL
*/
function renderError(err, fetchedURL) {
const headers = {
"content-type": "text/html",
};
if (crossOriginIsolated) {
headers["Cross-Origin-Embedd'er-Policy"] = "require-corp";
}
return new Response(
errorTemplate(
String(err),
fetchedURL
),
{
status: 500,
headers: headers
}
);
}

View file

@ -1,29 +1,29 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="prefetch" href="./scramjet.worker.js">
<link rel="prefetch" href="./scramjet.bundle.js">
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&amp;family=Inter+Tight:ital,wght@0,100..900;1,100..900&amp;family=Inter:wght@100..900&amp;display=swap&amp;" rel="stylesheet">
<style>
body, html, #app {
font-family: "Inter", system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
width:100vw;
height:100vh;
margin: 0;
padding: 0;
background-color:#121212;
overflow: hidden;
}
</style>
</head>
<body>
<script src="https://unpkg.com/dreamland"></script>
<script src="./bare-mux.js" defer></script>
<script src="./ui.js" defer></script>
<script src="./scramjet.codecs.js"></script>
<script src="./scramjet.config.js"></script>
</body>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="prefetch" href="/scramjet/scramjet.worker.js">
<link rel="prefetch" href="/scramjet/scramjet.bundle.js">
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&amp;family=Inter+Tight:ital,wght@0,100..900;1,100..900&amp;family=Inter:wght@100..900&amp;display=swap&amp;" rel="stylesheet">
<style>
body, html, #app {
font-family: "Inter", system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
width:100vw;
height:100vh;
margin: 0;
padding: 0;
background-color:#121212;
overflow: hidden;
}
</style>
</head>
<body>
<script src="https://unpkg.com/dreamland"></script>
<script src="/baremux/index.js" defer></script>
<script src="ui.js" defer></script>
<script src="/scramjet/scramjet.codecs.js"></script>
<script src="/scramjet/scramjet.config.js"></script>
</body>
</html>

View file

@ -1,17 +1,17 @@
import ScramjetServiceWorker from "./scramjet.worker.js";
import "./scramjet.codecs.js";
import "./scramjet.config.js";
const scramjet = new ScramjetServiceWorker();
async function handleRequest(event) {
if (scramjet.route(event)) {
return scramjet.fetch(event);
}
return fetch(event.request)
}
self.addEventListener("fetch", (event) => {
event.respondWith(handleRequest(event));
import ScramjetServiceWorker from "./scramjet.worker.js";
import "./scramjet.codecs.js";
import "./scramjet.config.js";
const scramjet = new ScramjetServiceWorker();
async function handleRequest(event) {
if (scramjet.route(event)) {
return scramjet.fetch(event);
}
return fetch(event.request)
}
self.addEventListener("fetch", (event) => {
event.respondWith(handleRequest(event));
});

View file

@ -1,100 +1,100 @@
navigator.serviceWorker.register("./sw.js", {
scope: __scramjet$config.prefix,
type: "module"
})
const connection = new BareMux.BareMuxConnection("/bare-mux-worker.js")
const flex = css`display: flex;`;
const col = css`flex-direction: column;`;
const store = $store({
url: "https://google.com",
wispurl: "wss://wisp.mercurywork.shop/",
bareurl: (location.protocol === "https:" ? "https" : "http") + "://" + location.host + "/bare/",
}, { ident: "settings", backing: "localstorage", autosave: "auto" });
connection.setTransport("/bare-client.js", [store.bareurl])
function App() {
this.urlencoded = "";
this.css = `
width: 100%;
height: 100%;
color: #e0def4;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
input,
button {
font-family: "Inter", system-ui, -apple-system, BlinkMacSystemFont,
sans-serif;
}
h1 {
font-family: "Inter Tight", "Inter", system-ui, -apple-system, BlinkMacSystemFont,
sans-serif;
margin-bottom: 0;
}
iframe {
border: 4px solid #313131;
background-color: #121212;
border-radius: 1rem;
margin: 2em;
margin-top: 0.5em;
width: calc(100% - 4em);
height: calc(100% - 8em);
}
input.bar {
border: none;
outline: none;
color: #fff;
height: 2em;
width: 60%;
text-align: center;
border-radius: 0.75em;
background-color: #313131;
padding: 0.45em;
}
.cfg * {
margin: 2px;
}
.buttons button {
border: 2px solid #4c8bf5;
background-color: #313131;
border-radius: 0.75em;
color: #fff;
padding: 0.45em;
}
.cfg input {
border: none;
background-color: #313131;
border-radius: 0.75em;
color: #fff;
outline: none;
padding: 0.45em;
}
`;
return html`
<div>
<h1>Percury Unblocker</h1>
<p>surf the unblocked and mostly buggy web</p>
<div class=${`${flex} ${col} cfg`}>
<input bind:value=${use(store.wispurl)}></input>
<input bind:value=${use(store.bareurl)}></input>
<div class=${`${flex} buttons`}>
<button on:click=${() => connection.setTransport("/bare-client.js", [store.bareurl])}>use bare server 3</button>
<button on:click=${() => connection.setTransport("/curl-client.js", [{ wisp: store.wispurl }])}>use libcurl.js</button>
<button on:click=${() => connection.setTransport("/epoxy-client.js", [{ wisp: store.wispurl }])}>use epoxy</button>
<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>
<iframe src=${use(this.urlencoded)}></iframe>
</div>
`
}
window.addEventListener("load", () => {
document.body.appendChild(h(App))
})
navigator.serviceWorker.register("./sw.js", {
scope: __scramjet$config.prefix,
type: "module"
})
const connection = new BareMux.BareMuxConnection("/bare-mux-worker.js")
const flex = css`display: flex;`;
const col = css`flex-direction: column;`;
const store = $store({
url: "https://google.com",
wispurl: "wss://wisp.mercurywork.shop/",
bareurl: (location.protocol === "https:" ? "https" : "http") + "://" + location.host + "/bare/",
}, { ident: "settings", backing: "localstorage", autosave: "auto" });
connection.setTransport("/baremod/index.mjs", [store.bareurl])
function App() {
this.urlencoded = "";
this.css = `
width: 100%;
height: 100%;
color: #e0def4;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
input,
button {
font-family: "Inter", system-ui, -apple-system, BlinkMacSystemFont,
sans-serif;
}
h1 {
font-family: "Inter Tight", "Inter", system-ui, -apple-system, BlinkMacSystemFont,
sans-serif;
margin-bottom: 0;
}
iframe {
border: 4px solid #313131;
background-color: #121212;
border-radius: 1rem;
margin: 2em;
margin-top: 0.5em;
width: calc(100% - 4em);
height: calc(100% - 8em);
}
input.bar {
border: none;
outline: none;
color: #fff;
height: 2em;
width: 60%;
text-align: center;
border-radius: 0.75em;
background-color: #313131;
padding: 0.45em;
}
.cfg * {
margin: 2px;
}
.buttons button {
border: 2px solid #4c8bf5;
background-color: #313131;
border-radius: 0.75em;
color: #fff;
padding: 0.45em;
}
.cfg input {
border: none;
background-color: #313131;
border-radius: 0.75em;
color: #fff;
outline: none;
padding: 0.45em;
}
`;
return html`
<div>
<h1>Percury Unblocker</h1>
<p>surf the unblocked and mostly buggy web</p>
<div class=${`${flex} ${col} cfg`}>
<input bind:value=${use(store.wispurl)}></input>
<input bind:value=${use(store.bareurl)}></input>
<div class=${`${flex} buttons`}>
<button on:click=${() => connection.setTransport("/baremod/index.mjs", [store.bareurl])}>use bare server 3</button>
<button on:click=${() => connection.setTransport("/libcurl/index.mjs", [{ wisp: store.wispurl }])}>use libcurl.js</button>
<button on:click=${() => connection.setTransport("/epoxy/index.mjs", [{ wisp: store.wispurl }])}>use epoxy</button>
<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>
<iframe src=${use(this.urlencoded)}></iframe>
</div>
`
}
window.addEventListener("load", () => {
document.body.appendChild(h(App))
})

View file

@ -1,11 +1,10 @@
{
"compilerOptions": {
// "allowJs": true,
"allowImportingTsExtensions": true,
"rootDir": "./src",
"target": "ES2022",
"moduleResolution": "Bundler",
"module": "ES2022",
"noEmit": true
}
{
"compilerOptions": {
// "allowJs": true,
"rootDir": "./src",
"target": "ES2022",
"moduleResolution": "Bundler",
"module": "ES2022",
},
"include": ["src"],
}