@dbcube/core
Version:
1,389 lines (1,381 loc) • 46.3 kB
JavaScript
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
}) : x)(function(x) {
if (typeof require !== "undefined") return require.apply(this, arguments);
throw Error('Dynamic require of "' + x + '" is not supported');
});
// src/lib/Engine.ts
import path from "path";
// 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/Binary.ts
var Binary = class {
static get() {
const arch2 = new Arquitecture();
const platform2 = arch2.getPlatform();
const architecture = arch2.getArchitecture();
switch (platform2) {
case "windows":
if (architecture == "x86_64") {
return {
query_engine: "query-engine-windows-x64.exe",
schema_engine: "schema-engine-windows-x64.exe"
};
}
break;
}
return {
query_engine: "",
schema_engine: ""
};
}
};
// src/lib/Config.ts
var Config = class {
data = {};
databases = {};
/**
* Establece la configuración
* @param configData - Datos de configuración
*/
set(configData) {
this.data = configData;
if (configData.databases) {
this.databases = configData.databases;
}
}
/**
* Obtiene un valor de configuración
* @param key - Clave de configuración
* @returns Valor de configuración
*/
get(key) {
return this.data[key];
}
/**
* Obtiene la configuración de una base de datos específica
* @param dbName - Nombre de la base de datos
* @returns Configuración de la base de datos o null
*/
getDatabase(dbName) {
return this.databases[dbName] || null;
}
/**
* Obtiene todas las bases de datos configuradas
* @returns Todas las configuraciones de bases de datos
*/
getAllDatabases() {
return this.databases;
}
};
// src/lib/Engine.ts
import { spawn } from "child_process";
var Engine = class {
name;
config;
arguments;
binary;
timeout;
constructor(name, timeout = 3e4) {
this.name = name;
this.config = this.setConfig(name);
const binary = Binary.get();
this.binary = {
query_engine: path.resolve(__dirname, "../bin", binary.query_engine),
schema_engine: path.resolve(__dirname, "../bin", binary.schema_engine)
};
this.arguments = this.setArguments();
this.timeout = timeout;
}
setArguments() {
let args = [];
if (this.config.type == "sqlite") {
args = [
"--id",
"dbcube-" + this.name,
"--database-ref",
this.name,
"--database",
this.config.config.DATABASE + ".db",
"--motor",
this.config.type
];
} else {
args = [
"--id",
"dbcube-" + this.name,
"--database-ref",
this.name,
"--database",
this.config.config.DATABASE,
"--host",
this.config.config.HOST,
"--port",
this.config.config.PORT,
"--user",
this.config.config.USER,
"--password",
this.config.config.PASSWORD,
"--motor",
this.config.type
];
}
return args;
}
setConfig(name) {
const configInstance = new Config();
const configFilePath = path.resolve(process.cwd(), "dbcube.config.js");
const configFn = __require(configFilePath);
if (typeof configFn === "function") {
configFn(configInstance);
} else {
console.error("\u274C El archivo dbcube.config.js no exporta una funci\xF3n.");
}
return configInstance.getDatabase(name);
}
getConfig() {
return this.config;
}
async run(binary, args) {
return new Promise((resolve2, reject) => {
const child = spawn(this.binary[binary], [...this.arguments, ...args]);
let stdoutBuffer = "";
let stderrBuffer = "";
let isResolved = false;
const timeoutId = setTimeout(() => {
if (!isResolved) {
isResolved = true;
child.kill();
reject(new Error("Process timeout"));
}
}, this.timeout);
const resolveOnce = (response) => {
if (!isResolved) {
isResolved = true;
clearTimeout(timeoutId);
resolve2(response);
}
};
child.stdout.on("data", (data) => {
stdoutBuffer += data.toString();
const match = stdoutBuffer.match(/PROCESS_RESPONSE:(\{.*\})/);
if (match) {
try {
const response = JSON.parse(match[1]);
resolveOnce({
status: response.status,
message: response.message,
data: response.data
});
} catch (error) {
resolveOnce({
status: 500,
message: "Failed to parse response JSON",
data: null
});
}
}
});
child.stderr.on("data", (data) => {
stderrBuffer += data.toString();
const match = stderrBuffer.match(/PROCESS_RESPONSE:(\{.*\})/);
if (match) {
try {
const response = JSON.parse(match[1]);
resolveOnce({
status: response.status,
message: response.message,
data: response.data
});
} catch (error) {
resolveOnce({
status: 500,
message: "Failed to parse response JSON",
data: null
});
}
}
});
child.on("close", (code) => {
clearTimeout(timeoutId);
if (!isResolved) {
resolveOnce({
status: code === 0 ? 200 : 500,
message: code === 0 ? "Process completed" : `Process exited with code ${code}`,
data: null
});
}
});
child.on("error", (error) => {
clearTimeout(timeoutId);
if (!isResolved) {
resolveOnce({
status: 500,
message: `Process error: ${error.message}`,
data: null
});
}
});
child.unref();
});
}
};
// src/lib/DbConfig.ts
import * as sqlite3 from "sqlite3";
import * as path2 from "path";
import fs from "fs";
var rootPath = path2.resolve(process.cwd(), "dbcube");
var SQLite = class {
db = null;
database;
constructor(config) {
this.database = config.DATABASE;
}
ifExist() {
if (this.database) {
const dbPath = this.database || ":memory:";
const configPath = path2.join(rootPath, dbPath + ".db");
if (fs.existsSync(configPath)) {
return true;
}
}
return false;
}
async connect() {
if (!this.db) {
try {
const dbPath = this.database || ":memory:";
const configPath = path2.join(rootPath, dbPath + ".db");
this.db = new sqlite3.Database(configPath, (err) => {
if (err) throw err;
});
} catch (error) {
throw error;
}
}
return this.db;
}
async disconnect() {
if (this.db) {
return new Promise((resolve2, reject) => {
this.db.close((err) => {
if (err) {
return reject(err);
}
this.db = null;
resolve2();
});
});
}
}
/**
* Executes a SQL query on the currently set database.
*
* @param {string} sqlQuery - The SQL query to execute.
* @returns {Promise<QueryResult>} - Returns a JSON object with the status, message, and data (if any).
*
* @example
* const result = await db.query('SELECT * FROM users;');
* console.log(result);
* // { status: 'success', message: 'Query executed successfully', data: [{ id: 1, name: 'John' }, { id: 2, name: 'Jane' }] }
*
* @example
* const result = await db.query('INVALID SQL QUERY;');
* console.log(result);
* // { status: 'error', message: 'SQL syntax error', data: null }
*/
async query(sqlQuery) {
if (typeof sqlQuery !== "string") {
throw new Error("The SQL query must be a string.");
}
const sqlCommands = sqlQuery.split(";").filter((cmd) => cmd.trim().length > 0);
return new Promise((resolve2) => {
try {
if (!this.db) {
throw new Error("Database connection is not available.");
}
const results = [];
let commandsProcessed = 0;
for (const command of sqlCommands) {
const query = `${command};`;
const isSelect = query.trim().toLowerCase().startsWith("select");
if (isSelect) {
this.db.all(query, [], (err, rows) => {
if (err) {
throw err;
}
results.push(rows);
commandsProcessed++;
if (commandsProcessed === sqlCommands.length) {
resolve2({
status: "success",
message: "Query executed successfully",
data: results.length === 1 ? results[0] : results
});
}
});
} else {
this.db.run(query, [], function(err) {
if (err) {
throw err;
}
results.push({ changes: this.changes, lastID: this.lastID });
commandsProcessed++;
if (commandsProcessed === sqlCommands.length) {
resolve2({
status: "success",
message: "Query executed successfully",
data: results.length === 1 ? results[0] : results
});
}
});
}
}
} catch (error) {
resolve2({
status: "error",
message: error.message || "An error occurred while executing the query.",
data: null
});
}
});
}
/**
* Executes a SQL query with parameters on the currently set database.
*
* @param {string} sqlQuery - The SQL query to execute with placeholders (?).
* @param {any[]} params - Array of parameters to bind to the query placeholders.
* @returns {Promise<QueryResult>} - Returns a JSON object with the status, message, and data (if any).
*
* @example
* const result = await db.queryWithParameters('INSERT INTO users (name, email) VALUES (?, ?)', ['John', 'john@example.com']);
* console.log(result);
* // { status: 'success', message: 'Query executed successfully', data: { changes: 1, lastID: 3 } }
*/
async queryWithParameters(sqlQuery, params = []) {
if (typeof sqlQuery !== "string") {
throw new Error("The SQL query must be a string.");
}
if (!Array.isArray(params)) {
throw new Error("Parameters must be an array.");
}
return new Promise((resolve2) => {
try {
if (!this.db) {
throw new Error("Database connection is not available.");
}
const isSelect = sqlQuery.trim().toLowerCase().startsWith("select");
if (isSelect) {
this.db.all(sqlQuery, params, (err, rows) => {
if (err) {
throw err;
}
resolve2({
status: "success",
message: "Query executed successfully",
data: rows
});
});
} else {
this.db.run(sqlQuery, params, function(err) {
if (err) {
throw err;
}
resolve2({
status: "success",
message: "Query executed successfully",
data: { changes: this.changes, lastID: this.lastID }
});
});
}
} catch (error) {
console.log(error);
resolve2({
status: "error",
message: error.message || "An error occurred while executing the query.",
data: null
});
}
});
}
convertToParameterizedQuery(sql) {
const normalizedSql = sql.replace(/\s+/g, " ").trim();
const baseQueryMatch = normalizedSql.match(/^(.+?)\s+VALUES\s*\(/i);
if (!baseQueryMatch) {
throw new Error("No se pudo encontrar la estructura VALUES en la consulta");
}
const baseQuery = baseQueryMatch[1];
const valuesStartIndex = normalizedSql.toUpperCase().indexOf("VALUES");
const valuesSection = normalizedSql.substring(valuesStartIndex);
const valuesMatch = valuesSection.match(/VALUES\s*\((.+)\)\s*;?\s*$/i);
if (!valuesMatch) {
throw new Error("No se pudieron extraer los valores de la consulta");
}
const valuesString = valuesMatch[1];
function parseValues(str) {
const values = [];
let currentValue = "";
let inQuotes = false;
let quoteChar = "";
let inCompute = false;
let computeDepth = 0;
for (let i = 0; i < str.length; i++) {
const char = str[i];
const prevChar = str[i - 1];
if (!inQuotes && str.substring(i, i + 8) === "@compute") {
inCompute = true;
}
if (inCompute && char === "(") {
computeDepth++;
} else if (inCompute && char === ")") {
computeDepth--;
if (computeDepth === 0) {
inCompute = false;
}
}
if (!inCompute && (char === '"' || char === "'") && prevChar !== "\\") {
if (!inQuotes) {
inQuotes = true;
quoteChar = char;
} else if (char === quoteChar) {
if (str[i + 1] === quoteChar) {
currentValue += char + char;
i++;
continue;
} else {
inQuotes = false;
quoteChar = "";
}
}
}
if (!inQuotes && !inCompute && char === ",") {
values.push(cleanValue(currentValue.trim()));
currentValue = "";
continue;
}
currentValue += char;
}
if (currentValue.trim()) {
values.push(cleanValue(currentValue.trim()));
}
return values;
}
function cleanValue(value) {
value = value.trim();
if (value.startsWith("@compute")) {
return value;
}
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
value = value.slice(1, -1);
value = value.replace(/''/g, "'").replace(/""/g, '"');
}
return value;
}
const parameters = parseValues(valuesString);
const placeholders = parameters.map(() => "?").join(", ");
const parametrizedQuery = `${baseQuery} VALUES (${placeholders});`;
return {
query: parametrizedQuery,
parameters
};
}
};
var DbConfig = new SQLite({ DATABASE: "config" });
var DbConfig_default = DbConfig;
// src/lib/FileLogger.ts
import * as fs2 from "fs";
import * as path3 from "path";
import { EventEmitter } from "events";
var FileLogger = class _FileLogger extends EventEmitter {
static watchers = /* @__PURE__ */ new Map();
// Store listener functions
static buffers = /* @__PURE__ */ new Map();
/**
* Escribe un log en el archivo especificado
* @param filePath - Ruta del archivo de log
* @param message - Mensaje a escribir
* @param level - Nivel del log (INFO, ERROR, WARN, DEBUG)
* @param append - Si debe agregar al final del archivo (default: true)
*/
static async write(filePath, message, level = "INFO", append = true) {
try {
const dir = path3.dirname(filePath);
if (!fs2.existsSync(dir)) {
fs2.mkdirSync(dir, { recursive: true });
}
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
const formattedMessage = `[${timestamp}] [${level}] ${message}
`;
if (_FileLogger.buffers.has(filePath)) {
_FileLogger.buffers.get(filePath).push(formattedMessage);
return true;
}
if (append) {
await fs2.promises.appendFile(filePath, formattedMessage, "utf8");
} else {
await fs2.promises.writeFile(filePath, formattedMessage, "utf8");
}
return true;
} catch (error) {
console.error("Error escribiendo log:", error);
throw error;
}
}
/**
* Inicia un buffer temporal para un archivo de log
* @param filePath - Ruta del archivo de log
*/
static startBuffer(filePath) {
if (!_FileLogger.buffers.has(filePath)) {
_FileLogger.buffers.set(filePath, []);
}
}
/**
* Confirma y escribe todos los logs del buffer al archivo
* @param filePath - Ruta del archivo de log
*/
static async commitBuffer(filePath) {
const buffer = _FileLogger.buffers.get(filePath);
if (buffer && buffer.length > 0) {
try {
const dir = path3.dirname(filePath);
if (!fs2.existsSync(dir)) {
fs2.mkdirSync(dir, { recursive: true });
}
const content = buffer.join("");
await fs2.promises.appendFile(filePath, content, "utf8");
_FileLogger.buffers.delete(filePath);
return true;
} catch (error) {
console.error("Error confirmando buffer:", error);
throw error;
}
}
_FileLogger.buffers.delete(filePath);
return false;
}
/**
* Descarta todos los logs del buffer sin escribirlos
* @param filePath - Ruta del archivo de log
*/
static discardBuffer(filePath) {
if (_FileLogger.buffers.has(filePath)) {
const discardedCount = _FileLogger.buffers.get(filePath).length;
_FileLogger.buffers.delete(filePath);
return discardedCount;
}
return 0;
}
/**
* Verifica si existe un buffer activo para un archivo
* @param filePath - Ruta del archivo de log
*/
static hasBuffer(filePath) {
return _FileLogger.buffers.has(filePath);
}
/**
* Obtiene el número de logs en el buffer
* @param filePath - Ruta del archivo de log
*/
static getBufferSize(filePath) {
const buffer = _FileLogger.buffers.get(filePath);
return buffer ? buffer.length : 0;
}
/**
* Intercepta console.log y console.error para escribir a archivo
* @param filePath - Ruta del archivo de log
* @param options - Opciones de interceptación
*/
static interceptConsole(filePath, options = {}) {
const {
interceptLog = true,
interceptError = true,
interceptWarn = true,
keepOriginal = true,
// Mantener el comportamiento original de console
useBuffer = false
// Usar buffer temporal
} = options;
if (useBuffer) {
_FileLogger.startBuffer(filePath);
}
const originalLog = console.log;
const originalError = console.error;
const originalWarn = console.warn;
if (interceptLog) {
console.log = function(...args) {
const message = args.map(
(arg) => typeof arg === "object" ? JSON.stringify(arg) : String(arg)
).join(" ");
_FileLogger.write(filePath, message, "INFO").catch((err) => {
originalError("Error escribiendo log:", err);
});
if (keepOriginal) {
originalLog.apply(console, args);
}
};
}
if (interceptError) {
console.error = function(...args) {
const message = args.map(
(arg) => typeof arg === "object" ? JSON.stringify(arg) : String(arg)
).join(" ");
_FileLogger.write(filePath, message, "ERROR").catch((err) => {
originalError("Error escribiendo error log:", err);
});
if (keepOriginal) {
originalError.apply(console, args);
}
};
}
if (interceptWarn) {
console.warn = function(...args) {
const message = args.map(
(arg) => typeof arg === "object" ? JSON.stringify(arg) : String(arg)
).join(" ");
_FileLogger.write(filePath, message, "WARN").catch((err) => {
originalError("Error escribiendo warn log:", err);
});
if (keepOriginal) {
originalWarn.apply(console, args);
}
};
}
return {
restore: () => {
if (interceptLog) console.log = originalLog;
if (interceptError) console.error = originalError;
if (interceptWarn) console.warn = originalWarn;
},
commit: async () => {
if (useBuffer) {
return await _FileLogger.commitBuffer(filePath);
}
return false;
},
discard: () => {
if (useBuffer) {
return _FileLogger.discardBuffer(filePath);
}
return 0;
},
hasBuffer: () => _FileLogger.hasBuffer(filePath),
getBufferSize: () => _FileLogger.getBufferSize(filePath)
};
}
/**
* Lee el contenido completo del archivo de log
* @param filePath - Ruta del archivo de log
* @param options - Opciones de lectura
* @returns Contenido del archivo
*/
static async read(filePath, options = {}) {
const {
lines = null,
// Número de líneas a leer (null = todas)
fromEnd = false,
// Si debe leer desde el final
asArray = false
// Si debe retornar como array de líneas
} = options;
try {
if (!fs2.existsSync(filePath)) {
return asArray ? [] : "";
}
let content = await fs2.promises.readFile(filePath, "utf8");
if (asArray) {
let linesArray = content.split("\n").filter((line) => line.trim() !== "");
if (lines !== null) {
if (fromEnd) {
linesArray = linesArray.slice(-lines);
} else {
linesArray = linesArray.slice(0, lines);
}
}
return linesArray;
}
if (lines !== null) {
let linesArray = content.split("\n").filter((line) => line.trim() !== "");
if (fromEnd) {
linesArray = linesArray.slice(-lines);
} else {
linesArray = linesArray.slice(0, lines);
}
content = linesArray.join("\n");
}
return content;
} catch (error) {
console.error("Error leyendo log:", error);
throw error;
}
}
/**
* Observa un archivo de log en tiempo real
* @param filePath - Ruta del archivo de log
* @param callback - Función callback para nuevas líneas
* @param options - Opciones del watcher
* @returns Objeto con métodos para controlar el watcher
*/
static watch(filePath, callback, options = {}) {
const {
persistent = true,
interval = 100,
fromEnd = true
// Empezar desde el final del archivo
} = options;
let lastSize = 0;
let lastPosition = 0;
if (fs2.existsSync(filePath)) {
const stats = fs2.statSync(filePath);
lastSize = stats.size;
lastPosition = fromEnd ? stats.size : 0;
}
const listener = async (curr, prev) => {
try {
if (curr.size > lastSize) {
const stream = fs2.createReadStream(filePath, {
start: lastPosition,
end: curr.size - 1,
encoding: "utf8"
});
let buffer = "";
stream.on("data", (chunk) => {
buffer += chunk;
const lines = buffer.split("\n");
for (let i = 0; i < lines.length - 1; i++) {
if (lines[i].trim()) {
callback(lines[i].trim(), filePath);
}
}
buffer = lines[lines.length - 1];
});
stream.on("end", () => {
if (buffer.trim()) {
callback(buffer.trim(), filePath);
}
});
lastSize = curr.size;
lastPosition = curr.size;
}
} catch (error) {
console.error("Error en watcher:", error);
}
};
fs2.watchFile(filePath, { persistent, interval }, listener);
const watcherId = `${filePath}_${Date.now()}`;
_FileLogger.watchers.set(watcherId, listener);
return {
id: watcherId,
stop: () => {
const storedListener = _FileLogger.watchers.get(watcherId);
if (storedListener) {
fs2.unwatchFile(filePath, storedListener);
_FileLogger.watchers.delete(watcherId);
}
},
isWatching: () => _FileLogger.watchers.has(watcherId)
};
}
/**
* Detiene todos los watchers activos
*/
static stopAllWatchers() {
for (const [watcherId] of _FileLogger.watchers) {
const filePath = watcherId.split("_")[0];
fs2.unwatchFile(filePath);
}
_FileLogger.watchers.clear();
}
/**
* Métodos de conveniencia para diferentes niveles de log
*/
static async info(filePath, message) {
return this.write(filePath, message, "INFO");
}
static async error(filePath, message) {
return this.write(filePath, message, "ERROR");
}
static async warn(filePath, message) {
return this.write(filePath, message, "WARN");
}
static async debug(filePath, message) {
return this.write(filePath, message, "DEBUG");
}
/**
* Limpia logs antiguos
* @param filePath - Ruta del archivo de log
* @param maxLines - Máximo número de líneas a mantener
*/
static async cleanup(filePath, maxLines = 1e3) {
try {
const lines = await this.read(filePath, { asArray: true });
if (lines.length > maxLines) {
const keepLines = lines.slice(-maxLines);
const content = keepLines.join("\n") + "\n";
await fs2.promises.writeFile(filePath, content, "utf8");
return lines.length - maxLines;
}
return 0;
} catch (error) {
console.error("Error limpiando logs:", error);
throw error;
}
}
/**
* Elimina un archivo de log
* @param filePath - Ruta del archivo de log a eliminar
*/
static async deleteLogFile(filePath) {
try {
if (fs2.existsSync(filePath)) {
await fs2.promises.unlink(filePath);
return true;
}
return false;
} catch (error) {
console.error("Error eliminando archivo de log:", error);
throw error;
}
}
/**
* Elimina múltiples archivos de log
* @param filePaths - Array de rutas de archivos de log a eliminar
*/
static async deleteLogFiles(filePaths) {
const results = [];
for (const filePath of filePaths) {
try {
const deleted = await this.deleteLogFile(filePath);
results.push({ filePath, deleted, error: null });
} catch (error) {
results.push({ filePath, deleted: false, error: error.message });
}
}
return results;
}
};
// src/lib/Processors.ts
var ComputedFieldProcessor = class {
static async getComputedFields(name) {
let computedFields = [];
if (DbConfig_default.ifExist()) {
await DbConfig_default.connect();
try {
const tableExistsQuery = `SELECT name FROM sqlite_master WHERE type='table' AND name='dbcube_computes_config'`;
const tableExistsResult = await DbConfig_default.query(tableExistsQuery);
if (tableExistsResult.status === "success" && tableExistsResult.data && tableExistsResult.data.length > 0) {
const queryComputes = await DbConfig_default.query(`SELECT * FROM dbcube_computes_config WHERE database_ref='${name}'`);
computedFields = queryComputes.data;
} else {
computedFields = [];
}
} catch (error) {
console.error("Error fetching computed fields:", error);
computedFields = [];
}
await DbConfig_default.disconnect();
}
return computedFields;
}
/**
* Processes computed field instruction and returns the computed value
* @param instruction - The @compute instruction
* @param rowData - The row data containing column values
* @returns The computed value or null if there's an error
*/
static processInstruction(instruction, rowData) {
try {
const functionMatch = instruction.match(/@compute\s*\(\s*\(\s*\)\s*=>\s*{([\s\S]*?)}\s*\)/);
if (!functionMatch) {
throw new Error("Invalid @compute instruction format");
}
let functionBody = functionMatch[1].trim();
if (functionBody.endsWith(";")) {
functionBody = functionBody.slice(0, -1).trim();
}
functionBody = functionBody.replace(/@column\(([^)]+)\)/g, (_match, columnName) => {
const cleanColumnName = columnName.trim().replace(/['"]/g, "");
const value = rowData[cleanColumnName];
if (value === null || value === void 0) {
return "null";
} else if (typeof value === "string") {
return `"${value.replace(/"/g, '\\"')}"`;
} else if (typeof value === "number" || typeof value === "boolean") {
return value.toString();
} else if (value instanceof Date) {
return `new Date("${value.toISOString()}")`;
} else {
return JSON.stringify(value);
}
});
const computeFunction = new Function(functionBody);
return computeFunction();
} catch (error) {
console.error("Error processing computed field:", error);
return null;
}
}
/**
* Extracts column dependencies from a computed field instruction
* @param instruction - The @compute instruction
* @returns Array of column names that this computed field depends on
*/
static extractDependencies(instruction) {
const dependencies = [];
const columnMatches = instruction.match(/@column\(([^)]+)\)/g);
if (columnMatches) {
for (const match of columnMatches) {
const innerMatch = match.match(/@column\(([^)]+)\)/);
if (innerMatch) {
const columnName = innerMatch[1].trim().replace(/['"]/g, "");
if (!dependencies.includes(columnName)) {
dependencies.push(columnName);
}
}
}
}
return dependencies;
}
/**
* Adds computed fields to an array of data objects based on configuration array
* @param data - Array of data objects
* @param computedConfigs - Array of computed field configurations
* @returns Array with the new computed fields added
*/
static computedFields(data, computedConfigs) {
return data.map((item, index) => {
let processedItem = { ...item };
computedConfigs.forEach((config) => {
try {
const { column: fieldName, type: type2, instruction } = config;
let processedExpression = instruction.replace(
/@column\(([^)]+)\)/g,
(match, columnName) => {
const cleanColumnName = columnName.replace(/['"]/g, "");
const value = processedItem[cleanColumnName];
if (typeof value === "string") {
return `"${value}"`;
} else if (value === null || value === void 0) {
return "null";
} else {
return String(value);
}
}
);
const computeMatch = processedExpression.match(/@compute\s*\(\s*\(\s*\)\s*=>\s*\{(.*)\}\s*\)/s);
if (!computeMatch) {
throw new Error(`Formato de @compute inv\xE1lido para campo ${fieldName}`);
}
const functionBody = computeMatch[1];
const computeFunction = new Function(functionBody);
let result = computeFunction();
result = convertToType(result, type2);
processedItem[fieldName] = result;
} catch (error) {
console.error(`Error procesando campo ${config.column} en item ${index}:`, error);
processedItem[config.column] = null;
}
});
return processedItem;
});
}
};
var TableProcessor = class {
static async generateAlterQueries(nowQuery, dbType, tableName, database_ref) {
function parseCreateTableQuery(query, dbType2) {
const cleanQuery = query.trim().replace(/\s+/g, " ");
const tableNameMatch = cleanQuery.match(/CREATE TABLE (?:IF NOT EXISTS )?(\w+)/i);
if (!tableNameMatch) {
throw new Error("No se pudo extraer el nombre de la tabla");
}
const tableName2 = tableNameMatch[1];
const columnsMatch = cleanQuery.match(/\((.+)\)/);
if (!columnsMatch) {
throw new Error("No se pudieron extraer las definiciones de columnas");
}
const columnsString = columnsMatch[1];
const columnDefinitions = columnsString.split(",").map((def) => def.trim());
const columns = [];
for (const def of columnDefinitions) {
if (def.toUpperCase().startsWith("PRIMARY KEY") || def.toUpperCase().startsWith("FOREIGN KEY") || def.toUpperCase().startsWith("CONSTRAINT")) {
continue;
}
const parts = def.split(/\s+/);
if (parts.length < 2) continue;
const columnName = parts[0];
const columnType = parts[1];
const column = {
name: columnName,
type: columnType,
nullable: !def.toUpperCase().includes("NOT NULL"),
primaryKey: def.toUpperCase().includes("PRIMARY KEY"),
autoIncrement: def.toUpperCase().includes("AUTOINCREMENT") || def.toUpperCase().includes("AUTO_INCREMENT")
};
columns.push(column);
}
return { tableName: tableName2, columns };
}
function generateMySQLQueries(nowColumns, oldColumns, tableName2) {
const queries = [];
for (const [columnName] of oldColumns) {
if (!nowColumns.has(columnName)) {
queries.push(`ALTER TABLE ${tableName2} DROP COLUMN ${columnName};`);
}
}
for (const [columnName, column] of nowColumns) {
const oldColumn = oldColumns.get(columnName);
if (!oldColumn) {
const columnDef = buildColumnDefinition(column, "mysql");
queries.push(`ALTER TABLE ${tableName2} ADD COLUMN ${columnDef};`);
} else if (!columnsEqual(column, oldColumn)) {
const columnDef = buildColumnDefinition(column, "mysql");
queries.push(`ALTER TABLE ${tableName2} MODIFY COLUMN ${columnDef};`);
}
}
return queries;
}
function generatePostgreSQLQueries(nowColumns, oldColumns, tableName2) {
const queries = [];
for (const [columnName] of oldColumns) {
if (!nowColumns.has(columnName)) {
queries.push(`ALTER TABLE ${tableName2} DROP COLUMN ${columnName};`);
}
}
for (const [columnName, column] of nowColumns) {
const oldColumn = oldColumns.get(columnName);
if (!oldColumn) {
const columnDef = buildColumnDefinition(column, "postgres");
queries.push(`ALTER TABLE ${tableName2} ADD COLUMN ${columnDef};`);
} else if (!columnsEqual(column, oldColumn)) {
if (column.type !== oldColumn.type) {
const pgType = column.type.replace("INTEGER", "INT").replace("TEXT", "VARCHAR");
queries.push(`ALTER TABLE ${tableName2} ALTER COLUMN ${columnName} TYPE ${pgType};`);
}
if (column.nullable !== oldColumn.nullable) {
const constraint = column.nullable ? "DROP NOT NULL" : "SET NOT NULL";
queries.push(`ALTER TABLE ${tableName2} ALTER COLUMN ${columnName} ${constraint};`);
}
}
}
return queries;
}
function generateSQLiteQueries(nowSchema, oldSchema, tableName2) {
const queries = [];
if (schemasEqual(nowSchema, oldSchema)) {
return queries;
}
const tempTableName = `${tableName2}_temp_${Date.now()}`;
const createTempQuery = buildCreateTableQuery(nowSchema, "sqlite", tempTableName);
queries.push(createTempQuery);
const commonColumns = nowSchema.columns.filter((col) => oldSchema.columns.some((oldCol) => oldCol.name.toLowerCase() === col.name.toLowerCase())).map((col) => col.name);
if (commonColumns.length > 0) {
const columnsList = commonColumns.join(", ");
queries.push(`INSERT INTO ${tempTableName} (${columnsList}) SELECT ${columnsList} FROM ${tableName2};`);
}
queries.push(`DROP TABLE ${tableName2};`);
queries.push(`ALTER TABLE ${tempTableName} RENAME TO ${tableName2};`);
return queries;
}
function generateMongoDBQueries(nowColumns, oldColumns, collectionName) {
const queries = [];
const fieldsToRemove = [];
for (const [fieldName] of oldColumns) {
if (!nowColumns.has(fieldName)) {
fieldsToRemove.push(fieldName);
}
}
if (fieldsToRemove.length > 0) {
const unsetFields = fieldsToRemove.reduce((acc, field) => {
acc[field] = "";
return acc;
}, {});
queries.push(`db.${collectionName}.updateMany({}, { $unset: ${JSON.stringify(unsetFields)} });`);
}
const fieldsToAdd = {};
for (const [fieldName, column] of nowColumns) {
if (!oldColumns.has(fieldName)) {
let defaultValue = null;
if (!column.nullable) {
switch (column.type.toUpperCase()) {
case "INTEGER":
defaultValue = 0;
break;
case "TEXT":
case "VARCHAR":
defaultValue = "";
break;
default:
defaultValue = null;
}
}
fieldsToAdd[fieldName] = defaultValue;
}
}
if (Object.keys(fieldsToAdd).length > 0) {
queries.push(`db.${collectionName}.updateMany({}, { $set: ${JSON.stringify(fieldsToAdd)} });`);
}
return queries;
}
function buildColumnDefinition(column, dbType2) {
let def = `${column.name} ${column.type}`;
if (column.primaryKey) {
def += " PRIMARY KEY";
}
if (column.autoIncrement) {
if (dbType2 === "mysql") {
def += " AUTO_INCREMENT";
} else if (dbType2 === "sqlite") {
def += " AUTOINCREMENT";
} else if (dbType2 === "postgres") {
def = def.replace(column.type, "SERIAL");
}
}
if (!column.nullable) {
def += " NOT NULL";
}
return def;
}
function buildCreateTableQuery(schema, dbType2, tableName2) {
const name = tableName2 || schema.tableName;
const columnDefs = schema.columns.map((col) => buildColumnDefinition(col, dbType2));
return `CREATE TABLE ${name} (${columnDefs.join(", ")});`;
}
function columnsEqual(col1, col2) {
return col1.name.toLowerCase() === col2.name.toLowerCase() && col1.type === col2.type && col1.nullable === col2.nullable && col1.primaryKey === col2.primaryKey && col1.autoIncrement === col2.autoIncrement;
}
function schemasEqual(schema1, schema2) {
if (schema1.columns.length !== schema2.columns.length) {
return false;
}
const cols1 = new Map(schema1.columns.map((col) => [col.name.toLowerCase(), col]));
const cols2 = new Map(schema2.columns.map((col) => [col.name.toLowerCase(), col]));
for (const [name, col1] of cols1) {
const col2 = cols2.get(name);
if (!col2 || !columnsEqual(col1, col2)) {
return false;
}
}
return true;
}
if (DbConfig_default.ifExist()) {
await DbConfig_default.connect();
try {
const tableExistsQuery = `SELECT name FROM sqlite_master WHERE type='table' AND name='dbcube_computes_config'`;
const tableExistsResult = await DbConfig_default.query(tableExistsQuery);
if (tableExistsResult.status === "success" && tableExistsResult.data && tableExistsResult.data.length > 0) {
const queryComputes = await DbConfig_default.query(`SELECT * FROM dbcube_schemas_config WHERE table_ref='${tableName}' AND database_ref='${database_ref}'`);
const oldQuery = queryComputes.data[0];
const nowSchema = parseCreateTableQuery(nowQuery, dbType);
const oldSchema = parseCreateTableQuery(oldQuery.struct, dbType);
if (nowSchema.tableName.toLowerCase() !== tableName.toLowerCase()) {
throw new Error(`El nombre de tabla en la query (${nowSchema.tableName}) no coincide con el par\xE1metro (${tableName})`);
}
const nowColumns = new Map(nowSchema.columns.map((col) => [col.name.toLowerCase(), col]));
const oldColumns = new Map(oldSchema.columns.map((col) => [col.name.toLowerCase(), col]));
switch (dbType) {
case "mysql":
return generateMySQLQueries(nowColumns, oldColumns, tableName);
case "postgres":
return generatePostgreSQLQueries(nowColumns, oldColumns, tableName);
case "sqlite":
return generateSQLiteQueries(nowSchema, oldSchema, tableName);
case "mongodb":
return generateMongoDBQueries(nowColumns, oldColumns, tableName);
default:
throw new Error(`Tipo de base de datos no soportado: ${dbType}`);
}
}
} catch (error) {
console.error("Error fetching computed fields:", error);
}
await DbConfig_default.disconnect();
return [nowQuery];
} else {
return [nowQuery];
}
}
static async saveQuery(table_ref, database_ref, struct) {
if (DbConfig_default.ifExist()) {
await DbConfig_default.connect();
try {
await DbConfig_default.query(`DELETE FROM dbcube_schemas_config WHERE table_ref='${table_ref}' AND database_ref='${database_ref}'`);
const tableExistsQuery = `INSERT INTO dbcube_schemas_config (table_ref, database_ref, struct) VALUES (?, ?, ?)`;
await DbConfig_default.queryWithParameters(tableExistsQuery, [table_ref, database_ref, struct]);
} catch (error) {
console.error("Error fetching computed fields:", error);
}
await DbConfig_default.disconnect();
}
}
};
var TriggerProcessor = class {
static async getTriggers(name) {
let triggers = [];
if (DbConfig_default.ifExist()) {
await DbConfig_default.connect();
try {
const tableExistsQuery = `SELECT name FROM sqlite_master WHERE type='table' AND name='dbcube_triggers_config'`;
const tableExistsResult = await DbConfig_default.query(tableExistsQuery);
if (tableExistsResult.status === "success" && tableExistsResult.data && tableExistsResult.data.length > 0) {
const queryComputes = await DbConfig_default.query(`SELECT * FROM dbcube_triggers_config WHERE database_ref='${name}'`);
triggers = queryComputes.data;
} else {
triggers = [];
}
} catch (error) {
console.error("Error fetching computed fields:", error);
triggers = [];
}
await DbConfig_default.disconnect();
}
return triggers;
}
};
function convertToType(value, type2) {
switch (type2) {
case "string":
return String(value);
case "number":
const num = Number(value);
return isNaN(num) ? 0 : num;
case "boolean":
return Boolean(value);
case "date":
return value instanceof Date ? value : new Date(value);
case "object":
return value;
default:
return value;
}
}
export {
Arquitecture,
Binary,
ComputedFieldProcessor,
Config,
DbConfig,
Engine,
FileLogger,
TableProcessor,
TriggerProcessor
};
//# sourceMappingURL=index.js.map