From a82d14e4db4dca989ffa2e7dbe78fd43551da256 Mon Sep 17 00:00:00 2001 From: morluto <76467478+morluto@users.noreply.github.com> Date: Sun, 11 Oct 2026 13:07:54 +0800 Subject: [PATCH] perf(cli): defer command-specific modules until dispatch --- src/application/DirectAnalysis.ts | 5 +- src/application/Doctor.ts | 2 - src/application/DoctorProviderIds.ts | 2 + src/application/Uninstall.ts | 18 +----- src/application/UninstallFailure.ts | 18 ++++++ src/application/Update.ts | 19 +----- src/application/UpdateFailure.ts | 19 ++++++ src/cli/setupCommands.ts | 57 +++++++++-------- src/composition/directAnalysis.ts | 66 +++++++++++++++----- tests/boundary/cli/cliStartupImports.test.ts | 64 +++++++++++++++++-- 10 files changed, 183 insertions(+), 87 deletions(-) create mode 100644 src/application/DoctorProviderIds.ts create mode 100644 src/application/UninstallFailure.ts create mode 100644 src/application/UpdateFailure.ts diff --git a/src/application/DirectAnalysis.ts b/src/application/DirectAnalysis.ts index c8108e614..624bcd17d 100644 --- a/src/application/DirectAnalysis.ts +++ b/src/application/DirectAnalysis.ts @@ -1,7 +1,5 @@ import type { ExecutableFormatHint } from "../domain/dosCom.js"; import type { JsonValue } from "../domain/jsonValue.js"; -import { EnhancedTools } from "./EnhancedTools.js"; -import { executeFunctionAnalysisEvidence } from "./FunctionAnalysisEvidence.js"; import type { DirectAnalysisDependencies } from "./DirectAnalysisDependencies.js"; import type { BinarySession } from "./binary/BinarySession.js"; import type { SessionProviderRoute } from "./binary/SessionProviderRouter.js"; @@ -332,6 +330,8 @@ const executeAnalysisTool = async (input: { }): Promise<{ readonly output: JsonValue; readonly evidence?: Evidence }> => { const { session, tool, signal, evidenceProfile } = input; if (tool === "analyze_function") { + const { executeFunctionAnalysisEvidence } = + await import("./FunctionAnalysisEvidence.js"); const result = await executeFunctionAnalysisEvidence( session, input.arguments, @@ -350,6 +350,7 @@ const executeAnalysisTool = async (input: { tool === "trace_native_values" || tool === "trace_native_ui_action" ) { + const { EnhancedTools } = await import("./EnhancedTools.js"); const result = await new EnhancedTools(session).execute( tool, input.arguments, diff --git a/src/application/Doctor.ts b/src/application/Doctor.ts index ecf0bcc72..2a781b448 100644 --- a/src/application/Doctor.ts +++ b/src/application/Doctor.ts @@ -36,8 +36,6 @@ import { type DoctorScopeReport, } from "./DoctorScope.js"; -/** Deep provider IDs whose readiness can be selected explicitly by doctor. */ -export const DOCTOR_PROVIDER_IDS = ["hopper", "ghidra", "ida"] as const; /** One actionable environment diagnostic. */ export interface DoctorCheck { readonly name: string; diff --git a/src/application/DoctorProviderIds.ts b/src/application/DoctorProviderIds.ts new file mode 100644 index 000000000..4b7f40331 --- /dev/null +++ b/src/application/DoctorProviderIds.ts @@ -0,0 +1,2 @@ +/** Deep-analysis providers whose readiness can be selected explicitly by doctor. */ +export const DOCTOR_PROVIDER_IDS = ["hopper", "ghidra", "ida"] as const; diff --git a/src/application/Uninstall.ts b/src/application/Uninstall.ts index 6310ce99b..3e3099ea3 100644 --- a/src/application/Uninstall.ts +++ b/src/application/Uninstall.ts @@ -29,6 +29,7 @@ import { supportedClients, type SetupClient, } from "./SupportedClients.js"; +export { isUninstallFailure } from "./UninstallFailure.js"; interface ManagedPathStats { readonly uid?: number; @@ -74,23 +75,6 @@ export interface UninstallResult { readonly items: readonly UninstallItem[]; } -/** Whether any requested REA-owned uninstall action failed. */ -export const isUninstallFailure = (result: UninstallResult): boolean => { - const status = result.status; - switch (status) { - case "complete": - return false; - case "failed": - return true; - default: { - const exhaustive: never = status; - throw new TypeError( - `Unhandled uninstall result status: ${String(exhaustive)}`, - ); - } - } -}; - /** Filesystem boundary used by the contained uninstall workflow. */ export interface UninstallHost { clients(): Promise; diff --git a/src/application/UninstallFailure.ts b/src/application/UninstallFailure.ts new file mode 100644 index 000000000..6bb29bf74 --- /dev/null +++ b/src/application/UninstallFailure.ts @@ -0,0 +1,18 @@ +import type { UninstallResult } from "./Uninstall.js"; + +/** Whether any requested REA-owned uninstall action failed. */ +export const isUninstallFailure = (result: UninstallResult): boolean => { + const status = result.status; + switch (status) { + case "complete": + return false; + case "failed": + return true; + default: { + const exhaustive: never = status; + throw new TypeError( + `Unhandled uninstall result status: ${String(exhaustive)}`, + ); + } + } +}; diff --git a/src/application/Update.ts b/src/application/Update.ts index 16b4b6a3a..526fe7f22 100644 --- a/src/application/Update.ts +++ b/src/application/Update.ts @@ -3,6 +3,7 @@ import { gt, valid } from "semver"; import { PRODUCT_IDENTITY } from "../identity.js"; import type { Result } from "../domain/result.js"; import type { IntegrationMaintenance } from "./UpdateMaintenance.js"; +export { isUpdateFailure } from "./UpdateFailure.js"; /** Global npm installation that owns the running package and its entry point. */ export interface NpmInstallation { @@ -68,24 +69,6 @@ export type UpdateResult = readonly remediation: string; }; -/** Whether the update command left the requested release unapplied. */ -export const isUpdateFailure = (result: UpdateResult): boolean => { - const status = result.status; - switch (status) { - case "current": - case "updated": - return false; - case "failed": - return true; - default: { - const exhaustive: never = status; - throw new TypeError( - `Unhandled update result status: ${String(exhaustive)}`, - ); - } - } -}; - /** Resolve the exact npm command for the identified prefix and release. */ export const updateInstallCommand = ( installation: NpmInstallation, diff --git a/src/application/UpdateFailure.ts b/src/application/UpdateFailure.ts new file mode 100644 index 000000000..d7791eaab --- /dev/null +++ b/src/application/UpdateFailure.ts @@ -0,0 +1,19 @@ +import type { UpdateResult } from "./Update.js"; + +/** Whether the update command left the requested release unapplied. */ +export const isUpdateFailure = (result: UpdateResult): boolean => { + const status = result.status; + switch (status) { + case "current": + case "updated": + return false; + case "failed": + return true; + default: { + const exhaustive: never = status; + throw new TypeError( + `Unhandled update result status: ${String(exhaustive)}`, + ); + } + } +}; diff --git a/src/cli/setupCommands.ts b/src/cli/setupCommands.ts index 95d4235ff..d036f708a 100644 --- a/src/cli/setupCommands.ts +++ b/src/cli/setupCommands.ts @@ -1,24 +1,14 @@ import { z } from "incur"; -import { - DOCTOR_PROVIDER_IDS, - runDoctor, - type DoctorScope, -} from "../application/Doctor.js"; +import type { DoctorScope } from "../application/Doctor.js"; +import { DOCTOR_PROVIDER_IDS } from "../application/DoctorProviderIds.js"; import { isSetupFailure, type SetupOptions, } from "../application/SetupTypes.js"; -import { runSetup } from "../application/Setup.js"; -import { systemSetupHost } from "../application/SetupHost.js"; -import { - isUninstallFailure, - runUninstall, - systemUninstallHost, -} from "../application/Uninstall.js"; +import { isUninstallFailure } from "../application/UninstallFailure.js"; import { homeDirectoryFromEnvironment } from "../config/homeDirectory.js"; -import { isUpdateFailure, runUpdate } from "../application/Update.js"; -import { systemUpdateHost } from "../application/UpdateRuntime.js"; +import { isUpdateFailure } from "../application/UpdateFailure.js"; import { confirmInteractiveSetup, renderInteractiveSetupResult, @@ -26,7 +16,6 @@ import { } from "./interactiveSetup.js"; import { PRODUCT_IDENTITY } from "../identity.js"; import { logCliCommand } from "../cliLogging.js"; -import { createSystemDoctorHost } from "../doctorRuntime.js"; import { CLI_COMMANDS } from "../cliCommandNames.js"; import type { Logger } from "pino"; import type { CliInstance } from "./types.js"; @@ -130,13 +119,17 @@ const registerDoctorCommand = ( .describe("Require the installed REA skill identity to be aligned"), }), run: ({ options }) => - logCliCommand(logger, "doctor", () => - runDoctor( + logCliCommand(logger, "doctor", async () => { + const [{ runDoctor }, { createSystemDoctorHost }] = await Promise.all([ + import("../application/Doctor.js"), + import("../doctorRuntime.js"), + ]); + return runDoctor( options.target, createSystemDoctorHost(environment), doctorScope(options), - ), - ), + ); + }), }); }; @@ -157,15 +150,18 @@ const registerMaintenanceCommands = ( logCliCommand( logger, "uninstall", - () => - runUninstall( + async () => { + const { runUninstall, systemUninstallHost } = + await import("../application/Uninstall.js"); + return runUninstall( options.purgeData, systemUninstallHost( homeDirectoryFromEnvironment(environment, process.platform), undefined, environment, ), - ), + ); + }, isUninstallFailure, ), }); @@ -176,12 +172,17 @@ const registerMaintenanceCommands = ( logCliCommand( logger, CLI_COMMANDS.update, - () => - runUpdate( + async () => { + const [{ runUpdate }, { systemUpdateHost }] = await Promise.all([ + import("../application/Update.js"), + import("../application/UpdateRuntime.js"), + ]); + return runUpdate( PRODUCT_IDENTITY.packageVersion, systemUpdateHost(undefined, undefined, environment), formatExplicit ? "structured" : "human", - ), + ); + }, isUpdateFailure, ), }); @@ -208,6 +209,12 @@ const runSetupCommand = async ( const { options } = input; const interactive = setupIsInteractive(options, input.formatExplicit); const hasExplicitScope = setupHasExplicitScope(options); + const [{ runSetup }, { systemSetupHost }, { createSystemDoctorHost }] = + await Promise.all([ + import("../application/Setup.js"), + import("../application/SetupHost.js"), + import("../doctorRuntime.js"), + ]); const result = await runSetup( setupRunOptions(input, interactive, hasExplicitScope), systemSetupHost(createSystemDoctorHost(environment), environment), diff --git a/src/composition/directAnalysis.ts b/src/composition/directAnalysis.ts index 45217d34b..c920626c4 100644 --- a/src/composition/directAnalysis.ts +++ b/src/composition/directAnalysis.ts @@ -1,13 +1,18 @@ import { snapshotEnvironment } from "../process/snapshotEnvironment.js"; import { parseConfig } from "../config/parseConfig.js"; -import { - runDirectAnalysis as executeDirectAnalysis, - runProviderAnalysis as executeProviderAnalysis, - runManagedProviderExecution as executeManagedProvider, -} from "../application/DirectAnalysis.js"; -import { runSessionStatus as executeSessionStatus } from "../application/DirectAnalysisStatus.js"; import type { DirectAnalysisDependencies } from "../application/DirectAnalysisDependencies.js"; +type DirectAnalysisRuntime = typeof import("../application/DirectAnalysis.js"); +type DirectAnalysisStatusRuntime = + typeof import("../application/DirectAnalysisStatus.js"); + +type BoundOperation = Operation extends ( + dependencies: infer _Dependencies, + ...args: infer Arguments +) => infer Result + ? (...args: Arguments) => Result + : never; + /** Bind one-shot analysis and status commands to one caller-selected environment. */ export const createDirectAnalysis = ( selectedEnvironment: Readonly>, @@ -24,10 +29,31 @@ export const createDirectAnalysis = ( return createManagedBinarySession(); }, }; + const runDirectAnalysis: BoundOperation< + DirectAnalysisRuntime["runDirectAnalysis"] + > = async (...args) => { + const { runDirectAnalysis } = + await import("../application/DirectAnalysis.js"); + return runDirectAnalysis(dependencies, ...args); + }; + const runProviderAnalysis: BoundOperation< + DirectAnalysisRuntime["runProviderAnalysis"] + > = async (...args) => { + const { runProviderAnalysis } = + await import("../application/DirectAnalysis.js"); + return runProviderAnalysis(dependencies, ...args); + }; + const runSessionStatus: BoundOperation< + DirectAnalysisStatusRuntime["runSessionStatus"] + > = async (...args) => { + const { runSessionStatus } = + await import("../application/DirectAnalysisStatus.js"); + return runSessionStatus(dependencies, ...args); + }; return { - runDirectAnalysis: executeDirectAnalysis.bind(undefined, dependencies), - runProviderAnalysis: executeProviderAnalysis.bind(undefined, dependencies), - runSessionStatus: executeSessionStatus.bind(undefined, dependencies), + runDirectAnalysis, + runProviderAnalysis, + runSessionStatus, }; }; @@ -35,12 +61,18 @@ export const createDirectAnalysis = ( export type DirectAnalysis = ReturnType; /** Execute managed metadata without configuration or native provider candidates. */ -export const runManagedProviderExecution = executeManagedProvider.bind( - undefined, - { - createManagedBinarySession: async () => { - const { createManagedBinarySession } = await import("./binary.js"); - return createManagedBinarySession(); +export const runManagedProviderExecution: BoundOperation< + DirectAnalysisRuntime["runManagedProviderExecution"] +> = async (...args) => { + const { runManagedProviderExecution } = + await import("../application/DirectAnalysis.js"); + return runManagedProviderExecution( + { + createManagedBinarySession: async () => { + const { createManagedBinarySession } = await import("./binary.js"); + return createManagedBinarySession(); + }, }, - }, -); + ...args, + ); +}; diff --git a/tests/boundary/cli/cliStartupImports.test.ts b/tests/boundary/cli/cliStartupImports.test.ts index 2b8bf9359..3c71bc540 100644 --- a/tests/boundary/cli/cliStartupImports.test.ts +++ b/tests/boundary/cli/cliStartupImports.test.ts @@ -14,6 +14,54 @@ const MARKER = "REA_TEST_RESOLVED "; // Dependencies and composition modules used by one command family; startup // must not resolve them just to register the complete command catalog. const DEFERRED_IMPORTS = [ + { + name: "direct-analysis", + path: "/dist/application/DirectAnalysis.js", + }, + { + name: "direct-analysis-status", + path: "/dist/application/DirectAnalysisStatus.js", + }, + { + name: "doctor", + path: "/dist/application/Doctor.js", + }, + { + name: "doctor-runtime", + path: "/dist/doctorRuntime.js", + }, + { + name: "setup", + path: "/dist/application/Setup.js", + }, + { + name: "setup-host", + path: "/dist/application/SetupHost.js", + }, + { + name: "uninstall", + path: "/dist/application/Uninstall.js", + }, + { + name: "update", + path: "/dist/application/Update.js", + }, + { + name: "update-runtime", + path: "/dist/application/UpdateRuntime.js", + }, + { + name: "catalog-identity", + path: "/dist/catalogIdentity.js", + }, + { + name: "server-identity", + path: "/dist/serverIdentity.js", + }, + { + name: "tool-contracts", + path: "/dist/contracts/toolContracts.js", + }, { name: "playwright-core", path: "/node_modules/playwright-core/", @@ -130,13 +178,17 @@ describe("CLI startup imports", () => { }); it("loads native provider composition when a provider operation is called", async () => { - await expect( - resolvedPackages( - evaluate( - 'const { createDirectAnalysis } = await import("./dist/composition/directAnalysis.js"); await createDirectAnalysis({}).runSessionStatus();', - ), + const resolved = await resolvedPackages( + evaluate( + 'const { createDirectAnalysis } = await import("./dist/composition/directAnalysis.js"); await createDirectAnalysis({}).runSessionStatus();', ), - ).resolves.toContain("binary-composition"); + ); + expect(resolved).toContain("binary-composition"); + expect(resolved).not.toContain("direct-analysis"); + expect(resolved).toContain("direct-analysis-status"); + expect(resolved).toContain("server-identity"); + expect(resolved).toContain("catalog-identity"); + expect(resolved).toContain("tool-contracts"); }); });