chemicaljs 2.6.4 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js ADDED
@@ -0,0 +1,115 @@
1
+ import { bootstrap } from "@mercuryworkshop/proxy-bootstrap";
2
+ import express from "express";
3
+ import fs from "node:fs";
4
+ import http from "node:http";
5
+ import path from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+
8
+ const swPath = fileURLToPath(new URL("./sw.js", import.meta.url));
9
+
10
+ export class Chemical {
11
+ constructor(options = {}) {
12
+ this.port = options.port || 8000;
13
+ this.staticDir = options.staticDir || "build";
14
+ this.spaMode = options.spaMode || false;
15
+
16
+ this.app = express();
17
+ this.server = http.createServer(this.app);
18
+ }
19
+
20
+ async start() {
21
+ const { routeRequest, routeUpgrade } = await bootstrap({
22
+ scramjetBundlePath: "/scramjet/scramjet.js",
23
+ scramjetWasmPath: "/scramjet/scramjet.wasm",
24
+ scramjetUtilsBundlePath: "/scramjet/scramjet-utils.js",
25
+ });
26
+
27
+ this.app.use((req, res, next) => {
28
+ if (routeRequest(req, res)) {
29
+ return;
30
+ }
31
+ next();
32
+ });
33
+
34
+ this.app.get("/sw.js", (req, res) => {
35
+ res.sendFile(swPath);
36
+ });
37
+
38
+ this.app.use(express.static(this.staticDir));
39
+
40
+ if (this.spaMode) {
41
+ this.app.get("*", (req, res) => {
42
+ res.sendFile(path.resolve(process.cwd(), this.staticDir, "index.html"));
43
+ });
44
+ }
45
+
46
+ this.server.on("upgrade", (req, socket, head) => {
47
+ routeUpgrade(req, socket, head);
48
+ });
49
+
50
+ return new Promise((resolve) => {
51
+ this.server.listen(this.port, () => {
52
+ console.log(`Running on http://localhost:${this.port}`);
53
+ resolve(this.server);
54
+ });
55
+ });
56
+ }
57
+
58
+ stop() {
59
+ if (this.server) {
60
+ this.server.close();
61
+ console.log("Server stopped");
62
+ }
63
+ }
64
+ }
65
+
66
+ export const ChemicalVitePlugin = () => ({
67
+ name: "chemical-vite-plugin",
68
+ config() {
69
+ return {
70
+ optimizeDeps: {
71
+ include: [
72
+ "@mercuryworkshop/scramjet",
73
+ "@mercuryworkshop/scramjet-controller",
74
+ "@mercuryworkshop/proxy-bootstrap",
75
+ ],
76
+ },
77
+ };
78
+ },
79
+ async configureServer(server) {
80
+ const { routeRequest, routeUpgrade } = await bootstrap({
81
+ scramjetBundlePath: "/scramjet/scramjet.js",
82
+ scramjetWasmPath: "/scramjet/scramjet.wasm",
83
+ scramjetUtilsBundlePath: "/scramjet/scramjet-utils.js",
84
+ });
85
+
86
+ server.middlewares.use((req, res, next) => {
87
+ const urlPath = req.url.split("?")[0];
88
+
89
+ if (urlPath === "/sw.js") {
90
+ fs.readFile(swPath, (err, data) => {
91
+ if (err) {
92
+ res.statusCode = 404;
93
+ res.end("Service Worker not found");
94
+ return;
95
+ }
96
+ res.setHeader("Content-Type", "application/javascript");
97
+ res.end(data);
98
+ });
99
+ return;
100
+ }
101
+
102
+ if (routeRequest(req, res)) {
103
+ return;
104
+ }
105
+
106
+ next();
107
+ });
108
+
109
+ if (server.httpServer) {
110
+ server.httpServer.on("upgrade", (req, socket, head) => {
111
+ routeUpgrade(req, socket, head);
112
+ });
113
+ }
114
+ },
115
+ });
package/package.json CHANGED
@@ -1,56 +1,40 @@
1
1
  {
2
2
  "name": "chemicaljs",
3
- "version": "2.6.4",
3
+ "version": "3.0.0",
4
4
  "description": "Easily create your own web proxy with no experience required.",
5
- "type": "module",
6
- "main": "./dist/index.cjs",
7
- "module": "./dist/index.js",
8
- "types": "./dist/index.d.ts",
9
- "exports": {
10
- "import": {
11
- "types": "./dist/index.d.ts",
12
- "default": "./dist/index.js"
13
- },
14
- "require": {
15
- "types": "./dist/index.d.cts",
16
- "default": "./dist/index.cjs"
17
- }
18
- },
19
- "files": [
20
- "dist",
21
- "client",
22
- "config",
23
- "ultraviolet"
24
- ],
5
+ "author": "Nebelung",
6
+ "license": "AGPL-3.0-only",
25
7
  "repository": {
26
8
  "type": "git",
27
9
  "url": "git+https://github.com/chemicaljs/chemical.git"
28
10
  },
29
- "author": "Nebelung",
30
- "license": "AGPL-3.0-only",
31
- "scripts": {
32
- "update": "npx npm-check-updates -u && npm i",
33
- "build-uv": "node build-uv.js",
34
- "build": "npm run build-uv && tsup"
11
+ "type": "module",
12
+ "main": "index.js",
13
+ "exports": {
14
+ ".": "./index.js",
15
+ "./Chemical": "./Chemical.svelte",
16
+ "./Frame": "./Frame.svelte"
17
+ },
18
+ "devEngines": {
19
+ "packageManager": {
20
+ "name": "pnpm",
21
+ "version": "12.8.1",
22
+ "onFail": "download"
23
+ }
35
24
  },
36
25
  "dependencies": {
37
- "@mercuryworkshop/bare-mux": "^2.1.7",
38
- "@mercuryworkshop/epoxy-transport": "^2.1.27",
39
- "@mercuryworkshop/libcurl-transport": "^1.3.15",
40
- "@mercuryworkshop/scramjet": "https://github.com/chemicaljs/chemical/raw/main/scramjet.tgz",
41
- "@mercuryworkshop/wisp-js": "^0.3.3",
42
- "@titaniumnetwork-dev/ultraviolet": "^3.2.10",
43
- "express": "^4.21.2",
44
- "glob": "^11.0.0",
45
- "nanoid": "^5.0.9",
46
- "rammerhead": "https://github.com/chemicaljs/chemical/raw/main/rammerhead.tgz",
47
- "typescript": "^5.7.2",
48
- "vite": "^6.0.6"
26
+ "@mercuryworkshop/epoxy-transport": "^3.0.1",
27
+ "@mercuryworkshop/libcurl-transport": "^2.0.5",
28
+ "@mercuryworkshop/proxy-bootstrap": "^0.0.5",
29
+ "@mercuryworkshop/scramjet": "^1.1.0",
30
+ "@mercuryworkshop/scramjet-controller": "^0.0.14",
31
+ "@mercuryworkshop/scramjet-utils": "^0.0.3",
32
+ "express": "^5.2.1"
49
33
  },
50
- "engines": {
51
- "node": ">=20.11.0"
34
+ "peerDependencies": {
35
+ "svelte": "^5.56.1"
52
36
  },
53
- "devDependencies": {
54
- "tsup": "^8.3.5"
37
+ "scripts": {
38
+ "fetch-adblock": "curl -o blacklist.txt https://raw.githubusercontent.com/hagezi/dns-blocklists/main/adblock/pro.txt"
55
39
  }
56
- }
40
+ }
@@ -0,0 +1,3 @@
1
+ allowBuilds:
2
+ '@mercuryworkshop/scramjet': true
3
+ bufferutil: true
package/sw.js ADDED
@@ -0,0 +1,7 @@
1
+ importScripts("./controller/controller.sw.js");
2
+
3
+ addEventListener("fetch", (e) => {
4
+ if ($scramjetController.shouldRoute(e)) {
5
+ e.respondWith($scramjetController.route(e));
6
+ }
7
+ });
@@ -0,0 +1,16 @@
1
+ <script>
2
+ import Chemical from "../../../chemical/Chemical.svelte";
3
+ import { setContext } from "svelte";
4
+
5
+ let { children } = $props();
6
+
7
+ let chemState = $state({
8
+ ready: false,
9
+ instance: null,
10
+ });
11
+
12
+ setContext("chemState", chemState);
13
+ </script>
14
+
15
+ <Chemical bind:this={chemState.instance} bind:ready={chemState.ready} />
16
+ {@render children()}
@@ -0,0 +1,12 @@
1
+ <script>
2
+ import Frame from "../../../chemical/Frame.svelte";
3
+ import { getContext } from "svelte";
4
+
5
+ let chemState = getContext("chemState");
6
+ </script>
7
+
8
+ <Frame
9
+ src="https://www.example.com"
10
+ bind:ready={chemState.ready}
11
+ bind:chemical={chemState.instance}
12
+ ></Frame>
@@ -0,0 +1,68 @@
1
+ <script>
2
+ import Chemical from "$lib/chemicaljs/Chemical.svelte";
3
+ import Frame from "$lib/chemicaljs/Frame.svelte";
4
+
5
+ let ready = $state(false);
6
+ let chemical = $state();
7
+ let transport = $state("epoxy");
8
+ let wisp = $state("wss://wisp.freewisp.org/");
9
+
10
+ let frame1 = $state();
11
+ </script>
12
+
13
+ <button onclick={() => console.log(chemical.fetch("https://example.com"))}
14
+ >fetch test</button
15
+ >
16
+ <button
17
+ onclick={async () =>
18
+ console.log(await chemical.autocomplete("math", "brave"))}
19
+ >autocomplete test</button
20
+ >
21
+ <button
22
+ onclick={async () =>
23
+ console.log(
24
+ await chemical.createDataURL("https://www.google.com/favicon.ico"),
25
+ )}>createDataURL test</button
26
+ >
27
+
28
+ <button onclick={() => (transport = "epoxy")}>epoxy</button>
29
+ <button onclick={() => (transport = "libcurl")}>libcurl</button>
30
+ <button onclick={() => (wisp = "wss://fake.freewisp.org/")}>change wisp</button>
31
+ <Chemical
32
+ bind:this={chemical}
33
+ bind:ready
34
+ bind:transport
35
+ bind:wisp
36
+ onready={() => console.log(chemical.url("https://playobsidian.com"))}
37
+ />
38
+
39
+ <input
40
+ onkeydown={(e) => e.key === "Enter" && frame1.go(e.target.value)}
41
+ placeholder="Enter URL"
42
+ />
43
+ <button onclick={() => frame1.go("example.com", { autoHttps: true })}
44
+ >autohttps</button
45
+ >
46
+ <button
47
+ onclick={() =>
48
+ frame1.go("kittens", {
49
+ searchEngine: chemical.searchEngines.google,
50
+ })}>search engine test</button
51
+ >
52
+ <button onclick={() => frame1.reload()}>reload</button>
53
+ <button onclick={() => frame1.forward()}>forward</button>
54
+ <button onclick={() => frame1.back()}>back</button>
55
+
56
+ <Frame
57
+ bind:this={frame1}
58
+ class="w-full h-screen"
59
+ adblock={true}
60
+ catchlinks={(url) =>
61
+ new URL(`/?goto=${encodeURIComponent(url.href)}`, location.origin)}
62
+ linkhandler={(url) => window.open("?url=" + encodeURIComponent(url))}
63
+ cache={false}
64
+ onchange={(url) => console.log("Frame URL changed to: " + url)}
65
+ src="https://www.example.com"
66
+ bind:ready
67
+ bind:chemical
68
+ ></Frame>
@@ -1,48 +0,0 @@
1
- class ChemicalTitle extends HTMLTitleElement {
2
- constructor() {
3
- super();
4
- }
5
- connectedCallback() {
6
- setTimeout(() => {
7
- this.setAttribute("data-title", this.innerText);
8
-
9
- const title =
10
- this.dataset.titleStore !== undefined
11
- ? localStorage.getItem("@chemical/title") ||
12
- this.getAttribute("data-title")
13
- : this.getAttribute("data-title");
14
-
15
- this.innerText = title;
16
- });
17
- }
18
- }
19
-
20
- class ChemicalIcon extends HTMLLinkElement {
21
- constructor() {
22
- super();
23
- }
24
- connectedCallback() {
25
- setTimeout(() => {
26
- this.setAttribute("data-icon", this.getAttribute("href"));
27
-
28
- const icon =
29
- this.dataset.iconStore !== undefined
30
- ? localStorage.getItem("@chemical/icon") ||
31
- this.getAttribute("data-icon")
32
- : this.getAttribute("data-icon");
33
-
34
- this.setAttribute("href", icon);
35
- });
36
- }
37
- }
38
-
39
- customElements.define("chemical-title", ChemicalTitle, { extends: "title" });
40
- customElements.define("chemical-icon", ChemicalIcon, { extends: "link" });
41
-
42
- window.chemical.aboutBlank = function (url) {
43
- var page = window.open();
44
- page.document.body.innerHTML =
45
- `<iframe style="height:100%; width: 100%; border: none; position: fixed; top: 0; right: 0; left: 0; bottom: 0; border: none" sandbox="allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-same-origin allow-scripts allow-top-navigation allow-top-navigation-by-user-activation" src="` +
46
- url +
47
- `"></iframe>`;
48
- };
@@ -1,214 +0,0 @@
1
- class ChemicalInput extends HTMLInputElement {
2
- constructor() {
3
- super();
4
- }
5
- connectedCallback() {
6
- this.addEventListener("keydown", async function (e) {
7
- if (e.key === "Enter" && window.chemical.loaded && e.target.value) {
8
- let service =
9
- this.dataset.serviceStore !== undefined
10
- ? localStorage.getItem("@chemical/service") ||
11
- this.dataset.service ||
12
- "uv"
13
- : this.dataset.service || "uv";
14
- let autoHttps =
15
- this.dataset.autoHttpsStore !== undefined
16
- ? localStorage.getItem("@chemical/autoHttps") === "true"
17
- : this.dataset.autoHttps !== undefined
18
- ? true
19
- : false;
20
- let searchEngine =
21
- this.dataset.searchEngineStore !== undefined
22
- ? localStorage.getItem("@chemical/searchEngine") || this.dataset.searchEngine
23
- : this.dataset.searchEngine;
24
- let action = this.dataset.action;
25
- let target = this.dataset.target;
26
- let frame = this.dataset.frame;
27
- let encodedURL = await chemical.encode(e.target.value, {
28
- service,
29
- autoHttps,
30
- searchEngine,
31
- });
32
-
33
- if (frame) {
34
- let forFrame = document.getElementById(frame);
35
- forFrame.src = encodedURL;
36
- forFrame.setAttribute("data-open", "true");
37
- }
38
-
39
- if (action) {
40
- window[action](encodedURL);
41
- }
42
-
43
- if (target) {
44
- if (target === "_self") {
45
- window.location = encodedURL;
46
- } else if (target === "_blank") {
47
- window.open(encodedURL);
48
- }
49
- }
50
- }
51
- });
52
- }
53
- }
54
-
55
- class ChemicalButton extends HTMLButtonElement {
56
- constructor() {
57
- super();
58
- }
59
- connectedCallback() {
60
- this.addEventListener("click", function (e) {
61
- let forInput = document.getElementById(this.dataset.for);
62
-
63
- if (forInput) {
64
- forInput.dispatchEvent(
65
- new KeyboardEvent("keydown", {
66
- key: "Enter",
67
- })
68
- );
69
- }
70
- });
71
- }
72
- }
73
-
74
- class ChemicalIFrame extends HTMLIFrameElement {
75
- static observedAttributes = ["data-open"];
76
- constructor() {
77
- super();
78
- }
79
- connectedCallback() {
80
- let open = this.dataset.open;
81
- this.style.display = open === "true" ? "" : "none";
82
- }
83
- attributeChangedCallback(name, oldValue, newValue) {
84
- if (name === "data-open") {
85
- let open = this.dataset.open;
86
- this.style.display = open === "true" ? "" : "none";
87
-
88
- let controls = document.getElementById(this.dataset.controls);
89
-
90
- if (controls) {
91
- controls.dataset.open = open;
92
- }
93
- }
94
- }
95
- }
96
-
97
- class ChemicalControls extends HTMLElement {
98
- static observedAttributes = ["data-open"];
99
- constructor() {
100
- super();
101
- }
102
- connectedCallback() {
103
- let open = this.dataset.open;
104
- this.style.display = open === "true" ? "" : "none";
105
- }
106
- attributeChangedCallback(name, oldValue, newValue) {
107
- if (name === "data-open") {
108
- let open = this.dataset.open;
109
- this.style.display = open === "true" ? "" : "none";
110
- }
111
- }
112
- }
113
-
114
- class ChemicalLink extends HTMLAnchorElement {
115
- constructor() {
116
- super();
117
- }
118
- async connectedCallback() {
119
- let href = this.dataset.href;
120
- let service = this.dataset.service || "uv";
121
- let autoHttps = this.dataset.autoHttps !== undefined ? true : false;
122
- let searchEngine = this.dataset.searchEngine;
123
- this.dataset.chemicalLoading = "true";
124
-
125
- if (window.chemical.loaded) {
126
- this.setAttribute(
127
- "href",
128
- await chemical.encode(href, {
129
- service,
130
- autoHttps,
131
- searchEngine,
132
- })
133
- );
134
- this.dataset.chemicalLoading = "false";
135
- } else {
136
- window.addEventListener("chemicalLoaded", async () => {
137
- this.setAttribute(
138
- "href",
139
- await chemical.encode(href, {
140
- service,
141
- autoHttps,
142
- searchEngine,
143
- })
144
- );
145
- this.dataset.chemicalLoading = "false";
146
- });
147
- }
148
- }
149
- }
150
-
151
- class ChemicalSelect extends HTMLSelectElement {
152
- constructor() {
153
- super();
154
- }
155
- connectedCallback() {
156
- const store = this.dataset.defaultStore;
157
-
158
- this.addEventListener("change", function () {
159
- window.chemical.setStore(store, this.value);
160
- });
161
-
162
- if (store) {
163
- const value = window.chemical.getStore(store);
164
-
165
- const observerOptions = {
166
- childList: true,
167
- subtree: false,
168
- };
169
-
170
- const observer = new MutationObserver((records, observer) => {
171
- for (const record of records) {
172
- for (const addedNode of record.addedNodes) {
173
- if (addedNode.tagName === "OPTION") {
174
- if (addedNode.getAttribute("value") === value) {
175
- addedNode.setAttribute("selected", "");
176
- }
177
- }
178
- }
179
- }
180
- });
181
- observer.observe(this, observerOptions);
182
- }
183
- }
184
- }
185
-
186
- customElements.define("chemical-input", ChemicalInput, { extends: "input" });
187
- customElements.define("chemical-button", ChemicalButton, { extends: "button" });
188
- customElements.define("chemical-iframe", ChemicalIFrame, { extends: "iframe" });
189
- customElements.define("chemical-controls", ChemicalControls, {
190
- extends: "section",
191
- });
192
- customElements.define("chemical-link", ChemicalLink, { extends: "a" });
193
- customElements.define("chemical-select", ChemicalSelect, { extends: "select" });
194
-
195
- window.chemical.componentAction = function (action, frameID) {
196
- let frame = document.getElementById(frameID);
197
-
198
- if (frame) {
199
- switch (action) {
200
- case "back":
201
- frame.contentWindow.history.back();
202
- break;
203
- case "forward":
204
- frame.contentWindow.history.forward();
205
- break;
206
- case "reload":
207
- frame.contentWindow.location.reload();
208
- break;
209
- case "close":
210
- frame.dataset.open = "false";
211
- frame.src = "";
212
- }
213
- }
214
- };
@@ -1,43 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>Demo Site</title>
7
- <style>
8
- *,
9
- *::before,
10
- *::after {
11
- box-sizing: border-box;
12
- }
13
-
14
- body {
15
- margin: 0;
16
- min-height: 100vh;
17
- display: flex;
18
- flex-direction: column;
19
- justify-content: center;
20
- align-items: center;
21
- font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji",
22
- "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
23
- background: #e5e5e5;
24
- color: black;
25
- }
26
-
27
- @media (prefers-color-scheme: dark) {
28
- body {
29
- background: #0a0a0a;
30
- color: #fafafa;
31
- }
32
- }
33
-
34
- h1 {
35
- margin: 0;
36
- }
37
- </style>
38
- </head>
39
- <body>
40
- <h1>Demo Site</h1>
41
- <p>This is a demo site without proxy functionality.</p>
42
- </body>
43
- </html>