import type { IReactiveFileSystem } from "../interfaces/IReactiveFileSystem"; import type { IFileSystemCapabilities } from "../interfaces/FileSystemCapabilities"; import type { FileEntry, FileStat, FileMetadata } from "../types/FileEntry"; import type { FSEvent } from "../types/Events"; import type { Disposable } from "../types/Common"; import { normalizePath } from "../utils/paths"; /** * Classe base abstrata com implementações padrão para FileSystem * Fornece implementações padrão para os novos métodos de permissão */ export abstract class BaseFileSystem implements IReactiveFileSystem { // Métodos abstratos que cada implementação deve fornecer abstract get events(): any; abstract readFile(path: string): Promise; abstract writeFile( path: string, content: any, metadata?: FileMetadata, ): Promise; abstract deleteFile(path: string): Promise; abstract exists(path: string): Promise; abstract readDir(path: string): Promise; abstract mkdir(path: string, recursive?: boolean): Promise; abstract rmdir(path: string, recursive?: boolean): Promise; abstract rename(oldPath: string, newPath: string): Promise; abstract move(sourcePaths: string[], targetPath: string): Promise; abstract copy(sourcePath: string, targetPath: string): Promise; abstract watch( pattern: string, callback: (event: FSEvent) => void, ): Disposable; abstract stat(path: string): Promise; abstract glob(pattern: string): Promise; abstract clear(): Promise; abstract size(): Promise; // Implementações padrão dos novos métodos /** * Por padrão, permite modificação se o arquivo existe ou se pode ser criado */ async canModify(path: string): Promise { try { const stat = await this.stat(path); return !stat.readonly; } catch { // Se não existe, verifica se pode criar no diretório pai const parentPath = this.getParentPath(path); if (parentPath === "/") { return true; // Root sempre permite criar } return this.canCreateIn(parentPath); } } /** * Por padrão, permite criar se o diretório existe e não é readonly */ async canCreateIn(parentPath: string): Promise { try { const stat = await this.stat(parentPath); return stat.type === "directory" && !stat.readonly; } catch { return false; // Diretório não existe } } /** * Implementação padrão de escrita atômica usando temp + rename */ async writeFileAtomic( path: string, content: any, metadata?: FileMetadata, ): Promise { const normalizedPath = normalizePath(path); const tempPath = `${normalizedPath}.tmp${Date.now()}`; try { // 1. Escreve no arquivo temporário await this.writeFile(tempPath, content, metadata); // 2. Remove o original se existir if (await this.exists(normalizedPath)) { await this.deleteFile(normalizedPath); } // 3. Renomeia temp para final return await this.rename(tempPath, normalizedPath); } catch (error) { // Cleanup em caso de erro try { await this.deleteFile(tempPath); } catch { // Ignora erro no cleanup } throw error; } } /** * Helper para extrair diretório pai */ protected getParentPath(path: string): string { const normalized = normalizePath(path); const lastSlash = normalized.lastIndexOf("/"); return lastSlash <= 0 ? "/" : normalized.substring(0, lastSlash); } }