@iyulab/oops
Version:
Core SDK for Oops - Safe text file editing with automatic backup
290 lines • 9.88 kB
JavaScript
;
/**
* File system operations for Oops
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.FileSystem = void 0;
const fs = __importStar(require("fs/promises"));
const path = __importStar(require("path"));
const errors_1 = require("./errors");
const transaction_1 = require("./transaction");
class FileSystem {
static async exists(filePath) {
try {
await fs.access(filePath);
return true;
}
catch {
return false;
}
}
static async readFile(filePath) {
try {
return await fs.readFile(filePath, 'utf8');
}
catch (error) {
if (error.code === 'ENOENT') {
throw new errors_1.FileNotFoundError(filePath);
}
if (error.code === 'EACCES') {
throw new errors_1.PermissionError(filePath, 'read');
}
throw error;
}
}
static async writeFile(filePath, content) {
try {
await fs.writeFile(filePath, content, 'utf8');
}
catch (error) {
if (error.code === 'EACCES') {
throw new errors_1.PermissionError(filePath, 'write');
}
throw error;
}
}
static async copyFile(source, destination) {
try {
await fs.copyFile(source, destination);
}
catch (error) {
if (error.code === 'ENOENT') {
throw new errors_1.FileNotFoundError(source);
}
if (error.code === 'EACCES') {
throw new errors_1.PermissionError(destination, 'write');
}
throw error;
}
}
static async mkdir(dirPath) {
try {
await fs.mkdir(dirPath, { recursive: true });
}
catch (error) {
if (error.code === 'EACCES') {
throw new errors_1.PermissionError(dirPath, 'create');
}
throw error;
}
}
static async stat(filePath) {
try {
return await fs.stat(filePath);
}
catch (error) {
if (error.code === 'ENOENT') {
throw new errors_1.FileNotFoundError(filePath);
}
throw error;
}
}
/**
* Atomic file operations using transactions
*/
static async safeWriteFile(filePath, content) {
const transaction = new transaction_1.Transaction();
try {
// Ensure directory exists
const dir = path.dirname(filePath);
if (!(await this.exists(dir))) {
transaction.addOperation(transaction_1.FileOperations.createDirectory(dir));
}
// Write file
transaction.addOperation(transaction_1.FileOperations.writeFile(filePath, content));
await transaction.execute();
}
catch (error) {
throw new errors_1.OopsError(`Failed to safely write file: ${filePath}`, 'SAFE_WRITE_ERROR', {
error,
});
}
}
static async safeCopyFile(source, destination) {
const transaction = new transaction_1.Transaction();
try {
// Ensure destination directory exists
const dir = path.dirname(destination);
if (!(await this.exists(dir))) {
transaction.addOperation(transaction_1.FileOperations.createDirectory(dir));
}
// Copy file
transaction.addOperation(transaction_1.FileOperations.copyFile(source, destination));
await transaction.execute();
}
catch (error) {
throw new errors_1.OopsError(`Failed to safely copy file: ${source} -> ${destination}`, 'SAFE_COPY_ERROR', { error });
}
}
static async safeMoveFile(source, destination) {
const transaction = new transaction_1.Transaction();
try {
// Ensure destination directory exists
const dir = path.dirname(destination);
if (!(await this.exists(dir))) {
transaction.addOperation(transaction_1.FileOperations.createDirectory(dir));
}
// Move file
transaction.addOperation(transaction_1.FileOperations.moveFile(source, destination));
await transaction.execute();
}
catch (error) {
throw new errors_1.OopsError(`Failed to safely move file: ${source} -> ${destination}`, 'SAFE_MOVE_ERROR', { error });
}
}
static async safeDeleteFile(filePath) {
const transaction = new transaction_1.Transaction();
try {
transaction.addOperation(transaction_1.FileOperations.deleteFile(filePath));
await transaction.execute();
}
catch (error) {
throw new errors_1.OopsError(`Failed to safely delete file: ${filePath}`, 'SAFE_DELETE_ERROR', {
error,
});
}
}
/**
* Validate file permissions before operations
*/
static async validatePermissions(filePath, operation) {
try {
await fs.stat(filePath);
// Check if file exists and has required permissions
switch (operation) {
case 'read':
await fs.access(filePath, fs.constants.R_OK);
break;
case 'write':
await fs.access(filePath, fs.constants.W_OK);
break;
case 'execute':
await fs.access(filePath, fs.constants.X_OK);
break;
}
}
catch (error) {
if (error.code === 'ENOENT') {
throw new errors_1.FileNotFoundError(filePath);
}
if (error.code === 'EACCES') {
throw new errors_1.PermissionError(filePath, operation);
}
throw error;
}
}
/**
* Get file metadata with validation
*/
static async getFileInfo(filePath) {
try {
const stats = await fs.stat(filePath);
// Check permissions
const permissions = {
readable: await this.checkPermission(filePath, fs.constants.R_OK),
writable: await this.checkPermission(filePath, fs.constants.W_OK),
executable: await this.checkPermission(filePath, fs.constants.X_OK),
};
return {
exists: true,
size: stats.size,
modified: stats.mtime,
isDirectory: stats.isDirectory(),
isFile: stats.isFile(),
permissions,
};
}
catch (error) {
if (error.code === 'ENOENT') {
return {
exists: false,
size: 0,
modified: new Date(0),
isDirectory: false,
isFile: false,
permissions: {
readable: false,
writable: false,
executable: false,
},
};
}
throw error;
}
}
/**
* Remove file or directory recursively
*/
static async remove(filePath) {
try {
const stats = await fs.stat(filePath);
if (stats.isDirectory()) {
await fs.rm(filePath, { recursive: true, force: true });
}
else {
await fs.unlink(filePath);
}
}
catch (error) {
if (error.code !== 'ENOENT') {
throw new errors_1.FileOperationError('remove', filePath, error.message);
}
}
}
/**
* Create temporary directory
*/
static async createTempDirectory(prefix = 'tmp-') {
try {
const tmpdir = require('os').tmpdir();
return await fs.mkdtemp(path.join(tmpdir, prefix));
}
catch (error) {
throw new errors_1.FileOperationError('create_temp_dir', prefix, error.message);
}
}
static async checkPermission(filePath, permission) {
try {
await fs.access(filePath, permission);
return true;
}
catch {
return false;
}
}
}
exports.FileSystem = FileSystem;
//# sourceMappingURL=file-system.js.map