UNPKG

apiver

Version:

Advanced API Versioning Without Duplication - Git-like CLI tool for managing multiple API versions in a single codebase

88 lines (79 loc) 3.11 kB
const fs = require('fs'); const path = require('path'); const { spawnSync } = require('child_process'); const { applyPatch } = require('./utils/diff'); const { decryptAndDecompress } = require('./utils/crypto'); module.exports = function inspectFile(version, filePath, opts) { const metaPath = path.join('.apiver', 'meta.json'); if (!fs.existsSync(metaPath)) { console.error('❌ No .APIver meta found. Did you run `apiver init`?'); process.exit(1); } const meta = JSON.parse(fs.readFileSync(metaPath, 'utf8')); if (!meta.versions[version]) { console.error(`❌ Version ${version} not found.`); process.exit(1); } let fileContent = reconstructFile(version, filePath, meta); if (opts.open) { const tmpPath = path.join('.apiver', 'tmp_inspect_' + path.basename(filePath)); fs.writeFileSync(tmpPath, fileContent, 'utf8'); let editor = process.env.EDITOR; if (!editor) { editor = process.platform === 'win32' ? 'notepad' : 'nano'; } try { spawnSync(editor, [tmpPath], { stdio: 'inherit' }); } finally { fs.unlinkSync(tmpPath); } } else { console.log(fileContent); } }; function reconstructFile(version, filePath, meta) { const cwd = process.cwd(); // Find the chain from the version back to its nearest full snapshot const chain = []; let cur = version; while (cur) { const entry = meta.versions[cur]; if (!entry) break; chain.unshift(cur); if (entry.type === 'full') break; cur = entry.base; } if (!chain.length) throw new Error('Failed to construct version chain'); // Extract full snapshot const fullName = chain[0]; const fullFile = path.join(cwd, '.apiver', 'snapshots', `${fullName}.full.apiver`); if (!fs.existsSync(fullFile)) throw new Error('Full snapshot not found'); const encFull = fs.readFileSync(fullFile); const zipBuffer = decryptAndDecompress(encFull); const snapshotData = JSON.parse(zipBuffer.toString('utf8')); // Traverse snapshotData to get file content const parts = filePath.split(path.sep); let node = snapshotData; for (const part of parts) { if (!node[part]) throw new Error(`File ${filePath} not found in snapshot`); node = node[part]; } let content = Buffer.from(node, 'base64').toString('utf8'); // Apply patches in order after full snapshot for (let i = 1; i < chain.length; i++) { const ver = chain[i]; const patchFile = path.join(cwd, '.apiver', 'patches', `${ver}.patch.apiver`); if (!fs.existsSync(patchFile)) continue; const encPatch = fs.readFileSync(patchFile); const patchBuf = decryptAndDecompress(encPatch); const patchObj = JSON.parse(patchBuf.toString('utf8')); // Only apply patch if file is modified const filePatch = (patchObj.changes || []).find(change => change.file === filePath); if (filePatch) { const diff = require('diff'); const applied = diff.applyPatch(content, filePatch.diff); if (applied !== false) content = applied; } } return content; }