mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
Merge branch 'investigate-agents-enonent'
This commit is contained in:
34
packages/server/src/server/file-explorer/service.test.ts
Normal file
34
packages/server/src/server/file-explorer/service.test.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { mkdtemp, mkdir, rm, symlink, writeFile } from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { listDirectoryEntries } from "./service.js";
|
||||
|
||||
async function createTempDir(prefix: string): Promise<string> {
|
||||
return mkdtemp(path.join(os.tmpdir(), prefix));
|
||||
}
|
||||
|
||||
describe("file explorer service", () => {
|
||||
it("lists directory entries even when a dangling symlink exists", async () => {
|
||||
const root = await createTempDir("paseo-file-explorer-");
|
||||
|
||||
try {
|
||||
await mkdir(path.join(root, "packages", "server"), { recursive: true });
|
||||
const serverDir = path.join(root, "packages", "server");
|
||||
await writeFile(path.join(serverDir, "README.md"), "# server\n", "utf-8");
|
||||
await symlink("CLAUDE.md", path.join(serverDir, "AGENTS.md"));
|
||||
|
||||
const result = await listDirectoryEntries({
|
||||
root,
|
||||
relativePath: "packages/server",
|
||||
});
|
||||
|
||||
expect(result.path).toBe("packages/server");
|
||||
const names = result.entries.map((entry) => entry.name);
|
||||
expect(names).toContain("README.md");
|
||||
expect(names).not.toContain("AGENTS.md");
|
||||
} finally {
|
||||
await rm(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -96,15 +96,32 @@ export async function listDirectoryEntries({
|
||||
|
||||
const dirents = await fs.readdir(directoryPath, { withFileTypes: true });
|
||||
|
||||
const entries = await Promise.all(
|
||||
const entriesWithNulls = await Promise.all(
|
||||
dirents.map(async (dirent) => {
|
||||
const targetPath = path.join(directoryPath, dirent.name);
|
||||
const kind: ExplorerEntryKind = dirent.isDirectory()
|
||||
? "directory"
|
||||
: "file";
|
||||
return buildEntryPayload({ root, targetPath, name: dirent.name, kind });
|
||||
try {
|
||||
return await buildEntryPayload({
|
||||
root,
|
||||
targetPath,
|
||||
name: dirent.name,
|
||||
kind,
|
||||
});
|
||||
} catch (error) {
|
||||
// Directories can contain dangling links (e.g. AGENTS.md -> CLAUDE.md).
|
||||
// Skip entries whose targets disappeared instead of failing the whole listing.
|
||||
if (isMissingEntryError(error)) {
|
||||
return null;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
})
|
||||
);
|
||||
const entries = entriesWithNulls.filter(
|
||||
(entry): entry is FileExplorerEntry => entry !== null
|
||||
);
|
||||
|
||||
entries.sort((a, b) => {
|
||||
const modifiedComparison =
|
||||
@@ -236,6 +253,11 @@ async function buildEntryPayload({
|
||||
};
|
||||
}
|
||||
|
||||
function isMissingEntryError(error: unknown): boolean {
|
||||
const code = (error as NodeJS.ErrnoException | null)?.code;
|
||||
return code === "ENOENT" || code === "ENOTDIR" || code === "ELOOP";
|
||||
}
|
||||
|
||||
function normalizeRelativePath({
|
||||
root,
|
||||
targetPath,
|
||||
|
||||
Reference in New Issue
Block a user