diff --git a/scripts/verify/architecture-guard-baselines.json b/scripts/verify/architecture-guard-baselines.json index 1cbf2d6e86..c71083ec61 100644 --- a/scripts/verify/architecture-guard-baselines.json +++ b/scripts/verify/architecture-guard-baselines.json @@ -79,47 +79,7 @@ "src/application/releaseExposure/ReleaseExposureService.ts": 3, "src/application/reverse/ReverseEngineeringService.ts": 12, "src/application/wasm/WasmInterfaceComparisonService.ts": 2, - "src/browser/CdpBrowserProvider.ts": 4, - "src/browser/CdpCaptureValues.ts": 2, - "src/browser/CdpConnection.ts": 4, - "src/browser/CdpElectronInspection.ts": 1, - "src/browser/CdpElectronProvider.ts": 3, - "src/browser/CdpElectronScripts.ts": 1, - "src/browser/CdpElectronWorkers.ts": 1, - "src/browser/CdpEndpoint.ts": 4, - "src/browser/CdpObservationSession.ts": 1, - "src/browser/CdpScriptSourceCapture.ts": 1, - "src/browser/CdpTargetSessionOwner.ts": 4, - "src/browser/ElectronFileScope.ts": 2, - "src/browser/IsolatedWebBundleAnalyzer.ts": 1, - "src/browser/PlaywrightBrowserScenarioProvider.ts": 2, - "src/browser/PlaywrightElectronActiveActions.ts": 1, - "src/browser/PlaywrightElectronActiveProvider.ts": 1, - "src/browser/PlaywrightExecutionBoundary.ts": 2, - "src/browser/WebBundleAnalysisWorker.ts": 8, - "src/browser/WebSourceMapFetcher.ts": 9, - "src/browser/assets/PublishWebScripts.ts": 3, - "src/browser/assets/WebArtifactReads.ts": 2, - "src/browser/execution/CdpExecutionCapture.ts": 1, - "src/browser/execution/CdpExecutionWindow.ts": 1, - "src/browser/execution/CdpWebRuntimeProvider.ts": 2, - "src/browser/history/CaptureFailures.ts": 3, - "src/browser/history/HistoricalCaptureDecoder.ts": 1, - "src/browser/history/HistoricalCaptureReply.ts": 1, - "src/browser/history/playwrightTrace/PlaywrightTraceDecoder.ts": 2, - "src/browser/history/playwrightTrace/TraceFailure.ts": 1, - "src/browser/modules/NativeModuleResolver.ts": 3, - "src/captures/TsharkCaptureProvider.ts": 2, - "src/electron/ElectronFuseFailures.ts": 3, "src/gdb/GdbSessionManager.ts": 13, - "src/inspector/JavaScriptRuntimeScope.ts": 1, - "src/inspector/V8InspectorProvider.ts": 2, - "src/javascript/analysis/JavaScriptAnalysisWorker.ts": 5, - "src/javascript/recovery/RecoveryFiles.ts": 3, - "src/javascript/recovery/WakaruCommand.ts": 2, - "src/javascript/recovery/WakaruProvider.ts": 2, - "src/javascript/sourceMaps/SourceMapCommand.ts": 4, - "src/javascript/sourceMaps/SourceMapDecoder.ts": 3, "src/native/AppleDispatchMetadata.ts": 3, "src/native/LldbCallTracer.ts": 2, "src/native/NativeCallObservation.ts": 2, diff --git a/src/browser/CdpBrowserProvider.ts b/src/browser/CdpBrowserProvider.ts index fdcdbbc32d..3edb965d73 100644 --- a/src/browser/CdpBrowserProvider.ts +++ b/src/browser/CdpBrowserProvider.ts @@ -54,8 +54,9 @@ import { } from "../domain/analysisErrorBase.js"; import { analysisErrorWithCleanupFailure } from "../domain/analysisErrorCleanup.js"; import { - AnalysisCancelledError, AnalysisInputError, + cancelledBy, + carriedReason, } from "../domain/analysisErrorCore.js"; import { BrowserObservationError } from "../domain/browserObservationError.js"; import { ProviderAdapterError } from "../domain/providerAdapterError.js"; @@ -334,12 +335,13 @@ export class CdpBrowserProvider implements BrowserObservationPort { // Capture and analyzer work can still throw the exact caller reason. if (options.signal?.aborted === true && cause === options.signal.reason) { return err( - new AnalysisCancelledError("analyze_web_bundle", { - cause, - ...(captured === undefined + cancelledBy( + "analyze_web_bundle", + carriedReason(cause), + captured === undefined ? {} - : { partialObservation: captured.inspection }), - }), + : { partialObservation: captured.inspection }, + ), ); } return err(providerError(cause, "analyze_web_bundle", options.signal)); @@ -615,11 +617,8 @@ const withTargetSession = async < if (outcome.ok && options.signal?.aborted === true) outcome = err( partialObservation === undefined - ? new AnalysisCancelledError(operation, { - cause: options.signal.reason, - }) - : new AnalysisCancelledError(operation, { - cause: options.signal.reason, + ? cancelledBy(operation, options.signal) + : cancelledBy(operation, options.signal, { partialObservation: partialObservation(outcome.value), }), ); @@ -724,7 +723,7 @@ const providerError = ( : cause instanceof AnalysisError ? cause : signal?.aborted === true - ? new AnalysisCancelledError(operation, { cause }) + ? cancelledBy(operation, carriedReason(cause)) : new ProviderAdapterError( CDP_BROWSER_PROVIDER_IDENTITY.id, operation, diff --git a/src/browser/CdpCaptureValues.ts b/src/browser/CdpCaptureValues.ts index 3ff6f4a652..1c2ae66aad 100644 --- a/src/browser/CdpCaptureValues.ts +++ b/src/browser/CdpCaptureValues.ts @@ -1,5 +1,5 @@ import { MAX_NODE_TIMER_DELAY_MS } from "../domain/analysisResourceLimits.js"; -import { AnalysisCancelledError } from "../domain/analysisErrorCore.js"; +import { cancelledBy, carriedReason } from "../domain/analysisErrorCore.js"; import type { BrowserObservationOperation } from "../domain/browserObservationErrors.js"; import { sanitizeBrowserUrl } from "../domain/browserObservation.js"; import { createWebTextArtifact } from "../domain/webContentArtifact.js"; @@ -82,8 +82,7 @@ export const delayWithCancellation = async ( ): Promise => { let remaining = durationMs; while (remaining > 0) { - if (signal?.aborted === true) - throw new AnalysisCancelledError(operation, { cause: signal.reason }); + if (signal?.aborted === true) throw cancelledBy(operation, signal); const delay = Math.min(remaining, MAX_NODE_TIMER_DELAY_MS); await new Promise((resolve, reject) => { const timer = setTimeout(() => { @@ -93,9 +92,7 @@ export const delayWithCancellation = async ( const onAbort = (): void => { clearTimeout(timer); signal?.removeEventListener("abort", onAbort); - reject( - new AnalysisCancelledError(operation, { cause: signal?.reason }), - ); + reject(cancelledBy(operation, carriedReason(signal?.reason))); }; signal?.addEventListener("abort", onAbort, { once: true }); if (signal?.aborted === true) onAbort(); diff --git a/src/browser/CdpConnection.ts b/src/browser/CdpConnection.ts index 6f908805ca..8e1d255c35 100644 --- a/src/browser/CdpConnection.ts +++ b/src/browser/CdpConnection.ts @@ -2,7 +2,7 @@ import { MAX_NODE_TIMER_DELAY_MS } from "../domain/analysisResourceLimits.js"; import WebSocket, { type RawData } from "ws"; import { isUtf8 } from "node:buffer"; -import { AnalysisCancelledError } from "../domain/analysisErrorCore.js"; +import { cancelledBy, carriedReason } from "../domain/analysisErrorCore.js"; import { AnalysisError } from "../domain/analysisErrorBase.js"; import { BrowserObservationError } from "../domain/browserObservationError.js"; import { CdpCommandRejection } from "./CdpCommandRejection.js"; @@ -144,10 +144,7 @@ class CdpTransportCore { throw new BrowserObservationError(operation, "protocol_error"); if (this.#closed || this.socket.readyState !== WebSocket.OPEN) throw this.transportError(this.#transportFailure, operation); - if (signal?.aborted === true) - throw new AnalysisCancelledError(operation, { - cause: signal.reason, - }); + if (signal?.aborted === true) throw cancelledBy(operation, signal); const id = this.#nextId; this.#nextId += 1; const message = JSON.stringify({ @@ -161,11 +158,7 @@ class CdpTransportCore { const pending = this.#pending.get(id); if (pending === undefined) return; this.#complete(id, pending); - reject( - new AnalysisCancelledError(operation, { - cause: signal?.reason, - }), - ); + reject(cancelledBy(operation, carriedReason(signal?.reason))); }; const failTimeout = (): void => { const pending = this.#pending.get(id); @@ -501,8 +494,7 @@ export class CdpConnection { }, onConnected?: (connection: CdpConnection) => void, ): Promise { - if (signal?.aborted === true) - throw new AnalysisCancelledError(operation, { cause: signal.reason }); + if (signal?.aborted === true) throw cancelledBy(operation, signal); const maxPayloadBytes = limits?.maxPayloadBytes ?? WEB_RUNTIME_LIMITS.protocolBytes; const commandTimeoutMs = @@ -655,7 +647,7 @@ const waitForOpen = async ( const onAbort = (): void => { cleanup(); terminateSocket(socket); - reject(new AnalysisCancelledError(operation, { cause: signal?.reason })); + reject(cancelledBy(operation, carriedReason(signal?.reason))); }; const cleanup = (): void => { clearTimeout(timer); diff --git a/src/browser/CdpElectronInspection.ts b/src/browser/CdpElectronInspection.ts index acd7b2a8e2..9dd0da8d49 100644 --- a/src/browser/CdpElectronInspection.ts +++ b/src/browser/CdpElectronInspection.ts @@ -7,7 +7,7 @@ import type { InspectElectronPageInput, } from "../domain/javascript/electronObservation.js"; import { BrowserObservationError } from "../domain/browserObservationError.js"; -import { AnalysisCancelledError } from "../domain/analysisErrorCore.js"; +import { cancelledBy } from "../domain/analysisErrorCore.js"; import type { CdpConnection, CdpEvent } from "./CdpConnection.js"; import type { CdpEndpointDiscovery, CdpEndpointTarget } from "./CdpEndpoint.js"; import { CdpCaptureCompleteness } from "./CdpCaptureCompleteness.js"; @@ -867,9 +867,7 @@ const selectedMainFrame = ( const throwIfElectronInspectionCancelled = (signal?: AbortSignal): void => { if (signal?.aborted === true) - throw new AnalysisCancelledError("inspect_electron_page", { - cause: signal.reason, - }); + throw cancelledBy("inspect_electron_page", signal); }; const report = async ( diff --git a/src/browser/CdpElectronProvider.ts b/src/browser/CdpElectronProvider.ts index bd9d96737a..fc3ce61520 100644 --- a/src/browser/CdpElectronProvider.ts +++ b/src/browser/CdpElectronProvider.ts @@ -18,7 +18,7 @@ import { } from "../domain/javascript/electronObservation.js"; import { selectedCdpCommandTimeoutMs } from "../domain/browserDeadline.js"; import { AnalysisError } from "../domain/analysisErrorBase.js"; -import { AnalysisCancelledError } from "../domain/analysisErrorCore.js"; +import { cancelledBy } from "../domain/analysisErrorCore.js"; import { BrowserObservationError } from "../domain/browserObservationError.js"; import { ProviderAdapterError } from "../domain/providerAdapterError.js"; import { err, ok, type Result } from "../domain/result.js"; @@ -238,8 +238,7 @@ export class CdpElectronProvider implements ElectronObservationPort { if (fileFailure !== undefined) return err(fileFailure); return err( options.signal?.aborted === true - ? new AnalysisCancelledError("list_electron_targets", { - cause: options.signal.reason, + ? cancelledBy("list_electron_targets", options.signal, { diagnostics: { completed_prefix: completedPrefix, }, @@ -334,8 +333,7 @@ export class CdpElectronProvider implements ElectronObservationPort { } const finalCancellation = result.ok && options.signal?.aborted === true - ? new AnalysisCancelledError("inspect_electron_page", { - cause: options.signal.reason, + ? cancelledBy("inspect_electron_page", options.signal, { partialObservation: { kind: "electron-page-observation", inspection: result.value, @@ -453,8 +451,7 @@ const throwIfElectronOperationCancelled = ( signal: AbortSignal | undefined, operation: "list_electron_targets" | "inspect_electron_page", ): void => { - if (signal?.aborted === true) - throw new AnalysisCancelledError(operation, { cause: signal.reason }); + if (signal?.aborted === true) throw cancelledBy(operation, signal); }; const providerError = ( diff --git a/src/browser/CdpElectronScripts.ts b/src/browser/CdpElectronScripts.ts index 0e7aa45333..07ec90087c 100644 --- a/src/browser/CdpElectronScripts.ts +++ b/src/browser/CdpElectronScripts.ts @@ -15,7 +15,7 @@ import type { } from "./CdpElectronScriptEvents.js"; import { authorizedElectronFile } from "./ElectronFileScope.js"; import { compareUnicodeCodePoints } from "../domain/unicodeCodePointOrder.js"; -import { AnalysisCancelledError } from "../domain/analysisErrorCore.js"; +import { cancelledBy } from "../domain/analysisErrorCore.js"; import { selectedCdpCommandTimeoutMs } from "../domain/browserDeadline.js"; import { CdpScriptSourceBudget } from "./CdpScriptSourceBudget.js"; @@ -129,7 +129,5 @@ export const captureElectronScripts = async ( const throwIfCallerCancelled = (signal: AbortSignal | undefined): void => { if (signal?.aborted === true) - throw new AnalysisCancelledError("inspect_electron_page", { - cause: signal.reason, - }); + throw cancelledBy("inspect_electron_page", signal); }; diff --git a/src/browser/CdpElectronWorkers.ts b/src/browser/CdpElectronWorkers.ts index 8f14d82353..e33a99ab0c 100644 --- a/src/browser/CdpElectronWorkers.ts +++ b/src/browser/CdpElectronWorkers.ts @@ -9,7 +9,7 @@ import type { CdpEndpointTarget } from "./CdpEndpoint.js"; import { optionalCdpCommand } from "./CdpOptionalCommand.js"; import { authorizedElectronFile } from "./ElectronFileScope.js"; import { compareUnicodeCodePoints } from "../domain/unicodeCodePointOrder.js"; -import { AnalysisCancelledError } from "../domain/analysisErrorCore.js"; +import { cancelledBy } from "../domain/analysisErrorCore.js"; import { parseCdpWorkerTarget, workerTargetOwnership, @@ -92,7 +92,5 @@ export const captureElectronWorkers = async (input: { const throwIfElectronWorkerCaptureCancelled = (signal?: AbortSignal): void => { if (signal?.aborted === true) - throw new AnalysisCancelledError("inspect_electron_page", { - cause: signal.reason, - }); + throw cancelledBy("inspect_electron_page", signal); }; diff --git a/src/browser/CdpEndpoint.ts b/src/browser/CdpEndpoint.ts index f207660ad3..f8e86a8063 100644 --- a/src/browser/CdpEndpoint.ts +++ b/src/browser/CdpEndpoint.ts @@ -9,7 +9,11 @@ import { cdpDiscoveryTimeoutRecovery, noRuntimeOverrideSentence, } from "../domain/browserDeadline.js"; -import { AnalysisCancelledError } from "../domain/analysisErrorCore.js"; +import { + AnalysisCancelledError, + cancelledBy, + carriedReason, +} from "../domain/analysisErrorCore.js"; import { AnalysisError } from "../domain/analysisErrorBase.js"; import { BrowserObservationError } from "../domain/browserObservationError.js"; import type { BrowserObservationOperation } from "../domain/browserObservationErrors.js"; @@ -194,7 +198,7 @@ export const withCdpDiscoveryDeadline = async ( } catch (cause: unknown) { if (cause === abortReason) { if (abortSource === "caller") - throw new AnalysisCancelledError(operation, { cause: abortReason }); + throw cancelledBy(operation, carriedReason(abortReason)); if (abortSource === "deadline") throw discoveryTimeout(operation, timeoutMs, "browser-socket"); } @@ -465,7 +469,7 @@ export const readCdpJson = async ( ): Promise => await new Promise((resolve, reject) => { if (signal?.aborted === true) { - reject(new AnalysisCancelledError(operation, { cause: signal.reason })); + reject(cancelledBy(operation, signal)); return; } let response: IncomingMessage | undefined; @@ -486,8 +490,7 @@ export const readCdpJson = async ( } else resolve(value); }; const onAbort = (): void => { - if (signal?.aborted === true) - finish(new AnalysisCancelledError(operation, { cause: signal.reason })); + if (signal?.aborted === true) finish(cancelledBy(operation, signal)); }; request_ = request( url, @@ -585,8 +588,7 @@ const endpointFailure = ( signal?: AbortSignal, ): AnalysisError => { if (cause instanceof AnalysisError) return cause; - if (signal?.aborted === true) - return new AnalysisCancelledError(operation, { cause: signal.reason }); + if (signal?.aborted === true) return cancelledBy(operation, signal); return new BrowserObservationError(operation, "endpoint_unreachable", { cause, }); diff --git a/src/browser/CdpObservationSession.ts b/src/browser/CdpObservationSession.ts index 7fadc994dc..de6608cf9d 100644 --- a/src/browser/CdpObservationSession.ts +++ b/src/browser/CdpObservationSession.ts @@ -4,7 +4,8 @@ import type { WebObservationSession, } from "../domain/browserSession.js"; import { - AnalysisCancelledError, + cancelledBy, + carriedReason, OwnerCancellation, } from "../domain/analysisErrorCore.js"; import { BrowserObservationError } from "../domain/browserObservationError.js"; @@ -779,9 +780,7 @@ const waitForWindow = async ( const onAbort = (): void => settle(() => { reject( - new AnalysisCancelledError("observe_web_session", { - cause: signal?.reason, - }), + cancelledBy("observe_web_session", carriedReason(signal?.reason)), ); }); signal?.addEventListener("abort", onAbort, { once: true }); diff --git a/src/browser/CdpScriptSourceCapture.ts b/src/browser/CdpScriptSourceCapture.ts index 12c80dd370..084ddba28f 100644 --- a/src/browser/CdpScriptSourceCapture.ts +++ b/src/browser/CdpScriptSourceCapture.ts @@ -1,4 +1,4 @@ -import { AnalysisCancelledError } from "../domain/analysisErrorCore.js"; +import { cancelledBy } from "../domain/analysisErrorCore.js"; import type { BrowserObservationOperation } from "../domain/browserObservationErrors.js"; import type { CdpConnection } from "./CdpConnection.js"; import type { CdpCaptureCompleteness } from "./CdpCaptureCompleteness.js"; @@ -33,9 +33,7 @@ export const capturePassiveCdpScriptSource = async (input: { }; const throwIfCancelled = () => { if (input.signal?.aborted === true) - throw new AnalysisCancelledError(input.operation, { - cause: input.signal.reason, - }); + throw cancelledBy(input.operation, input.signal); }; throwIfCancelled(); if (input.sourceBudget.timedOut) return omitForBudget(); diff --git a/src/browser/CdpTargetSessionOwner.ts b/src/browser/CdpTargetSessionOwner.ts index 95ca89bdfd..5443e5fdc2 100644 --- a/src/browser/CdpTargetSessionOwner.ts +++ b/src/browser/CdpTargetSessionOwner.ts @@ -11,6 +11,8 @@ import { ProviderCleanupError } from "../domain/providerCleanupError.js"; import { AnalysisCancelledError, AnalysisCapabilityUnavailableError, + cancelledBy, + carriedReason, } from "../domain/analysisErrorCore.js"; import { analysisErrorWithCleanupFailure } from "../domain/analysisErrorCleanup.js"; import { CdpConnection } from "./CdpConnection.js"; @@ -145,9 +147,7 @@ export class CdpTargetSessionOwner { while (true) { this.#assertOpen(operation); if (options.signal?.aborted === true) - throw new AnalysisCancelledError(operation, { - cause: options.signal.reason, - }); + throw cancelledBy(operation, options.signal); const admitted = this.#browserSockets.get(url); const socket = admitted ?? this.#candidateBrowserSockets.get(url); if (socket === undefined) return undefined; @@ -222,9 +222,10 @@ export class CdpTargetSessionOwner { const onAbort = (): void => { waiter.detachAbort(); if (!activePending.waiters.delete(waiter)) return; - const cancelError = new AnalysisCancelledError(operation, { - cause: options.signal?.reason, - }); + const cancelError = cancelledBy( + operation, + carriedReason(options.signal?.reason), + ); if (activePending.waiters.size > 0) { reject(cancelError); return; @@ -434,9 +435,7 @@ export class CdpTargetSessionOwner { await this.retryPending(); this.#assertOpen(operation); if (options.signal?.aborted === true) - throw new AnalysisCancelledError(operation, { - cause: options.signal.reason, - }); + throw cancelledBy(operation, options.signal); this.#admissions += 1; let connection: CdpConnection | undefined; try { @@ -671,10 +670,11 @@ const rebindPendingConnectFailure = ( : rebound; } if (cause instanceof AnalysisCancelledError) { - const rebound = new AnalysisCancelledError(waiter.operation, { - cause: waiter.signal?.reason ?? cause.cause, - cleanup: cause.cleanup, - }); + const rebound = cancelledBy( + waiter.operation, + carriedReason(waiter.signal?.reason ?? cause.cause), + { cleanup: cause.cleanup }, + ); return cause.cleanupIncomplete ? rebound.bindCleanupDependencies([cause]) : rebound; diff --git a/src/browser/ElectronFileScope.ts b/src/browser/ElectronFileScope.ts index ba4e074c19..d2463e4238 100644 --- a/src/browser/ElectronFileScope.ts +++ b/src/browser/ElectronFileScope.ts @@ -2,7 +2,7 @@ import type { Stats } from "node:fs"; import { open, realpath, stat } from "node:fs/promises"; import { isAbsolute, join, sep } from "node:path"; import { fileURLToPath } from "node:url"; -import { AnalysisCancelledError } from "../domain/analysisErrorCore.js"; +import { cancelledBy, carriedReason } from "../domain/analysisErrorCore.js"; import { AnalysisError } from "../domain/analysisErrorBase.js"; import { analysisErrorWithCleanupFailure } from "../domain/analysisErrorCleanup.js"; import { ProviderAdapterError } from "../domain/providerAdapterError.js"; @@ -65,8 +65,7 @@ export class ElectronFileScope { } catch (cause: unknown) { if (cause instanceof ArtifactReaderFailure && cause.cleanup !== undefined) throw cause; - if (signal?.aborted === true) - throw new AnalysisCancelledError(operation, { cause: signal.reason }); + if (signal?.aborted === true) throw cancelledBy(operation, signal); throw cause; } } @@ -123,9 +122,7 @@ export const mapElectronFileAuthorizationFailure = ( (physicalPrimary instanceof ArtifactReaderFailure && physicalPrimary.reason === "cancelled"); const primary = hasCancellation - ? new AnalysisCancelledError(operation, { - cause: cancellationReason, - }) + ? cancelledBy(operation, carriedReason(cancellationReason)) : physicalPrimary instanceof AnalysisError ? physicalPrimary : physicalPrimary === undefined diff --git a/src/browser/IsolatedWebBundleAnalyzer.ts b/src/browser/IsolatedWebBundleAnalyzer.ts index 315a847d14..f4dd85c373 100644 --- a/src/browser/IsolatedWebBundleAnalyzer.ts +++ b/src/browser/IsolatedWebBundleAnalyzer.ts @@ -13,7 +13,6 @@ import type { WebBundleScriptAnalysis } from "../domain/webBundleScriptAnalysis. import { AnalysisError } from "../domain/analysisErrorBase.js"; import { AnalysisCapabilityUnavailableError, - AnalysisCancelledError, AnalysisTimeoutError, AnalysisResourceConstraintError, AnalysisOutputError, @@ -312,8 +311,7 @@ export class IsolatedWebBundleAnalyzer { deadline.dispose(); } if (caller.aborted && deadline.interruption !== "timeout") { - const cancelled = new AnalysisCancelledError("analyze_web_bundle", { - cause: caller.reason, + const cancelled = cancelledBy("analyze_web_bundle", caller, { partialObservation: partial ?? inspection, }); failure = hasPrimaryFailure diff --git a/src/browser/PlaywrightBrowserScenarioProvider.ts b/src/browser/PlaywrightBrowserScenarioProvider.ts index e867e1e73b..f52753ea2a 100644 --- a/src/browser/PlaywrightBrowserScenarioProvider.ts +++ b/src/browser/PlaywrightBrowserScenarioProvider.ts @@ -37,6 +37,8 @@ import { AnalysisError } from "../domain/analysisErrorBase.js"; import { AnalysisCancelledError, AnalysisCapabilityUnavailableError, + isAborted, + cancelledBy, } from "../domain/analysisErrorCore.js"; import { BrowserObservationError, @@ -61,9 +63,6 @@ import { const OPERATION = "capture_browser_scenario" as const; import { PLAYWRIGHT_BROWSER_SCENARIO_PROVIDER_IDENTITY } from "./providerIdentities.js"; -const signalIsAborted = (signal: AbortSignal | undefined): boolean => - signal?.aborted === true; - type SnapshotKind = BrowserScenario["capture"]["after_each_step"][number]; const SNAPSHOT_KINDS = [ "screenshot", @@ -326,9 +325,7 @@ const runScenario = async ( if (operationOutcome.ok && options.signal?.aborted) operationOutcome = { ok: false, - cause: new AnalysisCancelledError(OPERATION, { - cause: options.signal.reason, - }), + cause: cancelledBy(OPERATION, options.signal), }; const cleanup = browserCleanupObservation(cleanupOutcome.cause); const partialObservation: BrowserScenarioPartialObservation = { @@ -388,9 +385,8 @@ const runScenario = async ( }), completeness, }); - if (signalIsAborted(options.signal)) - throw new AnalysisCancelledError(OPERATION, { - cause: options.signal?.reason, + if (isAborted(options.signal)) + throw cancelledBy(OPERATION, options.signal, { partialObservation: { kind: "browser-scenario-observation", capture: { diff --git a/src/browser/PlaywrightElectronActiveActions.ts b/src/browser/PlaywrightElectronActiveActions.ts index 854077a924..a8e67c9576 100644 --- a/src/browser/PlaywrightElectronActiveActions.ts +++ b/src/browser/PlaywrightElectronActiveActions.ts @@ -9,7 +9,10 @@ import type { } from "../artifacts/ArtifactResourceScope.js"; import { ProviderCleanupError } from "../domain/providerCleanupError.js"; import { AnalysisError } from "../domain/analysisErrorBase.js"; -import { AnalysisCancelledError } from "../domain/analysisErrorCore.js"; +import { + AnalysisCancelledError, + cancelledBy, +} from "../domain/analysisErrorCore.js"; import { analysisErrorWithCleanupFailure } from "../domain/analysisErrorCleanup.js"; import { BrowserObservationError } from "../domain/browserObservationError.js"; import { describeValidationFailure } from "../domain/validationFailure.js"; @@ -241,9 +244,7 @@ const releaseActionWindow = async ( error.bindCleanupResourceOwner(released.lease); released.lease.bindCleanupError(error); if (!signal?.aborted) return error; - const cancellation = new AnalysisCancelledError(OPERATION, { - cause: signal.reason, - }); + const cancellation = cancelledBy(OPERATION, signal); return actionStatus === "failed" ? error.retainSecondaryFailure(cancellation) : analysisErrorWithCleanupFailure( diff --git a/src/browser/PlaywrightElectronActiveProvider.ts b/src/browser/PlaywrightElectronActiveProvider.ts index 30e06c1466..ec1d42dcdd 100644 --- a/src/browser/PlaywrightElectronActiveProvider.ts +++ b/src/browser/PlaywrightElectronActiveProvider.ts @@ -29,7 +29,10 @@ import { AnalysisError, primaryAnalysisError, } from "../domain/analysisErrorBase.js"; -import { AnalysisCancelledError } from "../domain/analysisErrorCore.js"; +import { + AnalysisCancelledError, + cancelledBy, +} from "../domain/analysisErrorCore.js"; import { BrowserObservationError } from "../domain/browserObservationError.js"; import { ProviderAdapterError } from "../domain/providerAdapterError.js"; import { err, ok, type Result } from "../domain/result.js"; @@ -375,7 +378,7 @@ const captureOutcomeWithCancellation = ( return err( analysisErrorWithCancellation( outcome.error, - new AnalysisCancelledError(OPERATION, { cause: signal.reason }), + cancelledBy(OPERATION, signal), ), ); }; diff --git a/src/browser/PlaywrightExecutionBoundary.ts b/src/browser/PlaywrightExecutionBoundary.ts index 2d47cfc25b..87a984fa52 100644 --- a/src/browser/PlaywrightExecutionBoundary.ts +++ b/src/browser/PlaywrightExecutionBoundary.ts @@ -1,7 +1,8 @@ import { MAX_NODE_TIMER_DELAY_MS } from "../domain/analysisResourceLimits.js"; import { - AnalysisCancelledError, AnalysisTimeoutError, + cancelledBy, + carriedReason, } from "../domain/analysisErrorCore.js"; export const PLAYWRIGHT_NETWORK_READ_TIMEOUT_MS = 5_000; @@ -21,9 +22,7 @@ export const withPlaywrightExecutionBoundary = ( if (timeoutMs !== undefined && timeoutMs <= 0) return Promise.reject(timeoutError()); if (signal?.aborted === true) - return Promise.reject( - new AnalysisCancelledError(OPERATION, { cause: signal.reason }), - ); + return Promise.reject(cancelledBy(OPERATION, signal)); return new Promise((resolve, reject) => { let settled = false; @@ -60,7 +59,7 @@ export const withPlaywrightExecutionBoundary = ( reject(error); }; const onAbort = (): void => { - fail(new AnalysisCancelledError(OPERATION, { cause: signal?.reason })); + fail(cancelledBy(OPERATION, carriedReason(signal?.reason))); }; scheduleTimeout(); if (settled) return; diff --git a/src/browser/WebBundleAnalysisWorker.ts b/src/browser/WebBundleAnalysisWorker.ts index 36026089f0..fc2b8c3a2d 100644 --- a/src/browser/WebBundleAnalysisWorker.ts +++ b/src/browser/WebBundleAnalysisWorker.ts @@ -38,6 +38,8 @@ import { AnalysisTimeoutError, AnalysisOutputError, AnalysisResourceConstraintError, + cancelledBy, + carriedReason, } from "../domain/analysisErrorCore.js"; import { analysisErrorWithCleanupFailure } from "../domain/analysisErrorCleanup.js"; import { ProviderCleanupError } from "../domain/providerCleanupError.js"; @@ -299,17 +301,13 @@ export class WebBundleAnalysisWorker { ): Result => err({ error: signal?.aborted - ? analysisErrorWithCancellation( - error, - new AnalysisCancelledError(OPERATION, { cause: signal.reason }), - ) + ? analysisErrorWithCancellation(error, cancelledBy(OPERATION, signal)) : error, completed, ...(continuation === undefined ? {} : { continuation }), }); try { - if (signal?.aborted) - throw new AnalysisCancelledError(OPERATION, { cause: signal.reason }); + if (signal?.aborted) throw cancelledBy(OPERATION, signal); const admitted = this.#admittedResultBudget(); if (!admitted.ok) { continuation = "result_capacity_exhausted"; @@ -469,16 +467,15 @@ export class WebBundleAnalysisWorker { interruptionApplies && deadline?.interruption === "timeout" ? scriptTimeout(timeoutMs, cause) : cancellationCause !== undefined - ? new AnalysisCancelledError(OPERATION, { - cause: cancellationCause.reason, - }) + ? cancelledBy(OPERATION, carriedReason(cancellationCause.reason)) : interruptionApplies && (deadline?.interruption === "cancelled" || signal?.aborted) - ? new AnalysisCancelledError(OPERATION, { - cause: signal?.aborted - ? signal.reason - : deadline?.signal.reason, - }) + ? cancelledBy( + OPERATION, + carriedReason( + signal?.aborted ? signal.reason : deadline?.signal.reason, + ), + ) : undefined; const observedFailure = this.#observedFailure === undefined @@ -552,17 +549,13 @@ export class WebBundleAnalysisWorker { const primary = signal?.aborted ? analysisErrorWithCancellation( previous, - new AnalysisCancelledError(OPERATION, { cause: signal.reason }), + cancelledBy(OPERATION, signal), ) : previous; return failed(this.#cleanupFailure(cause, primary)); } if (signal?.aborted) - return failed( - result.ok - ? new AnalysisCancelledError(OPERATION, { cause: signal.reason }) - : result.error, - ); + return failed(result.ok ? cancelledBy(OPERATION, signal) : result.error); return result.ok ? result : failed(result.error); } @@ -718,9 +711,7 @@ export class WebBundleAnalysisWorker { if (deadline.interruption === "timeout") throw scriptTimeout(deadline.timeoutMs); if (deadline.interruption === "cancelled") - throw new AnalysisCancelledError(OPERATION, { - cause: deadline.signal.reason, - }); + throw cancelledBy(OPERATION, deadline.signal); const frame = this.#frames.shift(); if (frame !== undefined) return frame; if (this.#runtime.inputFailure !== undefined) @@ -751,9 +742,7 @@ export class WebBundleAnalysisWorker { if (completedFrame !== undefined) return completedFrame; throw deadline.interruption === "timeout" ? scriptTimeout(deadline.timeoutMs) - : new AnalysisCancelledError(OPERATION, { - cause: deadline.signal.reason, - }); + : cancelledBy(OPERATION, deadline.signal); } const completedFrame = this.#frames.shift(); if (completedFrame !== undefined) return completedFrame; diff --git a/src/browser/WebSourceMapFetcher.ts b/src/browser/WebSourceMapFetcher.ts index 3282d5537f..11fb0f7d76 100644 --- a/src/browser/WebSourceMapFetcher.ts +++ b/src/browser/WebSourceMapFetcher.ts @@ -6,6 +6,9 @@ import { sanitizeBrowserUrl } from "../domain/browserObservation.js"; import { AnalysisCancelledError, AnalysisResourceConstraintError, + isAborted, + cancelledBy, + carriedReason, } from "../domain/analysisErrorCore.js"; import { AnalysisError, @@ -106,12 +109,7 @@ export const fetchWebSourceMaps = async ( signal?: AbortSignal, host: SourceMapFetchHost = { fetch: globalThis.fetch }, ): Promise> => { - if (signalIsAborted(signal)) - return err( - new AnalysisCancelledError("analyze_web_bundle", { - cause: signal?.reason, - }), - ); + if (isAborted(signal)) return err(cancelledBy("analyze_web_bundle", signal)); if (requests.length === 0) return ok( webSourceMapsSchema.parse({ @@ -232,13 +230,7 @@ export const fetchWebSourceMaps = async ( signal?.aborted === true && (cause === signal.reason || cause === callerAbort) ) - return err( - retainCompleted( - new AnalysisCancelledError("analyze_web_bundle", { - cause: signal.reason, - }), - ), - ); + return err(retainCompleted(cancelledBy("analyze_web_bundle", signal))); if (cause instanceof AnalysisError) { if ( signal?.aborted === true && @@ -249,11 +241,7 @@ export const fetchWebSourceMaps = async ( failure.cause === signal.reason, ) ) - cause.retainSecondaryFailure( - new AnalysisCancelledError("analyze_web_bundle", { - cause: signal.reason, - }), - ); + cause.retainSecondaryFailure(cancelledBy("analyze_web_bundle", signal)); return err(retainCompleted(cause)); } if (signal?.aborted === true) @@ -261,11 +249,7 @@ export const fetchWebSourceMaps = async ( retainCompleted( new ProviderAdapterError("rea-cdp-browser", "analyze_web_bundle", { cause, - }).retainSecondaryFailure( - new AnalysisCancelledError("analyze_web_bundle", { - cause: signal.reason, - }), - ), + }).retainSecondaryFailure(cancelledBy("analyze_web_bundle", signal)), ), ); if (isSourceMapLimit(cause)) @@ -279,13 +263,7 @@ export const fetchWebSourceMaps = async ( signal?.removeEventListener("abort", abortFromCaller); } if (signal?.aborted === true) - return err( - retainCompleted( - new AnalysisCancelledError("analyze_web_bundle", { - cause: signal.reason, - }), - ), - ); + return err(retainCompleted(cancelledBy("analyze_web_bundle", signal))); return ok( webSourceMapsSchema.parse({ status: sourceMapsStatus(items, limitation !== undefined), @@ -382,26 +360,22 @@ class SourceMapResponseCleanup extends Error { super("Source-map response cleanup", { cause: primary }); this.#resource = { url: resource, body }; let reason = "Source-map response cancellation is pending."; - const options = { - cause: sourceMapCallerReason(primary), - cleanup: { - get reason() { - return reason; - }, - resources: [resource], + const cause = sourceMapCallerReason(primary); + const cleanup = { + get reason() { + return reason; }, + resources: [resource], }; this.failure = signal?.aborted === true && primary === signal.reason && !(primary instanceof SourceMapDeadlineError) - ? new AnalysisCancelledError("analyze_web_bundle", options) + ? cancelledBy("analyze_web_bundle", carriedReason(cause), { cleanup }) : new ProviderAdapterError("rea-cdp-browser", "analyze_web_bundle", { - ...options, - userMessage: - sourceMapCallerReason(primary) instanceof Error - ? (sourceMapCallerReason(primary) as Error).message - : String(sourceMapCallerReason(primary)), + cause, + cleanup, + userMessage: cause instanceof Error ? cause.message : String(cause), }); this.failure.bindCleanupResourceOwner(this); const failed = (cause: unknown): void => { @@ -474,21 +448,21 @@ class SourceMapPendingFetch extends Error { const reason = signal.reason; super("Source-map fetch is settling after cancellation", { cause: reason }); this.#resource = { url: resource }; - const options = { - cause: sourceMapCallerReason(reason), - cleanup: { - reason: "Source-map fetch is still settling after cancellation.", - resources: [resource], - }, + const cleanup = { + reason: "Source-map fetch is still settling after cancellation.", + resources: [resource], }; this.failure = reason instanceof SourceMapDeadlineError - ? new ProviderAdapterError( - "rea-cdp-browser", + ? new ProviderAdapterError("rea-cdp-browser", "analyze_web_bundle", { + cause: reason, + cleanup, + }) + : cancelledBy( "analyze_web_bundle", - options, - ) - : new AnalysisCancelledError("analyze_web_bundle", options); + carriedReason(sourceMapCallerReason(reason)), + { cleanup }, + ); this.failure.bindCleanupResourceOwner(this); const finished = (): void => { this.#resource = undefined; @@ -572,9 +546,10 @@ const cancelUnreadResponse = async ( primary !== cause ) cleanup.failure.retainSecondaryFailure( - new AnalysisCancelledError("analyze_web_bundle", { - cause: sourceMapCallerReason(cause), - }), + cancelledBy( + "analyze_web_bundle", + carriedReason(sourceMapCallerReason(cause)), + ), ); throw cleanup; } @@ -803,7 +778,7 @@ const fetchFollowingApprovedRedirects = async ( current, ); observeResponse(current); - if (signal !== undefined && signalIsAborted(signal)) { + if (signal !== undefined && isAborted(signal)) { await cancelUnreadResponse( response, current, @@ -911,9 +886,10 @@ const readBoundedText = async ( !(completionFailure instanceof SourceMapDeadlineError) ) cleanup.failure.retainSecondaryFailure( - new AnalysisCancelledError("analyze_web_bundle", { - cause: sourceMapCallerReason(signal.reason), - }), + cancelledBy( + "analyze_web_bundle", + carriedReason(sourceMapCallerReason(signal.reason)), + ), ); } } @@ -978,9 +954,6 @@ const awaitWithAbort = ( }); }; -const signalIsAborted = (signal: AbortSignal | undefined): boolean => - signal?.aborted === true; - const promiseWithAbort = ( promise: Promise, signal: AbortSignal | undefined, diff --git a/src/browser/assets/PublishWebScripts.ts b/src/browser/assets/PublishWebScripts.ts index 7da7e96127..77db6971e2 100644 --- a/src/browser/assets/PublishWebScripts.ts +++ b/src/browser/assets/PublishWebScripts.ts @@ -12,7 +12,11 @@ import { SafeOutputTree } from "../../artifacts/SafeOutputTree.js"; import { outputDirectoryAdmissionRefusal } from "../../artifacts/SafeOutputTreeAdmission.js"; import { SafeOutputTreeCreationFailure } from "../../artifacts/SafeOutputTreeCreationFailure.js"; import { PLAYWRIGHT_RESPONSE_BODY_PROVENANCE_LIMITATION } from "../../domain/browserNetworkEvidence.js"; -import { AnalysisCancelledError } from "../../domain/analysisErrorCore.js"; +import { + AnalysisCancelledError, + cancelledBy, + carriedReason, +} from "../../domain/analysisErrorCore.js"; import { webScriptExportManifestSchema, webScriptExportResultSchema, @@ -52,10 +56,7 @@ export const publishWebScripts = async ( ): Promise => options.resources.run(async () => { const assertActive = (): void => { - if (options.signal?.aborted) - throw new AnalysisCancelledError(OPERATION, { - cause: options.signal.reason, - }); + if (options.signal?.aborted) throw cancelledBy(OPERATION, options.signal); }; const planned = planWebScriptExport(capture.scripts); const scenarioResponseBodyCaptured = @@ -135,9 +136,7 @@ export const publishWebScripts = async ( ? cause : cause instanceof ArtifactReaderFailure && cause.cancellation !== undefined - ? new AnalysisCancelledError(OPERATION, { - cause: cause.cancellation.reason, - }) + ? cancelledBy(OPERATION, carriedReason(cause.cancellation.reason)) : undefined; if (cause instanceof SafeOutputTreeCreationFailure) tree = cause.tree; if (tree !== undefined) { @@ -156,13 +155,16 @@ export const publishWebScripts = async ( ? ({ kind: "web-script-export", result } as const) : undefined; if (cancellation !== undefined) { - const cancelled = new AnalysisCancelledError(OPERATION, { - cause: cancellation.cause, - cleanup: { reason: diagnostic, resources: [tree.outputRoot] }, - ...(partialObservation === undefined - ? {} - : { partialObservation }), - }); + const cancelled = cancelledBy( + OPERATION, + carriedReason(cancellation.cause), + { + cleanup: { reason: diagnostic, resources: [tree.outputRoot] }, + ...(partialObservation === undefined + ? {} + : { partialObservation }), + }, + ); cleanup.lease.bindCleanupError(cancelled); throw cancelled; } diff --git a/src/browser/assets/WebArtifactReads.ts b/src/browser/assets/WebArtifactReads.ts index 8e1c59dc6a..ed331b3885 100644 --- a/src/browser/assets/WebArtifactReads.ts +++ b/src/browser/assets/WebArtifactReads.ts @@ -19,8 +19,9 @@ import { } from "../../domain/analysisErrorBase.js"; import { AnalysisAccessDeniedError, - AnalysisCancelledError, AnalysisInputError, + cancelledBy, + carriedReason, } from "../../domain/analysisErrorCore.js"; import { ArtifactOperationError } from "../../domain/artifactOperationError.js"; import { ProviderAdapterError } from "../../domain/providerAdapterError.js"; @@ -174,9 +175,10 @@ const webArtifactPrimaryError = ( cause instanceof ArtifactReaderFailure && cause.reason === "cancelled" ) - return new AnalysisCancelledError(context.operation, { - cause: artifactFileCancellationCause(cause), - }); + return cancelledBy( + context.operation, + carriedReason(artifactFileCancellationCause(cause)), + ); if ( origin === "owned-or-classified" && signal?.aborted === true && @@ -186,9 +188,7 @@ const webArtifactPrimaryError = ( "code" in cause && cause.code === "ABORT_ERR")) ) - return new AnalysisCancelledError(context.operation, { - cause: signal.reason, - }); + return cancelledBy(context.operation, signal); const selection = context.callerSelected ? selectedFileError({ cause, origin }, context) : undefined; diff --git a/src/browser/execution/CdpExecutionCapture.ts b/src/browser/execution/CdpExecutionCapture.ts index ee38c7e133..a2caca9e6e 100644 --- a/src/browser/execution/CdpExecutionCapture.ts +++ b/src/browser/execution/CdpExecutionCapture.ts @@ -1,6 +1,6 @@ import { AnalysisError } from "../../domain/analysisErrorBase.js"; import { BrowserObservationError } from "../../domain/browserObservationError.js"; -import { AnalysisCancelledError } from "../../domain/analysisErrorCore.js"; +import { cancelledBy } from "../../domain/analysisErrorCore.js"; import type { ObserveWebExecutionInput, WebExecution, @@ -64,9 +64,7 @@ export const captureWebExecution = async ( const reason = await waiting; session.beginCompletion(); if (session.options.signal?.aborted) - throw new AnalysisCancelledError(session.operation, { - cause: session.options.signal.reason, - }); + throw cancelledBy(session.operation, session.options.signal); sources.check(); requests.check(); let sampled: CdpCommandResult<"Profiler.takePreciseCoverage"> | undefined; diff --git a/src/browser/execution/CdpExecutionWindow.ts b/src/browser/execution/CdpExecutionWindow.ts index dc4cb1aac7..1e840b4d14 100644 --- a/src/browser/execution/CdpExecutionWindow.ts +++ b/src/browser/execution/CdpExecutionWindow.ts @@ -1,4 +1,4 @@ -import { AnalysisCancelledError } from "../../domain/analysisErrorCore.js"; +import { cancelledBy, carriedReason } from "../../domain/analysisErrorCore.js"; import type { AnalysisError } from "../../domain/analysisErrorBase.js"; import type { WebExecution } from "../../domain/webExecution.js"; import type { CdpEvent } from "../CdpConnection.js"; @@ -94,9 +94,7 @@ export class CdpExecutionWindow { const signal = this.session.options.signal; const abort = (): void => { this.fail( - new AnalysisCancelledError(this.session.operation, { - cause: signal?.reason, - }), + cancelledBy(this.session.operation, carriedReason(signal?.reason)), ); }; this.#removeAbort = () => signal?.removeEventListener("abort", abort); diff --git a/src/browser/execution/CdpWebRuntimeProvider.ts b/src/browser/execution/CdpWebRuntimeProvider.ts index e0952961ad..7d02bd7f51 100644 --- a/src/browser/execution/CdpWebRuntimeProvider.ts +++ b/src/browser/execution/CdpWebRuntimeProvider.ts @@ -6,8 +6,8 @@ import { type AnalysisPartialObservation, } from "../../domain/analysisErrorBase.js"; import { - AnalysisCancelledError, AnalysisOutputError, + cancelledBy, } from "../../domain/analysisErrorCore.js"; import type { BrowserObservationOperation } from "../../domain/browserObservationErrors.js"; import { ProviderAdapterError } from "../../domain/providerAdapterError.js"; @@ -123,8 +123,7 @@ export class CdpWebRuntimeProvider implements WebRuntimePort { result.ok ? options.signal?.aborted === true ? analysisErrorWithCleanupFailure( - new AnalysisCancelledError(operation, { - cause: options.signal.reason, + cancelledBy(operation, options.signal, { partialObservation: result.value, }), failure, @@ -137,8 +136,7 @@ export class CdpWebRuntimeProvider implements WebRuntimePort { const completed = result.ok ? ok(confirmCleanup(result.value)) : result; if (completed.ok && options.signal?.aborted === true) return err( - new AnalysisCancelledError(operation, { - cause: options.signal.reason, + cancelledBy(operation, options.signal, { partialObservation: completed.value, }), ); diff --git a/src/browser/history/CaptureFailures.ts b/src/browser/history/CaptureFailures.ts index d2f8aa716d..23c7761914 100644 --- a/src/browser/history/CaptureFailures.ts +++ b/src/browser/history/CaptureFailures.ts @@ -15,11 +15,12 @@ import { AnalysisError } from "../../domain/analysisErrorBase.js"; import { AnalysisAccessDeniedError, AnalysisArtifactChangedError, - AnalysisCancelledError, AnalysisInputError, AnalysisOutputError, AnalysisResourceConstraintError, AnalysisTimeoutError, + cancelledBy, + carriedReason, } from "../../domain/analysisErrorCore.js"; import { ProviderAdapterError } from "../../domain/providerAdapterError.js"; import { ProviderCleanupError } from "../../domain/providerCleanupError.js"; @@ -138,14 +139,16 @@ export const historicalCaptureFailure = ( : cleanup; } if (cause.reason === "cancelled") - return new AnalysisCancelledError(OPERATION, { - cause: + return cancelledBy( + OPERATION, + carriedReason( cause.cancellation === undefined ? options?.signal?.aborted === true ? options.signal.reason : undefined : cause.cancellation.reason, - }); + ), + ); if (cause.reason === "timeout") { const timeoutMs = selectedWebNetworkCaptureTimeoutMs( input.capture_timeout_ms, @@ -200,9 +203,10 @@ export const historicalCaptureFailure = ( cause instanceof ArtifactReaderFailure && cause.reason === "cancelled" ) - return new AnalysisCancelledError(OPERATION, { - cause: artifactFileCancellationCause(cause), - }); + return cancelledBy( + OPERATION, + carriedReason(artifactFileCancellationCause(cause)), + ); if ( phase === "decoder" && origin === "owned-or-classified" && @@ -296,9 +300,7 @@ export const historicalCaptureFailure = ( "code" in cause && cause.code === "ABORT_ERR")) ) - return new AnalysisCancelledError(OPERATION, { - cause: options.signal.reason, - }); + return cancelledBy(OPERATION, options.signal); return new ProviderAdapterError(input.format, OPERATION, { cause, diagnostics: { diff --git a/src/browser/history/HistoricalCaptureDecoder.ts b/src/browser/history/HistoricalCaptureDecoder.ts index df9fe0292b..7f541ed027 100644 --- a/src/browser/history/HistoricalCaptureDecoder.ts +++ b/src/browser/history/HistoricalCaptureDecoder.ts @@ -46,6 +46,7 @@ import { AnalysisInputError, AnalysisOutputError, AnalysisResourceConstraintError, + cancelledBy, } from "../../domain/analysisErrorCore.js"; import { ProviderAdapterError } from "../../domain/providerAdapterError.js"; import { ProviderCleanupError } from "../../domain/providerCleanupError.js"; @@ -504,11 +505,7 @@ export class HistoricalCaptureDecoder { } } if (result.ok && options?.signal?.aborted) - result = err( - new AnalysisCancelledError(OPERATION, { - cause: options.signal.reason, - }), - ); + result = err(cancelledBy(OPERATION, options.signal)); if (cleanupFailure !== undefined) { const cleanupError = new ProviderCleanupError( input.format, diff --git a/src/browser/history/HistoricalCaptureReply.ts b/src/browser/history/HistoricalCaptureReply.ts index b2f93710c9..2f6696bbe7 100644 --- a/src/browser/history/HistoricalCaptureReply.ts +++ b/src/browser/history/HistoricalCaptureReply.ts @@ -12,8 +12,9 @@ import { readStableArtifact, } from "../../artifacts/readStableArtifact.js"; import { - AnalysisCancelledError, AnalysisOutputError, + cancelledBy, + carriedReason, } from "../../domain/analysisErrorCore.js"; import { projectAnalysisError } from "../../domain/analysisErrorProjection.js"; import { withCaptureReaderCleanup } from "./CaptureFailures.js"; @@ -96,9 +97,9 @@ export const readHistoricalCaptureReply = async ( { cause }, ); if (finalization?.reason === "cancelled") { - const cancellation = new AnalysisCancelledError( + const cancellation = cancelledBy( "inspect_web_network_capture", - { cause: artifactFileCancellationCause(finalization) }, + carriedReason(artifactFileCancellationCause(finalization)), ); error.retainSecondaryFailure(cancellation, { reply_finalization_error: projectAnalysisError(cancellation), diff --git a/src/browser/history/playwrightTrace/PlaywrightTraceDecoder.ts b/src/browser/history/playwrightTrace/PlaywrightTraceDecoder.ts index 8f0ed2ecdc..10a8593234 100644 --- a/src/browser/history/playwrightTrace/PlaywrightTraceDecoder.ts +++ b/src/browser/history/playwrightTrace/PlaywrightTraceDecoder.ts @@ -13,8 +13,8 @@ import { assertArtifactFileSelectedSource } from "../../../artifacts/ArtifactFil import { hashStableRootArtifactHandle } from "../../../artifacts/inventory/hashStableRootArtifact.js"; import { AnalysisResourceConstraintError, - AnalysisCancelledError, AnalysisUnsupportedTargetError, + cancelledBy, } from "../../../domain/analysisErrorCore.js"; import { fixedAllowanceOptions } from "../../../domain/fixedAllowanceRemediation.js"; import { err, ok } from "../../../domain/result.js"; @@ -209,8 +209,7 @@ class TraceArchiveRead { reader, }); if (outcome.kind === "completed" && this.signal?.aborted) { - const cancellation = new AnalysisCancelledError(operation, { - cause: this.signal.reason, + const cancellation = cancelledBy(operation, this.signal, { partialObservation: this.complete ?? this.identity, }); this.primary = cancellation; @@ -232,10 +231,7 @@ export class PlaywrightTraceDecoder implements PlaywrightTracePort { #running = false; constructor(readonly resources: ArtifactResourceScope) {} async inspect(path: string, signal?: AbortSignal) { - if (signal?.aborted) - return err( - new AnalysisCancelledError(operation, { cause: signal.reason }), - ); + if (signal?.aborted) return err(cancelledBy(operation, signal)); if (this.#running) return err( new AnalysisResourceConstraintError( diff --git a/src/browser/history/playwrightTrace/TraceFailure.ts b/src/browser/history/playwrightTrace/TraceFailure.ts index 54ce863b7f..a315dd702d 100644 --- a/src/browser/history/playwrightTrace/TraceFailure.ts +++ b/src/browser/history/playwrightTrace/TraceFailure.ts @@ -2,9 +2,10 @@ import { AnalysisError } from "../../../domain/analysisErrorBase.js"; import { AnalysisAccessDeniedError, AnalysisArtifactChangedError, - AnalysisCancelledError, AnalysisInputError, AnalysisResourceConstraintError, + cancelledBy, + carriedReason, } from "../../../domain/analysisErrorCore.js"; import { ArtifactOperationError } from "../../../domain/artifactOperationError.js"; import { PLAYWRIGHT_TRACE_OPERATION as operation } from "../../../domain/playwrightTrace/playwrightTrace.js"; @@ -18,9 +19,10 @@ export const traceFailure = (cause: unknown, path: string): AnalysisError => { if (cause instanceof AnalysisError) return cause; if (cause instanceof ArtifactReaderFailure) { if (cause.reason === "cancelled") - return new AnalysisCancelledError(operation, { - cause: artifactFileCancellationCause(cause), - }); + return cancelledBy( + operation, + carriedReason(artifactFileCancellationCause(cause)), + ); if (cause.reason === "integrity") return new AnalysisArtifactChangedError(operation, path, cause.message, { cause, diff --git a/src/browser/modules/NativeModuleResolver.ts b/src/browser/modules/NativeModuleResolver.ts index 3570ae1c0f..ff4e199d9b 100644 --- a/src/browser/modules/NativeModuleResolver.ts +++ b/src/browser/modules/NativeModuleResolver.ts @@ -18,10 +18,10 @@ import type { AnalysisError } from "../../domain/analysisErrorBase.js"; import { analysisErrorWithCleanupFailure } from "../../domain/analysisErrorCleanup.js"; import { analysisErrorWithCancellation } from "../../domain/analysisErrorCancellation.js"; import { - AnalysisCancelledError, AnalysisCapabilityUnavailableError, AnalysisOutputError, AnalysisTimeoutError, + cancelledBy, } from "../../domain/analysisErrorCore.js"; import { BrowserObservationError } from "../../domain/browserObservationError.js"; import { jsonObjectSchema } from "../../domain/jsonValue.js"; @@ -128,9 +128,7 @@ export class NativeModuleResolver implements WebModuleResolutionPort { options?: ExecutionOptions, ): ReturnType { if (options?.signal?.aborted === true) - return err( - new AnalysisCancelledError(OPERATION, { cause: options.signal.reason }), - ); + return err(cancelledBy(OPERATION, options.signal)); const pending = await this.#retryCleanup(); if (pending !== undefined) return err(pending); const executable = this.environment.REA_BROWSER_EXECUTABLE; @@ -159,9 +157,7 @@ export class NativeModuleResolver implements WebModuleResolutionPort { let interruption: AnalysisError | undefined; const interrupted = (): AnalysisError => deadline.interruption === "cancelled" - ? new AnalysisCancelledError(OPERATION, { - cause: deadline.signal.reason, - }) + ? cancelledBy(OPERATION, deadline.signal) : new AnalysisTimeoutError(OPERATION, TIMEOUT_MS); const assertActive = (): void => { if (!deadline.signal.aborted) return; @@ -288,14 +284,15 @@ export class NativeModuleResolver implements WebModuleResolutionPort { result = err( analysisErrorWithCancellation( result.ok ? undefined : result.error, - new AnalysisCancelledError(OPERATION, { - cause: options.signal.reason, - ...(result.ok + cancelledBy( + OPERATION, + options.signal, + result.ok ? { partialObservation: completedObservation(input, result.value), } - : {}), - }), + : {}, + ), ), ); if (cleanupError !== undefined) diff --git a/src/captures/TsharkCaptureProvider.ts b/src/captures/TsharkCaptureProvider.ts index 7604768bb6..688bec93df 100644 --- a/src/captures/TsharkCaptureProvider.ts +++ b/src/captures/TsharkCaptureProvider.ts @@ -51,10 +51,11 @@ import { } from "../domain/analysisErrorBase.js"; import { AnalysisArtifactChangedError, - AnalysisCancelledError, AnalysisCapabilityUnavailableError, AnalysisOutputError, AnalysisResourceConstraintError, + cancelledBy, + carriedReason, } from "../domain/analysisErrorCore.js"; import { createAnalysisExecutionEvidence } from "../domain/analysisObservation.js"; import { fixedAllowanceOptions } from "../domain/fixedAllowanceRemediation.js"; @@ -153,9 +154,7 @@ export class TsharkCaptureProvider implements PacketCapturePort { let result: Result; try { if (options?.signal?.aborted) - throw new AnalysisCancelledError(operation, { - cause: options.signal.reason, - }); + throw cancelledBy(operation, options.signal); await this.#resources.release(); if (!tsharkHostSupported(this.platform)) throw new Error( @@ -527,12 +526,11 @@ export class TsharkCaptureProvider implements PacketCapturePort { operation, ); if (cause instanceof ArtifactReaderFailure && cause.reason === "cancelled") - return new AnalysisCancelledError(operation, { - cause: artifactFileCancellationCause(cause), - ...(observedOutput === undefined - ? {} - : { capturedOutput: observedOutput }), - }); + return cancelledBy( + operation, + carriedReason(artifactFileCancellationCause(cause)), + observedOutput === undefined ? {} : { capturedOutput: observedOutput }, + ); if (cause instanceof FieldDiscoveryBudgetError) return new AnalysisResourceConstraintError( operation, diff --git a/src/electron/ElectronFuseFailures.ts b/src/electron/ElectronFuseFailures.ts index fbbd472c52..4119e964ab 100644 --- a/src/electron/ElectronFuseFailures.ts +++ b/src/electron/ElectronFuseFailures.ts @@ -14,10 +14,11 @@ import { AnalysisError } from "../domain/analysisErrorBase.js"; import { AnalysisAccessDeniedError, AnalysisArtifactChangedError, - AnalysisCancelledError, AnalysisInputError, AnalysisOutputError, AnalysisResourceConstraintError, + cancelledBy, + carriedReason, } from "../domain/analysisErrorCore.js"; import { fixedAllowanceOptions } from "../domain/fixedAllowanceRemediation.js"; import { ProviderAdapterError } from "../domain/providerAdapterError.js"; @@ -54,9 +55,7 @@ export const electronFuseFailure = ( ? electronFuseFailure(outcome.cause, path, operation) : cancellation === undefined ? undefined - : new AnalysisCancelledError(operation, { - cause: cancellation.reason, - }); + : cancelledBy(operation, carriedReason(cancellation.reason)); if (primary !== undefined) return analysisErrorWithCleanupFailure( primary, @@ -67,14 +66,13 @@ export const electronFuseFailure = ( return cleanup; } if (cause instanceof ArtifactReaderFailure && cause.reason === "cancelled") - return new AnalysisCancelledError(operation, { - cause: artifactFileCancellationCause(cause), - }); + return cancelledBy( + operation, + carriedReason(artifactFileCancellationCause(cause)), + ); const cancellation = regularFileCancellation(cause); if (cancellation !== undefined) - return new AnalysisCancelledError(operation, { - cause: cancellation.reason, - }); + return cancelledBy(operation, carriedReason(cancellation.reason)); if (cause instanceof ElectronFusePathFailure) return new AnalysisInputError(operation, { cause }, [ { path: ["path"], reason: cause.reason, message: cause.message }, diff --git a/src/inspector/JavaScriptRuntimeScope.ts b/src/inspector/JavaScriptRuntimeScope.ts index c13946eefb..ed46d46347 100644 --- a/src/inspector/JavaScriptRuntimeScope.ts +++ b/src/inspector/JavaScriptRuntimeScope.ts @@ -13,7 +13,7 @@ import type { JavaScriptRuntimeLocation, JavaScriptRuntimeTargetLocation, } from "../domain/javascript/javascriptRuntimeObservation.js"; -import { AnalysisCancelledError } from "../domain/analysisErrorCore.js"; +import { cancelledBy } from "../domain/analysisErrorCore.js"; import { authorizedElectronFile, type ElectronFileScope, @@ -33,8 +33,7 @@ export const throwIfRuntimeObservationCancelled = ( | "observe_javascript_runtime" | "list_javascript_runtime_targets" = "observe_javascript_runtime", ): void => { - if (signal?.aborted === true) - throw new AnalysisCancelledError(operation, { cause: signal.reason }); + if (signal?.aborted === true) throw cancelledBy(operation, signal); }; /** diff --git a/src/inspector/V8InspectorProvider.ts b/src/inspector/V8InspectorProvider.ts index 51c4459645..db197743a2 100644 --- a/src/inspector/V8InspectorProvider.ts +++ b/src/inspector/V8InspectorProvider.ts @@ -19,6 +19,8 @@ import { import { AnalysisError } from "../domain/analysisErrorBase.js"; import { AnalysisCancelledError, + cancelledBy, + carriedReason, OwnerCancellation, } from "../domain/analysisErrorCore.js"; import { BrowserObservationError } from "../domain/browserObservationError.js"; @@ -316,29 +318,32 @@ export class V8InspectorProvider implements JavaScriptRuntimeObservationPort { ), ]; return err( - new AnalysisCancelledError("list_javascript_runtime_targets", { - cause: cause.cause, - diagnostics: { - completed_target_inventory: { - runtime: discovery.runtime, - targets: allowed - .sort((left, right) => - left.id < right.id ? -1 : left.id > right.id ? 1 : 0, - ) - .map(projectTarget), - excluded: { ...excluded, unconnectable }, - targets_examined: targetsExamined, - targets_discovered: discovery.targets.length, - complete, - limitations: complete - ? limitations - : [ - ...limitations, - "Cancellation stopped target authorization; unexamined targets are unknown, not excluded.", - ], + cancelledBy( + "list_javascript_runtime_targets", + carriedReason(cause.cause), + { + diagnostics: { + completed_target_inventory: { + runtime: discovery.runtime, + targets: allowed + .sort((left, right) => + left.id < right.id ? -1 : left.id > right.id ? 1 : 0, + ) + .map(projectTarget), + excluded: { ...excluded, unconnectable }, + targets_examined: targetsExamined, + targets_discovered: discovery.targets.length, + complete, + limitations: complete + ? limitations + : [ + ...limitations, + "Cancellation stopped target authorization; unexamined targets are unknown, not excluded.", + ], + }, }, }, - }), + ), ); } return err(providerError(cause, "list_javascript_runtime_targets")); @@ -479,12 +484,10 @@ export class V8InspectorProvider implements JavaScriptRuntimeObservationPort { // waiting for its bounded transport close. Preserve it as the primary // result even when cleanup completed normally. if (options.signal?.aborted === true && outcome?.ok === true) { - const cancellation = new AnalysisCancelledError( + const cancellation = cancelledBy( "observe_javascript_runtime", - { - cause: options.signal.reason, - partialObservation: outcome.value, - }, + options.signal, + { partialObservation: outcome.value }, ); outcome = err(cancellation); } diff --git a/src/javascript/analysis/JavaScriptAnalysisWorker.ts b/src/javascript/analysis/JavaScriptAnalysisWorker.ts index b61c4d7a45..4b3d6f4c8e 100644 --- a/src/javascript/analysis/JavaScriptAnalysisWorker.ts +++ b/src/javascript/analysis/JavaScriptAnalysisWorker.ts @@ -40,6 +40,7 @@ import { AnalysisTimeoutError, AnalysisOutputError, AnalysisResourceConstraintError, + cancelledBy, } from "../../domain/analysisErrorCore.js"; import { ProviderAdapterError } from "../../domain/providerAdapterError.js"; import { ProviderCleanupError } from "../../domain/providerCleanupError.js"; @@ -332,8 +333,7 @@ export class JavaScriptAnalysisWorker { JavaScriptSourceAnalysisFailure >; try { - if (signal?.aborted === true) - throw new AnalysisCancelledError(OPERATION, { cause: signal.reason }); + if (signal?.aborted === true) throw cancelledBy(OPERATION, signal); if (!file.text.included || file.kind !== "javascript") throw new TypeError( "JavaScript analysis worker requires included JavaScript text", @@ -566,8 +566,7 @@ export class JavaScriptAnalysisWorker { if (deadline?.interruption === "cancelled") analysisErrorWithCancellation( error, - new AnalysisCancelledError(OPERATION, { - cause: deadline.signal.reason, + cancelledBy(OPERATION, deadline.signal, { capturedOutput: this.#captured(), }), ); @@ -616,8 +615,7 @@ export class JavaScriptAnalysisWorker { const primary = signal?.aborted ? analysisErrorWithCancellation( previous, - new AnalysisCancelledError(OPERATION, { - cause: signal.reason, + cancelledBy(OPERATION, signal, { capturedOutput: this.#captured(), }), ) @@ -635,10 +633,7 @@ export class JavaScriptAnalysisWorker { if (signal?.aborted === true) { const error = analysisErrorWithCancellation( result.ok ? undefined : result.error.error, - new AnalysisCancelledError(OPERATION, { - cause: signal.reason, - capturedOutput: this.#captured(), - }), + cancelledBy(OPERATION, signal, { capturedOutput: this.#captured() }), ); if (result.ok) this.#lastFailure = error; result = err({ error, javascript, module, projection }); @@ -1221,8 +1216,7 @@ export class JavaScriptAnalysisWorker { remediationAction: "Reuse accessible partial_observation Evidence to avoid recomputation; reading it is not a prerequisite for a corrected retry. Increase analysis_timeout_ms through MCP, or use rea analyze-javascript-application --analysis-timeout-ms on the CLI. Alternatively, reduce semantic limits or select exact inventoried source_paths (CLI: --source-path on that command). Omit source_paths to analyze all sources; an empty array is invalid. detail: summary reduces response size, not analysis work. Allow execution, cleanup and delivery headroom in the MCP client tool deadline; increasing that client deadline alone does not change this server budget. Resolve any reported incomplete cleanup before retrying.", }) - : new AnalysisCancelledError(OPERATION, { - cause: deadline.signal.reason, + : cancelledBy(OPERATION, deadline.signal, { capturedOutput: this.#captured(), }); } diff --git a/src/javascript/recovery/RecoveryFiles.ts b/src/javascript/recovery/RecoveryFiles.ts index e4830ee3e3..253635c309 100644 --- a/src/javascript/recovery/RecoveryFiles.ts +++ b/src/javascript/recovery/RecoveryFiles.ts @@ -28,6 +28,8 @@ import { AnalysisOutputError, AnalysisResourceConstraintError, AnalysisTimeoutError, + cancelledBy, + carriedReason, } from "../../domain/analysisErrorCore.js"; import { NonRegularFileReadError, @@ -109,19 +111,23 @@ const scanRecoveryFile = async ( } catch (cause: unknown) { if (cause instanceof ArtifactFileCleanupFailure) throw cause; if (cause instanceof ArtifactFileCancelled) - throw new AnalysisCancelledError(OPERATION, { - cause: cause.fileCancellation.reason, - partialObservation: fileSnapshotPartialObservationSchema.parse( - Object.assign({}, cause.fileCancellation.outcome.value, { - kind: "file-snapshot-hash", - path, - }), - ), - }); + throw cancelledBy( + OPERATION, + carriedReason(cause.fileCancellation.reason), + { + partialObservation: fileSnapshotPartialObservationSchema.parse( + Object.assign({}, cause.fileCancellation.outcome.value, { + kind: "file-snapshot-hash", + path, + }), + ), + }, + ); if (cause instanceof ArtifactReaderFailure && cause.reason === "cancelled") - throw new AnalysisCancelledError(OPERATION, { - cause: artifactFileCancellationCause(cause), - }); + throw cancelledBy( + OPERATION, + carriedReason(artifactFileCancellationCause(cause)), + ); if ( cause instanceof Error && "code" in cause && @@ -482,8 +488,7 @@ export const recoveryFailureMessage = (cause: unknown): string => /** Cancellation remains distinct from malformed source or provider output. */ export const checkRecoveryCancellation = (signal?: AbortSignal): void => { - if (signal?.aborted === true) - throw new AnalysisCancelledError(OPERATION, { cause: signal.reason }); + if (signal?.aborted === true) throw cancelledBy(OPERATION, signal); }; /** Keep both tool invocations within one operation's execution budget. */ diff --git a/src/javascript/recovery/WakaruCommand.ts b/src/javascript/recovery/WakaruCommand.ts index 89a5e8f4c1..28a091af95 100644 --- a/src/javascript/recovery/WakaruCommand.ts +++ b/src/javascript/recovery/WakaruCommand.ts @@ -8,6 +8,7 @@ import { AnalysisCancelledError, AnalysisCapabilityUnavailableError, AnalysisOutputError, + cancelledBy, } from "../../domain/analysisErrorCore.js"; import { ArtifactReaderFailure } from "../../artifacts/ArtifactReader.js"; import type { ArtifactResourceScope } from "../../artifacts/ArtifactResourceScope.js"; @@ -200,9 +201,7 @@ export const runWakaruCommand = async (context: WakaruCommandContext) => { context.signal?.aborted === true ? analysisErrorWithCancellation( physical, - new AnalysisCancelledError(OPERATION, { - cause: context.signal.reason, - }), + cancelledBy(OPERATION, context.signal), ) : physical; } @@ -269,9 +268,7 @@ export const runWakaruCommand = async (context: WakaruCommandContext) => { const stopped = await supervisor.stop(); const snapshot = supervisor.snapshot(); if (context.signal?.aborted === true) { - const cancelled = new AnalysisCancelledError(OPERATION, { - cause: context.signal.reason, - }); + const cancelled = cancelledBy(OPERATION, context.signal); if (failure === undefined || failure instanceof AnalysisError) failure = analysisErrorWithCancellation(failure, cancelled); } diff --git a/src/javascript/recovery/WakaruProvider.ts b/src/javascript/recovery/WakaruProvider.ts index d2167b318c..ef0df3a700 100644 --- a/src/javascript/recovery/WakaruProvider.ts +++ b/src/javascript/recovery/WakaruProvider.ts @@ -14,9 +14,10 @@ import { SafeOutputTreeCreationFailure } from "../../artifacts/SafeOutputTreeCre import { AnalysisError } from "../../domain/analysisErrorBase.js"; import { analysisErrorWithCancellation } from "../../domain/analysisErrorCancellation.js"; import { - AnalysisCancelledError, AnalysisCapabilityUnavailableError, AnalysisOutputError, + cancelledBy, + carriedReason, } from "../../domain/analysisErrorCore.js"; import { ArtifactReaderFailure } from "../../artifacts/ArtifactReader.js"; import { artifactFileCancellationCause } from "../../artifacts/ArtifactFileOperation.js"; @@ -167,15 +168,18 @@ export class WakaruProvider implements JavaScriptRecoveryPort { primary instanceof ArtifactReaderFailure && primary.reason === "cancelled" && primary.cleanup === undefined - ? new AnalysisCancelledError(OPERATION, { - cause: artifactFileCancellationCause(primary), - ...(primary.partialObservation === undefined - ? {} - : { partialObservation: primary.partialObservation }), - ...(reportFailure === undefined - ? {} - : { capturedOutput: reportFailure.capturedOutput }), - }) + ? cancelledBy( + OPERATION, + carriedReason(artifactFileCancellationCause(primary)), + { + ...(primary.partialObservation === undefined + ? {} + : { partialObservation: primary.partialObservation }), + ...(reportFailure === undefined + ? {} + : { capturedOutput: reportFailure.capturedOutput }), + }, + ) : primary; const residuals: string[] = []; const cleanupFailures: { resource: string; reason: string }[] = []; @@ -269,7 +273,7 @@ export class WakaruProvider implements JavaScriptRecoveryPort { }); failure = analysisErrorWithCancellation( analysisFailure, - new AnalysisCancelledError(OPERATION, { cause: signal.reason }), + cancelledBy(OPERATION, signal), ); } if (residuals.length > 0) { diff --git a/src/javascript/sourceMaps/SourceMapCommand.ts b/src/javascript/sourceMaps/SourceMapCommand.ts index 137ac06f5b..603f52cf66 100644 --- a/src/javascript/sourceMaps/SourceMapCommand.ts +++ b/src/javascript/sourceMaps/SourceMapCommand.ts @@ -13,10 +13,11 @@ import { AnalysisError } from "../../domain/analysisErrorBase.js"; import { analysisErrorWithCleanupFailure } from "../../domain/analysisErrorCleanup.js"; import { analysisErrorWithCancellation } from "../../domain/analysisErrorCancellation.js"; import { - AnalysisCancelledError, AnalysisOutputError, AnalysisResourceConstraintError, AnalysisTimeoutError, + cancelledBy, + carriedReason, } from "../../domain/analysisErrorCore.js"; import { ProviderAdapterError } from "../../domain/providerAdapterError.js"; import { ProviderCleanupError } from "../../domain/providerCleanupError.js"; @@ -148,7 +149,7 @@ export const runSourceMapCommand = async ( const primary = signal?.aborted ? analysisErrorWithCancellation( knownPhysical ? physical : undefined, - new AnalysisCancelledError(OPERATION, { cause: signal.reason }), + cancelledBy(OPERATION, signal), ) : physical; if (preparation?.cleanupFailure === undefined) return err(primary); @@ -194,8 +195,7 @@ export const runSourceMapCommand = async ( ? err( analysisErrorWithCancellation( result.ok ? undefined : result.error, - new AnalysisCancelledError(OPERATION, { - cause: options.signal.reason, + cancelledBy(OPERATION, options.signal, { partialObservation: observation, }), ), @@ -394,9 +394,7 @@ class OwnedCodec implements SourceMapCleanupOwner { if (outcome?.kind === "physical") throw this.#physicalFailure(outcome.observation); if (outcome?.kind === "cancelled") - throw new AnalysisCancelledError(OPERATION, { - cause: outcome.callerReason, - }); + throw cancelledBy(OPERATION, carriedReason(outcome.callerReason)); if (outcome?.kind === "timeout") throw new AnalysisTimeoutError( OPERATION, @@ -411,9 +409,7 @@ class OwnedCodec implements SourceMapCleanupOwner { if (outcome?.kind === "physical") return this.#physicalFailure(outcome.observation); if (outcome?.kind === "cancelled") - return new AnalysisCancelledError(OPERATION, { - cause: outcome.callerReason, - }); + return cancelledBy(OPERATION, carriedReason(outcome.callerReason)); if (outcome?.kind === "timeout") return new AnalysisTimeoutError( OPERATION, diff --git a/src/javascript/sourceMaps/SourceMapDecoder.ts b/src/javascript/sourceMaps/SourceMapDecoder.ts index 1ca387a5fc..ced145a540 100644 --- a/src/javascript/sourceMaps/SourceMapDecoder.ts +++ b/src/javascript/sourceMaps/SourceMapDecoder.ts @@ -8,9 +8,9 @@ import type { WebSourceMapPort } from "../../application/WebSourceLocationPorts. import type { AnalysisError } from "../../domain/analysisErrorBase.js"; import { analysisErrorWithCancellation } from "../../domain/analysisErrorCancellation.js"; import { - AnalysisCancelledError, AnalysisCapabilityUnavailableError, AnalysisInputError, + cancelledBy, } from "../../domain/analysisErrorCore.js"; import { ArtifactOperationError } from "../../domain/artifactOperationError.js"; import { err, type Result } from "../../domain/result.js"; @@ -79,16 +79,12 @@ export class SourceMapDecoder implements WebSourceMapPort { options?.signal?.aborted ? analysisErrorWithCancellation( cleanupFailure, - new AnalysisCancelledError(OPERATION, { - cause: options.signal.reason, - }), + cancelledBy(OPERATION, options.signal), ) : cleanupFailure, ); if (options?.signal?.aborted) - return err( - new AnalysisCancelledError(OPERATION, { cause: options.signal.reason }), - ); + return err(cancelledBy(OPERATION, options.signal)); const request = sourceMapCodecInputSchema.safeParse({ text: input.text, url: input.url, @@ -125,9 +121,7 @@ export class SourceMapDecoder implements WebSourceMapPort { encodedBytes += bytes; } if (options?.signal?.aborted) - return err( - new AnalysisCancelledError(OPERATION, { cause: options.signal.reason }), - ); + return err(cancelledBy(OPERATION, options.signal)); const serialized = encoded.join(""); const retainedCleanup: SourceMapCleanupRetention = {}; const response = await runSourceMapCommand(