import type { Page } from "playwright"; export type NetworkTraceEntry = { kind: "req" | "resp"; url: string; method?: string; status?: number; contentType?: string; }; export function isNetworkVisibilityEnabled(): boolean { return process.env.RPA_QUOTE_NETWORK_VISIBILITY === "true"; } export type NetworkVisibilityHandle = { trace: NetworkTraceEntry[]; detach: () => void; logSummary: (label: string) => void; }; /** submit 窗口内记录全部 REQ/RESP(RPA_QUOTE_NETWORK_VISIBILITY=true) */ export function attachNetworkVisibility(page: Page): NetworkVisibilityHandle { const trace: NetworkTraceEntry[] = []; const verbose = isNetworkVisibilityEnabled(); const onRequest = (request: { url: () => string; method: () => string; }): void => { const url = request.url(); const method = request.method(); trace.push({ kind: "req", url, method }); if (verbose) { console.log(`[rpa] REQ: ${method} ${url}`); } }; const onResponse = (response: { url: () => string; status: () => number; headers: () => Record; }): void => { const url = response.url(); const status = response.status(); const contentType = response.headers()["content-type"] ?? ""; trace.push({ kind: "resp", url, status, contentType }); if (verbose) { console.log(`[rpa] RESP: ${status} ${url} ct=${contentType}`); } }; page.on("request", onRequest); page.on("response", onResponse); return { trace, detach: () => { page.off("request", onRequest); page.off("response", onResponse); }, logSummary: (label: string) => { const reqs = trace.filter((t) => t.kind === "req"); const resps = trace.filter((t) => t.kind === "resp"); const quoteLike = trace.filter( (t) => /quote|rate|graphql|pricing|mothership\.com\/api/i.test(t.url) && !/segment|analytics|hotjar|sentry/i.test(t.url), ); console.log( `[rpa] quote-network ${label}: req=${reqs.length} resp=${resps.length} quoteLike=${quoteLike.length}`, ); if (quoteLike.length > 0) { for (const entry of quoteLike.slice(0, 30)) { const extra = entry.kind === "resp" ? ` status=${entry.status} ct=${entry.contentType ?? ""}` : ` method=${entry.method ?? ""}`; console.log(`[rpa] quote-network hit: ${entry.kind} ${entry.url}${extra}`); } } else if (verbose && trace.length > 0) { for (const entry of trace.slice(-40)) { console.log( `[rpa] quote-network trace: ${entry.kind} ${entry.url}`, ); } } }, }; }