- Implemented AlertDialog component with overlay, content, header, footer, title, description, action, and cancel functionalities. - Created Badge component with variant support for different styles. - Developed Checkbox component with custom styling and indicator. - Added Dialog component with trigger, close, overlay, content, header, footer, title, and description. - Introduced Label component for form elements. - Built Select component with trigger, content, group, item, label, separator, and scroll buttons. - Created Sheet component with trigger, close, overlay, content, header, footer, title, and description. - Implemented Table component with header, body, footer, row, head, cell, and caption. - Added Textarea component with custom styling. - Established API service for game management with CRUD operations and metadata search functionalities. - Updated dependencies in package lock files.
167 lines
6.0 KiB
JavaScript
167 lines
6.0 KiB
JavaScript
"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 };
|