@dbcube/schema-builder
Version:
The DBCube Query Builder is a lightweight, flexible, and fluent library for building queries across multiple database engines, including MySQL, PostgreSQL, SQLite, and MongoDB, using JavaScript/Node.js. Its agnostic design allows you to generate data man
338 lines (335 loc) • 11.4 kB
JavaScript
// src/lib/Schema.ts
import fs2 from "fs";
import { Engine, TableProcessor } from "@dbcube/core";
import path2 from "path";
// src/lib/FileUtils.ts
import * as fs from "fs";
import * as path from "path";
var FileUtils = class {
/**
* Verifica si un archivo existe (asincrónico).
* @param filePath - Ruta del archivo.
* @returns True si el archivo existe, false si no.
*/
static async fileExists(filePath) {
return new Promise((resolve2) => {
fs.access(path.resolve(filePath), fs.constants.F_OK, (err) => {
resolve2(!err);
});
});
}
/**
* Verifica si un archivo existe (sincrónico).
* @param filePath - Ruta del archivo.
* @returns True si el archivo existe, false si no.
*/
static fileExistsSync(filePath) {
try {
fs.accessSync(path.resolve(filePath), fs.constants.F_OK);
return true;
} catch {
return false;
}
}
static extractDatabaseName(input) {
const match = input.match(/@database\(["']?([\w-]+)["']?\)/);
return match ? match[1] : null;
}
/**
* Lee recursivamente archivos que terminan en un sufijo dado y los ordena numéricamente.
* @param dir - Directorio base (relativo o absoluto).
* @param suffix - Sufijo de archivo (como 'table.cube').
* @returns Rutas absolutas de los archivos encontrados y ordenados.
*/
static getCubeFilesRecursively(dir, suffix) {
const baseDir = path.resolve(dir);
const cubeFiles = [];
function recurse(currentDir) {
const entries = fs.readdirSync(currentDir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(currentDir, entry.name);
if (entry.isDirectory()) {
recurse(fullPath);
} else if (entry.isFile() && entry.name.endsWith(suffix)) {
cubeFiles.push(fullPath);
}
}
}
recurse(baseDir);
cubeFiles.sort((a, b) => {
const aNum = parseInt(path.basename(a));
const bNum = parseInt(path.basename(b));
return (isNaN(aNum) ? 0 : aNum) - (isNaN(bNum) ? 0 : bNum);
});
return cubeFiles;
}
};
var FileUtils_default = FileUtils;
// src/lib/Schema.ts
var Schema = class {
name;
engine;
constructor(name) {
this.name = name;
const engine = new Engine(name);
this.engine = engine;
}
async createDatabase() {
const rootPath = path2.resolve(process.cwd());
const response = await this.engine.run("schema_engine", [
"--action",
"create_database",
"--path",
rootPath
]);
if (response.status != 200) {
returnFormattedError(response.status, response.message);
}
return response.data;
}
async refreshTables() {
const cubesDir = path2.join(process.cwd(), "dbcube", "cubes");
if (!fs2.existsSync(cubesDir)) {
throw new Error("\u274C The cubes folder does not exist");
}
const cubeFiles = FileUtils_default.getCubeFilesRecursively("dbcube", "table.cube");
if (cubeFiles.length === 0) {
throw new Error("\u274C There are no cubes to execute");
} else {
for (const file of cubeFiles) {
const filePath = path2.isAbsolute(file) ? file : path2.join(cubesDir, file);
const stats = fs2.statSync(filePath);
if (stats.isFile()) {
const dml = await this.engine.run("schema_engine", [
"--action",
"parse_table",
"--mode",
"refresh",
"--schema-path",
filePath
]);
if (dml.status != 200) {
returnFormattedError(dml.status, dml.message);
}
const parseJson = JSON.stringify(dml.data.actions).replace(/[\r\n\t]/g, "").replace(/\\[rnt]/g, "").replace(/\s{2,}/g, " ");
const queries = await this.engine.run("schema_engine", [
"--action",
"generate",
"--mode",
"refresh",
"--dml",
parseJson
]);
if (queries.status != 200) {
returnFormattedError(queries.status, queries.message);
}
delete queries.data.database_type;
const parseJsonQueries = JSON.stringify(queries.data);
const response = await this.engine.run("schema_engine", [
"--action",
"execute",
"--mode",
"refresh",
"--dml",
parseJsonQueries
]);
if (response.status != 200) {
returnFormattedError(response.status, response.message);
}
const createQuery = queries.data.regular_queries.filter((q) => q.includes("CREATE"))[0];
await TableProcessor.saveQuery(dml.data.table, dml.data.database, createQuery);
return response.data;
}
}
}
return null;
}
async freshTables() {
const cubesDir = path2.join(process.cwd(), "dbcube", "cubes");
if (!fs2.existsSync(cubesDir)) {
throw new Error("\u274C The cubes folder does not exist");
}
const cubeFiles = FileUtils_default.getCubeFilesRecursively("dbcube", "table.cube");
if (cubeFiles.length === 0) {
throw new Error("\u274C There are no cubes to execute");
} else {
for (const file of cubeFiles) {
const filePath = path2.isAbsolute(file) ? file : path2.join(cubesDir, file);
const stats = fs2.statSync(filePath);
if (stats.isFile()) {
const dml = await this.engine.run("schema_engine", [
"--action",
"parse_table",
"--schema-path",
filePath,
"--mode",
"fresh"
]);
if (dml.status != 200) {
returnFormattedError(dml.status, dml.message);
}
const parseJson = JSON.stringify(dml.data.actions).replace(/[\r\n\t]/g, "").replace(/\\[rnt]/g, "").replace(/\s{2,}/g, " ");
const queries = await this.engine.run("schema_engine", [
"--action",
"generate",
"--dml",
parseJson
]);
if (queries.status != 200) {
returnFormattedError(queries.status, queries.message);
}
delete queries.data._type;
const createQuery = queries.data.regular_queries.filter((q) => q.includes("CREATE"))[0];
if (queries.data.regular_queries.length > 0) {
const nowQueries = await TableProcessor.generateAlterQueries(queries.data.regular_queries[0], dml.data.motor, dml.data.table, dml.data.database);
queries.data.regular_queries = nowQueries;
}
const parseJsonQueries = JSON.stringify(queries.data);
const response = await this.engine.run("schema_engine", [
"--action",
"execute",
"--mode",
"fresh",
"--dml",
parseJsonQueries
]);
if (response.status != 200) {
returnFormattedError(response.status, response.message);
}
await TableProcessor.saveQuery(dml.data.table, dml.data.database, createQuery);
return response.data;
}
}
}
return null;
}
async executeSeeders() {
const cubesDir = path2.join(process.cwd(), "dbcube", "cubes");
if (!fs2.existsSync(cubesDir)) {
throw new Error("\u274C The cubes folder does not exist");
}
const cubeFiles = FileUtils_default.getCubeFilesRecursively("dbcube", "seeder.cube");
if (cubeFiles.length === 0) {
throw new Error("\u274C There are no cubes to execute");
} else {
for (const file of cubeFiles) {
const filePath = path2.isAbsolute(file) ? file : path2.join(cubesDir, file);
const stats = fs2.statSync(filePath);
if (stats.isFile()) {
const response = await this.engine.run("schema_engine", [
"--action",
"seeder",
"--schema-path",
filePath
]);
if (response.status != 200) {
returnFormattedError(response.status, response.message);
}
return response.data;
}
}
}
return null;
}
async executeTriggers() {
const cubesDir = path2.join(process.cwd(), "dbcube", "cubes");
const triggersDirExit = path2.join(process.cwd(), "dbcube", "triggers");
if (!fs2.existsSync(cubesDir)) {
throw new Error("\u274C The cubes folder does not exist");
}
const cubeFiles = FileUtils_default.getCubeFilesRecursively("dbcube", "trigger.cube");
if (cubeFiles.length === 0) {
throw new Error("\u274C There are no cubes to execute");
} else {
for (const file of cubeFiles) {
const filePath = path2.isAbsolute(file) ? file : path2.join(cubesDir, file);
const stats = fs2.statSync(filePath);
if (stats.isFile()) {
const response = await this.engine.run("schema_engine", [
"--action",
"trigger",
"--path-exit",
triggersDirExit,
"--schema-path",
filePath
]);
if (response.status != 200) {
returnFormattedError(response.status, response.message);
}
return response.data;
}
}
}
return null;
}
};
function returnFormattedError(status, message) {
const RESET = "\x1B[0m";
const RED = "\x1B[31m";
const YELLOW = "\x1B[33m";
const BOLD = "\x1B[1m";
const CYAN = "\x1B[36m";
const GRAY = "\x1B[90m";
const UNDERLINE = "\x1B[4m";
const MAGENTA = "\x1B[35m";
let output = "";
let help = "";
const color = status === 600 ? YELLOW : RED;
if (message.includes("[help]")) {
const parts = message.split("[help]");
output += `
${RED}${BOLD}${parts[0]}${RESET}`;
help += `
${MAGENTA}${BOLD}[help]${RESET} ${GRAY}${parts[1]}${RESET}
`;
} else {
output += `
${color}${BOLD}${message}${RESET}
`;
}
const err = new Error();
const stackLines = err.stack?.split("\n") || [];
const relevantStackLine = stackLines.find(
(line) => line.includes(".js:") && !line.includes("node_modules")
);
if (relevantStackLine) {
const match = relevantStackLine.match(/\((.*):(\d+):(\d+)\)/) || relevantStackLine.match(/at (.*):(\d+):(\d+)/);
if (match) {
const [, filePath, lineStr, columnStr] = match;
const lineNum = parseInt(lineStr, 10);
const errorLocation = `${filePath}:${lineStr}:${columnStr}`;
try {
const codeLines = fs2.readFileSync(filePath, "utf-8").split("\n");
const start = Math.max(0, lineNum - 3);
const end = Math.min(codeLines.length, lineNum + 2);
output += `
${CYAN}${BOLD}[code] ${RESET}${YELLOW} ${UNDERLINE}${errorLocation}${RESET}
`;
for (let i = start; i < end; i++) {
const line = codeLines[i];
const lineLabel = `${i + 1}`.padStart(4, " ");
const pointer = i + 1 === lineNum ? `${RED}<-${RESET}` : " ";
output += `${GRAY}${lineLabel}${RESET} ${pointer} ${line}
`;
}
} catch (err2) {
output += `${YELLOW}\u26A0\uFE0F No se pudo leer el archivo de origen: ${filePath}${RESET}
`;
output += `
${CYAN}${BOLD}Stack Trace:${RESET}
${stackLines.slice(2).join("\n")}
`;
}
}
}
output += help;
console.error(output);
process.exit(1);
}
// src/index.ts
var index_default = Schema;
export {
Schema,
index_default as default
};
//# sourceMappingURL=index.js.map