UNPKG

@dbcube/core

Version:
1,389 lines (1,381 loc) 46.3 kB
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