@iyulab/oops
Version:
Core SDK for Oops - Safe text file editing with automatic backup
386 lines • 15.2 kB
JavaScript
"use strict";
/**
* Main Oops SDK class
*/
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.Oops = void 0;
const config_1 = require("./config");
const workspace_1 = require("./workspace");
const tracker_1 = require("./tracker");
const backup_1 = require("./backup");
const diff_1 = require("./diff");
const version_1 = require("./version");
const simple_version_1 = require("./simple-version");
class Oops {
configManager;
workspaceManager;
fileTracker;
backupManager;
diffProcessor;
versionManager;
simpleVersionManagers = new Map();
constructor(config = {}, workspacePath) {
this.configManager = new config_1.ConfigManager(config);
this.workspaceManager = new workspace_1.WorkspaceManager(workspacePath);
this.fileTracker = new tracker_1.FileTracker(this.workspaceManager.getWorkspacePath());
this.backupManager = new backup_1.BackupManager(this.workspaceManager.getWorkspacePath());
this.diffProcessor = new diff_1.DiffProcessor();
this.versionManager = new version_1.VersionManager(this.workspaceManager.getWorkspacePath());
}
// Workspace operations
async init() {
await this.workspaceManager.init();
}
async getWorkspaceInfo() {
try {
return await this.workspaceManager.getInfo();
}
catch {
// If workspace doesn't exist, return basic info
return {
path: this.workspaceManager.getWorkspacePath(),
type: 'local',
exists: false,
isHealthy: false,
trackedFiles: [],
createdAt: new Date(),
};
}
}
// File tracking operations
async track(filePath, message) {
// Initialize workspace if needed
if (!(await this.workspaceManager.exists())) {
await this.init();
}
return await this.fileTracker.startTracking(filePath, message);
}
async isTracked(filePath) {
return await this.fileTracker.isTracked(filePath);
}
async getTrackingInfo(filePath) {
return await this.fileTracker.getTrackingInfo(filePath);
}
// Diff operations
async diff(filePath) {
const trackingInfo = await this.fileTracker.getTrackingInfo(filePath);
return await this.diffProcessor.generateDiff(trackingInfo.backupPath, filePath);
}
async hasChanges(filePath) {
try {
const trackingInfo = await this.fileTracker.getTrackingInfo(filePath);
return await this.diffProcessor.hasChanges(trackingInfo.backupPath, filePath);
}
catch {
// If we can't get tracking info, assume no changes
return false;
}
}
// Backup operations
async keep(filePath) {
// Verify file is being tracked
if (!(await this.isTracked(filePath))) {
throw new Error(`File is not being tracked: ${filePath}`);
}
// Apply changes by simply stopping tracking
// The current file already contains the desired changes
await this.fileTracker.stopTracking(filePath);
}
async undo(filePath) {
// Verify file is being tracked
if (!(await this.isTracked(filePath))) {
throw new Error(`File is not being tracked: ${filePath}`);
}
// Get tracking info to find backup path
const trackingInfo = await this.fileTracker.getTrackingInfo(filePath);
// Restore file from backup
const { FileSystem } = await Promise.resolve().then(() => __importStar(require('./file-system')));
if (await FileSystem.exists(trackingInfo.backupPath)) {
await FileSystem.copyFile(trackingInfo.backupPath, filePath);
}
else {
throw new Error(`Backup not found: ${trackingInfo.backupPath}`);
}
// Stop tracking
await this.fileTracker.stopTracking(filePath);
}
async abort(filePath) {
await this.fileTracker.stopTracking(filePath);
}
// Workspace management
async isWorkspaceHealthy() {
try {
const info = await this.getWorkspaceInfo();
return info.exists && info.isHealthy;
}
catch {
return false;
}
}
async cleanWorkspace() {
await this.workspaceManager.clean();
}
async getWorkspaceSize() {
const info = await this.getWorkspaceInfo();
let totalSize = 0;
// Calculate total size of all tracked files
for (const file of info.trackedFiles) {
try {
const { FileSystem } = await Promise.resolve().then(() => __importStar(require('./file-system')));
if (await FileSystem.exists(file.filePath)) {
const fileInfo = await FileSystem.getFileInfo(file.filePath);
totalSize += fileInfo.size;
}
}
catch {
// Ignore errors for individual files
}
}
return {
files: info.trackedFiles.length,
sizeBytes: totalSize,
};
}
async getAllTrackedFiles() {
return await this.fileTracker.getAllTracked();
}
async keepAll() {
const trackedFiles = await this.getAllTrackedFiles();
for (const file of trackedFiles) {
await this.keep(file.filePath);
}
}
async undoAll() {
const trackedFiles = await this.getAllTrackedFiles();
for (const file of trackedFiles) {
await this.undo(file.filePath);
}
}
async abortAll() {
const trackedFiles = await this.getAllTrackedFiles();
for (const file of trackedFiles) {
await this.abort(file.filePath);
}
}
async validateTrackedFiles() {
const errors = [];
const trackedFiles = await this.getAllTrackedFiles();
for (const file of trackedFiles) {
try {
const { FileSystem } = await Promise.resolve().then(() => __importStar(require('./file-system')));
if (!(await FileSystem.exists(file.filePath))) {
errors.push(`Tracked file does not exist: ${file.filePath}`);
}
if (!(await FileSystem.exists(file.backupPath))) {
errors.push(`Backup file does not exist: ${file.backupPath}`);
}
}
catch (error) {
errors.push(`Error validating ${file.filePath}: ${error.message}`);
}
}
return { valid: errors.length === 0, errors };
}
async getVersion() {
try {
const fs = await Promise.resolve().then(() => __importStar(require('fs/promises')));
const path = await Promise.resolve().then(() => __importStar(require('path')));
const packagePath = path.join(__dirname, '../../package.json');
const packageContent = await fs.readFile(packagePath, 'utf8');
const packageJson = JSON.parse(packageContent);
return packageJson.version;
}
catch {
return '0.1.0';
}
}
static async createTempWorkspace() {
const { FileSystem } = await Promise.resolve().then(() => __importStar(require('./file-system')));
const tempDir = await FileSystem.createTempDirectory('oops-temp-');
return new Oops({}, tempDir);
}
static async createLocalWorkspace(workspaceDir) {
return new Oops({}, workspaceDir);
}
// Configuration
getConfig() {
return this.configManager.get();
}
setConfig(key, value) {
this.configManager.set(key, value);
}
// Simple Version System Methods (for TDD tests)
async trackWithVersion(filePath, message) {
// Initialize workspace if needed
if (!(await this.workspaceManager.exists())) {
await this.init();
}
// Get or create simple version manager for this file
const versionManager = this.getSimpleVersionManager(filePath);
await versionManager.initialize();
// Create initial version
return await versionManager.createInitialVersion(message || 'Initial version');
}
async commitVersion(filePath, message) {
const versionManager = this.getSimpleVersionManager(filePath);
return await versionManager.commitVersion(message);
}
async getVersionHistory(filePath) {
const versionManager = this.getSimpleVersionManager(filePath);
const versions = await versionManager.getAllVersions();
if (versions.length === 0) {
throw new Error(`File is not being tracked: ${filePath}`);
}
return versions;
}
async checkoutVersion(filePath, version) {
const versionManager = this.getSimpleVersionManager(filePath);
const versions = await versionManager.getAllVersions();
if (versions.length === 0) {
throw new Error(`File is not being tracked: ${filePath}`);
}
await versionManager.checkoutVersion(version);
}
async getCurrentVersion(filePath) {
const versionManager = this.getSimpleVersionManager(filePath);
const versions = await versionManager.getAllVersions();
if (versions.length === 0) {
throw new Error(`File is not being tracked: ${filePath}`);
}
const currentVersion = await versionManager.getCurrentVersion();
return currentVersion || 1;
}
async getVersionDiff(filePath, fromVersion, toVersion) {
const versionManager = this.getSimpleVersionManager(filePath);
const versions = await versionManager.getAllVersions();
if (versions.length === 0) {
throw new Error(`File is not being tracked: ${filePath}`);
}
return await versionManager.getDiff(fromVersion, toVersion);
}
async hasVersionChanges(filePath) {
try {
const versionManager = this.getSimpleVersionManager(filePath);
return await versionManager.hasChanges();
}
catch {
return false;
}
}
getSimpleVersionManager(filePath) {
if (!this.simpleVersionManagers.has(filePath)) {
const versionManager = new simple_version_1.SimpleVersionManager(this.workspaceManager.getWorkspacePath(), filePath);
this.simpleVersionManagers.set(filePath, versionManager);
}
return this.simpleVersionManagers.get(filePath);
}
// Version management operations (legacy)
async trackWithVersioning(filePath, message) {
// Initialize workspace if needed
if (!(await this.workspaceManager.exists())) {
await this.init();
}
// Check if already versioned
try {
const history = await this.versionManager.getVersionHistory(filePath);
return history.versions[history.versions.length - 1]; // Return latest version
}
catch {
// Not versioned yet, initialize
return await this.versionManager.initializeVersioning(filePath, message);
}
}
async commit(filePath, message) {
return await this.versionManager.createCommit(filePath, message);
}
async commitAll(message) {
const trackedFiles = await this.getAllTrackedFiles();
const commits = [];
const errors = [];
for (const file of trackedFiles) {
try {
if (await this.hasVersionChanges(file.filePath)) {
const commit = await this.versionManager.createCommit(file.filePath, message);
commits.push(commit);
}
}
catch (error) {
// If version system fails, try to initialize versioning first
try {
await this.trackWithVersioning(file.filePath, 'Initial version');
if (await this.hasVersionChanges(file.filePath)) {
const commit = await this.versionManager.createCommit(file.filePath, message);
commits.push(commit);
}
}
catch (initError) {
// Only add to errors, don't print to console
errors.push(`Failed to commit ${file.filePath}: ${initError.message}`);
}
}
}
// If we have errors but no commits, this might indicate a real problem
if (errors.length > 0 && commits.length === 0) {
// Only log the first few errors to avoid spam
const significantErrors = errors.slice(0, 3);
throw new Error(`No files could be committed. Recent errors: ${significantErrors.join('; ')}`);
}
return commits;
}
async getVersions(filePath) {
return await this.versionManager.listVersions(filePath);
}
async versionDiff(filePath, fromVersion, toVersion) {
return await this.versionManager.diff(filePath, fromVersion, toVersion);
}
async untrackWithVersioning(filePath) {
// Remove version tracking
await this.versionManager.removeVersioning(filePath);
// Remove old-style tracking if it exists
if (await this.isTracked(filePath)) {
await this.abort(filePath);
}
}
async undoWithVersioning(filePath, version) {
const targetVersion = version || '1.0'; // Default to initial version
// Checkout the specified version
await this.versionManager.checkout(filePath, targetVersion);
// Remove version tracking (stop tracking)
await this.versionManager.removeVersioning(filePath);
}
}
exports.Oops = Oops;
//# sourceMappingURL=oops.js.map