UNPKG

@dbcube/core

Version:
426 lines (423 loc) 14.4 kB
#!/usr/bin/env node // src/lib/Arquitecture.ts import * as os from "os"; var Arquitecture = class { systemInfo; constructor() { this.systemInfo = this.detectSystemInfo(); } /** * Detecta información completa del sistema */ detectSystemInfo() { return { platform: os.platform(), arch: os.arch(), release: os.release(), type: os.type(), endianness: os.endianness(), cpus: os.cpus().length }; } /** * Obtiene la plataforma normalizada */ getPlatform() { const platform2 = this.systemInfo.platform; switch (platform2) { case "win32": return "windows"; case "darwin": return "macos"; case "linux": return "linux"; case "freebsd": return "freebsd"; case "openbsd": return "openbsd"; case "sunos": return "solaris"; default: return platform2; } } /** * Obtiene la arquitectura normalizada */ getArchitecture() { const arch2 = this.systemInfo.arch; switch (arch2) { case "x64": return "x86_64"; case "x32": case "ia32": return "i686"; case "arm64": return "aarch64"; case "arm": return "armv7"; case "ppc64": return "powerpc64"; case "ppc": return "powerpc"; case "s390x": return "s390x"; case "mips": return "mips"; case "mipsel": return "mipsel"; default: return arch2; } } /** * Genera el nombre del binario basado en la arquitectura */ getBinaryName(baseName) { const platform2 = this.getPlatform(); const arch2 = this.getArchitecture(); const extension = platform2 === "windows" ? ".exe" : ""; return `${baseName}-${platform2}-${arch2}${extension}`; } /** * Obtiene información completa del sistema */ getSystemInfo() { return { ...this.systemInfo }; } /** * Obtiene el triple de destino (target triple) para Rust */ getRustTargetTriple() { const platform2 = this.getPlatform(); const arch2 = this.getArchitecture(); const targetMap = { "linux-x86_64": "x86_64-unknown-linux-gnu", "linux-i686": "i686-unknown-linux-gnu", "linux-aarch64": "aarch64-unknown-linux-gnu", "linux-armv7": "armv7-unknown-linux-gnueabihf", "macos-x86_64": "x86_64-apple-darwin", "macos-aarch64": "aarch64-apple-darwin", "windows-x86_64": "x86_64-pc-windows-msvc", "windows-i686": "i686-pc-windows-msvc", "windows-aarch64": "aarch64-pc-windows-msvc", "freebsd-x86_64": "x86_64-unknown-freebsd" }; const key = `${platform2}-${arch2}`; return targetMap[key] || `${arch2}-unknown-${platform2}`; } /** * Muestra información detallada del sistema */ printSystemInfo() { console.log("\u{1F5A5}\uFE0F System Information:"); console.log("\u251C\u2500 Platform:", this.getPlatform()); console.log("\u251C\u2500 Arquitecture:", this.getArchitecture()); console.log("\u251C\u2500 OS Type:", this.systemInfo.type); console.log("\u251C\u2500 OS Release:", this.systemInfo.release); console.log("\u251C\u2500 Endianness:", this.systemInfo.endianness); console.log("\u251C\u2500 CPUs:", this.systemInfo.cpus); console.log("\u2514\u2500 Rust Target:", this.getRustTargetTriple()); } }; // src/lib/Donwloader.ts import * as fs from "fs"; import * as path from "path"; import * as os2 from "os"; import followRedirects from "follow-redirects"; import * as unzipper from "unzipper"; import ora from "ora"; import chalk from "chalk"; var { https } = followRedirects; var Downloader = class { static mainSpinner = null; static currentSpinner = null; static get(prefix) { const arch2 = new Arquitecture(); const platform2 = arch2.getPlatform(); const architecture = arch2.getArchitecture(); const platformMap = { windows: "windows", linux: "linux", darwin: "macos" }; const archMap = { x86_64: "x64", aarch64: "arm64" }; const plat = platformMap[platform2]; const archSuffix = archMap[architecture]; if (plat && archSuffix) { const baseName = `${prefix}-engine-${plat}-${archSuffix}`; const binaryName = platform2 === "windows" ? `${baseName}.exe` : baseName; const url = `https://github.com/Dbcube/binaries/releases/download/${prefix}-engine/${prefix}-engine-latest-${plat}-${archSuffix}.zip`; return { name: binaryName, url, query_engine: binaryName, schema_engine: `${prefix}-engine-${plat}-${archSuffix}${platform2 === "windows" ? ".exe" : ""}` }; } return { name: "", url: "", query_engine: "", schema_engine: "" }; } static async download(targetDir) { const binaries = ["schema", "query", "sqlite"]; const binDir = targetDir || this.getDefaultBinDir(); fs.mkdirSync(binDir, { recursive: true }); this.mainSpinner = ora({ text: chalk.blue("Descargando binarios necesarios..."), spinner: "dots12" }).start(); const binariesToDownload = []; let existingCount = 0; for (const prefix of binaries) { const binaryInfo = this.get(prefix); if (!binaryInfo.name || !binaryInfo.url) { throw new Error(`Plataforma o arquitectura no soportada para ${prefix}`); } const finalBinaryPath = path.join(binDir, binaryInfo.name); if (fs.existsSync(finalBinaryPath)) { existingCount++; continue; } binariesToDownload.push({ prefix, binaryInfo, tempZipPath: path.join(os2.tmpdir(), `dbcube-${prefix}-${Date.now()}.zip`), finalBinaryPath }); } if (binariesToDownload.length === 0) { this.mainSpinner.succeed(chalk.green("Todos los binarios ya est\xE1n descargados")); return; } this.updateMainProgress("paralelo", existingCount, binaries.length, "downloading"); try { await Promise.all(binariesToDownload.map(async (binary, index) => { const maxRetries = 3; let attempt = 0; while (attempt <= maxRetries) { try { await this.downloadFileWithProgress(binary.binaryInfo.url, binary.tempZipPath, binary.prefix); await this.extractBinary(binary.tempZipPath, binary.finalBinaryPath, binary.prefix); const completed = existingCount + index + 1; this.updateMainProgress(binary.prefix, completed, binaries.length, "completed"); break; } catch (error) { const errorMessage = error instanceof Error ? error.message : "Error desconocido"; if (attempt < maxRetries && (errorMessage.includes("ECONNRESET") || errorMessage.includes("timeout") || errorMessage.includes("ETIMEDOUT") || errorMessage.includes("ENOTFOUND"))) { attempt++; this.updateMainProgress(binary.prefix, existingCount, binaries.length, "retrying", attempt); await new Promise((resolve2) => setTimeout(resolve2, 1e3 + Math.random() * 1e3)); } else { throw new Error(`Error descargando ${binary.prefix}: ${errorMessage}`); } } } })); this.mainSpinner.succeed(chalk.green("Binarios descargados correctamente")); } catch (error) { const errorMessage = error instanceof Error ? error.message : "Error desconocido"; this.mainSpinner.fail(chalk.red(`Error en descarga paralela: ${errorMessage}`)); throw error; } } static updateMainProgress(binary, current, total, status, attempt) { const progressBar = this.createProgressBar(current, total); const statusEmojis = { downloading: "\u{1F4E5}", extracting: "\u{1F4E6}", completed: "\u2705", exists: "\u2705", retrying: "\u{1F504}" }; const statusMessages = { downloading: chalk.blue("descargando"), extracting: chalk.yellow("extrayendo"), completed: chalk.green("completado"), exists: chalk.gray("existe"), retrying: chalk.yellow(`reintentando (${attempt}/${3})`) }; const emoji = statusEmojis[status] || "\u{1F4E5}"; const message = statusMessages[status] || status; this.mainSpinner.text = `${progressBar} ${emoji} ${chalk.bold(binary)} - ${message}`; } static createProgressBar(current, total, width = 20) { const filled = Math.round(current / total * width); const empty = width - filled; const filledBar = chalk.green("\u2588".repeat(filled)); const emptyBar = chalk.gray("\u2591".repeat(empty)); const percentage = chalk.bold(`${current}/${total}`); return `[${filledBar}${emptyBar}] ${percentage}`; } static downloadFileWithProgress(url, outputPath, prefix) { return new Promise((resolve2, reject) => { const request = https.get(url, { timeout: 0 }, (response) => { if (response.statusCode === 302 || response.statusCode === 301) { const redirectUrl = response.headers.location; if (redirectUrl) { return this.downloadFileWithProgress(redirectUrl, outputPath, prefix).then(resolve2).catch(reject); } } if (response.statusCode !== 200) { reject(new Error(`HTTP ${response.statusCode} para ${prefix}`)); return; } const file = fs.createWriteStream(outputPath); const totalBytes = parseInt(response.headers["content-length"] || "0", 10); let downloadedBytes = 0; response.on("data", (chunk) => { downloadedBytes += chunk.length; file.write(chunk); if (totalBytes > 0) { const progress = { downloaded: downloadedBytes, total: totalBytes, percentage: downloadedBytes / totalBytes * 100 }; this.updateDownloadProgress(prefix, progress); } }); response.on("end", () => { file.end(); resolve2(); }); response.on("error", (err) => { file.close(); this.cleanupFile(outputPath); reject(err); }); file.on("error", (err) => { file.close(); this.cleanupFile(outputPath); reject(err); }); }); request.on("error", reject); request.on("timeout", () => { request.destroy(); reject(new Error(`Timeout descargando ${prefix}`)); }); }); } static updateDownloadProgress(binary, progress) { const percentage = progress.percentage.toFixed(1); const downloaded = (progress.downloaded / 1024 / 1024).toFixed(1); const total = (progress.total / 1024 / 1024).toFixed(1); const barWidth = 15; const filled = Math.round(progress.percentage / 100 * barWidth); const empty = barWidth - filled; const progressBar = chalk.green("\u2588".repeat(filled)) + chalk.gray("\u2591".repeat(empty)); const progressText = `[${progressBar}] ${percentage}% (${downloaded}/${total}MB)`; this.mainSpinner.text = `\u{1F4E5} ${chalk.bold(binary)} - ${progressText}`; } static extractBinary(zipPath, outputPath, prefix) { return new Promise((resolve2, reject) => { let extracted = false; fs.createReadStream(zipPath).pipe(unzipper.Parse()).on("entry", (entry) => { if (entry.type === "File" && !extracted) { extracted = true; const writeStream = fs.createWriteStream(outputPath); entry.pipe(writeStream); writeStream.on("finish", () => { if (process.platform !== "win32") { fs.chmodSync(outputPath, 493); } this.cleanupFile(zipPath); resolve2(); }); writeStream.on("error", (err) => { this.cleanupFile(zipPath); reject(err); }); } else { entry.autodrain(); } }).on("error", (err) => { this.cleanupFile(zipPath); reject(err); }).on("close", () => { if (!extracted) { this.cleanupFile(zipPath); reject(new Error(`No se encontr\xF3 archivo v\xE1lido en el ZIP para ${prefix}`)); } }); }); } static cleanupFile(filePath) { try { if (fs.existsSync(filePath)) { fs.unlinkSync(filePath); } } catch { } } static getDefaultBinDir() { const possibleDirs = [ path.resolve(process.cwd(), ".dbcube", "bin"), path.resolve(process.cwd(), "node_modules", ".dbcube", "bin"), path.resolve(__dirname, "..", "bin") ]; for (const dir of possibleDirs) { try { if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } const testFile = path.join(dir, ".test"); fs.writeFileSync(testFile, "test"); fs.unlinkSync(testFile); return dir; } catch { continue; } } const tempDir = path.join(os2.tmpdir(), ".dbcube", "bin"); fs.mkdirSync(tempDir, { recursive: true }); return tempDir; } }; // src/bin.ts async function main() { const args = process.argv.slice(2); const command = args[0]; switch (command) { case "install": case "download": console.log("\u{1F680} Iniciando descarga de binarios DBCube..."); try { await Downloader.download(); console.log("\u2705 \xA1Todos los binarios descargados exitosamente!"); } catch (error) { console.error("\u274C Error descargando binarios:", error); } break; case "check": console.log("\u{1F50D} Verificando binarios..."); const binaries = ["schema", "query", "sqlite"]; for (const prefix of binaries) { const binary = Downloader.get(prefix); console.log(`${prefix}-engine: ${binary.name}`); console.log(` URL: ${binary.url}`); } break; default: console.log("DBCube Binary Manager"); console.log(""); console.log("Comandos disponibles:"); console.log(" install|download - Descargar todos los binarios"); console.log(" check - Verificar informaci\xF3n de binarios"); console.log(""); console.log("Uso:"); console.log(" npx @dbcube/core install"); console.log(" npx @dbcube/core check"); } } main().catch((error) => { console.error("Error:", error); }); //# sourceMappingURL=bin.js.map