"use strict";
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.readFile = readFile;
exports.writeFileSync = writeFileSync;
exports.appendFileSync = appendFileSync;
exports.deleteFileSync = deleteFileSync;
exports.copyFileSync = copyFileSync;
exports.moveFileSync = moveFileSync;
exports.ensureDirectory = ensureDirectory;
exports.fileExists = fileExists;
exports.isDirectory = isDirectory;
exports.getFileStats = getFileStats;
exports.listDirectory = listDirectory;
exports.zipCreate = zipCreate;
exports.fileChecksum = fileChecksum;
exports.zipExtract = zipExtract;
exports.zipList = zipList;
exports.diffFiles = diffFiles;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const zlib = __importStar(require("zlib"));
const securityEnhanced_1 = require("./securityEnhanced");
// ── File Operations ──
function readFile(filePath) {
try {
return fs.readFileSync((0, securityEnhanced_1.expandPath)(filePath), "utf8");
}
catch (err) {
throw new Error(`Failed to read file: ${filePath} — ${err.message}`);
}
}
function writeFileSync(filePath, content) {
const validation = (0, securityEnhanced_1.validateWritePath)(filePath);
if (!validation.valid) {
throw new Error(validation.error);
}
const safePath = validation.sanitized;
try {
const dir = path.dirname(safePath);
if (!fs.existsSync(dir))
fs.mkdirSync(dir, { recursive: true });
fs.writeFileSync(safePath, content, "utf8");
}
catch (err) {
throw new Error(`Failed to write file: ${filePath} — ${err.message}`);
}
}
function appendFileSync(filePath, content) {
const validation = (0, securityEnhanced_1.validateWritePath)(filePath);
if (!validation.valid) {
throw new Error(validation.error);
}
const safePath = validation.sanitized;
try {
fs.appendFileSync(safePath, content, "utf8");
}
catch (err) {
throw new Error(`Failed to append to file: ${filePath} — ${err.message}`);
}
}
function deleteFileSync(filePath) {
const validation = (0, securityEnhanced_1.validateWritePath)(filePath);
if (!validation.valid) {
throw new Error(validation.error);
}
const safePath = validation.sanitized;
try {
fs.unlinkSync(safePath);
}
catch (err) {
throw new Error(`Failed to delete file: ${filePath} — ${err.message}`);
}
}
function copyFileSync(srcPath, destPath) {
const validation = (0, securityEnhanced_1.validateWritePath)(destPath);
if (!validation.valid) {
throw new Error(validation.error);
}
const safeDest = validation.sanitized;
try {
fs.copyFileSync((0, securityEnhanced_1.expandPath)(srcPath), safeDest);
}
catch (err) {
throw new Error(`Failed to copy file: ${srcPath} → ${destPath} — ${err.message}`);
}
}
function moveFileSync(srcPath, destPath) {
const validation = (0, securityEnhanced_1.validateWritePath)(destPath);
if (!validation.valid) {
throw new Error(validation.error);
}
const safeDest = validation.sanitized;
try {
fs.renameSync((0, securityEnhanced_1.expandPath)(srcPath), safeDest);
}
catch (err) {
throw new Error(`Failed to move file: ${srcPath} → ${destPath} — ${err.message}`);
}
}
function ensureDirectory(dirPath) {
const validation = (0, securityEnhanced_1.validateWritePath)(dirPath);
if (!validation.valid) {
throw new Error(validation.error);
}
const safePath = validation.sanitized;
try {
fs.mkdirSync(safePath, { recursive: true });
}
catch (err) {
throw new Error(`Failed to create directory: ${dirPath} — ${err.message}`);
}
}
function fileExists(filePath) {
try {
return fs.existsSync((0, securityEnhanced_1.expandPath)(filePath));
}
catch {
return false;
}
}
function isDirectory(dirPath) {
try {
return fs.statSync((0, securityEnhanced_1.expandPath)(dirPath)).isDirectory();
}
catch {
return false;
}
}
function getFileStats(filePath) {
const stat = fs.statSync((0, securityEnhanced_1.expandPath)(filePath));
return {
size: stat.size,
createdAt: stat.birthtime,
modifiedAt: stat.mtime,
accessedAt: stat.atime,
isDirectory: stat.isDirectory(),
isFile: stat.isFile(),
isSymbolicLink: stat.isSymbolicLink(),
};
}
function listDirectory(dirPath, options = {}) {
const entries = [];
const maxDepth = options.maxDepth ?? Infinity;
function scan(dir, depth) {
if (depth > maxDepth)
return;
const files = fs.readdirSync((0, securityEnhanced_1.expandPath)(dir), { withFileTypes: true });
for (const file of files) {
const fullPath = path.join(dir, file.name);
let size = 0;
let birthtime = new Date(0);
let mtime = new Date(0);
if (!file.isDirectory()) {
try {
const stat = fs.statSync(fullPath);
size = stat.size;
birthtime = stat.birthtime;
mtime = stat.mtime;
}
catch { }
}
entries.push({
name: file.name,
path: fullPath,
isDirectory: file.isDirectory(),
isFile: file.isFile(),
size,
createdAt: birthtime,
modifiedAt: mtime,
});
if (file.isDirectory() && options.recursive) {
scan(fullPath, depth + 1);
}
}
}
scan(dirPath, 0);
return entries;
}
function crc32(buf) {
let crc = 0xFFFFFFFF;
const table = [];
for (let i = 0; i < 256; i++) {
let c = i;
for (let j = 0; j < 8; j++) {
c = (c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1);
}
table[i] = c;
}
for (let i = 0; i < buf.length; i++) {
crc = table[(crc ^ buf[i]) & 0xFF] ^ (crc >>> 8);
}
return (crc ^ 0xFFFFFFFF) >>> 0;
}
function writeUint32LE(buf, offset, value) {
buf.writeUInt32LE(value, offset);
}
function writeUint16LE(buf, offset, value) {
buf.writeUInt16LE(value, offset);
}
/**
* Build a valid PKZIP archive.
*/
function buildZip(entries) {
const parts = [];
const localOffsets = [];
for (const entry of entries) {
const localOffset = parts.reduce((sum, b) => sum + b.length, 0);
localOffsets.push(localOffset);
let compressed;
let compressionMethod = 0;
if (entry.isDirectory) {
compressed = Buffer.alloc(0);
entry.compressedSize = 0;
entry.uncompressedSize = 0;
}
else {
compressed = zlib.deflateRawSync(entry.data);
entry.compressedSize = compressed.length;
compressionMethod = 8;
}
const crc = entry.isDirectory ? 0 : crc32(entry.data);
const nameBuf = Buffer.from(entry.name, "utf8");
const localHeaderSize = 30 + nameBuf.length;
const localHeader = Buffer.alloc(localHeaderSize);
writeUint32LE(localHeader, 0, 0x04034b50);
writeUint16LE(localHeader, 4, 20);
writeUint16LE(localHeader, 6, 0);
writeUint16LE(localHeader, 8, compressionMethod);
writeUint16LE(localHeader, 10, 0);
writeUint16LE(localHeader, 12, 0);
writeUint32LE(localHeader, 14, crc);
writeUint32LE(localHeader, 18, entry.compressedSize);
writeUint32LE(localHeader, 22, entry.uncompressedSize);
writeUint16LE(localHeader, 26, nameBuf.length);
writeUint16LE(localHeader, 28, 0);
localHeader.set(nameBuf, 30);
parts.push(localHeader);
parts.push(compressed);
}
const centralDirParts = [];
for (let i = 0; i < entries.length; i++) {
const entry = entries[i];
const nameBuf = Buffer.from(entry.name, "utf8");
const cdEntrySize = 46 + nameBuf.length;
const cd = Buffer.alloc(cdEntrySize);
writeUint32LE(cd, 0, 0x02014b50);
writeUint16LE(cd, 4, 20);
writeUint16LE(cd, 6, 20);
writeUint16LE(cd, 8, 0);
writeUint16LE(cd, 10, entry.isDirectory ? 0 : 8);
writeUint16LE(cd, 12, 0);
writeUint16LE(cd, 14, 0);
writeUint32LE(cd, 16, entry.isDirectory ? 0 : crc32(entry.data));
writeUint32LE(cd, 20, entry.compressedSize);
writeUint32LE(cd, 24, entry.uncompressedSize);
writeUint16LE(cd, 28, nameBuf.length);
writeUint16LE(cd, 30, 0);
writeUint16LE(cd, 32, 0);
writeUint16LE(cd, 34, 0);
writeUint16LE(cd, 36, 0);
writeUint32LE(cd, 38, 0);
writeUint32LE(cd, 42, localOffsets[i]);
cd.set(nameBuf, 46);
centralDirParts.push(cd);
}
const totalCentralDirEntries = entries.length;
const localSize = parts.reduce((s, b) => s + b.length, 0);
const centralSize = centralDirParts.reduce((s, b) => s + b.length, 0);
const eocdSize = 22;
const eocd = Buffer.alloc(eocdSize);
writeUint32LE(eocd, 0, 0x06054b50);
writeUint16LE(eocd, 4, 0);
writeUint16LE(eocd, 6, 0);
writeUint16LE(eocd, 8, totalCentralDirEntries);
writeUint16LE(eocd, 10, totalCentralDirEntries);
writeUint32LE(eocd, 12, centralSize);
writeUint32LE(eocd, 16, localSize);
writeUint16LE(eocd, 20, 0);
const totalSize = localSize + centralSize + eocdSize;
const zip = Buffer.alloc(totalSize);
let offset = 0;
for (const b of parts) {
b.copy(zip, offset);
offset += b.length;
}
for (const b of centralDirParts) {
b.copy(zip, offset);
offset += b.length;
}
eocd.copy(zip, offset);
return zip;
}
function zipCreate(entries, outputPath) {
try {
const zipEntries = entries.map((e) => {
const buf = fs.readFileSync((0, securityEnhanced_1.expandPath)(e.source));
return {
name: e.name,
data: buf,
uncompressedSize: buf.length,
compressedSize: 0,
isDirectory: false,
};
});
const zip = buildZip(zipEntries);
const outDir = path.dirname((0, securityEnhanced_1.expandPath)(outputPath));
if (!fs.existsSync(outDir))
fs.mkdirSync(outDir, { recursive: true });
fs.writeFileSync((0, securityEnhanced_1.expandPath)(outputPath), zip);
const totalUncompressed = zipEntries.reduce((s, e) => s + e.uncompressedSize, 0);
return {
success: true,
outputPath: (0, securityEnhanced_1.expandPath)(outputPath),
filesCount: zipEntries.length,
totalUncompressedSize: totalUncompressed,
compressedSize: zip.length,
};
}
catch (err) {
return {
success: false,
outputPath: "",
filesCount: 0,
totalUncompressedSize: 0,
compressedSize: 0,
error: err.message,
};
}
}
function fileChecksum(filePath, algorithm = "sha256") {
try {
const crypto = require("crypto");
const hash = crypto.createHash(algorithm);
const stream = fs.createReadStream((0, securityEnhanced_1.expandPath)(filePath));
return new Promise((resolve, reject) => {
stream.on("data", (chunk) => hash.update(chunk));
stream.on("end", () => resolve(hash.digest("hex")));
stream.on("error", (err) => reject(err));
});
}
catch (err) {
throw new Error(`Failed to compute checksum: ${filePath} — ${err.message}`);
}
}
function zipExtract(zipPath, destDir) {
try {
const zip = fs.readFileSync((0, securityEnhanced_1.expandPath)(zipPath));
const dest = (0, securityEnhanced_1.expandPath)(destDir);
// Find EOCD: search from end for 0x06054b50
let eocdOffset = -1;
for (let i = zip.length - 22; i >= Math.max(0, zip.length - 65557); i--) {
if (zip.readUInt32LE(i) === 0x06054b50) {
eocdOffset = i;
break;
}
}
if (eocdOffset === -1)
return { success: false, extractedFiles: [], error: "Not a valid zip" };
const cdOffset = zip.readUInt32LE(eocdOffset + 16);
const cdEntries = zip.readUInt16LE(eocdOffset + 8);
const extractedFiles = [];
let offset = cdOffset;
for (let i = 0; i < cdEntries; i++) {
const entrySig = zip.readUInt32LE(offset);
if (entrySig !== 0x02014b50)
break;
const compSize = zip.readUInt32LE(offset + 20);
const uncompSize = zip.readUInt32LE(offset + 24);
const nameLen = zip.readUInt16LE(offset + 28);
const extraLen = zip.readUInt16LE(offset + 30);
const commentLen = zip.readUInt16LE(offset + 32);
const compression = zip.readUInt16LE(offset + 10);
const localHeaderOffset = zip.readUInt32LE(offset + 42);
const name = zip.subarray(offset + 46, offset + 46 + nameLen).toString("utf8");
const localSig = zip.readUInt32LE(localHeaderOffset);
if (localSig !== 0x04034b50) {
offset += 46 + nameLen + extraLen + commentLen;
continue;
}
const lNameLen = zip.readUInt16LE(localHeaderOffset + 26);
const lExtraLen = zip.readUInt16LE(localHeaderOffset + 28);
const dataOffset = localHeaderOffset + 30 + lNameLen + lExtraLen;
const raw = zip.subarray(dataOffset, dataOffset + compSize);
const fullPath = path.resolve(dest, name);
const destRoot = path.resolve(dest);
if (!fullPath.startsWith(destRoot + path.sep) && fullPath !== destRoot) {
return { success: false, extractedFiles, error: `Blocked path traversal entry: ${name}` };
}
const parentDir = path.dirname(fullPath);
if (!fs.existsSync(parentDir))
fs.mkdirSync(parentDir, { recursive: true });
let content;
if (name.endsWith("/")) {
if (!fs.existsSync(fullPath))
fs.mkdirSync(fullPath, { recursive: true });
content = Buffer.alloc(0);
}
else if (compSize === 0) {
content = Buffer.alloc(0);
}
else if (compression === 0) {
content = raw;
}
else {
content = zlib.inflateRawSync(raw);
}
if (!name.endsWith("/"))
fs.writeFileSync(fullPath, content);
extractedFiles.push(name);
offset += 46 + nameLen + extraLen + commentLen;
}
return { success: true, extractedFiles };
}
catch (err) {
return { success: false, extractedFiles: [], error: err.message };
}
}
function zipList(zipPath) {
try {
const zip = fs.readFileSync((0, securityEnhanced_1.expandPath)(zipPath));
// Find EOCD
let eocdOffset = -1;
for (let i = zip.length - 22; i >= Math.max(0, zip.length - 65557); i--) {
if (zip.readUInt32LE(i) === 0x06054b50) {
eocdOffset = i;
break;
}
}
if (eocdOffset === -1)
return { success: false, entries: [], error: "Not a valid zip" };
const cdOffset = zip.readUInt32LE(eocdOffset + 16);
const cdEntries = zip.readUInt16LE(eocdOffset + 8);
const entries = [];
let offset = cdOffset;
for (let i = 0; i < cdEntries; i++) {
const entrySig = zip.readUInt32LE(offset);
if (entrySig !== 0x02014b50)
break;
const compSize = zip.readUInt32LE(offset + 20);
const uncompSize = zip.readUInt32LE(offset + 24);
const nameLen = zip.readUInt16LE(offset + 28);
const compression = zip.readUInt16LE(offset + 10);
const name = zip.subarray(offset + 46, offset + 46 + nameLen).toString("utf8");
entries.push({
name,
compressedSize: compSize,
uncompressedSize: uncompSize,
isDirectory: name.endsWith("/") || (compSize === 0 && uncompSize === 0),
});
offset += 46 + nameLen + zip.readUInt16LE(offset + 30) + zip.readUInt16LE(offset + 32);
}
return { success: true, entries };
}
catch (err) {
return { success: false, entries: [], error: err.message };
}
}
// ── Diff Operations (pure JS fallback, no external deps) ──
function diffFiles(file1, file2) {
try {
const content1 = fs.readFileSync((0, securityEnhanced_1.expandPath)(file1), "utf8");
const content2 = fs.readFileSync((0, securityEnhanced_1.expandPath)(file2), "utf8");
return diffLinesPure(content1, content2);
}
catch (err) {
throw new Error(`Failed to diff files: ${file1} vs ${file2} — ${err.message}`);
}
}
/**
* Pure-JS line-by-line diff using Myers' algorithm for memory efficiency.
* Falls back to full LCS for small files (< 5000 lines).
* Returns unified diff format.
*/
function diffLinesPure(a, b) {
if (a === b)
return "";
const linesA = a.split("\n");
const linesB = b.split("\n");
const m = linesA.length;
const n = linesB.length;
// For small files, use full LCS (simpler and fast enough)
if (m <= 5000 && n <= 5000) {
return diffLcs(linesA, linesB);
}
// For large files, use a simplified Myers' diff (O(ND) time, O(min(m,n)) space)
return diffMyers(linesA, linesB);
}
/**
* Full LCS-based diff — fine for files under 5000 lines.
*/
function diffLcs(linesA, linesB) {
const m = linesA.length;
const n = linesB.length;
// Use two rows to save memory
const prev = new Int32Array(n + 1);
const curr = new Int32Array(n + 1);
for (let i = 1; i <= m; i++) {
for (let j = 1; j <= n; j++) {
if (linesA[i - 1] === linesB[j - 1]) {
curr[j] = prev[j - 1] + 1;
}
else {
curr[j] = Math.max(prev[j], curr[j - 1]);
}
}
prev.set(curr);
}
// We need the full table for backtracking — use it if small enough
if (m <= 2000 && n <= 2000) {
const dp = [];
for (let i = 0; i <= m; i++) {
dp[i] = new Int32Array(n + 1);
}
for (let i = 1; i <= m; i++) {
for (let j = 1; j <= n; j++) {
if (linesA[i - 1] === linesB[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
}
else {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
const result = [];
let i = m, j = n;
while (i > 0 || j > 0) {
if (i > 0 && j > 0 && linesA[i - 1] === linesB[j - 1]) {
result.unshift(` ${linesA[i - 1]}`);
i--;
j--;
}
else if (j > 0 && (i === 0 || dp[i][j - 1] >= dp[i - 1][j])) {
result.unshift(`+${linesB[j - 1]}`);
j--;
}
else {
result.unshift(`-${linesA[i - 1]}`);
i--;
}
}
return result.join("\n");
}
// For very large files where we can't afford the full table,
// fall back to a line-by-line comparison
return diffLinesByComparison(linesA, linesB);
}
/**
* Myers-inspired diff for large files — O(min(m,n)) space.
*/
function diffMyers(linesA, linesB) {
const result = [];
let i = 0;
let j = 0;
while (i < linesA.length && j < linesB.length) {
if (linesA[i] === linesB[j]) {
result.unshift(` ${linesA[i]}`);
i++;
j++;
}
else {
// Simple heuristic: check context windows
let found = false;
for (let k = 1; k <= 10 && i + k <= linesA.length; k++) {
if (linesA[i + k] === linesB[j]) {
for (let x = 0; x < k; x++) {
result.unshift(`-${linesA[i + x]}`);
}
i += k;
found = true;
break;
}
}
if (!found) {
for (let k = 1; k <= 10 && j + k <= linesB.length; k++) {
if (linesB[j + k] === linesA[i]) {
for (let x = 0; x < k; x++) {
result.unshift(`+${linesB[j + x]}`);
}
j += k;
found = true;
break;
}
}
}
if (!found) {
result.unshift(`-${linesA[i]}`);
i++;
}
}
}
while (i < linesA.length) {
result.unshift(`-${linesA[i]}`);
i++;
}
while (j < linesB.length) {
result.unshift(`+${linesB[j]}`);
j++;
}
return result.join("\n");
}
/**
* Simple line-by-line comparison for very large files.
*/
function diffLinesByComparison(linesA, linesB) {
const result = [];
const maxLen = Math.max(linesA.length, linesB.length);
for (let i = 0; i < maxLen; i++) {
const aLine = i < linesA.length ? linesA[i] : undefined;
const bLine = i < linesB.length ? linesB[i] : undefined;
if (aLine === bLine) {
result.push(` ${aLine}`);
}
else {
if (aLine !== undefined)
result.push(`-${aLine}`);
if (bLine !== undefined)
result.push(`+${bLine}`);
}
}
return result.join("\n");
}
//# sourceMappingURL=fileUtilities.js.map