@iyulab/oops
Version:
Core SDK for Oops - Safe text file editing with automatic backup
233 lines • 8.91 kB
JavaScript
;
/**
* 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