@dbcube/core
Version:
426 lines (423 loc) • 14.4 kB
JavaScript
// 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