/** * Universal File System implementation * Browser: Uses OPFS (Origin Private File System) * Node.js: Uses built-in fs/promises * Serverless: Uses memory-based fallback */ import { isBrowser, isNode } from '../utils/environment.js'; let nodeFs = null; // Dynamic import for Node.js fs (only in Node.js environment) if (isNode()) { try { nodeFs = await import('fs/promises'); } catch { // Ignore import errors in non-Node environments } } /** * Browser implementation using OPFS */ class BrowserFS { async getRoot() { if ('storage' in navigator && 'getDirectory' in navigator.storage) { return await navigator.storage.getDirectory(); } throw new Error('OPFS not supported in this browser'); } async getFileHandle(path, create = false) { const root = await this.getRoot(); const parts = path.split('/').filter(p => p); let dir = root; for (let i = 0; i < parts.length - 1; i++) { dir = await dir.getDirectoryHandle(parts[i], { create }); } const fileName = parts[parts.length - 1]; return await dir.getFileHandle(fileName, { create }); } async getDirHandle(path, create = false) { const root = await this.getRoot(); const parts = path.split('/').filter(p => p); let dir = root; for (const part of parts) { dir = await dir.getDirectoryHandle(part, { create }); } return dir; } async readFile(path, encoding) { try { const fileHandle = await this.getFileHandle(path); const file = await fileHandle.getFile(); return await file.text(); } catch (error) { throw new Error(`File not found: ${path}`); } } async writeFile(path, data, encoding) { const fileHandle = await this.getFileHandle(path, true); const writable = await fileHandle.createWritable(); await writable.write(data); await writable.close(); } async mkdir(path, options = { recursive: true }) { await this.getDirHandle(path, true); } async exists(path) { try { await this.getFileHandle(path); return true; } catch { try { await this.getDirHandle(path); return true; } catch { return false; } } } async readdir(path, options) { const dir = await this.getDirHandle(path); if (options?.withFileTypes) { const entries = []; for await (const [name, handle] of dir.entries()) { entries.push({ name, isDirectory: () => handle.kind === 'directory', isFile: () => handle.kind === 'file' }); } return entries; } else { const entries = []; for await (const [name] of dir.entries()) { entries.push(name); } return entries; } } async unlink(path) { const parts = path.split('/').filter(p => p); const fileName = parts.pop(); const dirPath = parts.join('/'); if (dirPath) { const dir = await this.getDirHandle(dirPath); await dir.removeEntry(fileName); } else { const root = await this.getRoot(); await root.removeEntry(fileName); } } async stat(path) { try { await this.getFileHandle(path); return { isFile: () => true, isDirectory: () => false }; } catch { try { await this.getDirHandle(path); return { isFile: () => false, isDirectory: () => true }; } catch { throw new Error(`Path not found: ${path}`); } } } async access(path, mode) { const exists = await this.exists(path); if (!exists) { throw new Error(`ENOENT: no such file or directory, access '${path}'`); } } } /** * Node.js implementation using fs/promises */ class NodeFS { async readFile(path, encoding = 'utf-8') { return await nodeFs.readFile(path, encoding); } async writeFile(path, data, encoding = 'utf-8') { await nodeFs.writeFile(path, data, encoding); } async mkdir(path, options = { recursive: true }) { await nodeFs.mkdir(path, options); } async exists(path) { try { await nodeFs.access(path); return true; } catch { return false; } } async readdir(path, options) { if (options?.withFileTypes) { return await nodeFs.readdir(path, { withFileTypes: true }); } return await nodeFs.readdir(path); } async unlink(path) { await nodeFs.unlink(path); } async stat(path) { const stats = await nodeFs.stat(path); return { isFile: () => stats.isFile(), isDirectory: () => stats.isDirectory() }; } async access(path, mode) { await nodeFs.access(path, mode); } } /** * Memory-based fallback for serverless/edge environments */ class MemoryFS { constructor() { this.files = new Map(); this.dirs = new Set(); } async readFile(path, encoding) { const content = this.files.get(path); if (content === undefined) { throw new Error(`File not found: ${path}`); } return content; } async writeFile(path, data, encoding) { this.files.set(path, data); // Ensure parent directories exist const parts = path.split('/').slice(0, -1); for (let i = 1; i <= parts.length; i++) { this.dirs.add(parts.slice(0, i).join('/')); } } async mkdir(path, options = { recursive: true }) { this.dirs.add(path); if (options.recursive) { const parts = path.split('/'); for (let i = 1; i <= parts.length; i++) { this.dirs.add(parts.slice(0, i).join('/')); } } } async exists(path) { return this.files.has(path) || this.dirs.has(path); } async readdir(path, options) { const entries = new Set(); const pathPrefix = path + '/'; for (const filePath of this.files.keys()) { if (filePath.startsWith(pathPrefix)) { const relativePath = filePath.slice(pathPrefix.length); const firstSegment = relativePath.split('/')[0]; entries.add(firstSegment); } } for (const dirPath of this.dirs) { if (dirPath.startsWith(pathPrefix)) { const relativePath = dirPath.slice(pathPrefix.length); const firstSegment = relativePath.split('/')[0]; if (firstSegment) entries.add(firstSegment); } } if (options?.withFileTypes) { return Array.from(entries).map(name => ({ name, isDirectory: () => this.dirs.has(path + '/' + name), isFile: () => this.files.has(path + '/' + name) })); } return Array.from(entries); } async unlink(path) { this.files.delete(path); } async stat(path) { const isFile = this.files.has(path); const isDir = this.dirs.has(path); if (!isFile && !isDir) { throw new Error(`Path not found: ${path}`); } return { isFile: () => isFile, isDirectory: () => isDir }; } async access(path, mode) { const exists = await this.exists(path); if (!exists) { throw new Error(`ENOENT: no such file or directory, access '${path}'`); } } } // Create the appropriate filesystem implementation let fsImpl; if (isBrowser()) { fsImpl = new BrowserFS(); } else if (isNode() && nodeFs) { fsImpl = new NodeFS(); } else { fsImpl = new MemoryFS(); } // Export the filesystem operations export const readFile = fsImpl.readFile.bind(fsImpl); export const writeFile = fsImpl.writeFile.bind(fsImpl); export const mkdir = fsImpl.mkdir.bind(fsImpl); export const exists = fsImpl.exists.bind(fsImpl); export const readdir = fsImpl.readdir.bind(fsImpl); export const unlink = fsImpl.unlink.bind(fsImpl); export const stat = fsImpl.stat.bind(fsImpl); export const access = fsImpl.access.bind(fsImpl); // Default export with promises namespace compatibility export default { readFile, writeFile, mkdir, exists, readdir, unlink, stat, access }; // Named export for fs/promises compatibility export const promises = { readFile, writeFile, mkdir, exists, readdir, unlink, stat, access }; //# sourceMappingURL=fs.js.map