browser-debugger-cli
Version:
DevTools telemetry in your terminal. For humans and agents. Direct WebSocket to Chrome's debugging port.
113 lines • 4.29 kB
JavaScript
import * as crypto from 'crypto';
import * as fs from 'fs';
import { createLogger } from '../ui/logging/index.js';
import { getErrorMessage } from '../utils/errors.js';
const log = createLogger('atomic-file');
/**
* Atomic file operations using tmp-file-then-rename pattern.
*
* Provides safe file writing that prevents corruption from interrupted writes
* or concurrent access by using unique temporary files and atomic rename operation.
*/
export class AtomicFileWriter {
/**
* Generate a unique temporary file path.
*
* Uses process PID and random UUID to ensure uniqueness across concurrent processes.
*
* @param filePath - Target file path
* @returns Unique temporary file path
*/
static getTempPath(filePath) {
const uuid = crypto.randomUUID();
return `${filePath}.${process.pid}.${uuid}.tmp`;
}
/**
* Write data to a file atomically (synchronous).
*
* Creates a unique temporary file, writes the data, then atomically renames it to the target path.
* This ensures the target file is never in a partially written state and prevents corruption
* from concurrent writes by different processes.
*
* @param filePath - Target file path
* @param data - Data to write
* @param options - Write options
* @throws Error if write operation fails
*/
static writeSync(filePath, data, options = {}) {
const tmpPath = this.getTempPath(filePath);
try {
fs.writeFileSync(tmpPath, data, { encoding: options.encoding ?? 'utf-8' });
fs.renameSync(tmpPath, filePath);
}
catch (error) {
try {
fs.unlinkSync(tmpPath);
}
catch (cleanupError) {
log.debug(`Failed to cleanup temp file ${tmpPath}: ${getErrorMessage(cleanupError)}`);
}
throw error;
}
}
/**
* Write data to a file atomically (asynchronous).
*
* Creates a unique temporary file, writes the data, then atomically renames it to the target path.
* This ensures the target file is never in a partially written state and prevents corruption
* from concurrent writes by different processes.
*
* @param filePath - Target file path
* @param data - Data to write
* @param options - Write options
* @returns Promise that resolves when write completes
* @throws Error if write operation fails
*/
static async writeAsync(filePath, data, options = {}) {
const tmpPath = this.getTempPath(filePath);
try {
await fs.promises.writeFile(tmpPath, data, { encoding: options.encoding ?? 'utf-8' });
await fs.promises.rename(tmpPath, filePath);
}
catch (error) {
try {
await fs.promises.unlink(tmpPath);
}
catch (cleanupError) {
log.debug(`Failed to cleanup temp file ${tmpPath}: ${getErrorMessage(cleanupError)}`);
}
throw error;
}
}
/**
* Write binary data (Buffer) to a file atomically (asynchronous).
*
* Creates a unique temporary file, writes the binary data, then atomically renames it to the target path.
* This ensures the target file is never in a partially written state and prevents corruption
* from concurrent writes by different processes.
*
* Useful for screenshots, images, and other binary file exports.
*
* @param filePath - Target file path
* @param buffer - Binary data to write
* @returns Promise that resolves when write completes
* @throws Error if write operation fails
*/
static async writeBufferAsync(filePath, buffer) {
const tmpPath = this.getTempPath(filePath);
try {
await fs.promises.writeFile(tmpPath, buffer);
await fs.promises.rename(tmpPath, filePath);
}
catch (error) {
try {
await fs.promises.unlink(tmpPath);
}
catch (cleanupError) {
log.debug(`Failed to cleanup temp file ${tmpPath}: ${getErrorMessage(cleanupError)}`);
}
throw error;
}
}
}
//# sourceMappingURL=atomicFile.js.map