UNPKG

browse

Version:

Unified Browserbase CLI for browser automation and cloud APIs.

108 lines (107 loc) 3.75 kB
// Deterministic reducer for `browse cloud sessions logs --only-errors`. // Turns the raw CDP firehose (~hundreds of events per session) into the high-signal error slice: // console errors/warnings/asserts, uncaught exceptions, HTTP 4xx/5xx responses, and net-level load // failures. No LLM — pure allowlist + severity/status filter + field projection + dedupe + stack trim. function paramsOf(e) { try { const parsed = JSON.parse(e.request?.rawBody ?? "{}"); return parsed.params ?? {}; } catch { return {}; } } // Keep the message line + the app (`/src/`) stack frames; drop framework/vendor frames and hosts. function trimStack(s) { return (s || "") .split("\n") .filter((l, i) => i === 0 || (/\/src\//.test(l) && !/node_modules|\.vite/.test(l))) .slice(0, 4) .map((l) => l .replace(/https?:\/\/[^/)]+/g, "") .replace(/\?[^):]*/, "") .trim()) .join("\n"); } export function reduceLogs(raw, opts = {}) { const out = []; const seen = new Set(); const push = (rec) => { const k = JSON.stringify(rec); if (!seen.has(k)) { seen.add(k); out.push(rec); } }; for (const e of raw) { const p = paramsOf(e); const m = e.method; const responseStatus = p.response?.status; let rec = null; if (m === "Runtime.consoleAPICalled" && typeof p.type === "string" && ["error", "warning", "assert"].includes(p.type)) { const text = (Array.isArray(p.args) ? p.args : []) .map((a) => a && typeof a === "object" ? a.description || a.value || "" : "") .filter(Boolean) .join(" "); if (text && !/^%[os]/.test(text)) rec = { kind: `console.${p.type}`, domain: "Runtime", severity: p.type, text: trimStack(text), }; } else if (m === "Runtime.exceptionThrown") { rec = { kind: "exception", domain: "Runtime", severity: "error", text: trimStack(p.exceptionDetails?.exception?.description ?? p.exceptionDetails?.text ?? ""), }; } else if (m === "Log.entryAdded" && typeof p.entry?.level === "string" && ["error", "warning"].includes(p.entry?.level)) { rec = { kind: `log.${p.entry.level}`, domain: "Log", severity: p.entry.level, text: p.entry.text, url: p.entry.url, }; } else if (m === "Network.responseReceived" && typeof responseStatus === "number" && responseStatus >= 400) { rec = { kind: "network", domain: "Network", status: responseStatus, url: p.response?.url, type: p.type, }; } else if (m === "Network.loadingFailed" && p.errorText !== "net::ERR_ABORTED") { rec = { kind: "network.failed", domain: "Network", error: p.errorText, type: p.type, }; } else { continue; // everything else (byte-chunk / lifecycle events) is noise } if (!rec) continue; // e.g. a console.error whose text was empty / formatting noise if (opts.failedRequests && rec.domain !== "Network") continue; push(rec); } return out; }