import type { Server } from "node:http"; import type { AddressInfo } from "node:net"; import express from "express"; import type { ResolvedBrowserConfig } from "./config.js"; import { registerBrowserRoutes } from "./routes/index.js"; import type { BrowserRouteRegistrar } from "./routes/types.js"; import { type BrowserServerState, createBrowserRouteContext, type ProfileContext, } from "./server-context.js"; export type BrowserBridge = { server: Server; port: number; baseUrl: string; state: BrowserServerState; }; export async function startBrowserBridgeServer(params: { resolved: ResolvedBrowserConfig; host?: string; port?: number; authToken?: string; onEnsureAttachTarget?: (profile: ProfileContext["profile"]) => Promise; }): Promise { const host = params.host ?? "127.0.0.1"; const port = params.port ?? 0; const app = express(); app.use(express.json({ limit: "1mb" })); const authToken = params.authToken?.trim(); if (authToken) { app.use((req, res, next) => { const auth = String(req.headers.authorization ?? "").trim(); if (auth === `Bearer ${authToken}`) return next(); res.status(401).send("Unauthorized"); }); } const state: BrowserServerState = { server: null as unknown as Server, port, resolved: params.resolved, profiles: new Map(), }; const ctx = createBrowserRouteContext({ getState: () => state, onEnsureAttachTarget: params.onEnsureAttachTarget, }); registerBrowserRoutes(app as unknown as BrowserRouteRegistrar, ctx); const server = await new Promise((resolve, reject) => { const s = app.listen(port, host, () => resolve(s)); s.once("error", reject); }); const address = server.address() as AddressInfo | null; const resolvedPort = address?.port ?? port; state.server = server; state.port = resolvedPort; state.resolved.controlPort = resolvedPort; const baseUrl = `http://${host}:${resolvedPort}`; return { server, port: resolvedPort, baseUrl, state }; } export async function stopBrowserBridgeServer(server: Server): Promise { await new Promise((resolve) => { server.close(() => resolve()); }); }