fix: invalidate VFS caches recursively on rmdir to prevent orphaned reads

When rmdir({ recursive: true }) deleted a directory tree, child file paths
remained in contentCache and statCache, causing readFile() to return stale
data for deleted files. Adds recursive flag to invalidateCaches() that
evicts all descendant keys by prefix.
This commit is contained in:
David Snelling 2026-01-31 09:09:12 -08:00
parent 88d0c89143
commit 66d7aa736c

View file

@ -979,7 +979,7 @@ export class VirtualFileSystem implements IVirtualFileSystem {
// Invalidate caches (recursive invalidation handles all descendants) // Invalidate caches (recursive invalidation handles all descendants)
this.pathResolver.invalidatePath(path, true) this.pathResolver.invalidatePath(path, true)
this.invalidateCaches(path) this.invalidateCaches(path, true)
// Trigger watchers // Trigger watchers
this.triggerWatchers(path, 'rename') this.triggerWatchers(path, 'rename')
@ -1424,9 +1424,23 @@ export class VirtualFileSystem implements IVirtualFileSystem {
return new RegExp(`^${regex}$`).test(name) return new RegExp(`^${regex}$`).test(name)
} }
private invalidateCaches(path: string): void { private invalidateCaches(path: string, recursive = false): void {
this.contentCache.delete(path) this.contentCache.delete(path)
this.statCache.delete(path) this.statCache.delete(path)
if (recursive) {
const prefix = path.endsWith('/') ? path : path + '/'
for (const cachedPath of this.contentCache.keys()) {
if (cachedPath.startsWith(prefix)) {
this.contentCache.delete(cachedPath)
}
}
for (const cachedPath of this.statCache.keys()) {
if (cachedPath.startsWith(prefix)) {
this.statCache.delete(cachedPath)
}
}
}
} }
private triggerWatchers(path: string, event: 'rename' | 'change'): void { private triggerWatchers(path: string, event: 'rename' | 'change'): void {