mirror of
https://github.com/morluto/rea.git
synced 2026-10-28 18:35:53 +00:00
* feat(javascript): recover readable modules with Wakaru Integrate pinned upstream source behind a typed optional recovery port, preserve source identity and producer provenance, and publish verified artifacts for existing JavaScript analysis through CLI and MCP. Add focused real-tool CI with genuine bundler fixtures and bounded resource, cancellation, cleanup, schema and packaged-artifact checks. Assisted-by: Yukari (gensokyo-skills:yukari-boundary-analysis) Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing) * fix(javascript): align recovery catalog and browser verification Share canonical Wakaru provider identity, describe CLI mode options, and format the new workflow test. Disconnect the real page transport after its observation readiness progress instead of a fixed delay. Assisted-by: Yukari (gensokyo-skills:yukari-boundary-analysis) Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing) * refactor(javascript): consume the investigation evidence writer Verify that the SDK recovery handler records complete Evidence through the newly shared investigation owner. Assisted-by: Yukari (gensokyo-skills:yukari-boundary-analysis) * chore(test): account for isolated recovery fixture compilers * fix(javascript): validate recovery paths before acquiring the engine Return field-specific invalid_format diagnostics for relative paths and verify compiled CLI/MCP parity even when the optional engine is unavailable. Assisted-by: Yukari (gensokyo-skills:yukari-boundary-analysis) Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
96 lines
3.3 KiB
TypeScript
96 lines
3.3 KiB
TypeScript
import { isAbsolute } from "node:path";
|
|
|
|
import {
|
|
AnalysisCancelledError,
|
|
AnalysisInputError,
|
|
AnalysisOutputError,
|
|
} from "../domain/analysisErrorCore.js";
|
|
import type { AnalysisError } from "../domain/analysisErrorBase.js";
|
|
import { createEvidence, type Evidence } from "../domain/evidence.js";
|
|
import {
|
|
javascriptRecoveryInputSchema,
|
|
javascriptRecoveryResultSchema,
|
|
} from "../domain/javascriptRecovery.js";
|
|
import { jsonObjectSchema } from "../domain/jsonValue.js";
|
|
import { projectInputIssues } from "../domain/inputIssueProjection.js";
|
|
import { err, ok, type Result } from "../domain/result.js";
|
|
import type { ExecutionOptions } from "./AnalysisProvider.js";
|
|
import type { JavaScriptRecoveryPort } from "./JavaScriptRecoveryPort.js";
|
|
|
|
const OPERATION = "recover_javascript_sources";
|
|
|
|
/** Shared CLI/MCP parsing and Evidence for derived JavaScript artifacts. */
|
|
export class JavaScriptRecoveryService {
|
|
constructor(readonly provider: JavaScriptRecoveryPort) {}
|
|
|
|
/** Recover sources and bind the result to its original immutable input. */
|
|
async recover(
|
|
input: unknown,
|
|
options?: ExecutionOptions,
|
|
): Promise<Result<Evidence, AnalysisError>> {
|
|
if (options?.signal?.aborted === true)
|
|
return err(new AnalysisCancelledError(OPERATION));
|
|
const parsed = javascriptRecoveryInputSchema.safeParse(input);
|
|
if (!parsed.success)
|
|
return err(
|
|
new AnalysisInputError(
|
|
OPERATION,
|
|
{ cause: parsed.error },
|
|
projectInputIssues(parsed.error.issues, input),
|
|
),
|
|
);
|
|
const invalidPaths = (["path", "output_directory"] as const).filter(
|
|
(field) => !isAbsolute(parsed.data[field]),
|
|
);
|
|
if (invalidPaths.length > 0)
|
|
return err(
|
|
new AnalysisInputError(
|
|
OPERATION,
|
|
undefined,
|
|
invalidPaths.map((field) => ({
|
|
path: [field],
|
|
reason: "invalid_format",
|
|
message: `${field} must be an absolute filesystem path on this host.`,
|
|
})),
|
|
),
|
|
);
|
|
const execution = await this.provider.recover(parsed.data, options);
|
|
if (!execution.ok) return execution;
|
|
const result = javascriptRecoveryResultSchema.safeParse(
|
|
execution.value.result,
|
|
);
|
|
if (!result.success || execution.value.subject === null)
|
|
return err(
|
|
new AnalysisOutputError(
|
|
OPERATION,
|
|
"Recovery did not return a valid result bound to the original source",
|
|
),
|
|
);
|
|
if (
|
|
result.data.source.path !== parsed.data.path ||
|
|
execution.value.subject.path !== result.data.source.path ||
|
|
execution.value.subject.sha256 !== result.data.source.sha256 ||
|
|
execution.value.provider.id !== result.data.engine.id ||
|
|
execution.value.provider.version !== result.data.engine.version
|
|
)
|
|
return err(
|
|
new AnalysisOutputError(
|
|
OPERATION,
|
|
"Recovery source or engine identity disagrees with its Evidence binding",
|
|
),
|
|
);
|
|
return ok(
|
|
createEvidence(execution.value.subject, execution.value.provider, {
|
|
operation: OPERATION,
|
|
parameters: jsonObjectSchema.parse(parsed.data),
|
|
result: result.data,
|
|
rawResult: execution.value.rawResult,
|
|
locations: execution.value.locations,
|
|
limitations: execution.value.limitations,
|
|
confidence: "derived",
|
|
authority: "shipped-artifact",
|
|
}),
|
|
);
|
|
}
|
|
}
|