"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.listArchiveEntries = listArchiveEntries; exports.streamArchiveEntry = streamArchiveEntry; /** * Servicio: archiveReader * * Lista el contenido de contenedores comunes (ZIP, 7z) sin necesidad de * extraerlos completamente. Intenta usar la utilidad `7z` (7-Zip) con la * opción `-slt` para obtener un listado con metadatos; si falla y el archivo * es ZIP, intenta usar `unzip -l` como fallback. * * La función principal exportada es `listArchiveEntries(filePath, logger)` y * devuelve un array de objetos `{ name, size }` con las entradas encontradas. * * Nota: este servicio depende de binarios del sistema (`7z`, `unzip`) cuando * se trabaja con formatos comprimidos. En entornos de CI/producción debe * asegurarse la presencia de dichas utilidades o los tests deben mockear * las llamadas a `child_process.exec`. */ const path_1 = __importDefault(require("path")); const child_process_1 = require("child_process"); async function listArchiveEntries(filePath, logger = console) { const ext = path_1.default.extname(filePath).toLowerCase().replace(/^\./, ''); if (!['zip', '7z'].includes(ext)) return []; const execCmd = (cmd) => new Promise((resolve, reject) => { (0, child_process_1.exec)(cmd, (err, stdout, stderr) => { if (err) return reject(err); resolve({ stdout: String(stdout), stderr: String(stderr) }); }); }); // Intentamos 7z -slt (salida técnica fácil de parsear) const try7z = async () => { const { stdout } = await execCmd(`7z l -slt ${JSON.stringify(filePath)}`); const blocks = String(stdout).split(/\r?\n\r?\n/); const entries = []; for (const block of blocks) { const lines = block.split(/\r?\n/); const pathLine = lines.find((l) => l.startsWith('Path = ')); if (!pathLine) continue; const name = pathLine.replace(/^Path = /, '').trim(); const sizeLine = lines.find((l) => l.startsWith('Size = ')); const size = sizeLine ? parseInt(sizeLine.replace(/^Size = /, ''), 10) || 0 : 0; entries.push({ name, size }); } return entries; }; try { return await try7z(); } catch (err) { logger.warn?.({ err, filePath }, 'archiveReader: 7z failed, attempting fallback'); if (ext === 'zip') { try { const { stdout } = await execCmd(`unzip -l ${JSON.stringify(filePath)}`); const lines = String(stdout).split(/\r?\n/); const entries = []; for (const line of lines) { // línea típica: " 12345 path/to/file.bin" const m = line.match(/^\s*(\d+)\s+(.+)$/); if (m) { const size = parseInt(m[1], 10); const name = m[2].trim(); entries.push({ name, size }); } } return entries; } catch (err2) { logger.warn?.({ err2, filePath }, 'archiveReader: unzip fallback failed'); return []; } } return []; } } async function streamArchiveEntry(filePath, entryPath, logger = console) { const ext = path_1.default.extname(filePath).toLowerCase().replace(/^\./, ''); if (!['zip', '7z'].includes(ext)) return null; const waitForStreamOrError = (proc) => new Promise((resolve) => { let settled = false; const onProcError = () => { if (settled) return; settled = true; resolve(null); }; const onStdoutError = () => { if (settled) return; settled = true; resolve(null); }; const onData = () => { if (settled) return; settled = true; try { proc.removeListener('error', onProcError); } catch (e) { } if (proc.stdout && proc.stdout.removeListener) { try { proc.stdout.removeListener('error', onStdoutError); proc.stdout.removeListener('readable', onData); proc.stdout.removeListener('data', onData); } catch (e) { } } resolve(proc.stdout); }; proc.once('error', onProcError); if (proc.stdout && proc.stdout.once) { proc.stdout.once('error', onStdoutError); proc.stdout.once('readable', onData); proc.stdout.once('data', onData); } else { // no stdout available resolve(null); } proc.once('close', () => { if (!settled) { settled = true; resolve(null); } }); }); // Try 7z first try { let proc; try { proc = (0, child_process_1.spawn)('7z', ['x', '-so', filePath, entryPath]); } catch (err) { throw err; } const stream = await waitForStreamOrError(proc); if (stream) return stream; } catch (err) { logger.warn?.({ err, filePath }, 'archiveReader: 7z spawn failed'); } // Fallback for zip if (ext === 'zip') { try { const proc2 = (0, child_process_1.spawn)('unzip', ['-p', filePath, entryPath]); const stream2 = await waitForStreamOrError(proc2); if (stream2) return stream2; } catch (err2) { logger.warn?.({ err2, filePath }, 'archiveReader: unzip spawn failed'); } } return null; } exports.default = { listArchiveEntries, streamArchiveEntry };