aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatarLarge Libravatar memdmp <memdmpestrogenzone>2025-02-24 01:09:00 +0100
committerLibravatarLarge Libravatar memdmp <memdmpestrogenzone>2025-02-24 01:09:00 +0100
commit54a41f2431c3d60f5845a15447f13413299e41f2 (patch)
treef9395bb35ae4223a8ee944299ce430168de4d657 /src
downloadhttptool-54a41f2431c3d60f5845a15447f13413299e41f2.tar.gz
httptool-54a41f2431c3d60f5845a15447f13413299e41f2.tar.bz2
httptool-54a41f2431c3d60f5845a15447f13413299e41f2.tar.lz
httptool-54a41f2431c3d60f5845a15447f13413299e41f2.zip

feat: da extension

Diffstat (limited to 'src')
-rw-r--r--src/app.css9
-rw-r--r--src/app.d.ts17
-rw-r--r--src/app.html12
-rw-r--r--src/background.ts67
-rw-r--r--src/lib/index.ts1
-rw-r--r--src/routes/+layout.svelte20
-rw-r--r--src/routes/+layout.ts2
-rw-r--r--src/routes/+page.svelte42
-rw-r--r--src/routes/Monaco.svelte144
-rw-r--r--src/routes/browser.d.ts4
-rw-r--r--src/routes/userland.d.ts4
11 files changed, 322 insertions, 0 deletions
diff --git a/src/app.css b/src/app.css
new file mode 100644
index 0000000..3e1f888
--- /dev/null
+++ b/src/app.css
@@ -0,0 +1,9 @@
+@import 'tailwindcss';
+html {
+ background: var(--bg, #23222b);
+ color: var(--fg, #dedede);
+}
+/* html.light {
+ background: #dedede;
+ color: #191919;
+} */
diff --git a/src/app.d.ts b/src/app.d.ts
new file mode 100644
index 0000000..c689736
--- /dev/null
+++ b/src/app.d.ts
@@ -0,0 +1,17 @@
+// See https://svelte.dev/docs/kit/types#app.d.ts
+// for information about these interfaces
+
+import Browser from 'webextension-polyfill';
+declare const browser: typeof Browser;
+
+declare global {
+ namespace App {
+ // interface Error {}
+ // interface Locals {}
+ // interface PageData {}
+ // interface PageState {}
+ // interface Platform {}
+ }
+}
+
+export {};
diff --git a/src/app.html b/src/app.html
new file mode 100644
index 0000000..77a5ff5
--- /dev/null
+++ b/src/app.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8" />
+ <link rel="icon" href="%sveltekit.assets%/favicon.png" />
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+ %sveltekit.head%
+ </head>
+ <body data-sveltekit-preload-data="hover">
+ <div style="display: contents">%sveltekit.body%</div>
+ </body>
+</html>
diff --git a/src/background.ts b/src/background.ts
new file mode 100644
index 0000000..65d7187
--- /dev/null
+++ b/src/background.ts
@@ -0,0 +1,67 @@
+import browser from 'webextension-polyfill';
+import type { UserlandBrowser } from './routes/userland';
+
+console.debug('Loading Extension');
+setTimeout(async () => {
+ const { sc } = await browser.storage.local.get('sc');
+ if (typeof sc === 'undefined') {
+ console.warn('No value set!');
+ } else if (typeof sc !== 'string') {
+ throw new Error('typeof sc is not str nor undefined');
+ } else {
+ console.debug('Found Script (length', sc.length, ':3)');
+ console.debug('Loading esbuild-wasm');
+ const { initialize, transform } = await import('esbuild-wasm').catch(
+ (e) => {
+ console.error('Failed to load esbuild-wasm!');
+ throw e;
+ }
+ );
+ console.debug("Loading esbuild's WASM Module");
+ const wasmModule = new WebAssembly.Module(
+ await fetch('./generated/esbuild.wasm')
+ .catch((e) => {
+ console.error('Failed to fetch() the esbuild wasm file.');
+ throw e;
+ })
+ .then((v) => v.arrayBuffer())
+ );
+ console.debug('Attempting to initialize esbuild-wasm');
+ try {
+ await initialize({
+ wasmModule,
+ worker: false,
+ });
+ } catch (error) {
+ console.error(error);
+ throw new Error('Failed to initialize esbuild - see above error');
+ }
+ console.debug('Transforming Content');
+ const built = await transform(sc, {
+ loader: 'ts',
+ });
+ console.debug('Got result', built);
+ const b = browser;
+ const brow: UserlandBrowser = {
+ get webRequest() {
+ return b.webRequest;
+ },
+ };
+ const f = new Function(
+ 'browser',
+ built.code.includes('await')
+ ? `return (async()=>{${built.code}
+})();`
+ : built.code
+ );
+ console.debug('Environment Information:', {
+ func: f,
+ browser: brow,
+ realBrowser: b,
+ });
+ (globalThis as any).browser = brow;
+ console.debug('Evaluating...');
+ await f(brow);
+ console.debug('Function Exited');
+ }
+}, 0);
diff --git a/src/lib/index.ts b/src/lib/index.ts
new file mode 100644
index 0000000..856f2b6
--- /dev/null
+++ b/src/lib/index.ts
@@ -0,0 +1 @@
+// place files you want to import through the `$lib` alias in this folder.
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte
new file mode 100644
index 0000000..b0e82a6
--- /dev/null
+++ b/src/routes/+layout.svelte
@@ -0,0 +1,20 @@
+<script lang="ts">
+ import { onDestroy, onMount } from 'svelte';
+ import '../app.css';
+ let { children } = $props();
+
+ let mounted = $state(false);
+ onMount(async () => {
+ (globalThis as any).browser = (
+ await import('webextension-polyfill')
+ ).default;
+ mounted = true;
+ });
+ onDestroy(() => (mounted = false));
+</script>
+
+{#if mounted}
+ {@render children()}
+{:else}
+ <p>Waiting on Mount</p>
+{/if}
diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts
new file mode 100644
index 0000000..748cfd9
--- /dev/null
+++ b/src/routes/+layout.ts
@@ -0,0 +1,2 @@
+export const prerender = true;
+export const csr = true;
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
new file mode 100644
index 0000000..7320c2a
--- /dev/null
+++ b/src/routes/+page.svelte
@@ -0,0 +1,42 @@
+<script lang="ts">
+ import { onMount } from 'svelte';
+ import Monaco from './Monaco.svelte';
+ let content_script = $state('');
+ let default_value = $state(`// Do not put untrusted content here!
+// See https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Intercept_HTTP_requests and https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/webRequest for a tutorial on how to handle things.
+// Content saves on every keystroke due to laziness.
+// Reload (disable & re-enable) extension or restart browser to apply changes.
+`);
+ let mounted = $state(false);
+ onMount(async () => {
+ const s = (await browser.storage.local.get('sc')).sc;
+ default_value = s && `${s}`.length ? `${s}` : default_value;
+ content_script = default_value ?? '';
+ setTimeout(() => {
+ mounted = true;
+ setTimeout(() => {
+ content_script = default_value;
+ }, 0);
+ }, 0);
+ });
+ $effect(() => {
+ (async (cs: string) => {
+ if (mounted) {
+ await browser.storage.local.set({ sc: cs });
+ if (browser.runtime.lastError) {
+ console.warn('Last Error is ', browser.runtime.lastError.message);
+ browser.runtime.lastError = undefined;
+ }
+ }
+ })(content_script);
+ });
+</script>
+
+{#if mounted}
+ <Monaco
+ defaultValue={default_value}
+ bind:value={content_script}
+ />
+{:else}
+ Getting Storage
+{/if}
diff --git a/src/routes/Monaco.svelte b/src/routes/Monaco.svelte
new file mode 100644
index 0000000..efdd67a
--- /dev/null
+++ b/src/routes/Monaco.svelte
@@ -0,0 +1,144 @@
+<script lang="ts">
+ import type monaco from 'monaco-editor';
+ import { onDestroy, onMount } from 'svelte';
+ import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
+ import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
+ import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker';
+ import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker';
+ import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';
+ // @ts-ignore
+ import wrqTypes from '@types/webextension-polyfill/namespaces/webRequest.d.ts?raw';
+ // @ts-ignore
+ import evTypes from '@types/webextension-polyfill/namespaces/events.d.ts?raw';
+ import userland from './userland.d.ts?raw';
+
+ let divEl: HTMLDivElement | null = $state(null);
+ let editor: monaco.editor.IStandaloneCodeEditor = $state(null as any);
+ let Monaco: typeof monaco;
+ let {
+ defaultValue = `processRequest = (rq) => rq;
+processResponse = (rs) => rs;
+`,
+ typeDefs = `import type { UserlandBrowser } from './userland';
+declare global {
+ /**
+ * The subset of the host extension's browser type available to the extension
+ * Note: We don't properly sandbox anything. You can likely easily get access to shit outside of here from the browser global.
+ */
+ declare const browser: UserlandBrowser;
+}
+`,
+ value = $bindable(''),
+ }: {
+ value?: string;
+ defaultValue?: string;
+ typeDefs?: string;
+ } = $props();
+ let writeDebounce = false;
+
+ onMount(async () => {
+ // @ts-ignore
+ globalThis.MonacoEnvironment = {
+ getWorker: function (_moduleId: any, label: string) {
+ if (label === 'json') {
+ return new jsonWorker();
+ }
+ if (label === 'css' || label === 'scss' || label === 'less') {
+ return new cssWorker();
+ }
+ if (label === 'html' || label === 'handlebars' || label === 'razor') {
+ return new htmlWorker();
+ }
+ if (label === 'typescript' || label === 'javascript') {
+ return new tsWorker();
+ }
+ return new editorWorker();
+ },
+ };
+
+ Monaco = await import('monaco-editor');
+ Monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
+ allowNonTsExtensions: true,
+ moduleResolution: Monaco.languages.typescript.ModuleResolutionKind.NodeJs,
+ module: Monaco.languages.typescript.ModuleKind.ESNext,
+ noEmit: true,
+ typeRoots: ['node_modules/@types'],
+ });
+ Monaco.editor.defineTheme('redirext', {
+ base: 'vs-dark',
+ inherit: true,
+ rules: [],
+ colors: {
+ 'editor.foreground': '#dedede',
+ 'editor.background': '#23222b',
+ 'editor.selectionBackground': '#4c2889',
+ 'editor.inactiveSelectionBackground': '#444d56',
+ 'editor.lineHighlightBackground': '#444d56',
+ 'editorCursor.foreground': '#ffffff',
+ 'editorWhitespace.foreground': '#6a737d',
+ 'editorIndentGuide.background': '#6a737d',
+ 'editorIndentGuide.activeBackground': '#f6f8fa',
+ 'editor.selectionHighlightBorder': '#444d56',
+ },
+ });
+ if (!divEl) while (!divEl) await new Promise((rs) => setTimeout(rs, 100));
+ Monaco.languages.typescript.typescriptDefaults.addExtraLib(
+ evTypes,
+ 'node_modules/@types/webextension-polyfill/namespaces/events.d.ts'
+ );
+ Monaco.languages.typescript.typescriptDefaults.addExtraLib(
+ wrqTypes,
+ 'node_modules/@types/webextension-polyfill/namespaces/webRequest.d.ts'
+ );
+ Monaco.languages.typescript.typescriptDefaults.addExtraLib(
+ `export * from './namespaces/webRequest';`,
+ 'node_modules/@types/webextension-polyfill/index.d.ts'
+ );
+ Monaco.languages.typescript.typescriptDefaults.addExtraLib(
+ userland,
+ 'node_modules/@types/redirext/userland.d.ts'
+ );
+ Monaco.languages.typescript.typescriptDefaults.addExtraLib(
+ typeDefs,
+ 'node_modules/@types/redirext/index.d.ts'
+ );
+ editor = Monaco.editor.create(divEl, {
+ value: defaultValue,
+ language: 'typescript',
+ theme: 'redirext',
+ autoDetectHighContrast: false,
+ });
+ editor.getModel()?.onDidChangeContent((e) => {
+ writeDebounce = true;
+ const upd = () => {
+ try {
+ value =
+ editor.getValue({ lineEnding: '\n', preserveBOM: false }).trim() +
+ '\n';
+ // console.debug('Updated value from editor to:', value);
+ } catch (error) {
+ writeDebounce = false;
+ throw error;
+ }
+ setTimeout(() => {
+ writeDebounce = false;
+ }, 0);
+ };
+ setTimeout(upd, 0);
+ });
+ });
+ $effect(() => {
+ if (editor && !writeDebounce) {
+ // console.debug('Updating editor with value', value);
+ editor.setValue(value.trim() + '\n');
+ }
+ });
+ onDestroy(() => {
+ if (editor) editor.dispose();
+ });
+</script>
+
+<div
+ bind:this={divEl}
+ class="h-screen overflow-hidden bg-card box-border max-h-full"
+></div>
diff --git a/src/routes/browser.d.ts b/src/routes/browser.d.ts
new file mode 100644
index 0000000..ed54792
--- /dev/null
+++ b/src/routes/browser.d.ts
@@ -0,0 +1,4 @@
+import Browser from 'webextension-polyfill';
+declare global {
+ declare const browser: typeof Browser;
+}
diff --git a/src/routes/userland.d.ts b/src/routes/userland.d.ts
new file mode 100644
index 0000000..7b659b8
--- /dev/null
+++ b/src/routes/userland.d.ts
@@ -0,0 +1,4 @@
+import type { WebRequest } from 'webextension-polyfill';
+export type UserlandBrowser = {
+ get webRequest(): WebRequest.Static;
+};