@firesystem/core
Version:
Virtual File System core interfaces and types
111 lines (101 loc) • 3.67 kB
text/typescript
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<FileEntry>;
abstract writeFile(
path: string,
content: any,
metadata?: FileMetadata,
): Promise<FileEntry>;
abstract deleteFile(path: string): Promise<void>;
abstract exists(path: string): Promise<boolean>;
abstract readDir(path: string): Promise<FileEntry[]>;
abstract mkdir(path: string, recursive?: boolean): Promise<FileEntry>;
abstract rmdir(path: string, recursive?: boolean): Promise<void>;
abstract rename(oldPath: string, newPath: string): Promise<FileEntry>;
abstract move(sourcePaths: string[], targetPath: string): Promise<void>;
abstract copy(sourcePath: string, targetPath: string): Promise<FileEntry>;
abstract watch(
pattern: string,
callback: (event: FSEvent) => void,
): Disposable;
abstract stat(path: string): Promise<FileStat>;
abstract glob(pattern: string): Promise<string[]>;
abstract clear(): Promise<void>;
abstract size(): Promise<number>;
// 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<boolean> {
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<boolean> {
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<FileEntry> {
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);
}
}