UNPKG

epd

Version:

Enhanced peer dependency resolution for npm, yarn, and pnpm

127 lines 4.83 kB
/** * Enhanced Peer Dependencies (epd) * Copyright (c) 2024 Enhanced Peer Dependencies (epd) * Licensed under the MIT License */ import fs from 'fs/promises'; import { existsSync } from 'fs'; import path from 'path'; export class DependencyTimeline { timelinePath; backupDir; constructor(projectRoot = process.cwd()) { this.timelinePath = path.join(projectRoot, '.epd', 'timeline.json'); this.backupDir = path.join(projectRoot, '.epd', 'backups'); } async init() { const epdDir = path.dirname(this.timelinePath); if (!existsSync(epdDir)) { await fs.mkdir(epdDir, { recursive: true }); } if (!existsSync(this.backupDir)) { await fs.mkdir(this.backupDir, { recursive: true }); } } async snapshot(action, packages) { await this.init(); const packageJson = JSON.parse(await fs.readFile('package.json', 'utf-8')); const hash = this.generateHash(packageJson); const entry = { timestamp: new Date().toISOString(), action: action, packages, packageJson, hash }; // Save backup await fs.writeFile(path.join(this.backupDir, `${hash}.json`), JSON.stringify(packageJson, null, 2)); // Update timeline const timeline = await this.loadTimeline(); timeline.entries.push(entry); timeline.current = timeline.entries.length - 1; await fs.writeFile(this.timelinePath, JSON.stringify(timeline, null, 2)); } async getTimeline() { return this.loadTimeline(); } async rollback(target) { const timeline = await this.loadTimeline(); let targetIndex; if (typeof target === 'string') { // Date-based rollback const targetDate = new Date(target); targetIndex = timeline.entries.findIndex(entry => new Date(entry.timestamp) <= targetDate); if (targetIndex === -1) { targetIndex = timeline.entries.findIndex(entry => new Date(entry.timestamp) >= targetDate) - 1; } } else { targetIndex = target; } if (targetIndex < 0 || targetIndex >= timeline.entries.length) { throw new Error('Invalid rollback target'); } const targetEntry = timeline.entries[targetIndex]; // Restore package.json await fs.writeFile('package.json', JSON.stringify(targetEntry.packageJson, null, 2)); // Update timeline current pointer timeline.current = targetIndex; await fs.writeFile(this.timelinePath, JSON.stringify(timeline, null, 2)); return true; } async showHistory(limit = 10) { const timeline = await this.loadTimeline(); const entries = timeline.entries.slice(-limit).reverse(); console.log('📅 Dependency Timeline:\n'); entries.forEach((entry, index) => { const isCurrent = timeline.current === timeline.entries.length - 1 - index; const marker = isCurrent ? '→' : ' '; const date = new Date(entry.timestamp).toLocaleString(); console.log(`${marker} ${date} - ${entry.action}`); Object.entries(entry.packages).forEach(([pkg, change]) => { if (change.from) { console.log(` ${pkg}: ${change.from}${change.to}`); } else { console.log(` ${pkg}: ${change.to} (new)`); } }); console.log(); }); } async loadTimeline() { if (!existsSync(this.timelinePath)) { return { entries: [], current: -1 }; } try { const content = await fs.readFile(this.timelinePath, 'utf-8'); return JSON.parse(content); } catch { return { entries: [], current: -1 }; } } generateHash(packageJson) { const deps = JSON.stringify({ dependencies: packageJson.dependencies || {}, devDependencies: packageJson.devDependencies || {} }); return Buffer.from(deps).toString('base64').slice(0, 8); } } export async function createSnapshot(action, packages) { const timeline = new DependencyTimeline(); await timeline.snapshot(action, packages); } export async function showTimeline(limit) { const timeline = new DependencyTimeline(); await timeline.showHistory(limit); } export async function rollbackTo(target) { const timeline = new DependencyTimeline(); const success = await timeline.rollback(target); if (success) { console.log(`✅ Rolled back to ${typeof target === 'string' ? target : `entry #${target}`}`); } } //# sourceMappingURL=timeline.js.map