From 628104662bfc06a0f49dbd20f22d18a75ebebe7b Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Fri, 29 May 2026 10:19:51 +0200 Subject: [PATCH] refactor: share browser client request helpers --- extensions/browser/src/browser/client.ts | 121 ++++++++++++----------- 1 file changed, 63 insertions(+), 58 deletions(-) diff --git a/extensions/browser/src/browser/client.ts b/extensions/browser/src/browser/client.ts index 38b4cbc6810..69a458e851d 100644 --- a/extensions/browser/src/browser/client.ts +++ b/extensions/browser/src/browser/client.ts @@ -15,6 +15,54 @@ export type { BrowserDoctorCheck, BrowserDoctorReport } from "./doctor.js"; const BROWSER_STATUS_REQUEST_TIMEOUT_MS = 7_500; const BROWSER_DOCTOR_REQUEST_TIMEOUT_MS = 7_500; const BROWSER_DEEP_DOCTOR_REQUEST_TIMEOUT_MS = 10_000; +const JSON_HEADERS = { "Content-Type": "application/json" }; + +type BrowserClientTimeoutOptions = { + timeoutMs?: number; +}; + +type BrowserClientProfileOptions = BrowserClientTimeoutOptions & { + profile?: string; +}; + +function resolveBrowserClientTimeoutMs( + opts: BrowserClientTimeoutOptions | undefined, + fallbackMs: number, +): number { + return typeof opts?.timeoutMs === "number" && Number.isFinite(opts.timeoutMs) + ? Math.max(1, Math.floor(opts.timeoutMs)) + : fallbackMs; +} + +function withProfilePath(baseUrl: string | undefined, path: string, profile?: string): string { + return withBaseUrl(baseUrl, `${path}${buildProfileQuery(profile)}`); +} + +async function sendProfilePost( + baseUrl: string | undefined, + path: string, + opts: BrowserClientProfileOptions | undefined, + fallbackTimeoutMs: number, +): Promise { + await fetchBrowserJson(withProfilePath(baseUrl, path, opts?.profile), { + method: "POST", + timeoutMs: resolveBrowserClientTimeoutMs(opts, fallbackTimeoutMs), + }); +} + +async function sendTabTargetRequest(params: { + baseUrl: string | undefined; + path: string; + method: "POST" | "DELETE"; + opts: BrowserClientProfileOptions | undefined; + body?: object; +}): Promise { + await fetchBrowserJson(withProfilePath(params.baseUrl, params.path, params.opts?.profile), { + method: params.method, + ...(params.body ? { headers: JSON_HEADERS, body: JSON.stringify(params.body) } : {}), + timeoutMs: resolveBrowserClientTimeoutMs(params.opts, 5000), + }); +} export type ProfileStatus = { name: string; @@ -75,12 +123,8 @@ export async function browserStatus( baseUrl?: string, opts?: { profile?: string; timeoutMs?: number }, ): Promise { - const q = buildProfileQuery(opts?.profile); - return await fetchBrowserJson(withBaseUrl(baseUrl, `/${q}`), { - timeoutMs: - typeof opts?.timeoutMs === "number" && Number.isFinite(opts.timeoutMs) - ? Math.max(1, Math.floor(opts.timeoutMs)) - : BROWSER_STATUS_REQUEST_TIMEOUT_MS, + return await fetchBrowserJson(withProfilePath(baseUrl, "/", opts?.profile), { + timeoutMs: resolveBrowserClientTimeoutMs(opts, BROWSER_STATUS_REQUEST_TIMEOUT_MS), }); } @@ -110,10 +154,7 @@ export async function browserProfiles( const res = await fetchBrowserJson<{ profiles: ProfileStatus[] }>( withBaseUrl(baseUrl, `/profiles`), { - timeoutMs: - typeof opts?.timeoutMs === "number" && Number.isFinite(opts.timeoutMs) - ? Math.max(1, Math.floor(opts.timeoutMs)) - : 3000, + timeoutMs: resolveBrowserClientTimeoutMs(opts, 3000), }, ); return res.profiles ?? []; @@ -123,28 +164,14 @@ export async function browserStart( baseUrl?: string, opts?: { profile?: string; timeoutMs?: number }, ): Promise { - const q = buildProfileQuery(opts?.profile); - await fetchBrowserJson(withBaseUrl(baseUrl, `/start${q}`), { - method: "POST", - timeoutMs: - typeof opts?.timeoutMs === "number" && Number.isFinite(opts.timeoutMs) - ? Math.max(1, Math.floor(opts.timeoutMs)) - : 15000, - }); + await sendProfilePost(baseUrl, "/start", opts, 15000); } export async function browserStop( baseUrl?: string, opts?: { profile?: string; timeoutMs?: number }, ): Promise { - const q = buildProfileQuery(opts?.profile); - await fetchBrowserJson(withBaseUrl(baseUrl, `/stop${q}`), { - method: "POST", - timeoutMs: - typeof opts?.timeoutMs === "number" && Number.isFinite(opts.timeoutMs) - ? Math.max(1, Math.floor(opts.timeoutMs)) - : 15000, - }); + await sendProfilePost(baseUrl, "/stop", opts, 15000); } export async function browserResetProfile( @@ -186,7 +213,7 @@ export async function browserCreateProfile( withBaseUrl(baseUrl, `/profiles/create`), { method: "POST", - headers: { "Content-Type": "application/json" }, + headers: JSON_HEADERS, body: JSON.stringify({ name: opts.name, color: opts.color, @@ -222,14 +249,10 @@ export async function browserTabs( baseUrl?: string, opts?: { profile?: string; timeoutMs?: number }, ): Promise { - const q = buildProfileQuery(opts?.profile); const res = await fetchBrowserJson<{ running: boolean; tabs: BrowserTab[] }>( - withBaseUrl(baseUrl, `/tabs${q}`), + withProfilePath(baseUrl, "/tabs", opts?.profile), { - timeoutMs: - typeof opts?.timeoutMs === "number" && Number.isFinite(opts.timeoutMs) - ? Math.max(1, Math.floor(opts.timeoutMs)) - : 3000, + timeoutMs: resolveBrowserClientTimeoutMs(opts, 3000), }, ); return res.tabs ?? []; @@ -240,15 +263,11 @@ export async function browserOpenTab( url: string, opts?: { profile?: string; label?: string; timeoutMs?: number }, ): Promise { - const q = buildProfileQuery(opts?.profile); - return await fetchBrowserJson(withBaseUrl(baseUrl, `/tabs/open${q}`), { + return await fetchBrowserJson(withProfilePath(baseUrl, "/tabs/open", opts?.profile), { method: "POST", - headers: { "Content-Type": "application/json" }, + headers: JSON_HEADERS, body: JSON.stringify({ url, ...(opts?.label ? { label: opts.label } : {}) }), - timeoutMs: - typeof opts?.timeoutMs === "number" && Number.isFinite(opts.timeoutMs) - ? Math.max(1, Math.floor(opts.timeoutMs)) - : 15000, + timeoutMs: resolveBrowserClientTimeoutMs(opts, 15000), }); } @@ -257,16 +276,8 @@ export async function browserFocusTab( targetId: string, opts?: { profile?: string; timeoutMs?: number }, ): Promise { - const q = buildProfileQuery(opts?.profile); - await fetchBrowserJson(withBaseUrl(baseUrl, `/tabs/focus${q}`), { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ targetId }), - timeoutMs: - typeof opts?.timeoutMs === "number" && Number.isFinite(opts.timeoutMs) - ? Math.max(1, Math.floor(opts.timeoutMs)) - : 5000, - }); + const body = { targetId }; + await sendTabTargetRequest({ baseUrl, path: "/tabs/focus", method: "POST", opts, body }); } export async function browserCloseTab( @@ -274,14 +285,8 @@ export async function browserCloseTab( targetId: string, opts?: { profile?: string; timeoutMs?: number }, ): Promise { - const q = buildProfileQuery(opts?.profile); - await fetchBrowserJson(withBaseUrl(baseUrl, `/tabs/${encodeURIComponent(targetId)}${q}`), { - method: "DELETE", - timeoutMs: - typeof opts?.timeoutMs === "number" && Number.isFinite(opts.timeoutMs) - ? Math.max(1, Math.floor(opts.timeoutMs)) - : 5000, - }); + const path = `/tabs/${encodeURIComponent(targetId)}`; + await sendTabTargetRequest({ baseUrl, path, method: "DELETE", opts }); } export async function browserTabAction(