* feat(javascript): recover code-split bundles from multiple inputs recover_javascript_sources accepted a single path, and validateModuleSet bound every module's provenance to that one snapshot. A code-split app (runtime + chunks, or an Electron main/preload/renderer set) had to be recovered one file per call, and modules recovered alone could not resolve imports across chunks: cross-chunk require(id) references stayed as numeric ids with property reads. Add an optional additional_paths input. The listed files are concatenated, in order after path, into the single engine snapshot, so one Wakaru run sees every module and cross-chunk imports resolve. validateModuleSet's single-snapshot binding is unchanged. Module provenance is attributed back to the input file its first extraction range starts in, with byte ranges rebased to that file's own offsets; a module whose ranges cross an input boundary keeps the concatenated snapshot identity and offsets, recorded as an explicit limitation. Single-path behavior is byte-for-byte unchanged (one segment, offset 0). Fixes #1605 * fix(javascript): preserve source identity in multi-input recovery --------- Co-authored-by: JingHao-Leon <w15909130000@gmail.com> Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
83 lines
3.0 KiB
TypeScript
83 lines
3.0 KiB
TypeScript
import { z } from "incur";
|
|
import { JavaScriptRecoveryService } from "../application/javascript/JavaScriptRecoveryService.js";
|
|
import { createJavaScriptRecoveryProvider } from "../composition/javascriptRecovery.js";
|
|
import { CLI_COMMANDS } from "../cliCommandNames.js";
|
|
import { logCliCommand } from "../cliLogging.js";
|
|
import { projectAnalysisError } from "../domain/analysisErrorProjection.js";
|
|
import type { Logger } from "pino";
|
|
import type { CliInstance } from "./types.js";
|
|
import { withCommandCancellation } from "./commandCancellation.js";
|
|
|
|
/** Share source recovery with MCP while keeping CLI spelling at this boundary. */
|
|
export const registerJavaScriptRecoveryCommands = (
|
|
cli: CliInstance,
|
|
logger: Logger,
|
|
environment: Readonly<Record<string, string | undefined>>,
|
|
): void => {
|
|
const service = new JavaScriptRecoveryService(
|
|
createJavaScriptRecoveryProvider(environment),
|
|
);
|
|
cli.command(CLI_COMMANDS.recoverJavaScriptSources, {
|
|
description:
|
|
"Recover readable JavaScript modules with source provenance and verified files",
|
|
args: z.object({
|
|
path: z
|
|
.string()
|
|
.describe("Absolute UTF-8 JavaScript input; never executed"),
|
|
outputDirectory: z.string().describe("Absolute absent output directory"),
|
|
}),
|
|
options: z.object({
|
|
additionalPath: z
|
|
.array(z.string())
|
|
.optional()
|
|
.describe(
|
|
"Related absolute UTF-8 JavaScript file; repeat for multiple chunks",
|
|
),
|
|
extractionMode: z
|
|
.enum(["structural", "heuristic", "inspection"])
|
|
.optional()
|
|
.describe(
|
|
"Structural boundaries, heuristic fallback, or inspection-only regions",
|
|
),
|
|
rewriteLevel: z
|
|
.enum(["minimal", "standard", "aggressive"])
|
|
.optional()
|
|
.describe(
|
|
"Upstream syntax recovery level; runtime equivalence remains unknown",
|
|
),
|
|
}),
|
|
run: ({ args, options }) =>
|
|
withCommandCancellation(async (signal) => {
|
|
try {
|
|
return await logCliCommand(
|
|
logger,
|
|
CLI_COMMANDS.recoverJavaScriptSources,
|
|
async () => {
|
|
const result = await service.recover(
|
|
{
|
|
path: args.path,
|
|
output_directory: args.outputDirectory,
|
|
...(options.additionalPath === undefined
|
|
? {}
|
|
: { additional_paths: options.additionalPath }),
|
|
...(options.extractionMode === undefined
|
|
? {}
|
|
: { extraction_mode: options.extractionMode }),
|
|
...(options.rewriteLevel === undefined
|
|
? {}
|
|
: { rewrite_level: options.rewriteLevel }),
|
|
},
|
|
{ signal },
|
|
);
|
|
return result.ok
|
|
? result.value
|
|
: projectAnalysisError(result.error);
|
|
},
|
|
);
|
|
} finally {
|
|
await service.close();
|
|
}
|
|
}),
|
|
});
|
|
};
|