claude-flow-tbowman01
Version:
Enterprise-grade AI agent orchestration with ruv-swarm integration (Alpha Release)
244 lines • 8.31 kB
JavaScript
/**
* 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