mirror of
https://github.com/morluto/rea.git
synced 2026-10-28 18:35:53 +00:00
* fix(apktool): resolve Windows PATH launchers before invocation Resolve the default launcher through the selected PATH/PATHEXT before choosing native process creation or the existing conservative cmd.exe wrapper. Reuse the resulting selection and one canonical environment for the version probe and decode. Follow-up to the release #1693 review of #1777. Source inspected and formatted; tests, builds, check reruns and real Windows Apktool execution were skipped at maintainer request. * fix(apktool): bound lookup and retain only selected environment Source inspection and targeted formatting only. Tests, builds, lint, typechecks and provider workloads were not run at the user's explicit request. * fix(apktool): use supported timeout diagnostics Retain lookup phase through AnalysisErrorOptions diagnostics while preserving the operation and timeout identity. Source inspection and targeted formatting only; tests, checks, builds and provider workloads were skipped at the user request. --------- Co-authored-by: morluto <morluto@users.noreply.github.com>
155 lines
5.0 KiB
TypeScript
155 lines
5.0 KiB
TypeScript
import type { AnalysisProfileCommitment } from "../../domain/analysisProfile.js";
|
|
import type { BinaryTarget } from "../../domain/binaryTargetTypes.js";
|
|
import { AnalysisCancelledError } from "../../domain/analysisErrorCore.js";
|
|
import { AnalysisError } from "../../domain/analysisErrorBase.js";
|
|
import { ProviderAdapterError } from "../../domain/providerAdapterError.js";
|
|
import type { ProviderOperationHealth } from "../../domain/providerOperationHealth.js";
|
|
import { err, ok } from "../../domain/result.js";
|
|
import { ABORTED, waitForAbortable } from "../../process/AbortablePromise.js";
|
|
import type {
|
|
AnalysisClient,
|
|
AnalysisClientContext,
|
|
AnalysisProvider,
|
|
CapabilityDescriptor,
|
|
ProviderIdentity,
|
|
ProviderRequestActivitySnapshot,
|
|
ProviderRuntimeLineageSnapshot,
|
|
} from "../AnalysisProvider.js";
|
|
|
|
type LoadAnalysisProvider = () => Promise<AnalysisProvider>;
|
|
|
|
const isAborted = (signal: AbortSignal | undefined): boolean =>
|
|
signal?.aborted === true;
|
|
|
|
/**
|
|
* Preserve synchronous provider discovery while loading implementation modules
|
|
* only when a target-bound client first executes.
|
|
*/
|
|
export class LazyAnalysisProvider implements AnalysisProvider {
|
|
readonly #providerIdentity: ProviderIdentity;
|
|
readonly #capabilityDescriptors: readonly CapabilityDescriptor[];
|
|
readonly #loadProvider: LoadAnalysisProvider;
|
|
#provider: Promise<AnalysisProvider> | undefined;
|
|
|
|
constructor(options: {
|
|
readonly identity: ProviderIdentity;
|
|
readonly capabilities: readonly CapabilityDescriptor[];
|
|
readonly load: LoadAnalysisProvider;
|
|
}) {
|
|
this.#providerIdentity = options.identity;
|
|
this.#capabilityDescriptors = options.capabilities;
|
|
this.#loadProvider = options.load;
|
|
}
|
|
|
|
/** Return source-declared provider identity without loading its implementation. */
|
|
identity(): ProviderIdentity {
|
|
return this.#providerIdentity;
|
|
}
|
|
|
|
/** Return source-declared capability metadata without loading its implementation. */
|
|
capabilities(): readonly CapabilityDescriptor[] {
|
|
return this.#capabilityDescriptors;
|
|
}
|
|
|
|
/** Create a client whose implementation is loaded on first execution. */
|
|
createClient(
|
|
target: BinaryTarget,
|
|
profile?: AnalysisProfileCommitment,
|
|
context?: AnalysisClientContext,
|
|
): AnalysisClient {
|
|
return new LazyAnalysisClient(async () => {
|
|
const provider = await this.#load();
|
|
return provider.createClient(target, profile, context);
|
|
}, this.#providerIdentity.id);
|
|
}
|
|
|
|
async #load(): Promise<AnalysisProvider> {
|
|
this.#provider ??= this.#loadProvider().catch((cause: unknown) => {
|
|
this.#provider = undefined;
|
|
throw cause;
|
|
});
|
|
return this.#provider;
|
|
}
|
|
}
|
|
|
|
class LazyAnalysisClient implements AnalysisClient {
|
|
readonly #loadClient: () => Promise<AnalysisClient>;
|
|
readonly #providerId: string;
|
|
#client: AnalysisClient | undefined;
|
|
#loading: Promise<AnalysisClient> | undefined;
|
|
#closed = false;
|
|
|
|
executeIfReady: NonNullable<AnalysisClient["executeIfReady"]> = (
|
|
operation,
|
|
parameters,
|
|
options,
|
|
) =>
|
|
this.#client?.executeIfReady?.(operation, parameters, options) ??
|
|
Promise.resolve(undefined);
|
|
|
|
constructor(loadClient: () => Promise<AnalysisClient>, providerId: string) {
|
|
this.#loadClient = loadClient;
|
|
this.#providerId = providerId;
|
|
}
|
|
|
|
execute: AnalysisClient["execute"] = async (
|
|
operation,
|
|
parameters,
|
|
options,
|
|
) => {
|
|
try {
|
|
if (isAborted(options?.signal))
|
|
return err(new AnalysisCancelledError(operation));
|
|
const loaded = await waitForAbortable(
|
|
this.#load(operation),
|
|
options?.signal,
|
|
);
|
|
if (loaded === ABORTED) return err(new AnalysisCancelledError(operation));
|
|
return await loaded.execute(operation, parameters, options);
|
|
} catch (cause: unknown) {
|
|
if (cause instanceof AnalysisError) return err(cause);
|
|
return err(
|
|
new ProviderAdapterError(this.#providerId, operation, {
|
|
cause,
|
|
}),
|
|
);
|
|
}
|
|
};
|
|
|
|
runtimeLineageSnapshots(): readonly ProviderRuntimeLineageSnapshot[] {
|
|
return this.#client?.runtimeLineageSnapshots?.() ?? [];
|
|
}
|
|
|
|
requestActivitySnapshots(): readonly ProviderRequestActivitySnapshot[] {
|
|
return this.#client?.requestActivitySnapshots?.() ?? [];
|
|
}
|
|
|
|
operationHealthSnapshot(): ProviderOperationHealth | undefined {
|
|
return this.#client?.operationHealthSnapshot?.();
|
|
}
|
|
|
|
close: AnalysisClient["close"] = async (options) => {
|
|
this.#closed = true;
|
|
if (this.#loading === undefined) return ok(null);
|
|
const client = await this.#loading;
|
|
return client.close(options);
|
|
};
|
|
|
|
async #load(operation: string): Promise<AnalysisClient> {
|
|
if (this.#closed)
|
|
throw new ProviderAdapterError(this.#providerId, operation, {
|
|
diagnostics: { reason: "client_closed" },
|
|
});
|
|
this.#loading ??= this.#loadClient()
|
|
.then((client) => {
|
|
this.#client = client;
|
|
return client;
|
|
})
|
|
.catch((cause: unknown) => {
|
|
this.#loading = undefined;
|
|
throw cause;
|
|
});
|
|
return this.#loading;
|
|
}
|
|
}
|