* refactor(domain): split error taxonomy into thematic modules Move each error family out of domain/errors.ts into its own module (base, core, provider selection/adapter, browser observation, artifact, evidence, unknown registry, hopper, configuration) and relocate AnalysisErrorProjection to analysisErrorProjection. Migrate all 162 importers to the owning modules and delete errors.ts. No re-export shims; behavior is unchanged. * refactor(runtime): name all silent catch causes without changing diagnostics Bind cause in every bare catch and unhandled rejection across src/. Real failures preserve the cause for logging; best-effort cleanup carries an explicit comment. Caller-visible and Evidence strings are byte-identical to before.
56 lines
1.7 KiB
TypeScript
56 lines
1.7 KiB
TypeScript
import { lstat, realpath } from "node:fs/promises";
|
|
|
|
interface ClientConfigPathStats {
|
|
readonly uid?: number;
|
|
isFile?(): boolean;
|
|
isSymbolicLink(): boolean;
|
|
}
|
|
|
|
interface ClientConfigPathFileSystem {
|
|
lstat(path: string): Promise<ClientConfigPathStats>;
|
|
realpath?(path: string): Promise<string>;
|
|
}
|
|
|
|
const systemFileSystem: ClientConfigPathFileSystem = { lstat, realpath };
|
|
|
|
/**
|
|
* Resolve an existing, user-owned config symlink to one stable transaction
|
|
* path. Missing regular paths remain eligible for first-run creation.
|
|
*/
|
|
export const resolveClientConfigTransactionPath = async (
|
|
requestedPath: string,
|
|
fileSystem: ClientConfigPathFileSystem = systemFileSystem,
|
|
): Promise<string | undefined> => {
|
|
let requestedStats: ClientConfigPathStats;
|
|
try {
|
|
requestedStats = await fileSystem.lstat(requestedPath);
|
|
} catch (cause: unknown) {
|
|
return isMissing(cause) ? requestedPath : undefined;
|
|
}
|
|
if (!requestedStats.isSymbolicLink()) return requestedPath;
|
|
|
|
const currentUid =
|
|
typeof process.getuid === "function" ? process.getuid() : undefined;
|
|
if (
|
|
currentUid === undefined ||
|
|
requestedStats.uid !== currentUid ||
|
|
fileSystem.realpath === undefined
|
|
)
|
|
return undefined;
|
|
|
|
try {
|
|
const canonicalPath = await fileSystem.realpath(requestedPath);
|
|
const targetStats = await fileSystem.lstat(canonicalPath);
|
|
return targetStats.isFile?.() === true && targetStats.uid === currentUid
|
|
? canonicalPath
|
|
: undefined;
|
|
} catch (cause: unknown) {
|
|
// best-effort cleanup: unreadable or vanishing config paths mean no registration.
|
|
void cause;
|
|
return undefined;
|
|
}
|
|
};
|
|
|
|
const isMissing = (cause: unknown): boolean =>
|
|
cause instanceof Error && "code" in cause && cause.code === "ENOENT";
|