UNPKG

@dbnx/mysql

Version:

A powerful and enhanced ORM library for MySQL, offering query execution, model creation, and full relational management.

233 lines (232 loc) 10.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Model = void 0; const query_1 = require("../query"); const config_js_1 = require("./config.js"); const model_define_js_1 = require("./model-define.js"); class Model extends model_define_js_1.ModelDefine { static hooks = {}; constructor(attributes) { super(); Object.assign(this, attributes); } static init(model, attributes, instance, options = {}) { Object.defineProperty(this, "name", { value: model }); if (!instance) { throw new Error("🌋No instance passed Or instance not match"); } if (!model) { throw new Error("🚨 Model name is required but not provided."); } config_js_1.GlobalConfig.logger_function(`✨ Creating model: \`${model}\``); this.modelAttributes = attributes; const parts = model?.split("."); this.database = parts?.length > 1 ? parts[0] : instance.getConfig?.database; this.tableName = parts?.[parts.length - 1]; this.tableOptions = options; this.dbInstance = instance; this.dbTableIdentifier = `${this.database ? `${this.database}.` : ""}${this.tableName}`; this.generateDDL(attributes); return this; } static async create(...args) { let values = args[0]; let options = args[1] || {}; if (!values || typeof values !== "object" || !Object.keys(values).length) { throw new Error("Values must be a non-empty object."); } const result = await this.dbInstance.execute((0, query_1.insert)(this.dbTableIdentifier, values, options)); config_js_1.GlobalConfig.logger_function(`🆕 Create into: \`${this.dbTableIdentifier}\``); return result; } static async findAll(Config) { if (Config && typeof Config !== "object") { throw new Error("Config must be a non-empty object."); } config_js_1.GlobalConfig.logger_function(`📋 Find all from: \`${this.dbTableIdentifier}\``); const result = await this.dbInstance.execute((0, query_1.find)(this.dbTableIdentifier, Config)); return result; } static async findOne(Config) { if (Config && typeof Config !== "object") { throw new Error("Config must be a non-empty object."); } config_js_1.GlobalConfig.logger_function(`🔎 Find one from: \`${this.dbTableIdentifier}\``); let config = Config ?? {}; config.limitSkip = { limit: 1 }; const result = await this.dbInstance.execute((0, query_1.find)(this.dbTableIdentifier, config)); return result; } static async update(Props) { if (typeof Props !== "object") { throw new Error("Props must be a non-empty object."); } config_js_1.GlobalConfig.logger_function(`✏️ Update from: \`${this.dbTableIdentifier}\``); const result = await this.dbInstance.execute((0, query_1.update)(this.dbTableIdentifier, Props)); return result; } static async delete(Props) { if (typeof Props !== "object") { throw new Error("Props must be a non-empty object."); } config_js_1.GlobalConfig.logger_function(`🗑️ Delete from: \`${this.dbTableIdentifier}\``); const result = await this.dbInstance.execute((0, query_1.destroy)(this.dbTableIdentifier, Props)); return result; } static async getColumnMetadata() { const db = await this.dbInstance.execute(` SELECT c.COLUMN_NAME AS \`field\`, c.COLUMN_TYPE AS \`type\`, c.IS_NULLABLE AS \`null\`, c.COLUMN_KEY AS \`key\`, c.COLUMN_DEFAULT AS \`default\`, c.EXTRA AS \`extra\`, kcu.CONSTRAINT_NAME as constraintName FROM INFORMATION_SCHEMA.COLUMNS c LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu ON c.COLUMN_NAME = kcu.COLUMN_NAME AND c.TABLE_NAME = kcu.TABLE_NAME AND c.TABLE_SCHEMA = kcu.TABLE_SCHEMA WHERE c.TABLE_NAME = '${this.tableName}' AND c.TABLE_SCHEMA = '${this.database}' `); return db?.result; } static async listConstraints() { const query = ` SELECT CONSTRAINT_NAME AS constraintName, CONSTRAINT_TYPE AS constraintType FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = '${this.tableName}' AND TABLE_SCHEMA = '${this.database}' `; return (await this.dbInstance.execute(query))?.result; } static async dropTableConstraints() { const constraintList = await this.listConstraints(); if (!constraintList?.length) { return; } const constraintsQuery = new Set(); const columnMetadata = await this.getColumnMetadata(); columnMetadata ?.filter((r) => r?.key === "PRI") ?.forEach((r) => { const defaultValue = r?.default ? ` DEFAULT ${r?.default}` : r?.null === "YES" ? " DEFAULT NULL" : ""; constraintsQuery.add(`MODIFY COLUMN ${r?.field} ${r?.type}${defaultValue}`); }); if (constraintList?.length) { console.log("\x1b[36m%s\x1b[0m", "Constraints to drop:", this.dbTableIdentifier); console.table(constraintList); } for (const constraint of constraintList) { const { constraintName, constraintType } = constraint; switch (constraintType) { case "PRIMARY KEY": constraintsQuery.add(`DROP PRIMARY KEY`); break; case "FOREIGN KEY": constraintsQuery.add(`DROP FOREIGN KEY ${constraintName}`); break; case "UNIQUE": constraintsQuery.add(`DROP INDEX ${constraintName}`); break; case "CHECK": console.warn("\x1b[33m%s\x1b[0m", `CHECK constraints cannot be dropped directly. Consider alternative handling.`); break; case "DEFAULT": case "NOT NULL": console.log(`Attempting to drop ${constraintType} on ${constraintName}`); const column = columnMetadata.find((col) => col.field === constraintName); if (column) { const definition = `${column.type}${column.null === "YES" ? " NULL" : " NOT NULL"}`; constraintsQuery.add(`MODIFY COLUMN ${column.field} ${definition}`); } break; default: throw new Error(`Unknown constraint type: ${constraintType}`); } } if (constraintsQuery.size) { const query = `ALTER TABLE ${this.dbTableIdentifier}\n${[...constraintsQuery].join(",\n")}`; console.log("\x1b[36m%s\x1b[0m", `Executing Query:\n${query}`); const result = await this.dbInstance.execute(query); if (!result?.success) this.errorHandle(result); } } static async drop() { config_js_1.GlobalConfig.logger_function(`🗑️ Dropping table: \`${this.dbTableIdentifier}\``); await this.dropTableConstraints(); const query = `DROP TABLE IF EXISTS ${this.dbTableIdentifier}`; config_js_1.GlobalConfig.logger_function(`📜 Executing query: ${query}`); const data = await this.dbInstance.execute(query); config_js_1.GlobalConfig.logger_function(`✅ Table \`${this.dbTableIdentifier}\` dropped successfully.`); return this.errorHandle(data); } static async sync(force = false) { config_js_1.GlobalConfig.logger_function(`🔄 Starting model synchronization...`); config_js_1.GlobalConfig.logger_function(`🛠️ Synchronizing model: \`${this.dbTableIdentifier}\``); await this.dropTableConstraints(); if (force) { config_js_1.GlobalConfig.logger_function("🚀 Force sync enabled. Dropping the table and re-creating it..."); await this.drop(); config_js_1.GlobalConfig.logger_function("✅ Model table re-created successfully."); return this.errorHandle(await this.dbInstance.execute(this.ddlQuery)); } const columnData = (await this.getColumnMetadata())?.reduce((acc, curr) => { acc[curr?.field] = curr; return acc; }, {}); if (typeof columnData !== "object" || Object.keys(columnData)?.length === 0) { config_js_1.GlobalConfig.logger_function("⚠️ No existing table or metadata found. Re-creating the table..."); await this.drop(); config_js_1.GlobalConfig.logger_function("✅ Model table created successfully."); return this.errorHandle(await this.dbInstance.execute(this.ddlQuery)); } let query = `ALTER TABLE ${this.dbTableIdentifier}\n${this.getEngineOptions()}`; let modifyColumn = []; for (const key in this.modelAttributes) { if (!this.modelAttributes.hasOwnProperty(key)) continue; const value = this.modelAttributes[key]; if (value?.modifyColumn && value?.modifyColumn in columnData) { modifyColumn.push(value?.modifyColumn); query += `,\n CHANGE COLUMN \`${value?.modifyColumn}\` ${this.generateColumnsSQL({ [key]: value }, true)}`; } else if (key in columnData) { query += `,\n MODIFY COLUMN ${this.generateColumnsSQL({ [key]: value }, true)}`; } else { query += `,\n ADD COLUMN ${this.generateColumnsSQL({ [key]: value }, true)}`; } } for (const existingKey in columnData) { if (!(existingKey in this.modelAttributes) && !modifyColumn?.includes(existingKey)) { query += `,\n DROP COLUMN \`${existingKey}\``; } } const data = await this.dbInstance.execute(query); config_js_1.GlobalConfig.logger_function("✅ Table synchronized successfully."); return this.errorHandle(data); } static errorHandle(data) { if (!data?.success) { throw Error(data.error); } return data; } } exports.Model = Model;