@aceitadev/adatabase 0.5.0 → 0.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,9 @@
1
1
  export declare class SchemaManager {
2
2
  private models;
3
+ private changes;
3
4
  constructor(models: Function[]);
4
5
  migrate(): Promise<void>;
6
+ private printMigrationSummary;
5
7
  private createTable;
6
8
  private updateTable;
7
9
  private getSchemaFromModel;
@@ -14,31 +14,52 @@ const Table_1 = require("./decorators/Table");
14
14
  const Column_1 = require("./decorators/Column");
15
15
  const Nullable_1 = require("./decorators/Nullable");
16
16
  const Database_1 = require("./Database");
17
- const Logger_1 = require("./Logger");
18
17
  function camelToSnake(s) {
19
18
  return s.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
20
19
  }
21
20
  class SchemaManager {
22
21
  constructor(models) {
22
+ this.changes = new Map();
23
23
  this.models = models;
24
24
  }
25
25
  migrate() {
26
26
  return __awaiter(this, void 0, void 0, function* () {
27
- Logger_1.Logger.schemaLog("Synchronizing database schema...");
27
+ this.changes.clear();
28
28
  for (const model of this.models) {
29
29
  const table = (0, Table_1.getTableName)(model);
30
30
  if (!table)
31
31
  continue;
32
- Logger_1.Logger.schemaLog("", { table: table, status: "started" });
33
- const { columns, indexes } = this.getSchemaFromModel(model);
32
+ const { columns, indexes, primaryKey } = this.getSchemaFromModel(model);
34
33
  const existing = yield this.getExistingColumns(table);
35
34
  if (Object.keys(existing).length === 0) {
36
35
  yield this.createTable(table, columns, indexes);
37
36
  }
38
37
  else {
39
- yield this.updateTable(table, columns, indexes, existing);
38
+ yield this.updateTable(table, columns, indexes, existing, primaryKey);
40
39
  }
41
40
  }
41
+ this.printMigrationSummary();
42
+ });
43
+ }
44
+ printMigrationSummary() {
45
+ if (this.changes.size === 0) {
46
+ return;
47
+ }
48
+ console.log("Schema atualizado:");
49
+ const tableEntries = Array.from(this.changes.entries());
50
+ tableEntries.forEach(([table, changes], tableIndex) => {
51
+ const isLastTable = tableIndex === tableEntries.length - 1;
52
+ const tablePrefix = isLastTable ? "└─" : "├─";
53
+ if (tableIndex > 0) {
54
+ console.log("│");
55
+ }
56
+ console.log(`${tablePrefix} ${table}`);
57
+ changes.forEach((change, changeIndex) => {
58
+ const isLastChange = changeIndex === changes.length - 1;
59
+ const changeLinePrefix = isLastTable ? " " : "│ ";
60
+ const changeConnector = isLastChange ? "└─" : "├─";
61
+ console.log(` ${changeLinePrefix}${changeConnector} ${change}`);
62
+ });
42
63
  });
43
64
  }
44
65
  createTable(table, columns, indexes) {
@@ -53,7 +74,8 @@ class SchemaManager {
53
74
  const conn = yield (0, Database_1.getConnection)();
54
75
  try {
55
76
  yield (0, Database_1.execute)(sql, [], conn);
56
- Logger_1.Logger.schemaLog("", { table: table, status: "created" });
77
+ const columnChanges = Object.keys(columns).map(col => `+ ${col} (adicionado)`);
78
+ this.changes.set(table, columnChanges);
57
79
  }
58
80
  finally {
59
81
  conn.release();
@@ -73,39 +95,59 @@ class SchemaManager {
73
95
  }
74
96
  });
75
97
  }
76
- updateTable(table, desired, indexes, existing) {
98
+ updateTable(table, desired, indexes, existing, primaryKey) {
77
99
  return __awaiter(this, void 0, void 0, function* () {
78
- for (const [col, type] of Object.entries(desired)) {
79
- if (!existing.hasOwnProperty(col)) {
80
- const sql = `ALTER TABLE \`${table}\` ADD COLUMN \`${col}\` ${type};`;
81
- const conn = yield (0, Database_1.getConnection)();
82
- try {
100
+ const tableChanges = [];
101
+ const conn = yield (0, Database_1.getConnection)();
102
+ try {
103
+ for (const [col, type] of Object.entries(desired)) {
104
+ // Se a coluna for a chave primária, pule a verificação para evitar o erro.
105
+ if (col === primaryKey) {
106
+ continue;
107
+ }
108
+ if (!existing.hasOwnProperty(col)) {
109
+ const sql = `ALTER TABLE \`${table}\` ADD COLUMN \`${col}\` ${type};`;
83
110
  yield (0, Database_1.execute)(sql, [], conn);
84
- Logger_1.Logger.schemaLog("", { table: table, column: col, status: "added" });
111
+ tableChanges.push(`+ ${col} (adicionado)`);
85
112
  }
86
- finally {
87
- conn.release();
113
+ else {
114
+ const normalize = (t) => t.toLowerCase().replace(/\s/g, '').replace('character varying', 'varchar');
115
+ const existingType = normalize(existing[col]);
116
+ // Compara apenas o tipo base, ignorando extras como NOT NULL
117
+ const desiredType = normalize(type.split(' ')[0]);
118
+ if (existingType !== desiredType) {
119
+ const sql = `ALTER TABLE \`${table}\` MODIFY COLUMN \`${col}\` ${type};`;
120
+ yield (0, Database_1.execute)(sql, [], conn);
121
+ tableChanges.push(`~ ${col} (tipo alterado: ${existing[col].toUpperCase()} → ${type.split(' ')[0].toUpperCase()})`);
122
+ }
88
123
  }
89
124
  }
125
+ if (tableChanges.length > 0) {
126
+ this.changes.set(table, tableChanges);
127
+ }
128
+ }
129
+ finally {
130
+ conn.release();
90
131
  }
91
132
  });
92
133
  }
93
134
  getSchemaFromModel(model) {
94
135
  const columns = {};
95
136
  const indexes = [];
137
+ let primaryKey = null;
96
138
  const colMeta = (0, Column_1.getColumnMeta)(model);
97
139
  const nullableMeta = (0, Nullable_1.getNullableMeta)(model);
98
140
  const adapter = (0, Database_1.getAdapter)();
99
141
  if (!colMeta) {
100
- Logger_1.Logger.log(`No @Column or @Id decorators found on model ${model.name}.`, 'warn');
101
- return { columns, indexes };
142
+ return { columns, indexes, primaryKey };
102
143
  }
103
144
  for (const [prop, opts] of colMeta.entries()) {
104
- const colName = opts && opts.name ? opts.name : camelToSnake(prop);
145
+ const colName = (opts === null || opts === void 0 ? void 0 : opts.name) ? opts.name : camelToSnake(prop);
105
146
  if (opts.index) {
106
147
  indexes.push(colName);
107
148
  }
108
- if (opts && opts.id) {
149
+ if (opts === null || opts === void 0 ? void 0 : opts.id) {
150
+ primaryKey = colName;
109
151
  const type = opts.type || Number;
110
152
  if (adapter.type === 'postgres') {
111
153
  columns[colName] = "SERIAL PRIMARY KEY";
@@ -117,7 +159,6 @@ class SchemaManager {
117
159
  }
118
160
  const type = opts.type;
119
161
  if (!type) {
120
- Logger_1.Logger.log(`Type not provided for property '${prop}' on model '${model.name}'.`, 'error');
121
162
  continue;
122
163
  }
123
164
  let sqlType = this.getSqlTypeForClass(type);
@@ -129,7 +170,7 @@ class SchemaManager {
129
170
  sqlType = 'DECIMAL(10, 2)';
130
171
  }
131
172
  }
132
- if (type === String && opts && opts.limit) {
173
+ else if (type === String && (opts === null || opts === void 0 ? void 0 : opts.limit)) {
133
174
  sqlType = `VARCHAR(${opts.limit})`;
134
175
  }
135
176
  const isNullable = nullableMeta === null || nullableMeta === void 0 ? void 0 : nullableMeta.get(prop);
@@ -145,11 +186,11 @@ class SchemaManager {
145
186
  sqlType += " DEFAULT CURRENT_TIMESTAMP";
146
187
  }
147
188
  }
148
- if (opts && opts.unique)
189
+ if (opts === null || opts === void 0 ? void 0 : opts.unique)
149
190
  sqlType += " UNIQUE";
150
191
  columns[colName] = sqlType;
151
192
  }
152
- return { columns, indexes };
193
+ return { columns, indexes, primaryKey };
153
194
  }
154
195
  getSqlTypeForClass(type) {
155
196
  const adapterType = (0, Database_1.getAdapter)().type;
@@ -178,25 +219,35 @@ class SchemaManager {
178
219
  try {
179
220
  let sql;
180
221
  if (adapter.type === 'postgres') {
181
- sql = `SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = ?`;
222
+ sql = `SELECT column_name, udt_name || COALESCE('(' || character_maximum_length || ')', '') as column_type
223
+ FROM information_schema.columns
224
+ WHERE table_schema = current_schema() AND table_name = $1`;
182
225
  }
183
226
  else { // mysql
184
- sql = `SELECT COLUMN_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?`;
227
+ sql = `SELECT COLUMN_NAME, COLUMN_TYPE
228
+ FROM INFORMATION_SCHEMA.COLUMNS
229
+ WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?`;
185
230
  }
186
231
  const rows = yield (0, Database_1.query)(sql, [table], conn);
232
+ if (rows.length === 0)
233
+ return {};
187
234
  if (adapter.type === 'postgres') {
188
235
  for (const r of rows) {
189
- columns[r.column_name] = r.data_type;
236
+ columns[r.column_name] = r.column_type;
190
237
  }
191
238
  }
192
239
  else {
193
240
  for (const r of rows) {
194
- columns[r.COLUMN_NAME] = r.DATA_TYPE;
241
+ columns[r.COLUMN_NAME] = r.COLUMN_TYPE;
195
242
  }
196
243
  }
197
244
  }
245
+ catch (e) {
246
+ return {}; // Table likely does not exist
247
+ }
198
248
  finally {
199
- conn.release();
249
+ if (conn)
250
+ conn.release();
200
251
  }
201
252
  return columns;
202
253
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aceitadev/adatabase",
3
- "version": "0.5.0",
3
+ "version": "0.5.6",
4
4
  "description": "Uma biblioteca para facilitar a interação com bancos de dados MySQL e PostgreSQL em projetos TypeScript/Node.js.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",