mirror of
https://github.com/morluto/rea
synced 2026-10-25 22:08:11 -04:00
5891bfb302
Distinguish CDP session detachment, observed crashes, and transport loss. Preserve the observed window alongside the original typed coverage-stop failure and unconfirmed cleanup. Validated with 40 focused boundary tests, check:ci, docs:check, and the real Chrome public CLI/MCP verifier on macOS. Browser timing comparison remains incomplete because unchanged-main fixture readiness was intermittent. Fixes #5756
65 lines
2.4 KiB
TypeScript
65 lines
2.4 KiB
TypeScript
import { AnalysisError } from "../../domain/analysisErrorBase.js";
|
|
import { AnalysisOutputError } from "../../domain/analysisErrorCore.js";
|
|
import { cdpTargetEventMatches } from "../CdpTargetEvents.js";
|
|
import { CdpExecutionWindow } from "./CdpExecutionWindow.js";
|
|
import { CdpRuntimeRequests } from "./CdpRuntimeRequests.js";
|
|
import type { CdpRuntimeSources } from "./CdpRuntimeSources.js";
|
|
|
|
/** Own source/request collection and failure subscriptions through a precise-sample cutoff. */
|
|
export class CdpExecutionCollection {
|
|
readonly window: CdpExecutionWindow;
|
|
readonly requests: CdpRuntimeRequests;
|
|
readonly #unsubscribers: readonly (() => void)[];
|
|
#metadataOpen = true;
|
|
|
|
constructor(readonly sources: CdpRuntimeSources) {
|
|
const session = sources.session;
|
|
this.window = new CdpExecutionWindow(session);
|
|
this.requests = new CdpRuntimeRequests(sources);
|
|
this.#unsubscribers = [
|
|
session.transport.connection.onEvent((event) => {
|
|
if (!cdpTargetEventMatches(event, session.transport.sessionId)) return;
|
|
try {
|
|
if (
|
|
this.#metadataOpen &&
|
|
(this.window.reason === undefined ||
|
|
this.window.reason === "window_elapsed")
|
|
)
|
|
sources.ingest(event);
|
|
else if (this.window.reason === "window_elapsed")
|
|
sources.verifyKnownIdentity(event);
|
|
if (this.window.active) this.requests.ingest(event);
|
|
sources.check();
|
|
this.requests.check();
|
|
} catch (cause: unknown) {
|
|
this.window.fail(
|
|
cause instanceof AnalysisError
|
|
? cause
|
|
: new AnalysisOutputError(session.operation, String(cause)),
|
|
);
|
|
}
|
|
this.window.ingest(event);
|
|
}),
|
|
session.transport.connection.onDisconnect((error) =>
|
|
this.window.fail(error),
|
|
),
|
|
session.transport.connection.onProtocolFailure((error) =>
|
|
this.window.fail(error),
|
|
),
|
|
];
|
|
}
|
|
|
|
/** Freeze metadata after receiving the final sample so source joins use one fixed inventory. */
|
|
freezeMetadata(): void {
|
|
this.#metadataOpen = false;
|
|
this.sources.check();
|
|
}
|
|
|
|
/** Remove every owned subscription and local timer on success, cancellation or failure. */
|
|
close(): void {
|
|
this.#metadataOpen = false;
|
|
this.window.dispose();
|
|
for (const unsubscribe of this.#unsubscribers) unsubscribe();
|
|
}
|
|
}
|