mirror of
https://github.com/morluto/rea
synced 2026-10-28 23:11:11 -04:00
Merge pull request #1074 from morluto/fix/evidence-file-missing
fix(evidence): report missing evidence files instead of permission advice
This commit is contained in:
@@ -10,10 +10,14 @@ import { err, ok, type Result } from "../domain/result.js";
|
||||
export const readJsonFile = async (
|
||||
path: string,
|
||||
): Promise<Result<unknown, EvidenceFileError>> => {
|
||||
const requestedPath = resolve(path);
|
||||
try {
|
||||
const canonicalPath = await realpath(resolve(path));
|
||||
const canonicalPath = await realpath(requestedPath);
|
||||
const stats = await lstat(canonicalPath);
|
||||
if (!stats.isFile()) return err(new EvidenceFileError("read", "not-file"));
|
||||
if (!stats.isFile())
|
||||
return err(
|
||||
new EvidenceFileError("read", "not-file", { path: requestedPath }),
|
||||
);
|
||||
const encoded = await readFile(canonicalPath);
|
||||
let decoded: unknown;
|
||||
try {
|
||||
@@ -23,11 +27,21 @@ export const readJsonFile = async (
|
||||
),
|
||||
);
|
||||
} catch (cause: unknown) {
|
||||
return err(new EvidenceFileError("read", "invalid-json", { cause }));
|
||||
return err(
|
||||
new EvidenceFileError("read", "invalid-json", {
|
||||
cause,
|
||||
path: requestedPath,
|
||||
}),
|
||||
);
|
||||
}
|
||||
return ok(decoded);
|
||||
} catch (cause: unknown) {
|
||||
return err(new EvidenceFileError("read", "io", { cause }));
|
||||
return err(
|
||||
new EvidenceFileError("read", missingOrIo(cause), {
|
||||
cause,
|
||||
path: requestedPath,
|
||||
}),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -40,8 +54,8 @@ export const writeTextFile = async (
|
||||
Result<{ readonly path: string; readonly bytes: number }, EvidenceFileError>
|
||||
> => {
|
||||
const bytes = Buffer.byteLength(encoded, "utf8");
|
||||
const requestedPath = resolve(path);
|
||||
try {
|
||||
const requestedPath = resolve(path);
|
||||
const canonicalParent = await realpath(dirname(requestedPath));
|
||||
const destination = resolve(canonicalParent, basename(requestedPath));
|
||||
const existing = await lstat(destination).catch((cause: unknown) => {
|
||||
@@ -49,9 +63,14 @@ export const writeTextFile = async (
|
||||
throw cause;
|
||||
});
|
||||
if (existing !== undefined) {
|
||||
if (!overwrite) return err(new EvidenceFileError("write", "exists"));
|
||||
if (!overwrite)
|
||||
return err(
|
||||
new EvidenceFileError("write", "exists", { path: requestedPath }),
|
||||
);
|
||||
if (!existing.isFile() || existing.isSymbolicLink())
|
||||
return err(new EvidenceFileError("write", "not-file"));
|
||||
return err(
|
||||
new EvidenceFileError("write", "not-file", { path: requestedPath }),
|
||||
);
|
||||
}
|
||||
if (overwrite)
|
||||
await writeFileAtomic(destination, encoded, {
|
||||
@@ -63,11 +82,27 @@ export const writeTextFile = async (
|
||||
return ok({ path: requestedPath, bytes });
|
||||
} catch (cause: unknown) {
|
||||
if (!overwrite && fileErrorCode(cause) === "EEXIST")
|
||||
return err(new EvidenceFileError("write", "exists", { cause }));
|
||||
return err(new EvidenceFileError("write", "io", { cause }));
|
||||
return err(
|
||||
new EvidenceFileError("write", "exists", {
|
||||
cause,
|
||||
path: requestedPath,
|
||||
}),
|
||||
);
|
||||
return err(
|
||||
new EvidenceFileError("write", missingOrIo(cause), {
|
||||
cause,
|
||||
path: requestedPath,
|
||||
}),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/** A missing path or parent is a selection error, not a permission failure. */
|
||||
const missingOrIo = (cause: unknown): "missing" | "io" => {
|
||||
const code = fileErrorCode(cause);
|
||||
return code === "ENOENT" || code === "ENOTDIR" ? "missing" : "io";
|
||||
};
|
||||
|
||||
const publishNewFile = async (
|
||||
destination: string,
|
||||
encoded: string,
|
||||
|
||||
Reference in New Issue
Block a user