import type { Dirent } from "node:fs"; import { readdir, realpath, stat } from "node:fs/promises"; import path from "node:path"; export interface SearchHomeDirectoriesOptions { homeDir: string; query: string; limit?: number; maxDepth?: number; maxDirectoriesScanned?: number; } export type WorkspaceSuggestionKind = "file" | "directory"; export interface WorkspaceSuggestionEntry { path: string; kind: WorkspaceSuggestionKind; } export interface SearchWorkspaceEntriesOptions { cwd: string; query: string; limit?: number; includeFiles?: boolean; includeDirectories?: boolean; matchMode?: WorkspaceMatchMode; maxDepth?: number; maxEntriesScanned?: number; } export type WorkspaceMatchMode = "fuzzy" | "suffix"; const DEFAULT_LIMIT = 30; const MAX_LIMIT = 100; const DEFAULT_MAX_DEPTH = 12; const DEFAULT_MAX_DIRECTORIES_SCANNED = 20000; const DIRECTORY_LIST_CACHE_TTL_MS = 8_000; const DIRECTORY_LIST_CACHE_MAX_ENTRIES = 4_000; interface QueryParts { isPathQuery: boolean; parentPart: string; searchTerm: string; } interface RankedDirectory { absolutePath: string; matchTier: number; segmentIndex: number; matchOffset: number; depth: number; } interface ChildDirectoryEntry { name: string; absolutePath: string; } interface ChildWorkspaceEntry { name: string; absolutePath: string; kind: WorkspaceSuggestionKind; } interface DirectoryListCacheEntry { expiresAt: number; entries: ChildDirectoryEntry[]; } interface WorkspaceEntryListCacheEntry { expiresAt: number; entries: ChildWorkspaceEntry[]; } const directoryListCache = new Map(); const workspaceEntryListCache = new Map(); const NO_SEGMENT_INDEX = Number.MAX_SAFE_INTEGER; const NO_MATCH_OFFSET = Number.MAX_SAFE_INTEGER; const NO_FUZZY_SCORE = Number.MAX_SAFE_INTEGER; const NO_WORKSPACE_MATCH_TIER = 5; const WORKSPACE_IGNORED_DIRECTORY_NAMES = new Set([ "node_modules", "dist", "build", "target", "out", "coverage", "vendor", "__pycache__", ]); export async function searchHomeDirectories( options: SearchHomeDirectoriesOptions, ): Promise { const query = options.query.trim(); if (!query) { return []; } const limit = normalizeLimit(options.limit); const homeRoot = await resolveDirectory(options.homeDir); if (!homeRoot) { return []; } const queryParts = normalizeQueryParts(query, homeRoot); if (!queryParts) { return []; } if (queryParts.isPathQuery) { return searchWithinParentDirectory({ homeRoot, parentPart: queryParts.parentPart, searchTerm: queryParts.searchTerm, limit, }); } return searchAcrossHomeTree({ homeRoot, searchTerm: queryParts.searchTerm, limit, maxDepth: options.maxDepth ?? DEFAULT_MAX_DEPTH, maxDirectoriesScanned: options.maxDirectoriesScanned ?? DEFAULT_MAX_DIRECTORIES_SCANNED, }); } interface RankedWorkspaceEntry { relativePath: string; kind: WorkspaceSuggestionKind; matchTier: number; segmentIndex: number; matchOffset: number; fuzzyScore: number; depth: number; } export async function searchWorkspaceEntries( options: SearchWorkspaceEntriesOptions, ): Promise { const limit = normalizeLimit(options.limit); const includeDirectories = options.includeDirectories ?? true; const includeFiles = options.includeFiles ?? false; if (!includeDirectories && !includeFiles) { return []; } const workspaceRoot = await resolveDirectory(options.cwd); if (!workspaceRoot) { return []; } const queryParts = normalizeWorkspaceQueryParts(options.query, workspaceRoot); if (!queryParts) { return []; } const matchMode = options.matchMode ?? "fuzzy"; if (queryParts.isPathQuery && matchMode !== "suffix") { return searchWorkspaceWithinParentDirectory({ workspaceRoot, parentPart: queryParts.parentPart, searchTerm: queryParts.searchTerm, limit, includeDirectories, includeFiles, }); } const searchTerm = matchMode === "suffix" ? [queryParts.parentPart, queryParts.searchTerm].filter(Boolean).join("/") : queryParts.searchTerm; return searchWorkspaceAcrossTree({ workspaceRoot, searchTerm, limit, includeDirectories, includeFiles, matchMode, maxDepth: options.maxDepth ?? DEFAULT_MAX_DEPTH, maxEntriesScanned: options.maxEntriesScanned ?? DEFAULT_MAX_DIRECTORIES_SCANNED, }); } function normalizeLimit(limit: number | undefined): number { const candidate = limit ?? DEFAULT_LIMIT; if (!Number.isFinite(candidate)) { return DEFAULT_LIMIT; } const bounded = Math.trunc(candidate); return Math.max(1, Math.min(MAX_LIMIT, bounded)); } async function searchWithinParentDirectory(input: { homeRoot: string; parentPart: string; searchTerm: string; limit: number; }): Promise { const parentPath = path.resolve(input.homeRoot, input.parentPart || "."); const parentRoot = await resolveDirectory(parentPath); if (!parentRoot || !isPathInsideRoot(input.homeRoot, parentRoot)) { return []; } const searchLower = input.searchTerm.toLowerCase(); const ranked: RankedDirectory[] = []; const entries = await listChildDirectories({ directory: parentRoot, homeRoot: input.homeRoot, }); for (const entry of entries) { if (searchLower && !entry.name.toLowerCase().includes(searchLower)) { continue; } ranked.push( rankDirectory({ absolutePath: entry.absolutePath, homeRoot: input.homeRoot, searchLower, }), ); } return dedupeAndSort(ranked).slice(0, input.limit); } async function searchAcrossHomeTree(input: { homeRoot: string; searchTerm: string; limit: number; maxDepth: number; maxDirectoriesScanned: number; }): Promise { const queue: Array<{ directory: string; depth: number }> = [ { directory: input.homeRoot, depth: 0 }, ]; const visited = new Set([input.homeRoot]); const ranked: RankedDirectory[] = []; let scanned = 0; const searchLower = input.searchTerm.toLowerCase(); for ( let queueIndex = 0; queueIndex < queue.length && scanned < input.maxDirectoriesScanned; queueIndex += 1 ) { const current = queue[queueIndex]; if (!current) continue; const entries = await listChildDirectories({ directory: current.directory, homeRoot: input.homeRoot, }); for (const entry of entries) { const resolvedCandidate = entry.absolutePath; if (visited.has(resolvedCandidate)) { continue; } visited.add(resolvedCandidate); scanned += 1; const relativePath = normalizeRelativePath(input.homeRoot, resolvedCandidate); if ( relativePath.toLowerCase().includes(searchLower) || entry.name.toLowerCase().includes(searchLower) ) { ranked.push( rankDirectory({ absolutePath: resolvedCandidate, homeRoot: input.homeRoot, searchLower, }), ); } if (current.depth < input.maxDepth && scanned < input.maxDirectoriesScanned) { queue.push({ directory: resolvedCandidate, depth: current.depth + 1 }); } } } return dedupeAndSort(ranked).slice(0, input.limit); } async function searchWorkspaceWithinParentDirectory(input: { workspaceRoot: string; parentPart: string; searchTerm: string; limit: number; includeDirectories: boolean; includeFiles: boolean; }): Promise { const parentPath = path.resolve(input.workspaceRoot, input.parentPart || "."); const parentRoot = await resolveDirectory(parentPath); if (!parentRoot || !isPathInsideRoot(input.workspaceRoot, parentRoot)) { return []; } const searchLower = input.searchTerm.toLowerCase(); const ranked: RankedWorkspaceEntry[] = []; const entries = await listWorkspaceChildEntries({ directory: parentRoot, workspaceRoot: input.workspaceRoot, }); for (const entry of entries) { if (entry.kind === "directory" && !input.includeDirectories) { continue; } if (entry.kind === "file" && !input.includeFiles) { continue; } const rankedEntry = rankWorkspaceEntry({ absolutePath: entry.absolutePath, kind: entry.kind, workspaceRoot: input.workspaceRoot, searchLower, }); if (searchLower && rankedEntry.matchTier === NO_WORKSPACE_MATCH_TIER) { continue; } ranked.push(rankedEntry); } return dedupeAndSortWorkspaceEntries(ranked).slice(0, input.limit); } async function searchWorkspaceAcrossTree(input: { workspaceRoot: string; searchTerm: string; limit: number; includeDirectories: boolean; includeFiles: boolean; matchMode: WorkspaceMatchMode; maxDepth: number; maxEntriesScanned: number; }): Promise { const queue: Array<{ directory: string; depth: number }> = [ { directory: input.workspaceRoot, depth: 0 }, ]; const visited = new Set([input.workspaceRoot]); const ranked: RankedWorkspaceEntry[] = []; let scanned = 0; const searchLower = input.searchTerm.toLowerCase(); for ( let queueIndex = 0; queueIndex < queue.length && scanned < input.maxEntriesScanned; queueIndex += 1 ) { const current = queue[queueIndex]; if (!current) continue; const entries = await listWorkspaceChildEntries({ directory: current.directory, workspaceRoot: input.workspaceRoot, }); for (const entry of entries) { scanned += 1; if (entry.kind === "directory") { if ( !visited.has(entry.absolutePath) && current.depth < input.maxDepth && scanned < input.maxEntriesScanned ) { visited.add(entry.absolutePath); queue.push({ directory: entry.absolutePath, depth: current.depth + 1, }); } } if (entry.kind === "directory" && !input.includeDirectories) { continue; } if (entry.kind === "file" && !input.includeFiles) { continue; } if ( input.matchMode === "suffix" && !workspaceEntryMatchesSuffixQuery({ absolutePath: entry.absolutePath, workspaceRoot: input.workspaceRoot, query: input.searchTerm, }) ) { continue; } const rankedEntry = rankWorkspaceEntry({ absolutePath: entry.absolutePath, kind: entry.kind, workspaceRoot: input.workspaceRoot, searchLower, }); if ( input.matchMode !== "suffix" && searchLower && rankedEntry.matchTier === NO_WORKSPACE_MATCH_TIER ) { continue; } ranked.push(rankedEntry); } } return dedupeAndSortWorkspaceEntries(ranked).slice(0, input.limit); } function workspaceEntryMatchesSuffixQuery(input: { absolutePath: string; workspaceRoot: string; query: string; }): boolean { const querySegments = input.query .trim() .replace(/\\/g, "/") .replace(/^\.\/+/, "") .split("/") .filter(Boolean) .map((segment) => segment.toLowerCase()); if (querySegments.length === 0) { return false; } const pathSegments = normalizeRelativePath(input.workspaceRoot, input.absolutePath) .split("/") .filter(Boolean) .map((segment) => segment.toLowerCase()); if (querySegments.length > pathSegments.length) { return false; } const offset = pathSegments.length - querySegments.length; return querySegments.every((segment, index) => pathSegments[offset + index] === segment); } function dedupeAndSortWorkspaceEntries( rankedEntries: RankedWorkspaceEntry[], ): WorkspaceSuggestionEntry[] { const byPath = new Map(); for (const entry of rankedEntries) { const key = `${entry.kind}:${entry.relativePath}`; const existing = byPath.get(key); if (!existing || compareRankedWorkspaceEntries(entry, existing) < 0) { byPath.set(key, entry); } } return Array.from(byPath.values()) .sort(compareRankedWorkspaceEntries) .map((entry) => ({ path: entry.relativePath, kind: entry.kind, })); } function compareRankedWorkspaceEntries( left: RankedWorkspaceEntry, right: RankedWorkspaceEntry, ): number { if (left.matchTier !== right.matchTier) { return left.matchTier - right.matchTier; } if (left.segmentIndex !== right.segmentIndex) { return left.segmentIndex - right.segmentIndex; } if (left.matchOffset !== right.matchOffset) { return left.matchOffset - right.matchOffset; } if (left.fuzzyScore !== right.fuzzyScore) { return left.fuzzyScore - right.fuzzyScore; } if (left.depth !== right.depth) { return left.depth - right.depth; } if (left.kind !== right.kind) { return left.kind === "directory" ? -1 : 1; } return left.relativePath.localeCompare(right.relativePath); } function dedupeAndSort(ranked: RankedDirectory[]): string[] { const byPath = new Map(); for (const entry of ranked) { const existing = byPath.get(entry.absolutePath); if (!existing || compareRankedDirectories(entry, existing) < 0) { byPath.set(entry.absolutePath, entry); } } return Array.from(byPath.values()) .sort(compareRankedDirectories) .map((entry) => entry.absolutePath); } function compareRankedDirectories(left: RankedDirectory, right: RankedDirectory): number { if (left.matchTier !== right.matchTier) { return left.matchTier - right.matchTier; } if (left.segmentIndex !== right.segmentIndex) { return left.segmentIndex - right.segmentIndex; } if (left.matchOffset !== right.matchOffset) { return left.matchOffset - right.matchOffset; } if (left.depth !== right.depth) { return left.depth - right.depth; } return left.absolutePath.localeCompare(right.absolutePath); } function rankDirectory(input: { absolutePath: string; homeRoot: string; searchLower: string; }): RankedDirectory { const relative = normalizeRelativePath(input.homeRoot, input.absolutePath); const relativeLower = relative.toLowerCase(); const depth = relative === "." ? 0 : relative.split("/").length; const searchLower = input.searchLower; if (!searchLower) { return { absolutePath: input.absolutePath, matchTier: 3, segmentIndex: NO_SEGMENT_INDEX, matchOffset: 0, depth, }; } const segments = relativeLower === "." ? [] : relativeLower.split("/"); const exactSegmentIndex = findSegmentMatchIndex(segments, (segment) => segment === searchLower); const prefixSegmentIndex = findSegmentMatchIndex(segments, (segment) => segment.startsWith(searchLower), ); const partialSegmentIndex = findSegmentMatchIndex(segments, (segment) => segment.includes(searchLower), ); const matchOffset = relativeLower.indexOf(searchLower); let matchTier = 4; let segmentIndex = NO_SEGMENT_INDEX; if (exactSegmentIndex >= 0) { matchTier = 0; segmentIndex = exactSegmentIndex; } else if (prefixSegmentIndex >= 0) { matchTier = 1; segmentIndex = prefixSegmentIndex; } else if (partialSegmentIndex >= 0) { matchTier = 2; segmentIndex = partialSegmentIndex; } else if (relativeLower.startsWith(searchLower)) { matchTier = 3; } return { absolutePath: input.absolutePath, matchTier, segmentIndex, matchOffset: matchOffset >= 0 ? matchOffset : NO_MATCH_OFFSET, depth, }; } function rankWorkspaceEntry(input: { absolutePath: string; kind: WorkspaceSuggestionKind; workspaceRoot: string; searchLower: string; }): RankedWorkspaceEntry { const relativePath = normalizeRelativePath(input.workspaceRoot, input.absolutePath); const relativeLower = relativePath.toLowerCase(); const depth = relativePath === "." ? 0 : relativePath.split("/").length; const searchLower = input.searchLower; if (!searchLower) { return { relativePath, kind: input.kind, matchTier: 3, segmentIndex: NO_SEGMENT_INDEX, matchOffset: 0, fuzzyScore: NO_FUZZY_SCORE, depth, }; } const segments = relativeLower === "." ? [] : relativeLower.split("/"); const exactSegmentIndex = findSegmentMatchIndex(segments, (segment) => segment === searchLower); const prefixSegmentIndex = findSegmentMatchIndex(segments, (segment) => segment.startsWith(searchLower), ); const partialSegmentIndex = findSegmentMatchIndex(segments, (segment) => segment.includes(searchLower), ); const matchOffset = relativeLower.indexOf(searchLower); const basename = segments.at(-1) ?? ""; const fuzzyScore = scoreFuzzySubsequence(searchLower, basename); let matchTier = NO_WORKSPACE_MATCH_TIER; let segmentIndex = NO_SEGMENT_INDEX; if (exactSegmentIndex >= 0) { matchTier = 0; segmentIndex = exactSegmentIndex; } else if (prefixSegmentIndex >= 0) { matchTier = 1; segmentIndex = prefixSegmentIndex; } else if (partialSegmentIndex >= 0) { matchTier = 2; segmentIndex = partialSegmentIndex; } else if (relativeLower.startsWith(searchLower)) { matchTier = 3; } else if (fuzzyScore !== null) { matchTier = 4; } return { relativePath, kind: input.kind, matchTier, segmentIndex, matchOffset: matchOffset >= 0 ? matchOffset : NO_MATCH_OFFSET, fuzzyScore: fuzzyScore ?? NO_FUZZY_SCORE, depth, }; } function scoreFuzzySubsequence(query: string, candidate: string): number | null { if (!query) { return 0; } let queryIndex = 0; let firstMatchIndex = -1; let previousMatchIndex = -1; let gapScore = 0; for ( let candidateIndex = 0; candidateIndex < candidate.length && queryIndex < query.length; candidateIndex += 1 ) { if (candidate[candidateIndex] !== query[queryIndex]) { continue; } if (firstMatchIndex === -1) { firstMatchIndex = candidateIndex; } if (previousMatchIndex >= 0) { gapScore += candidateIndex - previousMatchIndex - 1; } previousMatchIndex = candidateIndex; queryIndex += 1; } if (queryIndex !== query.length || firstMatchIndex === -1) { return null; } return firstMatchIndex + gapScore; } function findSegmentMatchIndex( segments: string[], predicate: (segment: string) => boolean, ): number { for (let index = 0; index < segments.length; index += 1) { const segment = segments[index]; if (!segment) { continue; } if (predicate(segment)) { return index; } } return -1; } function normalizeRelativePath(homeRoot: string, absolutePath: string): string { const relative = path.relative(homeRoot, absolutePath); if (!relative) { return "."; } return relative.split(path.sep).join("/"); } function isPathInsideRoot(root: string, target: string): boolean { const relative = path.relative(root, target); return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative)); } function normalizeQueryParts(query: string, homeRoot: string): QueryParts | null { const typedQuery = query.trim().replace(/\\/g, "/"); let normalized = typedQuery; if (!normalized) { return null; } // Only treat the query as a literal path when the user explicitly roots it // with ~, ~/, ./, or an absolute path. Bare queries like "faro/main" are // search terms, not paths. let isRooted = false; if (normalized.startsWith("~")) { isRooted = true; normalized = normalized.slice(1); if (normalized.startsWith("/")) { normalized = normalized.slice(1); } } if (path.isAbsolute(normalized)) { isRooted = true; const absolute = path.resolve(normalized); if (!isPathInsideRoot(homeRoot, absolute)) { return null; } normalized = normalizeRelativePath(homeRoot, absolute); } if (normalized.startsWith("./")) { isRooted = true; } normalized = normalized.replace(/^\.\/+/, "").replace(/\/{2,}/g, "/"); if (!normalized) { // Treat "~" and "~/" as a request to browse the home root. if (typedQuery === "~" || typedQuery === "~/") { return { isPathQuery: true, parentPart: "", searchTerm: "", }; } return null; } const isPathQuery = isRooted && normalized.includes("/"); if (!isPathQuery) { return { isPathQuery: false, parentPart: "", searchTerm: normalized, }; } const slashIndex = normalized.lastIndexOf("/"); const parentPart = normalized.slice(0, slashIndex); const searchTerm = normalized.slice(slashIndex + 1); return { isPathQuery: true, parentPart, searchTerm, }; } function normalizeWorkspaceQueryParts(query: string, workspaceRoot: string): QueryParts | null { let normalized = query.trim().replace(/\\/g, "/"); if (path.isAbsolute(normalized)) { const absolute = path.resolve(normalized); if (!isPathInsideRoot(workspaceRoot, absolute)) { return null; } normalized = normalizeRelativePath(workspaceRoot, absolute); } normalized = normalized.replace(/^\.\/+/, "").replace(/\/{2,}/g, "/"); if (!normalized) { return { isPathQuery: true, parentPart: "", searchTerm: "", }; } const isPathQuery = normalized.includes("/"); const slashIndex = normalized.lastIndexOf("/"); const parentPart = slashIndex >= 0 ? normalized.slice(0, slashIndex) : ""; const searchTerm = slashIndex >= 0 ? normalized.slice(slashIndex + 1) : normalized; return { isPathQuery, parentPart, searchTerm, }; } async function resolveDirectory(inputPath: string): Promise { try { const resolved = await realpath(path.resolve(inputPath)); const stats = await stat(resolved); if (!stats.isDirectory()) { return null; } return resolved; } catch { return null; } } async function listChildDirectories(input: { directory: string; homeRoot: string; }): Promise { const now = Date.now(); const cached = directoryListCache.get(input.directory); if (cached && cached.expiresAt > now) { return cached.entries; } const dirents = await readdir(input.directory, { withFileTypes: true }).catch( () => [] as Dirent[], ); const candidates = dirents.filter( (dirent) => !isHiddenDirectoryName(dirent.name) && (dirent.isDirectory() || dirent.isSymbolicLink()), ); const resolved = await Promise.all( candidates.map(async (dirent) => { const candidatePath = path.join(input.directory, dirent.name); const absolutePath = await resolveDirectoryCandidate({ candidatePath, dirent, homeRoot: input.homeRoot, }); return absolutePath ? { name: dirent.name, absolutePath } : null; }), ); const entries: ChildDirectoryEntry[] = resolved.filter( (entry): entry is ChildDirectoryEntry => entry !== null, ); setDirectoryListCache(input.directory, { expiresAt: now + DIRECTORY_LIST_CACHE_TTL_MS, entries, }); return entries; } async function listWorkspaceChildEntries(input: { directory: string; workspaceRoot: string; }): Promise { const now = Date.now(); const cached = workspaceEntryListCache.get(input.directory); if (cached && cached.expiresAt > now) { return cached.entries; } const dirents = await readdir(input.directory, { withFileTypes: true }).catch( () => [] as Dirent[], ); const candidates = dirents.filter( (dirent) => !isHiddenDirectoryName(dirent.name) && !isIgnoredWorkspaceDirectoryName(dirent.name), ); const resolved = await Promise.all( candidates.map(async (dirent) => { const candidatePath = path.join(input.directory, dirent.name); const entry = await resolveWorkspaceCandidate({ candidatePath, dirent, workspaceRoot: input.workspaceRoot, }); return entry ? { name: dirent.name, absolutePath: entry.absolutePath, kind: entry.kind } : null; }), ); const entries: ChildWorkspaceEntry[] = resolved.filter( (entry): entry is ChildWorkspaceEntry => entry !== null, ); setWorkspaceEntryListCache(input.directory, { expiresAt: now + DIRECTORY_LIST_CACHE_TTL_MS, entries, }); return entries; } async function resolveDirectoryCandidate(input: { candidatePath: string; dirent: Dirent; homeRoot: string; }): Promise { if (input.dirent.isDirectory()) { const resolved = path.resolve(input.candidatePath); return isPathInsideRoot(input.homeRoot, resolved) ? resolved : null; } const resolved = await resolveDirectory(input.candidatePath); if (!resolved || !isPathInsideRoot(input.homeRoot, resolved)) { return null; } return resolved; } async function resolveWorkspaceCandidate(input: { candidatePath: string; dirent: Dirent; workspaceRoot: string; }): Promise<{ absolutePath: string; kind: WorkspaceSuggestionKind } | null> { if (input.dirent.isDirectory()) { const resolved = path.resolve(input.candidatePath); if (!isPathInsideRoot(input.workspaceRoot, resolved)) { return null; } return { absolutePath: resolved, kind: "directory" }; } if (input.dirent.isFile()) { const resolved = path.resolve(input.candidatePath); if (!isPathInsideRoot(input.workspaceRoot, resolved)) { return null; } return { absolutePath: resolved, kind: "file" }; } if (!input.dirent.isSymbolicLink()) { return null; } try { const resolved = await realpath(input.candidatePath); if (!isPathInsideRoot(input.workspaceRoot, resolved)) { return null; } const stats = await stat(resolved); if (stats.isDirectory()) { return { absolutePath: resolved, kind: "directory" }; } if (stats.isFile()) { return { absolutePath: resolved, kind: "file" }; } return null; } catch { return null; } } function isHiddenDirectoryName(name: string): boolean { return name.startsWith("."); } function isIgnoredWorkspaceDirectoryName(name: string): boolean { return WORKSPACE_IGNORED_DIRECTORY_NAMES.has(name); } function setDirectoryListCache(cacheKey: string, entry: DirectoryListCacheEntry): void { directoryListCache.set(cacheKey, entry); pruneDirectoryListCache(); } function setWorkspaceEntryListCache(cacheKey: string, entry: WorkspaceEntryListCacheEntry): void { workspaceEntryListCache.set(cacheKey, entry); pruneWorkspaceEntryListCache(); } function pruneDirectoryListCache(): void { if (directoryListCache.size <= DIRECTORY_LIST_CACHE_MAX_ENTRIES) { return; } const now = Date.now(); for (const [cacheKey, entry] of directoryListCache) { if (entry.expiresAt <= now) { directoryListCache.delete(cacheKey); } } while (directoryListCache.size > DIRECTORY_LIST_CACHE_MAX_ENTRIES) { const oldestKey = directoryListCache.keys().next().value; if (!oldestKey) { return; } directoryListCache.delete(oldestKey); } } function pruneWorkspaceEntryListCache(): void { if (workspaceEntryListCache.size <= DIRECTORY_LIST_CACHE_MAX_ENTRIES) { return; } const now = Date.now(); for (const [cacheKey, entry] of workspaceEntryListCache) { if (entry.expiresAt <= now) { workspaceEntryListCache.delete(cacheKey); } } while (workspaceEntryListCache.size > DIRECTORY_LIST_CACHE_MAX_ENTRIES) { const oldestKey = workspaceEntryListCache.keys().next().value; if (!oldestKey) { return; } workspaceEntryListCache.delete(oldestKey); } }