import type { Context } from "@earendil-works/chord"; import { getOrThrow } from "@earendil-works/pi-durable/env"; import { posix, win32 } from "node:path"; import type { Environment } from "@berry/api/environment.ts"; export async function folders( env: Environment, prefix: string, context: Context, ): Promise { if (prefix.includes("\0")) throw new Error("Invalid directory prefix"); const paths = env.platform === "win32" ? win32 : posix; const expanded = prefix === "~" || /^~[/\\]/u.test(prefix) ? env.home + prefix.slice(1) : prefix; const trailing = expanded === "" || (env.platform === "win32" ? /[/\\]$/u : /\/$/u).test(expanded); const parent = trailing ? expanded || "." : paths.dirname(expanded); const query = trailing ? "" : paths.basename(expanded); const absolute = getOrThrow(await env.absolutePath(parent, context)); const entries = getOrThrow(await env.listDir(absolute, context)); const matches = []; for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) { const matchesPrefix = env.platform === "win32" ? entry.name.toLowerCase().startsWith(query.toLowerCase()) : entry.name.startsWith(query); if (!matchesPrefix || (entry.kind !== "directory" && entry.kind !== "symlink")) continue; if (entry.kind === "symlink") { const canonical = await env.canonicalPath(entry.path, context); if (!canonical.ok) continue; const info = await env.fileInfo(canonical.value, context); if (!info.ok || info.value.kind !== "directory") continue; } matches.push(paths.join(parent === "." ? "" : parent, entry.name) + paths.sep); if (matches.length === 50) break; } return matches; }