diff --git a/packages/producer/src/services/render/stages/probeFailures.test.ts b/packages/producer/src/services/render/stages/probeFailures.test.ts new file mode 100644 index 0000000000..f26c3418d8 --- /dev/null +++ b/packages/producer/src/services/render/stages/probeFailures.test.ts @@ -0,0 +1,22 @@ +import { describe, expect, it } from "bun:test"; +import { isActionableProbeFailure } from "./probeFailures.js"; + +describe("isActionableProbeFailure", () => { + it("ignores favicon 404 noise without hiding real asset failures", () => { + expect( + isActionableProbeFailure( + "[Browser:HTTP404] GET http://127.0.0.1:4173/favicon.ico resource=image", + ), + ).toBe(false); + expect( + isActionableProbeFailure( + "[Browser:HTTP404] GET http://127.0.0.1:4173/assets/missing.png resource=image", + ), + ).toBe(true); + expect( + isActionableProbeFailure( + "[Browser:REQUESTFAILED] GET http://127.0.0.1:4173/assets/app.js resource=script error=net::ERR_FAILED", + ), + ).toBe(true); + }); +}); diff --git a/packages/producer/src/services/render/stages/probeFailures.ts b/packages/producer/src/services/render/stages/probeFailures.ts new file mode 100644 index 0000000000..4c0ee80d41 --- /dev/null +++ b/packages/producer/src/services/render/stages/probeFailures.ts @@ -0,0 +1,6 @@ +const NETWORK_FAILURE_PATTERN = /404|ERR_NAME_NOT_RESOLVED|ERR_CONNECTION_REFUSED|net::ERR_/i; +const FAVICON_PATTERN = /(?:^|[/?])favicon(?:\.[a-z0-9]+)?(?:[?#\s]|$)/i; + +export function isActionableProbeFailure(line: string): boolean { + return NETWORK_FAILURE_PATTERN.test(line) && !FAVICON_PATTERN.test(line); +} diff --git a/packages/producer/src/services/render/stages/probeStage.ts b/packages/producer/src/services/render/stages/probeStage.ts index 2313a6a916..0c7510f3fd 100644 --- a/packages/producer/src/services/render/stages/probeStage.ts +++ b/packages/producer/src/services/render/stages/probeStage.ts @@ -59,6 +59,7 @@ import { type CompositionMetadata, } from "../shared.js"; import type { RenderJob } from "../../renderOrchestrator.js"; +import { isActionableProbeFailure } from "./probeFailures.js"; export interface ProbeStageInput { projectDir: string; @@ -643,9 +644,7 @@ export async function runProbeStage(input: ProbeStageInput): Promise - /404|ERR_NAME_NOT_RESOLVED|ERR_CONNECTION_REFUSED|net::ERR_/i.test(line), - ); + const failedRequests = probeSession.browserConsoleBuffer.filter(isActionableProbeFailure); if (failedRequests.length > 0) { log.warn("Browser encountered network failures during page load:", { failures: failedRequests.slice(0, 10),