UNPKG

@iyulab/oops

Version:

Core SDK for Oops - Safe text file editing with automatic backup

278 lines 10.2 kB
"use strict"; /** * Git repository manager for per-file tracking */ 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.GitManager = void 0; const path = __importStar(require("path")); const git_1 = require("./git"); const file_system_1 = require("./file-system"); const errors_1 = require("./errors"); class GitManager { _workspaceRoot; gitInstances = new Map(); constructor(_workspaceRoot) { this._workspaceRoot = _workspaceRoot; } /** * Get or create a Git wrapper for a specific file */ async getGitForFile(filePath) { const fileHash = this.hashFilePath(filePath); const gitRepoPath = path.join(this._workspaceRoot, 'files', fileHash); if (!this.gitInstances.has(fileHash)) { const gitWrapper = new git_1.GitWrapper(gitRepoPath); this.gitInstances.set(fileHash, gitWrapper); } return this.gitInstances.get(fileHash); } /** * Initialize a Git repository for a file */ async initializeFileRepo(filePath) { const gitWrapper = await this.getGitForFile(filePath); try { await gitWrapper.init(); return gitWrapper; } catch (error) { throw new errors_1.GitOperationError('initialize', { error: error.message, filePath, workingDir: this._workspaceRoot, }); } } /** * Create initial backup commit for a file */ async createInitialCommit(filePath, message) { const gitWrapper = await this.getGitForFile(filePath); const fileHash = this.hashFilePath(filePath); const gitRepoPath = path.join(this._workspaceRoot, 'files', fileHash); const backupFilePath = path.join(gitRepoPath, 'backup'); try { // Copy file to git repo await file_system_1.FileSystem.safeCopyFile(filePath, backupFilePath); // Add and commit await gitWrapper.add('backup'); await gitWrapper.commit(message || `Initial backup of ${path.basename(filePath)}`); } catch (error) { throw new errors_1.GitOperationError('initial-commit', { error: error.message, filePath, workingDir: gitRepoPath, }); } } /** * Update backup with current file state */ async updateBackup(filePath, message) { const gitWrapper = await this.getGitForFile(filePath); const fileHash = this.hashFilePath(filePath); const gitRepoPath = path.join(this._workspaceRoot, 'files', fileHash); const backupFilePath = path.join(gitRepoPath, 'backup'); try { // Copy current file to backup location await file_system_1.FileSystem.safeCopyFile(filePath, backupFilePath); // Check if there are changes const status = await gitWrapper.status(); if (status.files.length > 0) { await gitWrapper.add('backup'); await gitWrapper.commit(message || `Update backup of ${path.basename(filePath)}`); } } catch (error) { throw new errors_1.GitOperationError('update-backup', { error: error.message, filePath, workingDir: gitRepoPath, }); } } /** * Restore file from backup */ async restoreFromBackup(filePath) { await this.getGitForFile(filePath); const fileHash = this.hashFilePath(filePath); const gitRepoPath = path.join(this._workspaceRoot, 'files', fileHash); const backupFilePath = path.join(gitRepoPath, 'backup'); try { // Check if backup exists if (!(await file_system_1.FileSystem.exists(backupFilePath))) { throw new errors_1.GitOperationError('restore', { error: 'No backup found', filePath, workingDir: gitRepoPath, }); } // Restore from backup await file_system_1.FileSystem.safeCopyFile(backupFilePath, filePath); } catch (error) { throw new errors_1.GitOperationError('restore', { error: error.message, filePath, workingDir: gitRepoPath, }); } } /** * Get diff between current file and backup */ async getDiff(filePath) { const gitWrapper = await this.getGitForFile(filePath); const fileHash = this.hashFilePath(filePath); const gitRepoPath = path.join(this._workspaceRoot, 'files', fileHash); try { // Copy current file to temp location for diff const tempFilePath = path.join(gitRepoPath, 'current'); await file_system_1.FileSystem.safeCopyFile(filePath, tempFilePath); // Get diff const diff = await gitWrapper.diff('backup'); // Clean up temp file if (await file_system_1.FileSystem.exists(tempFilePath)) { await file_system_1.FileSystem.safeDeleteFile(tempFilePath); } return diff; } catch (error) { throw new errors_1.GitOperationError('diff', { error: error.message, filePath, workingDir: gitRepoPath, }); } } /** * Check if file has backup */ async hasBackup(filePath) { try { const gitWrapper = await this.getGitForFile(filePath); return await gitWrapper.hasCommits(); } catch { return false; } } /** * Check if file has changes compared to backup */ async hasChanges(filePath) { try { const fileHash = this.hashFilePath(filePath); const gitRepoPath = path.join(this._workspaceRoot, 'files', fileHash); const backupFilePath = path.join(gitRepoPath, 'backup'); if (!(await file_system_1.FileSystem.exists(backupFilePath))) { return true; // No backup means changes exist } const currentContent = await file_system_1.FileSystem.readFile(filePath); const backupContent = await file_system_1.FileSystem.readFile(backupFilePath); return currentContent !== backupContent; } catch { return false; } } /** * Clean up Git repository for a file */ async cleanupFileRepo(filePath) { const fileHash = this.hashFilePath(filePath); const gitRepoPath = path.join(this._workspaceRoot, 'files', fileHash); try { // Remove from cache this.gitInstances.delete(fileHash); // Remove directory if (await file_system_1.FileSystem.exists(gitRepoPath)) { await file_system_1.FileSystem.safeDeleteFile(gitRepoPath); } } catch (error) { throw new errors_1.GitOperationError('cleanup', { error: error.message, filePath, workingDir: gitRepoPath, }); } } /** * Get repository health status */ async getRepoHealth(filePath) { try { const gitWrapper = await this.getGitForFile(filePath); const fileHash = this.hashFilePath(filePath); const gitRepoPath = path.join(this._workspaceRoot, 'files', fileHash); const backupFilePath = path.join(gitRepoPath, 'backup'); const exists = await file_system_1.FileSystem.exists(gitRepoPath); const healthy = exists && (await gitWrapper.isHealthy()); const commitCount = healthy ? await gitWrapper.getCommitCount() : 0; const hasBackup = await file_system_1.FileSystem.exists(backupFilePath); return { exists, healthy, commitCount, hasBackup, }; } catch { return { exists: false, healthy: false, commitCount: 0, hasBackup: false, }; } } /** * Hash file path to create unique repository directory */ hashFilePath(filePath) { // Create a hash-based path for the file // Using base64 encoding and replacing problematic characters return Buffer.from(path.resolve(filePath)) .toString('base64') .replace(/[/+=]/g, '_') .substring(0, 32); // Limit length } } exports.GitManager = GitManager; //# sourceMappingURL=git-manager.js.map