fix(reference): separate local filesystem diagnostics from graph commitments

This commit is contained in:
morluto committed 2026-10-24 03:11:07 +08:00
1 parent b165b0e830
commit cae03361cf
12 files changed
+220 -38

No files matched your search

+62 -19
View File
@@ -12,6 +12,7 @@ import {
failure,
filesystemFailureDetail,
safeSize,
type FilesystemFailureDetail,
} from "./ReferenceSourceReaderErrors.js";
import { isPathWithinRoot } from "../domain/localPath.js";
import { pathFromRoot } from "./ReferenceSourceReaderPaths.js";
@@ -37,8 +38,10 @@ const recordDirectoryFailure = (
absolute: string,
code: ReferenceSourceFailureCode,
message: string,
localDiagnostic?: string,
): void => {
if (absolute === state.root) state.rootFailure = { code, message };
if (absolute === state.root)
state.rootFailure = { code, message: localDiagnostic ?? message };
else
state.entries.push(
entryFailure(
@@ -46,6 +49,8 @@ const recordDirectoryFailure = (
"directory",
code,
message,
undefined,
localDiagnostic,
),
);
};
@@ -62,7 +67,13 @@ export const traverseDirectory = async (
);
if (isAborted(state.signal)) return { ok: false, error: cancelled() };
if (!before.ok) {
recordDirectoryFailure(state, current.path, before.code, before.message);
recordDirectoryFailure(
state,
current.path,
before.code,
before.message,
before.localDiagnostic,
);
return { ok: true, value: undefined };
}
const policy = await state.beforeDirectory?.(
@@ -71,7 +82,7 @@ export const traverseDirectory = async (
if (policy !== undefined && !policy.ok) return policy;
if (isAborted(state.signal)) return err(cancelled());
const directories: PendingDirectory[] = [];
let readFailure: string | undefined;
let readFailure: FilesystemFailureDetail | undefined;
let unrepresentableNameCount = 0;
let firstUnrepresentableNameHex: string | undefined;
let handle: Dir | undefined;
@@ -100,7 +111,9 @@ export const traverseDirectory = async (
}
if (child === null) break;
if (!Buffer.isBuffer(child.name)) {
readFailure = "Directory entry name was not returned as raw bytes";
readFailure = {
message: "Directory entry name was not returned as raw bytes",
};
break;
}
const decoded = decodeDirectoryEntryName(child.name);
@@ -121,7 +134,13 @@ export const traverseDirectory = async (
}
if (cleanup.kind === "failed") {
if (result.ok && !isAborted(state.signal) && readFailure !== undefined)
recordDirectoryFailure(state, current.path, "io", readFailure);
recordDirectoryFailure(
state,
current.path,
"io",
readFailure.message,
readFailure.localDiagnostic,
);
const observation = ArtifactReaderFailure.cleanupObservation(
cleanup.cause,
current.path,
@@ -132,7 +151,8 @@ export const traverseDirectory = async (
? cancelled()
: failure(
"io",
readFailure ??
readFailure?.localDiagnostic ??
readFailure?.message ??
`Reference directory cleanup failed: ${current.path}`,
)
: result.error;
@@ -154,7 +174,13 @@ export const traverseDirectory = async (
}
if (isAborted(state.signal)) return { ok: false, error: cancelled() };
if (readFailure !== undefined) {
recordDirectoryFailure(state, current.path, "io", readFailure);
recordDirectoryFailure(
state,
current.path,
"io",
readFailure.message,
readFailure.localDiagnostic,
);
scheduleObservedDirectories(state, directories);
return { ok: true, value: undefined };
}
@@ -171,6 +197,7 @@ export const traverseDirectory = async (
current.path,
after.ok ? "changed" : after.code,
after.ok ? "Directory changed while it was read" : after.message,
after.ok ? undefined : after.localDiagnostic,
);
scheduleObservedDirectories(state, directories);
return { ok: true, value: undefined };
@@ -200,27 +227,27 @@ const scheduleObservedDirectories = (
state.pending.push(...directories);
};
const directoryReadFailure = (cause: unknown): string => {
const message = filesystemFailureDetail(cause, "Directory could not be read");
if (message === undefined) throw cause;
return message;
const directoryReadFailure = (cause: unknown): FilesystemFailureDetail => {
const detail = filesystemFailureDetail(cause, "Directory could not be read");
if (detail === undefined) throw cause;
return detail;
};
const readMetadata = async (
path: string,
): Promise<
| { readonly ok: true; readonly value: BigIntStats }
| { readonly ok: false; readonly message: string }
| ({ readonly ok: false } & FilesystemFailureDetail)
> => {
try {
return { ok: true, value: await bigLstat(path) };
} catch (cause: unknown) {
const message = filesystemFailureDetail(
const detail = filesystemFailureDetail(
cause,
"Entry metadata could not be read",
);
if (message === undefined) throw cause;
return { ok: false, message };
if (detail === undefined) throw cause;
return { ok: false, ...detail };
}
};
@@ -246,7 +273,16 @@ const processEntry = async (
state.exclusions.push({ path, kind: "unknown" });
return { ok: true, value: undefined };
}
state.entries.push(entryFailure(path, "unknown", "io", metadata.message));
state.entries.push(
entryFailure(
path,
"unknown",
"io",
metadata.message,
undefined,
metadata.localDiagnostic,
),
);
return { ok: true, value: undefined };
}
const kind = metadata.value.isSymbolicLink()
@@ -357,12 +393,19 @@ export const readStableSymlinkTarget = async (
const code =
cause instanceof Error ? Reflect.get(cause, "code") : undefined;
if (code === "ENOENT" || code === "ENOTDIR") return changedSymlink(path);
const message = filesystemFailureDetail(
const detail = filesystemFailureDetail(
cause,
"Symbolic link target could not be read safely",
);
if (message === undefined) throw cause;
return entryFailure(path, "symlink", "io", message);
if (detail === undefined) throw cause;
return entryFailure(
path,
"symlink",
"io",
detail.message,
undefined,
detail.localDiagnostic,
);
}
};