mirror of
https://github.com/morluto/rea.git
synced 2026-10-26 17:35:11 +00:00
refactor(errors): centralize input failure construction
Carry caller messages through the existing schema issue owner instead of constructing and discarding an intermediate input error. Remove forwarding schema helpers and migrate equivalent constructors across investigation, firmware, dylib, process, and screenshot workflows while preserving diagnostic paths and causes.
This commit is contained in:
1 parent
5f4e39fea2
commit
1635eadfcc
9 files changed
+51
-78
No files matched your search
@@ -9,12 +9,11 @@ import {
|
||||
} from "../../domain/firmware/firmwareAnalysis.js";
|
||||
import {
|
||||
AnalysisCancelledError,
|
||||
AnalysisInputError,
|
||||
AnalysisOutputError,
|
||||
} from "../../domain/analysisErrorCore.js";
|
||||
import type { AnalysisError } from "../../domain/analysisErrorBase.js";
|
||||
import type { Evidence } from "../../domain/evidence.js";
|
||||
import { projectInputIssues } from "../../domain/inputIssueProjection.js";
|
||||
import { analysisInputErrorFromIssues } from "../../domain/inputIssueProjection.js";
|
||||
import { err, type Result } from "../../domain/result.js";
|
||||
import { completeProviderEvidence } from "../ProviderOperationExecution.js";
|
||||
|
||||
@@ -39,10 +38,11 @@ export class FirmwareAnalysisService {
|
||||
const parsed = firmwareRequestSchema.safeParse({ operation, input });
|
||||
if (!parsed.success)
|
||||
return err(
|
||||
new AnalysisInputError(
|
||||
analysisInputErrorFromIssues(
|
||||
operation,
|
||||
parsed.error.issues,
|
||||
{ operation, input },
|
||||
{ cause: parsed.error },
|
||||
projectInputIssues(parsed.error.issues, { operation, input }),
|
||||
),
|
||||
);
|
||||
const executed = await this.provider.execute(parsed.data, options);
|
||||
|
||||
@@ -149,10 +149,11 @@ const parseCaptureEvidence = (input: unknown, side: "left" | "right") => {
|
||||
const failure = parsed.error;
|
||||
switch (failure.kind) {
|
||||
case "invalid-evidence":
|
||||
throw new AnalysisInputError(
|
||||
throw analysisInputErrorFromIssues(
|
||||
"compare_process_captures",
|
||||
failure.cause.issues,
|
||||
input,
|
||||
{ cause: failure.cause },
|
||||
projectInputIssues(failure.cause.issues, input),
|
||||
);
|
||||
case "wrong-workflow":
|
||||
throw processInputFailure(
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
type DylibTreeView,
|
||||
type MachoImageFacts,
|
||||
} from "../../domain/apple/dylibResolution.js";
|
||||
import { projectInputIssues } from "../../domain/inputIssueProjection.js";
|
||||
import { analysisInputErrorFromIssues } from "../../domain/inputIssueProjection.js";
|
||||
import { ArtifactReaderFailure } from "../ArtifactReader.js";
|
||||
import { DirectoryArtifactReader } from "../DirectoryArtifactReader.js";
|
||||
import { consumeWithOrderedReads } from "../OrderedReads.js";
|
||||
@@ -109,10 +109,11 @@ export const traceDylibResolution = async (options: {
|
||||
}): Promise<DylibResolutionResult> => {
|
||||
const parsed = dylibResolutionInputSchema.safeParse(options.parameters);
|
||||
if (!parsed.success)
|
||||
throw new AnalysisInputError(
|
||||
throw analysisInputErrorFromIssues(
|
||||
OPERATION,
|
||||
parsed.error.issues,
|
||||
options.parameters,
|
||||
{ cause: parsed.error },
|
||||
projectInputIssues(parsed.error.issues, options.parameters),
|
||||
);
|
||||
const resources = options.resourceScope ?? new ArtifactResourceScope();
|
||||
return await resources.run(async () => {
|
||||
|
||||
@@ -20,7 +20,10 @@ import {
|
||||
import { compareBrowserScenariosInputSchema } from "../domain/browserScenarioDiffValues.js";
|
||||
import { browserScenarioCaptureSchema } from "../domain/browserScenarioCapture.js";
|
||||
import { compareWebCapturesInputSchema } from "../domain/webCaptureDiffSchemas.js";
|
||||
import { projectInputIssues } from "../domain/inputIssueProjection.js";
|
||||
import {
|
||||
analysisInputErrorFromIssues,
|
||||
projectInputIssues,
|
||||
} from "../domain/inputIssueProjection.js";
|
||||
import { discoverWebMcpToolsInputSchema } from "../domain/webMcpDiscovery.js";
|
||||
import {
|
||||
captureWebScreenshotInputSchema,
|
||||
@@ -251,10 +254,11 @@ const registerScreenshotDiff = (
|
||||
const parsed = compareWebScreenshotsInputSchema.safeParse(input);
|
||||
if (!parsed.success)
|
||||
return browserCliError(
|
||||
new AnalysisInputError(
|
||||
analysisInputErrorFromIssues(
|
||||
operation,
|
||||
parsed.error.issues,
|
||||
input,
|
||||
{ cause: parsed.error },
|
||||
projectInputIssues(parsed.error.issues, input),
|
||||
),
|
||||
);
|
||||
const result = await compareWebScreenshotEvidence(
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
comparisonSourceEvidenceIds,
|
||||
} from "./comparisonSourceEvidence.js";
|
||||
import { evidenceSchema, type Evidence } from "./evidence.js";
|
||||
import { AnalysisInputError } from "./analysisErrorCore.js";
|
||||
import type { AnalysisInputError } from "./analysisErrorCore.js";
|
||||
import { EvidenceIntegrityError } from "./evidenceErrors.js";
|
||||
import { analysisInputErrorFromIssues } from "./inputIssueProjection.js";
|
||||
import { readEvidence, readEvidencePayload } from "./evidenceValidation.js";
|
||||
@@ -128,10 +128,14 @@ export const findChangedBehavior = (input: unknown): FoundChangedBehavior => {
|
||||
.strictObject({ comparisons: z.array(z.unknown()).min(1) })
|
||||
.safeParse(input);
|
||||
if (!envelope.success)
|
||||
throw inputSchemaError(
|
||||
throw analysisInputErrorFromIssues(
|
||||
OPERATION,
|
||||
envelope.error.issues,
|
||||
input,
|
||||
envelope.error,
|
||||
"Provide at least one comparison Evidence item.",
|
||||
{
|
||||
cause: envelope.error,
|
||||
userMessage: "Provide at least one comparison Evidence item.",
|
||||
},
|
||||
);
|
||||
const { comparisons } = envelope.data;
|
||||
const evidence = comparisons.map((item, index) =>
|
||||
@@ -428,38 +432,15 @@ const firstDuplicateIndex = (values: readonly string[]): number | undefined => {
|
||||
|
||||
const OPERATION = "find_changed_behavior";
|
||||
|
||||
const inputSchemaError = (
|
||||
input: unknown,
|
||||
cause: z.ZodError,
|
||||
userMessage: string,
|
||||
): AnalysisInputError => {
|
||||
const projected = analysisInputErrorFromIssues(
|
||||
OPERATION,
|
||||
cause.issues,
|
||||
input,
|
||||
{ cause },
|
||||
);
|
||||
return new AnalysisInputError(
|
||||
OPERATION,
|
||||
{ cause, userMessage },
|
||||
projected.issues,
|
||||
);
|
||||
};
|
||||
|
||||
const callerInputError = (
|
||||
input: unknown,
|
||||
path: readonly (string | number)[],
|
||||
message: string,
|
||||
): AnalysisInputError => {
|
||||
const projected = analysisInputErrorFromIssues(OPERATION, [], input, {
|
||||
): AnalysisInputError =>
|
||||
analysisInputErrorFromIssues(OPERATION, [], input, {
|
||||
userMessage: message,
|
||||
additionalIssues: [{ path, reason: "invalid_value", message }],
|
||||
});
|
||||
return new AnalysisInputError(
|
||||
OPERATION,
|
||||
{ userMessage: message },
|
||||
projected.issues,
|
||||
);
|
||||
};
|
||||
|
||||
const evidenceIntegrityError = (message: string): EvidenceIntegrityError =>
|
||||
new EvidenceIntegrityError(message, { userMessage: message });
|
||||
@@ -20,6 +20,7 @@ describe("input issue projection", () => {
|
||||
input,
|
||||
{
|
||||
cause,
|
||||
userMessage: "Supply an integer count.",
|
||||
additionalIssues: [
|
||||
{ path: ["count"], reason: "out_of_range", minimum: 0 },
|
||||
],
|
||||
@@ -27,6 +28,7 @@ describe("input issue projection", () => {
|
||||
);
|
||||
|
||||
expect(error.cause).toBe(cause);
|
||||
expect(error.userMessage).toBe("Supply an integer count.");
|
||||
expect(error.issues).toEqual([
|
||||
{ path: ["count"], reason: "invalid_type", expected: "number" },
|
||||
{ path: ["count"], reason: "out_of_range", minimum: 0 },
|
||||
|
||||
@@ -11,12 +11,18 @@ export const analysisInputErrorFromIssues = (
|
||||
input: unknown,
|
||||
options: {
|
||||
readonly cause?: unknown;
|
||||
readonly userMessage?: string;
|
||||
readonly additionalIssues?: readonly AnalysisInputIssue[];
|
||||
} = {},
|
||||
): AnalysisInputError =>
|
||||
new AnalysisInputError(
|
||||
operation,
|
||||
options.cause === undefined ? undefined : { cause: options.cause },
|
||||
{
|
||||
...(options.cause === undefined ? {} : { cause: options.cause }),
|
||||
...(options.userMessage === undefined
|
||||
? {}
|
||||
: { userMessage: options.userMessage }),
|
||||
},
|
||||
[...projectInputIssues(issues, input), ...(options.additionalIssues ?? [])],
|
||||
);
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import { hasComparisonEvidenceIdentity } from "./comparisonEvidenceIdentity.js";
|
||||
import { readEvidenceBundle } from "./evidenceBundle.js";
|
||||
import { AnalysisInputError } from "./analysisErrorCore.js";
|
||||
import { EvidenceIntegrityError } from "./evidenceErrors.js";
|
||||
import { projectInputIssues } from "./inputIssueProjection.js";
|
||||
import { analysisInputErrorFromIssues } from "./inputIssueProjection.js";
|
||||
import { readEvidencePayload } from "./evidenceValidation.js";
|
||||
import { functionComparisonResultSchema } from "./functionComparisonSchemas.js";
|
||||
import { functionDossierSchema } from "./hopperValues.js";
|
||||
@@ -58,10 +58,11 @@ export const verifyReconstruction = (
|
||||
const parsedSpecification =
|
||||
reconstructionSpecificationSchema.safeParse(specificationInput);
|
||||
if (!parsedSpecification.success)
|
||||
throw new AnalysisInputError(
|
||||
throw analysisInputErrorFromIssues(
|
||||
"verify_reconstruction",
|
||||
parsedSpecification.error.issues,
|
||||
specificationInput,
|
||||
{ cause: parsedSpecification.error },
|
||||
projectInputIssues(parsedSpecification.error.issues, specificationInput),
|
||||
);
|
||||
const specification = parsedSpecification.data;
|
||||
const bundleResult = readEvidenceBundle(bundleInput);
|
||||
|
||||
@@ -2,7 +2,7 @@ import { digestCanonicalValue } from "./canonicalDigest.js";
|
||||
import { z } from "zod";
|
||||
|
||||
import { artifactComparisonResultSchema } from "./artifactComparison.js";
|
||||
import { AnalysisInputError } from "./analysisErrorCore.js";
|
||||
import type { AnalysisInputError } from "./analysisErrorCore.js";
|
||||
import { EvidenceIntegrityError } from "./evidenceErrors.js";
|
||||
import { analysisInputErrorFromIssues } from "./inputIssueProjection.js";
|
||||
import { readEvidence, readEvidencePayload } from "./evidenceValidation.js";
|
||||
@@ -169,11 +169,11 @@ export const correlateStaticAndRuntime = (
|
||||
): StaticRuntimeCorrelationResult => {
|
||||
const parsed = staticRuntimeCorrelationEnvelopeSchema.safeParse(input);
|
||||
if (!parsed.success)
|
||||
throw inputSchemaError(
|
||||
input,
|
||||
parsed.error,
|
||||
"Provide valid static and runtime comparison Evidence and mappings.",
|
||||
);
|
||||
throw analysisInputErrorFromIssues(OPERATION, parsed.error.issues, input, {
|
||||
cause: parsed.error,
|
||||
userMessage:
|
||||
"Provide valid static and runtime comparison Evidence and mappings.",
|
||||
});
|
||||
const { data } = parsed;
|
||||
const staticEvidence = data.static_comparisons.map((item, index) =>
|
||||
parseStatic(item, input, index),
|
||||
@@ -513,38 +513,15 @@ const firstDuplicateIndex = (values: readonly string[]): number | undefined => {
|
||||
|
||||
const OPERATION = "correlate_static_and_runtime";
|
||||
|
||||
const inputSchemaError = (
|
||||
input: unknown,
|
||||
cause: z.ZodError,
|
||||
userMessage: string,
|
||||
): AnalysisInputError => {
|
||||
const projected = analysisInputErrorFromIssues(
|
||||
OPERATION,
|
||||
cause.issues,
|
||||
input,
|
||||
{ cause },
|
||||
);
|
||||
return new AnalysisInputError(
|
||||
OPERATION,
|
||||
{ cause, userMessage },
|
||||
projected.issues,
|
||||
);
|
||||
};
|
||||
|
||||
const callerInputError = (
|
||||
input: unknown,
|
||||
path: readonly (string | number)[],
|
||||
message: string,
|
||||
): AnalysisInputError => {
|
||||
const projected = analysisInputErrorFromIssues(OPERATION, [], input, {
|
||||
): AnalysisInputError =>
|
||||
analysisInputErrorFromIssues(OPERATION, [], input, {
|
||||
userMessage: message,
|
||||
additionalIssues: [{ path, reason: "invalid_value", message }],
|
||||
});
|
||||
return new AnalysisInputError(
|
||||
OPERATION,
|
||||
{ userMessage: message },
|
||||
projected.issues,
|
||||
);
|
||||
};
|
||||
|
||||
const evidenceIntegrityError = (message: string): EvidenceIntegrityError =>
|
||||
new EvidenceIntegrityError(message, { userMessage: message });
|
||||
Reference in new issue
Block a user