import { analysisProfileSchema, type AnalysisProfileCommitment, } from "../../domain/analysisProfile.js"; import type { BinaryTarget } from "../../domain/binaryTargetTypes.js"; import { AnalysisCancelledError, AnalysisUnsupportedTargetError, } from "../../domain/analysisErrorCore.js"; import type { AnalysisError } from "../../domain/analysisErrorBase.js"; import { jsonObjectSchema, type JsonValue } from "../../domain/jsonValue.js"; import { err, ok, type Result } from "../../domain/result.js"; import type { AnalysisProviderCandidate } from "../AnalysisProvider.js"; import type { AnalysisProviderCandidateStatus } from "./AnalysisProviderRegistry.js"; import { ABORTED, waitForAbortable } from "./AbortablePromise.js"; /** Profile-resolution truth for one discovered provider candidate. */ export interface AnalysisProviderCandidateEvaluation { readonly candidate: AnalysisProviderCandidate; readonly status: AnalysisProviderCandidateStatus; readonly profile?: AnalysisProfileCommitment; readonly profileError?: AnalysisError; } /** Resolve and validate one provider profile without starting its client. */ export const evaluateAnalysisProviderCandidate = async ( candidate: AnalysisProviderCandidate, status: AnalysisProviderCandidateStatus, target: BinaryTarget, signal: AbortSignal | undefined, ): Promise< Result > => { if (signalIsAborted(signal)) return err(new AnalysisCancelledError("open_binary")); let resolved: Awaited>; try { const resolution = await waitForAbortable( candidate.resolveAnalysisProfile( target, signal === undefined ? undefined : { signal }, ), signal, ); if (resolution === ABORTED) return err(new AnalysisCancelledError("open_binary")); resolved = resolution; } catch (cause: unknown) { void cause; return ok( rejectedProfile(candidate, status, "Provider profile resolution threw"), ); } if (signalIsAborted(signal)) return err(new AnalysisCancelledError("open_binary")); if (!resolved.ok && resolved.error instanceof AnalysisCancelledError) return err(resolved.error); if (!resolved.ok) { const error = resolved.error; const rejection = error instanceof AnalysisUnsupportedTargetError ? { candidate, status: { ...status, targetSupport: { status: "unsupported" as const, code: "target_format_unsupported" as const, reason: error.reason, diagnostics: { path: error.path, error_tag: error._tag }, }, }, } : rejectedProfile(candidate, status, error.message, { error_tag: error._tag, }); return ok({ ...rejection, profileError: error }); } try { if (resolved.value.profile === null) return ok( rejectedProfile( candidate, status, "Provider version or analysis profile could not be resolved", ), ); const profile = analysisProfileSchema.parse(resolved.value.profile); const identity = candidate.identity(); if ( profile.provider.id !== identity.id || profile.provider.name !== identity.name || (identity.version !== null && profile.provider.version !== identity.version) ) return ok( rejectedProfile( candidate, status, "Resolved analysis profile has mismatched provider identity", ), ); return ok({ candidate, status, profile }); } catch (cause: unknown) { void cause; return ok( rejectedProfile( candidate, status, "Provider returned an invalid analysis profile", ), ); } }; const signalIsAborted = (signal: AbortSignal | undefined): boolean => signal?.aborted === true; const rejectedProfile = ( candidate: AnalysisProviderCandidate, status: AnalysisProviderCandidateStatus, reason: string, diagnostics: Readonly> = {}, ): AnalysisProviderCandidateEvaluation => ({ candidate, status: { ...status, availability: { status: "unavailable", code: "version_unresolved", reason, diagnostics: jsonObjectSchema.parse({ ...status.availability.diagnostics, ...diagnostics, }), }, }, });