UNPKG

@iyulab/oops

Version:

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

233 lines 8.91 kB
"use strict"; /** * File tracking management 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.FileTracker = void 0; const path = __importStar(require("path")); const file_system_1 = require("./file-system"); const errors_1 = require("./errors"); class FileTracker { workspacePath; constructor(workspacePath) { this.workspacePath = workspacePath; } async isTracked(filePath) { const trackingPath = this.getTrackingPath(filePath); return await file_system_1.FileSystem.exists(trackingPath); } async startTracking(filePath, message) { if (!(await file_system_1.FileSystem.exists(filePath))) { throw new errors_1.FileNotFoundError(filePath); } if (await this.isTracked(filePath)) { throw new errors_1.FileAlreadyTrackedError(filePath); } const trackingPath = this.getTrackingPath(filePath); const backupPath = path.join(trackingPath, 'backup'); // Create tracking directory await file_system_1.FileSystem.mkdir(trackingPath); // Create backup await file_system_1.FileSystem.copyFile(filePath, backupPath); // Create metadata const metadata = { originalPath: filePath, createdAt: new Date().toISOString(), message: message || 'Initial backup', checksum: 'TODO', // TODO: Implement checksum }; const metadataPath = path.join(trackingPath, 'metadata.json'); await file_system_1.FileSystem.writeFile(metadataPath, JSON.stringify(metadata, null, 2)); // Update workspace state await this.updateWorkspaceState(filePath, 'add'); const trackingInfo = { filePath, backupPath, workspacePath: trackingPath, isTracked: true, hasChanges: false, createdAt: new Date(), modifiedAt: new Date(), metadata, }; return trackingInfo; } async stopTracking(filePath) { if (!(await this.isTracked(filePath))) { throw new errors_1.FileNotTrackedError(filePath); } // Remove from workspace state await this.updateWorkspaceState(filePath, 'remove'); // Clean up tracking directory const trackingPath = this.getTrackingPath(filePath); if (await file_system_1.FileSystem.exists(trackingPath)) { // Remove the entire tracking directory and its contents await this.removeDirectory(trackingPath); } } async getTrackingInfo(filePath) { if (!(await this.isTracked(filePath))) { throw new errors_1.FileNotTrackedError(filePath); } const trackingPath = this.getTrackingPath(filePath); const backupPath = path.join(trackingPath, 'backup'); const metadataPath = path.join(trackingPath, 'metadata.json'); let metadata = {}; try { const metadataContent = await file_system_1.FileSystem.readFile(metadataPath); metadata = JSON.parse(metadataContent); } catch { // If metadata can't be read, use empty object } // Check if file has changes compared to backup let hasChanges = false; try { if ((await file_system_1.FileSystem.exists(backupPath)) && (await file_system_1.FileSystem.exists(filePath))) { const backupContent = await file_system_1.FileSystem.readFile(backupPath); const currentContent = await file_system_1.FileSystem.readFile(filePath); hasChanges = backupContent !== currentContent; } } catch { // If we can't compare, assume no changes } return { filePath, backupPath, workspacePath: trackingPath, isTracked: true, hasChanges, createdAt: new Date(), // TODO: get from metadata modifiedAt: new Date(), metadata, }; } async getAllTracked() { const statePath = path.join(this.workspacePath, 'state.json'); try { const stateContent = await file_system_1.FileSystem.readFile(statePath); const state = JSON.parse(stateContent); return state.trackedFiles || []; } catch { return []; } } async getStatus(filePath) { const isTracked = await this.isTracked(filePath); const exists = await file_system_1.FileSystem.exists(filePath); if (!exists) { return { path: filePath, status: 'deleted', isTracked, hasBackup: isTracked, lastModified: new Date(), }; } // TODO: Implement proper status detection return { path: filePath, status: 'clean', isTracked, hasBackup: isTracked, lastModified: new Date(), }; } getTrackingPath(filePath) { // Create a hash-based path for the file const hash = this.hashPath(filePath); return path.join(this.workspacePath, 'files', hash); } hashPath(filePath) { // Simple hash implementation - TODO: use proper hash return Buffer.from(filePath).toString('base64').replace(/[/+=]/g, '_'); } async updateWorkspaceState(filePath, action) { const statePath = path.join(this.workspacePath, 'state.json'); let state = { trackedFiles: [], lastModified: new Date().toISOString(), }; // Read existing state try { const stateContent = await file_system_1.FileSystem.readFile(statePath); state = JSON.parse(stateContent); } catch { // If state file doesn't exist or is invalid, use default } // Update tracked files list if (action === 'add') { const trackingInfo = { filePath: filePath, backupPath: path.join(this.getTrackingPath(filePath), 'backup'), workspacePath: this.getTrackingPath(filePath), isTracked: true, hasChanges: false, createdAt: new Date(), modifiedAt: new Date(), metadata: {}, }; // Remove if already exists (shouldn't happen, but safety) state.trackedFiles = state.trackedFiles.filter((f) => f.filePath !== filePath); // Add new tracking info state.trackedFiles.push(trackingInfo); } else if (action === 'remove') { state.trackedFiles = state.trackedFiles.filter((f) => f.filePath !== filePath); } // Update modification time state.lastModified = new Date().toISOString(); // Write updated state await file_system_1.FileSystem.writeFile(statePath, JSON.stringify(state, null, 2)); } async removeDirectory(dirPath) { const fs = await Promise.resolve().then(() => __importStar(require('fs/promises'))); try { await fs.rm(dirPath, { recursive: true, force: true }); } catch (error) { // Ignore errors if directory doesn't exist if (error.code !== 'ENOENT') { throw error; } } } } exports.FileTracker = FileTracker; //# sourceMappingURL=tracker.js.map