UNPKG

claude-flow-tbowman01

Version:

Enterprise-grade AI agent orchestration with ruv-swarm integration (Alpha Release)

244 lines 8.31 kB
/** * Async File Manager * Handles non-blocking file operations with queuing */ import { promises as fs } from 'node:fs'; import { pipeline } from 'node:stream/promises'; import { createWriteStream, createReadStream } from 'node:fs'; import { Readable } from 'node:stream'; import { dirname } from 'node:path'; import PQueue from 'p-queue'; import { Logger } from '../../core/logger.js'; export class AsyncFileManager { concurrency; writeQueue; readQueue; logger; metrics = { operations: new Map(), totalBytes: 0, errors: 0, }; constructor(concurrency = { write: 10, read: 20, }) { this.concurrency = concurrency; this.writeQueue = new PQueue({ concurrency: this.concurrency.write }); this.readQueue = new PQueue({ concurrency: this.concurrency.read }); // Use test-safe logger configuration const loggerConfig = process.env.CLAUDE_FLOW_ENV === 'test' ? { level: 'error', format: 'json', destination: 'console' } : { level: 'info', format: 'json', destination: 'console' }; this.logger = new Logger(loggerConfig, { component: 'AsyncFileManager' }); } async writeFile(path, data) { const start = Date.now(); return await this.writeQueue.add(async () => { try { // Ensure directory exists await this.ensureDirectory(dirname(path)); // Use streaming for large files if (data.length > 1024 * 1024) { // > 1MB await this.streamWrite(path, data); } else { await fs.writeFile(path, data, 'utf8'); } const duration = Date.now() - start; const size = Buffer.byteLength(data); this.trackOperation('write', size); return { path, operation: 'write', success: true, duration, size, }; } catch (error) { this.metrics.errors++; this.logger.error('Failed to write file', { path, error }); return { path, operation: 'write', success: false, duration: Date.now() - start, error: error, }; } }); } async readFile(path) { const start = Date.now(); return await this.readQueue.add(async () => { try { const data = await fs.readFile(path, 'utf8'); const duration = Date.now() - start; const size = Buffer.byteLength(data); this.trackOperation('read', size); return { path, operation: 'read', success: true, duration, size, data, }; } catch (error) { this.metrics.errors++; this.logger.error('Failed to read file', { path, error }); return { path, operation: 'read', success: false, duration: Date.now() - start, error: error, }; } }); } async writeJSON(path, data, pretty = true) { const jsonString = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data); return this.writeFile(path, jsonString); } async readJSON(path) { const result = await this.readFile(path); if (result.success && result.data) { try { const parsed = JSON.parse(result.data); return { ...result, data: parsed }; } catch (error) { return { ...result, success: false, error: new Error('Invalid JSON format'), }; } } return result; } async deleteFile(path) { const start = Date.now(); return this.writeQueue.add(async () => { try { await fs.unlink(path); this.trackOperation('delete', 0); return { path, operation: 'delete', success: true, duration: Date.now() - start, }; } catch (error) { this.metrics.errors++; this.logger.error('Failed to delete file', { path, error }); return { path, operation: 'delete', success: false, duration: Date.now() - start, error: error, }; } }); } async ensureDirectory(path) { const start = Date.now(); try { await fs.mkdir(path, { recursive: true }); this.trackOperation('mkdir', 0); return { path, operation: 'mkdir', success: true, duration: Date.now() - start, }; } catch (error) { this.metrics.errors++; this.logger.error('Failed to create directory', { path, error }); return { path, operation: 'mkdir', success: false, duration: Date.now() - start, error: error, }; } } async ensureDirectories(paths) { return Promise.all(paths.map((path) => this.ensureDirectory(path))); } async streamWrite(path, data) { const stream = createWriteStream(path); await pipeline(Readable.from(data), stream); } async streamRead(path) { return createReadStream(path); } async copyFile(source, destination) { const start = Date.now(); return this.writeQueue.add(async () => { try { await this.ensureDirectory(dirname(destination)); await fs.copyFile(source, destination); const stats = await fs.stat(destination); this.trackOperation('write', stats.size); return { path: destination, operation: 'write', success: true, duration: Date.now() - start, size: stats.size, }; } catch (error) { this.metrics.errors++; this.logger.error('Failed to copy file', { source, destination, error }); return { path: destination, operation: 'write', success: false, duration: Date.now() - start, error: error, }; } }); } async moveFile(source, destination) { const copyResult = await this.copyFile(source, destination); if (copyResult.success) { await this.deleteFile(source); } return copyResult; } trackOperation(type, bytes) { const count = this.metrics.operations.get(type) || 0; this.metrics.operations.set(type, count + 1); this.metrics.totalBytes += bytes; } getMetrics() { return { operations: Object.fromEntries(this.metrics.operations), totalBytes: this.metrics.totalBytes, errors: this.metrics.errors, writeQueueSize: this.writeQueue.size, readQueueSize: this.readQueue.size, writeQueuePending: this.writeQueue.pending, readQueuePending: this.readQueue.pending, }; } async waitForPendingOperations() { await Promise.all([this.writeQueue.onIdle(), this.readQueue.onIdle()]); } clearQueues() { this.writeQueue.clear(); this.readQueue.clear(); } } //# sourceMappingURL=async-file-manager.js.map