change background.js into split typescript files for better managemand and build background.js from typescript

This commit is contained in:
2026-05-01 19:28:36 +02:00
parent fb78fd0471
commit 7ee664153b
17 changed files with 2261 additions and 1636 deletions
+246
View File
@@ -0,0 +1,246 @@
// @ts-nocheck
/**
* browser-cli Extension — Background Service Worker
*
* Connects to the native host (com.browsercli.host) via Native Messaging.
*/
import { getProfileAlias } from './core';
import * as nav from './commands/navigation';
import * as tabs from './commands/tabs';
import * as groups from './commands/groups';
import * as windowsCmd from './commands/windows';
import * as dom from './commands/dom';
import * as browserData from './commands/browser-data';
import * as session from './commands/session';
const NATIVE_HOST = "com.browsercli.host";
let port = null;
let keepaliveEnabled = true;
// ── Connection management ─────────────────────────────────────────────────────
function sendControlMessage(targetPort, message) {
if (!targetPort) return;
try {
targetPort.postMessage(message);
} catch (e) {
console.warn("[browser-cli] Failed to send control message:", e);
}
}
function disconnectPort({ sendBye = false } = {}) {
const currentPort = port;
if (!currentPort) return;
if (sendBye) sendControlMessage(currentPort, { type: "bye" });
if (port === currentPort) port = null;
try {
currentPort.disconnect();
} catch (e) {
console.warn("[browser-cli] Failed to disconnect native port:", e);
}
}
async function connect() {
if (port || !keepaliveEnabled) return;
try {
const nativePort = chrome.runtime.connectNative(NATIVE_HOST);
port = nativePort;
nativePort.onMessage.addListener(onMessage);
nativePort.onDisconnect.addListener(() => {
if (port === nativePort) port = null;
const err = chrome.runtime.lastError;
if (err) console.warn("[browser-cli] Native host disconnected:", err.message);
});
// Send hello so native host knows which profile/alias this is
const alias = await getProfileAlias();
nativePort.postMessage({ type: "hello", alias });
console.log("[browser-cli] Connected to native host as profile:", alias);
} catch (e) {
port = null;
console.error("[browser-cli] Failed to connect:", e);
}
}
chrome.runtime.onInstalled.addListener(connect);
chrome.runtime.onStartup.addListener(connect);
chrome.runtime.onSuspend.addListener(() => {
disconnectPort({ sendBye: true });
});
chrome.windows.onCreated.addListener(() => {
keepaliveEnabled = true;
if (!port) connect();
});
chrome.windows.onRemoved.addListener(async () => {
const windows = await chrome.windows.getAll({});
if (windows.length > 0) return;
keepaliveEnabled = false;
disconnectPort({ sendBye: true });
});
// Keepalive alarm — prevents service worker suspension and reconnects if needed
chrome.alarms.create("keepalive", { periodInMinutes: 0.4 });
chrome.alarms.onAlarm.addListener((alarm) => {
if (alarm.name === "keepalive") {
if (!port && keepaliveEnabled) connect();
}
});
// ── Message dispatcher ────────────────────────────────────────────────────────
async function onMessage(msg) {
const { id, command, args } = msg;
if (!id || !command) return;
console.log("[browser-cli] ←", command, args);
let data, error;
try {
const { __page, ...commandArgs } = args || {};
data = await dispatch(command, commandArgs);
if (__page && Array.isArray(data)) {
data = makePagedData(data, __page);
}
} catch (e) {
error = e.message || String(e);
}
if (error !== undefined) {
console.log("[browser-cli] → ERROR", command, error);
port.postMessage({ id, success: false, error });
} else {
console.log("[browser-cli] →", command, data);
port.postMessage({ id, success: true, data });
}
if (command === "clients.rename_profile" && error === undefined) {
disconnectPort({ sendBye: true });
keepaliveEnabled = true;
await connect();
}
}
function makePagedData(items, page) {
const total = items.length;
const offset = Math.max(0, Number(page.offset) || 0);
const requestedLimit = Math.max(1, Number(page.limit) || 100);
const limit = Math.min(requestedLimit, 1000);
const end = Math.min(offset + limit, total);
return {
__browserCliPage: true,
items: items.slice(offset, end),
offset,
limit,
total,
nextOffset: end < total ? end : null,
};
}
async function dispatch(command, args) {
switch (command) {
// ── Navigation ────────────────────────────────────────────────────────
case "navigate.open": return nav.navOpen(args);
case "navigate.to": return nav.navTo(args);
case "navigate.reload": return nav.navReload(args, false);
case "navigate.hard_reload": return nav.navReload(args, true);
case "navigate.back": return nav.navBack(args);
case "navigate.forward": return nav.navForward(args);
case "navigate.focus": return nav.navFocus(args);
case "navigate.wait": return nav.navWait(args);
case "navigate.open_wait": return nav.navOpenWait(args);
// ── Tabs ──────────────────────────────────────────────────────────────
case "tabs.list": return tabs.tabsList();
case "tabs.close": return tabs.tabsClose(args);
case "tabs.move": return tabs.tabsMove(args);
case "tabs.active": return tabs.tabsActive(args);
case "tabs.active_in_window": return tabs.tabsActiveInWindow(args);
case "tabs.status": return tabs.tabsStatus(args);
case "tabs.filter": return tabs.tabsFilter(args);
case "tabs.count": return tabs.tabsCount(args);
case "tabs.query": return tabs.tabsQuery(args);
case "tabs.html": return tabs.tabsHtml(args);
case "tabs.dedupe": return tabs.tabsDedupe();
case "tabs.sort": return tabs.tabsSort(args);
case "tabs.merge_windows": return tabs.tabsMergeWindows();
case "tabs.mute": return tabs.tabsMute(args);
case "tabs.unmute": return tabs.tabsUnmute(args);
case "tabs.pin": return tabs.tabsPin(args);
case "tabs.unpin": return tabs.tabsUnpin(args);
case "tabs.screenshot": return tabs.tabsScreenshot(args);
case "tabs.watch_url": return tabs.tabsWatchUrl(args);
// ── Groups ────────────────────────────────────────────────────────────
case "group.list": return groups.groupList();
case "group.tabs": return groups.groupTabs(args);
case "group.count": return groups.groupCount();
case "group.query": return groups.groupQuery(args);
case "group.close": return groups.groupClose(args);
case "group.open": return groups.groupOpen(args);
case "group.add_tab": return groups.groupAddTab(args);
case "group.move": return groups.groupMove(args);
// ── Windows ───────────────────────────────────────────────────────────
case "windows.list": return windowsCmd.windowsList();
case "windows.rename": return windowsCmd.windowsRename(args);
case "windows.close": return windowsCmd.windowsClose(args);
case "windows.open": return windowsCmd.windowsOpen(args);
// ── DOM ───────────────────────────────────────────────────────────────
case "dom.query": return dom.domOp("domQuery", args);
case "dom.click": return dom.domOp("domClick", args);
case "dom.type": return dom.domOp("domType", args);
case "dom.attr": return dom.domOp("domAttr", args);
case "dom.text": return dom.domOp("domText", args);
case "dom.exists": return dom.domOp("domExists", args);
case "dom.scroll": return dom.domOp("domScroll", args);
case "dom.select": return dom.domOp("domSelect", args);
case "dom.key": return dom.domOp("domKey", args);
case "dom.hover": return dom.domOp("domHover", args);
case "dom.check": return dom.domOp("domCheck", { ...args, checked: true });
case "dom.uncheck": return dom.domOp("domCheck", { ...args, checked: false });
case "dom.clear": return dom.domOp("domClear", args);
case "dom.focus": return dom.domOp("domFocus", args);
case "dom.submit": return dom.domOp("domSubmit", args);
case "dom.eval": return dom.domEval(args);
case "dom.wait_for": return dom.domWaitFor(args);
case "dom.poll": return dom.domPoll(args);
// ── Page ──────────────────────────────────────────────────────────────
case "page.info": return dom.domOp("pageInfo", {});
// ── Storage ───────────────────────────────────────────────────────────
case "storage.get": return browserData.storageGet(args);
case "storage.set": return browserData.storageSet(args);
// ── Cookies ───────────────────────────────────────────────────────────
case "cookies.list": return browserData.cookiesList(args);
case "cookies.get": return browserData.cookiesGet(args);
case "cookies.set": return browserData.cookiesSet(args);
// ── Extract ───────────────────────────────────────────────────────────
case "extract.links": return dom.domOp("extractLinks", args);
case "extract.images": return dom.domOp("extractImages", args);
case "extract.text": return dom.domOp("extractText", args);
case "extract.json": return dom.domOp("extractJson", args);
case "extract.markdown": return dom.domOp("extractMarkdown", args);
case "extract.html": return tabs.tabsHtml({});
// ── Session ───────────────────────────────────────────────────────────
case "session.save": return session.sessionSave(args);
case "session.load": return session.sessionLoad(args);
case "session.list": return session.sessionList();
case "session.remove": return session.sessionRemove(args);
case "session.diff": return session.sessionDiff(args);
case "session.auto_save": return session.sessionAutoSave(args);
// ── Misc ──────────────────────────────────────────────────────────────
case "clients.list": return session.clientsList();
case "clients.rename_profile": return session.clientsRenameProfile(args);
default:
throw new Error(`Unknown command: ${command}`);
}
}
// ── Navigation ────────────────────────────────────────────────────────────────