@aceitadev/adatabase 0.1.0

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.
Files changed (38) hide show
  1. package/README.md +66 -0
  2. package/dist/ActiveRecord.d.ts +12 -0
  3. package/dist/ActiveRecord.js +115 -0
  4. package/dist/ColumnAdapter.d.ts +8 -0
  5. package/dist/ColumnAdapter.js +12 -0
  6. package/dist/Database.d.ts +12 -0
  7. package/dist/Database.js +58 -0
  8. package/dist/MySQL.d.ts +18 -0
  9. package/dist/MySQL.js +72 -0
  10. package/dist/PersistenceException.d.ts +4 -0
  11. package/dist/PersistenceException.js +16 -0
  12. package/dist/QueryBuilder.d.ts +17 -0
  13. package/dist/QueryBuilder.js +180 -0
  14. package/dist/SchemaManager.d.ts +11 -0
  15. package/dist/SchemaManager.js +200 -0
  16. package/dist/adapters/IDatabaseAdapter.d.ts +12 -0
  17. package/dist/adapters/IDatabaseAdapter.js +2 -0
  18. package/dist/adapters/MySQLAdapter.d.ts +14 -0
  19. package/dist/adapters/MySQLAdapter.js +74 -0
  20. package/dist/adapters/PostgresAdapter.d.ts +16 -0
  21. package/dist/adapters/PostgresAdapter.js +85 -0
  22. package/dist/decorators/BelongsTo.d.ts +7 -0
  23. package/dist/decorators/BelongsTo.js +17 -0
  24. package/dist/decorators/Column.d.ts +12 -0
  25. package/dist/decorators/Column.js +17 -0
  26. package/dist/decorators/HasMany.d.ts +7 -0
  27. package/dist/decorators/HasMany.js +17 -0
  28. package/dist/decorators/HasOne.d.ts +7 -0
  29. package/dist/decorators/HasOne.js +17 -0
  30. package/dist/decorators/Id.d.ts +1 -0
  31. package/dist/decorators/Id.js +12 -0
  32. package/dist/decorators/Nullable.d.ts +2 -0
  33. package/dist/decorators/Nullable.js +16 -0
  34. package/dist/decorators/Table.d.ts +2 -0
  35. package/dist/decorators/Table.js +13 -0
  36. package/dist/index.d.ts +13 -0
  37. package/dist/index.js +29 -0
  38. package/package.json +41 -0
package/README.md ADDED
@@ -0,0 +1,66 @@
1
+ ```markdown
2
+ # aMySQL - Node.js (TypeScript) port
3
+
4
+ Esta é uma implementação inicial do seu sistema aMySQL para Node.js usando TypeScript e decorators.
5
+
6
+ Principais características
7
+ - Pool de conexões (mysql2/promise)
8
+ - Decorators: @Table, @Column, @Id, @Nullable
9
+ - ColumnAdapter para tipos serializados customizados
10
+ - ActiveRecord com save/update/delete
11
+ - QueryBuilder (where/order/limit)
12
+ - SchemaManager para criar/atualizar colunas automaticamente
13
+
14
+ Como usar (exemplo rápido)
15
+
16
+ 1) Instale dependências
17
+ npm install
18
+
19
+ 2) Configure tsconfig (já incluído) e habilite experimentalDecorators e emitDecoratorMetadata.
20
+
21
+ 3) Exemplo de modelo (src/example.ts)
22
+ ```ts
23
+ import "reflect-metadata";
24
+ import { init } from "./MySQL";
25
+ import { Table } from "./decorators/Table";
26
+ import { Column } from "./decorators/Column";
27
+ import { Id } from "./decorators/Id";
28
+ import { ActiveRecord } from "./ActiveRecord";
29
+ import { SchemaManager } from "./SchemaManager";
30
+
31
+ @Table("users")
32
+ class User extends ActiveRecord {
33
+ @Id()
34
+ id!: number;
35
+
36
+ @Column({ limit: 150 })
37
+ name!: string;
38
+
39
+ @Column()
40
+ email!: string;
41
+ }
42
+
43
+ async function main() {
44
+ await init({ host: "127.0.0.1", database: "test", user: "root", password: "" });
45
+ const sm = new SchemaManager([User]);
46
+ await sm.migrate();
47
+
48
+ const u = new User();
49
+ u.name = "Fulano";
50
+ u.email = "fulano@example.com";
51
+ await u.save();
52
+
53
+ const fetched = await User.find().where("email", "=", "fulano@example.com").first();
54
+ console.log(fetched);
55
+ }
56
+
57
+ main().catch(console.error);
58
+ ```
59
+
60
+ Limitações & próximos passos
61
+ - QueryBuilder suporta nomes de campos como strings; não foi implementado introspecção de lambda getter como no Java.
62
+ - Adapters: você fornece uma classe com métodos serialize/deserialize para o Column decorator.
63
+ - Migração: apenas cria tabelas e adiciona colunas que faltam; não altera ou remove colunas existentes.
64
+ - Recomendado: adicionar testes, melhorar mapeamento de tipos (enums, UUID), e adicionar caching e transações.
65
+
66
+ ```
@@ -0,0 +1,12 @@
1
+ import { QueryBuilder } from "./QueryBuilder";
2
+ import { GenericConnection } from "./adapters/IDatabaseAdapter";
3
+ type Constructor<T> = {
4
+ new (...args: any[]): T;
5
+ };
6
+ export declare abstract class ActiveRecord {
7
+ private static getIdField;
8
+ save<T extends ActiveRecord>(this: T, tx?: GenericConnection): Promise<T>;
9
+ static find<T extends ActiveRecord>(this: Constructor<T>): QueryBuilder<T>;
10
+ delete(this: ActiveRecord, tx?: GenericConnection): Promise<void>;
11
+ }
12
+ export {};
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.ActiveRecord = void 0;
13
+ const Database_1 = require("./Database");
14
+ const Column_1 = require("./decorators/Column");
15
+ const PersistenceException_1 = require("./PersistenceException");
16
+ const QueryBuilder_1 = require("./QueryBuilder");
17
+ const Table_1 = require("./decorators/Table");
18
+ function camelToSnake(s) {
19
+ return s.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
20
+ }
21
+ class ActiveRecord {
22
+ static getIdField(ctor) {
23
+ return __awaiter(this, void 0, void 0, function* () {
24
+ const columns = (0, Column_1.getColumnMeta)(ctor);
25
+ if (!columns)
26
+ throw new PersistenceException_1.PersistenceException("Model has no @Column decorators", null);
27
+ for (const [prop, opts] of columns.entries()) {
28
+ if (opts === null || opts === void 0 ? void 0 : opts.id) {
29
+ return prop;
30
+ }
31
+ }
32
+ throw new PersistenceException_1.PersistenceException("No @Id field on class", null);
33
+ });
34
+ }
35
+ save(tx) {
36
+ return __awaiter(this, void 0, void 0, function* () {
37
+ const ctor = this.constructor;
38
+ const table = (0, Table_1.getTableName)(ctor);
39
+ if (!table)
40
+ throw new PersistenceException_1.PersistenceException("No @Table defined on class", null);
41
+ const columns = (0, Column_1.getColumnMeta)(ctor);
42
+ if (!columns)
43
+ throw new PersistenceException_1.PersistenceException("No @Column decorators found on class", null);
44
+ const conn = tx !== null && tx !== void 0 ? tx : yield (0, Database_1.getConnection)();
45
+ try {
46
+ const idField = yield ActiveRecord.getIdField(ctor);
47
+ const colEntries = [];
48
+ for (const [prop, opts] of columns.entries()) {
49
+ if (opts === null || opts === void 0 ? void 0 : opts.id)
50
+ continue;
51
+ if (!this.hasOwnProperty(prop))
52
+ continue;
53
+ const value = this[prop];
54
+ const colName = (opts === null || opts === void 0 ? void 0 : opts.name) ? opts.name : camelToSnake(prop);
55
+ colEntries.push({ colName, value });
56
+ }
57
+ const idValue = this[idField];
58
+ if (idValue == null || idValue === 0) {
59
+ const cols = colEntries.map(c => `\`${c.colName}\``).join(", ");
60
+ const placeholders = colEntries.map(() => "?").join(", ");
61
+ const params = colEntries.map(c => c.value);
62
+ const sql = `INSERT INTO \`${table}\` (${cols}) VALUES (${placeholders});`;
63
+ const res = yield (0, Database_1.execute)(sql, params, conn);
64
+ if (res.insertId) {
65
+ this[idField] = res.insertId;
66
+ }
67
+ }
68
+ else {
69
+ const setClause = colEntries.map(c => `\`${c.colName}\` = ?`).join(", ");
70
+ const params = [...colEntries.map(c => c.value), idValue];
71
+ const idColName = camelToSnake(idField);
72
+ const sql = `UPDATE \`${table}\` SET ${setClause} WHERE \`${idColName}\` = ?;`;
73
+ yield (0, Database_1.execute)(sql, params, conn);
74
+ }
75
+ }
76
+ catch (e) {
77
+ if (e.code === 'ER_DUP_ENTRY') {
78
+ throw new PersistenceException_1.PersistenceException(`Duplicate entry violation: ${e.message}`, e);
79
+ }
80
+ throw new PersistenceException_1.PersistenceException(`Failed to save entity: ${e.message}`, e);
81
+ }
82
+ finally {
83
+ if (!tx && conn.release) {
84
+ conn.release();
85
+ }
86
+ }
87
+ return this;
88
+ });
89
+ }
90
+ static find() {
91
+ return new QueryBuilder_1.QueryBuilder(this);
92
+ }
93
+ delete(tx) {
94
+ return __awaiter(this, void 0, void 0, function* () {
95
+ const ctor = this.constructor;
96
+ const table = (0, Table_1.getTableName)(ctor);
97
+ const idField = yield ActiveRecord.getIdField(ctor);
98
+ const idValue = this[idField];
99
+ if (idValue == null)
100
+ return;
101
+ const conn = tx !== null && tx !== void 0 ? tx : yield (0, Database_1.getConnection)();
102
+ try {
103
+ const idColName = camelToSnake(idField);
104
+ const sql = `DELETE FROM \`${table}\` WHERE \`${idColName}\` = ?`;
105
+ yield (0, Database_1.execute)(sql, [idValue], conn);
106
+ }
107
+ finally {
108
+ if (!tx && conn.release) {
109
+ conn.release();
110
+ }
111
+ }
112
+ });
113
+ }
114
+ }
115
+ exports.ActiveRecord = ActiveRecord;
@@ -0,0 +1,8 @@
1
+ export interface ColumnAdapter<T = any, R = any> {
2
+ serialize(object: T): R;
3
+ deserialize(object: R): T;
4
+ }
5
+ export declare class NoneAdapter implements ColumnAdapter<any, any> {
6
+ serialize(): any;
7
+ deserialize(): any;
8
+ }
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NoneAdapter = void 0;
4
+ class NoneAdapter {
5
+ serialize() {
6
+ throw new Error("NoneAdapter should not be used.");
7
+ }
8
+ deserialize() {
9
+ throw new Error("NoneAdapter should not be used.");
10
+ }
11
+ }
12
+ exports.NoneAdapter = NoneAdapter;
@@ -0,0 +1,12 @@
1
+ import { IDatabaseAdapter, GenericConnection } from './adapters/IDatabaseAdapter';
2
+ type DbType = 'mysql' | 'postgres';
3
+ export declare function init(dbType: DbType, config: any): void;
4
+ export declare function getAdapter(): IDatabaseAdapter;
5
+ export declare function query(sql: string, params?: any[], connection?: GenericConnection): Promise<any[]>;
6
+ export declare function execute(sql: string, params?: any[], connection?: GenericConnection): Promise<{
7
+ insertId?: number;
8
+ affectedRows?: number;
9
+ }>;
10
+ export declare function getConnection(): Promise<GenericConnection>;
11
+ export declare function closePool(): Promise<void>;
12
+ export {};
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.init = init;
13
+ exports.getAdapter = getAdapter;
14
+ exports.query = query;
15
+ exports.execute = execute;
16
+ exports.getConnection = getConnection;
17
+ exports.closePool = closePool;
18
+ const MySQLAdapter_1 = require("./adapters/MySQLAdapter");
19
+ const PostgresAdapter_1 = require("./adapters/PostgresAdapter");
20
+ let adapter;
21
+ function init(dbType, config) {
22
+ if (dbType === 'mysql') {
23
+ adapter = new MySQLAdapter_1.MySQLAdapter();
24
+ }
25
+ else if (dbType === 'postgres') {
26
+ adapter = new PostgresAdapter_1.PostgresAdapter();
27
+ }
28
+ else {
29
+ throw new Error(`Tipo de banco de dados não suportado: ${dbType}`);
30
+ }
31
+ adapter.init(config);
32
+ }
33
+ function getAdapter() {
34
+ if (!adapter) {
35
+ throw new Error("O banco de dados não foi inicializado. Chame a função init() primeiro.");
36
+ }
37
+ return adapter;
38
+ }
39
+ function query(sql_1) {
40
+ return __awaiter(this, arguments, void 0, function* (sql, params = [], connection) {
41
+ return getAdapter().query(sql, params, connection);
42
+ });
43
+ }
44
+ function execute(sql_1) {
45
+ return __awaiter(this, arguments, void 0, function* (sql, params = [], connection) {
46
+ return getAdapter().execute(sql, params, connection);
47
+ });
48
+ }
49
+ function getConnection() {
50
+ return __awaiter(this, void 0, void 0, function* () {
51
+ return getAdapter().getConnection();
52
+ });
53
+ }
54
+ function closePool() {
55
+ return __awaiter(this, void 0, void 0, function* () {
56
+ return getAdapter().close();
57
+ });
58
+ }
@@ -0,0 +1,18 @@
1
+ import mysql, { PoolConnection } from "mysql2/promise";
2
+ export type Auth = {
3
+ host: string;
4
+ port?: number;
5
+ database: string;
6
+ user: string;
7
+ password?: string;
8
+ };
9
+ export type PoolOptions = {
10
+ connectionLimit?: number;
11
+ queueLimit?: number;
12
+ idleTimeout?: number;
13
+ waitForConnections?: boolean;
14
+ };
15
+ export declare function init(auth: Auth, options?: PoolOptions): void;
16
+ export declare function getConnection(): Promise<PoolConnection>;
17
+ export declare function query(sql: string, params?: any[]): Promise<mysql.QueryResult>;
18
+ export declare function closePool(): Promise<void>;
package/dist/MySQL.js ADDED
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.init = init;
16
+ exports.getConnection = getConnection;
17
+ exports.query = query;
18
+ exports.closePool = closePool;
19
+ const promise_1 = __importDefault(require("mysql2/promise"));
20
+ let pool = null;
21
+ function init(auth, options = {}) {
22
+ var _a, _b, _c, _d, _e;
23
+ if (pool) {
24
+ console.warn("[aMySQL] Pool de conexões já foi inicializado. Ignorando nova chamada.");
25
+ return;
26
+ }
27
+ try {
28
+ pool = promise_1.default.createPool({
29
+ host: auth.host,
30
+ port: (_a = auth.port) !== null && _a !== void 0 ? _a : 3306,
31
+ database: auth.database,
32
+ user: auth.user,
33
+ password: auth.password,
34
+ waitForConnections: (_b = options.waitForConnections) !== null && _b !== void 0 ? _b : true,
35
+ connectionLimit: (_c = options.connectionLimit) !== null && _c !== void 0 ? _c : 10,
36
+ queueLimit: (_d = options.queueLimit) !== null && _d !== void 0 ? _d : 0,
37
+ idleTimeout: (_e = options.idleTimeout) !== null && _e !== void 0 ? _e : 60000,
38
+ namedPlaceholders: false
39
+ });
40
+ console.log("[aMySQL] ✅ Pool de conexões inicializado com sucesso.");
41
+ }
42
+ catch (error) {
43
+ console.error("[aMySQL] ❌ Falha catastrófica ao inicializar o pool de conexões:", error);
44
+ process.exit(1);
45
+ }
46
+ }
47
+ function getPool() {
48
+ if (!pool) {
49
+ throw new Error("[aMySQL] O pool de conexões não foi inicializado. Chame a função init() na inicialização da sua aplicação.");
50
+ }
51
+ return pool;
52
+ }
53
+ function getConnection() {
54
+ return __awaiter(this, void 0, void 0, function* () {
55
+ return getPool().getConnection();
56
+ });
57
+ }
58
+ function query(sql_1) {
59
+ return __awaiter(this, arguments, void 0, function* (sql, params = []) {
60
+ const [rows] = yield getPool().query(sql, params);
61
+ return rows;
62
+ });
63
+ }
64
+ function closePool() {
65
+ return __awaiter(this, void 0, void 0, function* () {
66
+ if (pool) {
67
+ yield pool.end();
68
+ pool = null;
69
+ console.log("[aMySQL] Pool de conexões foi fechado com sucesso.");
70
+ }
71
+ });
72
+ }
@@ -0,0 +1,4 @@
1
+ export declare class PersistenceException extends Error {
2
+ cause?: (Error | null) | undefined;
3
+ constructor(message: string, cause?: (Error | null) | undefined);
4
+ }
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PersistenceException = void 0;
4
+ class PersistenceException extends Error {
5
+ constructor(message, cause) {
6
+ var _a, _b;
7
+ super(message);
8
+ this.cause = cause;
9
+ if (cause) {
10
+ // preserve stack if present
11
+ this.stack = ((_a = this.stack) !== null && _a !== void 0 ? _a : "") + "\nCaused by: " + ((_b = cause.stack) !== null && _b !== void 0 ? _b : cause.message);
12
+ }
13
+ Object.setPrototypeOf(this, PersistenceException.prototype);
14
+ }
15
+ }
16
+ exports.PersistenceException = PersistenceException;
@@ -0,0 +1,17 @@
1
+ import { ActiveRecord } from "./ActiveRecord";
2
+ export declare class QueryBuilder<T extends ActiveRecord> {
3
+ private model;
4
+ private whereClauses;
5
+ private _limit?;
6
+ private _includes;
7
+ private primaryKey;
8
+ constructor(model: {
9
+ new (): T;
10
+ });
11
+ where(field: keyof T | string, operator: string, value: any): this;
12
+ include(...models: (new (...args: any[]) => any)[]): this;
13
+ first(): Promise<T | null>;
14
+ get(): Promise<T[]>;
15
+ private getColumnName;
16
+ private mapRowsToEntities;
17
+ }
@@ -0,0 +1,180 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.QueryBuilder = void 0;
13
+ const Table_1 = require("./decorators/Table");
14
+ const Column_1 = require("./decorators/Column");
15
+ const Database_1 = require("./Database");
16
+ const PersistenceException_1 = require("./PersistenceException");
17
+ const BelongsTo_1 = require("./decorators/BelongsTo");
18
+ const HasMany_1 = require("./decorators/HasMany");
19
+ const HasOne_1 = require("./decorators/HasOne");
20
+ function camelToSnake(s) {
21
+ return s.replace(/([a-z0-g])([A-Z])/g, "$1_$2").toLowerCase();
22
+ }
23
+ class QueryBuilder {
24
+ constructor(model) {
25
+ this.model = model;
26
+ this.whereClauses = [];
27
+ this._includes = [];
28
+ this.primaryKey = 'id';
29
+ const colMeta = (0, Column_1.getColumnMeta)(this.model);
30
+ if (colMeta) {
31
+ for (const [prop, opts] of colMeta.entries()) {
32
+ if (opts.id) {
33
+ this.primaryKey = prop;
34
+ break;
35
+ }
36
+ }
37
+ }
38
+ }
39
+ where(field, operator, value) {
40
+ this.whereClauses.push({ field: field, operator, value, booleanOp: "AND" });
41
+ return this;
42
+ }
43
+ include(...models) {
44
+ this._includes.push(...models);
45
+ return this;
46
+ }
47
+ first() {
48
+ return __awaiter(this, void 0, void 0, function* () {
49
+ this._limit = 1;
50
+ const rows = yield this.get();
51
+ return rows.length > 0 ? rows[0] : null;
52
+ });
53
+ }
54
+ get() {
55
+ return __awaiter(this, void 0, void 0, function* () {
56
+ const table = (0, Table_1.getTableName)(this.model);
57
+ if (!table)
58
+ throw new PersistenceException_1.PersistenceException("Model has no @Table", null);
59
+ const columnsMeta = (0, Column_1.getColumnMeta)(this.model);
60
+ if (!columnsMeta)
61
+ throw new PersistenceException_1.PersistenceException("Model has no @Column decorators", null);
62
+ const params = [];
63
+ const baseAlias = 't1';
64
+ let selectFields = `${baseAlias}.*`;
65
+ let joinClause = '';
66
+ const allRelationMetas = [
67
+ { meta: (0, BelongsTo_1.getBelongsToMeta)(this.model), type: 'BelongsTo' },
68
+ { meta: (0, HasMany_1.getHasManyMeta)(this.model), type: 'HasMany' },
69
+ { meta: (0, HasOne_1.getHasOneMeta)(this.model), type: 'HasOne' }
70
+ ];
71
+ if (this._includes.length > 0) {
72
+ this._includes.forEach((includeModel, index) => {
73
+ const relationAlias = `t${index + 2}`;
74
+ let relationInfo = null;
75
+ for (const { meta, type } of allRelationMetas) {
76
+ if (meta) {
77
+ for (const [prop, opts] of meta.entries()) {
78
+ if (opts.model() === includeModel) {
79
+ relationInfo = { prop, opts, type };
80
+ break;
81
+ }
82
+ }
83
+ }
84
+ if (relationInfo)
85
+ break;
86
+ }
87
+ if (relationInfo) {
88
+ const relatedTable = (0, Table_1.getTableName)(includeModel);
89
+ const relatedColumnsMeta = (0, Column_1.getColumnMeta)(includeModel);
90
+ if (!relatedTable || !relatedColumnsMeta)
91
+ return;
92
+ for (const prop of relatedColumnsMeta.keys()) {
93
+ const colName = this.getColumnName(prop, relatedColumnsMeta);
94
+ selectFields += `, ${relationAlias}.\`${colName}\` AS \`${relationInfo.prop}__${prop}\``;
95
+ }
96
+ if (relationInfo.type === 'BelongsTo') {
97
+ const foreignKey = relationInfo.opts.foreignKey;
98
+ const foreignKeyCol = this.getColumnName(foreignKey, columnsMeta);
99
+ joinClause += ` LEFT JOIN \`${relatedTable}\` AS ${relationAlias} ON ${baseAlias}.\`${foreignKeyCol}\` = ${relationAlias}.id`;
100
+ }
101
+ else { // HasOne or HasMany
102
+ const foreignKey = relationInfo.opts.foreignKey;
103
+ const foreignKeyCol = this.getColumnName(foreignKey, relatedColumnsMeta);
104
+ joinClause += ` LEFT JOIN \`${relatedTable}\` AS ${relationAlias} ON ${baseAlias}.id = ${relationAlias}.\`${foreignKeyCol}\``;
105
+ }
106
+ }
107
+ });
108
+ }
109
+ let sql = `SELECT ${selectFields} FROM \`${table}\` AS ${baseAlias}${joinClause}`;
110
+ if (this.whereClauses.length) {
111
+ sql += " WHERE ";
112
+ this.whereClauses.forEach((c, i) => {
113
+ if (i > 0)
114
+ sql += ` ${c.booleanOp} `;
115
+ const colName = this.getColumnName(c.field, columnsMeta);
116
+ sql += `${baseAlias}.\`${colName}\` ${c.operator} ?`;
117
+ params.push(c.value);
118
+ });
119
+ }
120
+ if (this._limit)
121
+ sql += ` LIMIT ${this._limit}`;
122
+ sql += ";";
123
+ const rows = yield (0, Database_1.query)(sql, params);
124
+ return this.mapRowsToEntities(rows);
125
+ });
126
+ }
127
+ getColumnName(prop, meta) {
128
+ const opts = meta.get(prop);
129
+ return (opts === null || opts === void 0 ? void 0 : opts.name) ? opts.name : camelToSnake(prop);
130
+ }
131
+ mapRowsToEntities(rows) {
132
+ const mainEntityMap = new Map();
133
+ const columnsMeta = (0, Column_1.getColumnMeta)(this.model);
134
+ const allRelationMetas = [
135
+ { meta: (0, BelongsTo_1.getBelongsToMeta)(this.model), type: 'BelongsTo' },
136
+ { meta: (0, HasMany_1.getHasManyMeta)(this.model), type: 'HasMany' },
137
+ { meta: (0, HasOne_1.getHasOneMeta)(this.model), type: 'HasOne' }
138
+ ];
139
+ for (const row of rows) {
140
+ const pkValue = row[this.primaryKey];
141
+ if (!mainEntityMap.has(pkValue)) {
142
+ const obj = new this.model();
143
+ for (const [prop, opts] of columnsMeta.entries()) {
144
+ const colName = this.getColumnName(prop, columnsMeta);
145
+ if (row.hasOwnProperty(colName)) {
146
+ obj[prop] = row[colName];
147
+ }
148
+ }
149
+ mainEntityMap.set(pkValue, obj);
150
+ }
151
+ const mainEntity = mainEntityMap.get(pkValue);
152
+ for (const { meta, type } of allRelationMetas) {
153
+ if (meta) {
154
+ for (const [relationProp, opts] of meta.entries()) {
155
+ const prefix = `${relationProp}__`;
156
+ if (row[`${prefix}id`] === null)
157
+ continue;
158
+ const relatedModelCtor = opts.model();
159
+ const relatedObj = new relatedModelCtor();
160
+ const relatedColsMeta = (0, Column_1.getColumnMeta)(relatedModelCtor);
161
+ for (const [prop] of relatedColsMeta.entries()) {
162
+ relatedObj[prop] = row[`${prefix}${prop}`];
163
+ }
164
+ if (type === 'HasMany') {
165
+ if (!mainEntity[relationProp]) {
166
+ mainEntity[relationProp] = [];
167
+ }
168
+ mainEntity[relationProp].push(relatedObj);
169
+ }
170
+ else { // BelongsTo or HasOne
171
+ mainEntity[relationProp] = relatedObj;
172
+ }
173
+ }
174
+ }
175
+ }
176
+ }
177
+ return Array.from(mainEntityMap.values());
178
+ }
179
+ }
180
+ exports.QueryBuilder = QueryBuilder;
@@ -0,0 +1,11 @@
1
+ export declare class SchemaManager {
2
+ private models;
3
+ private logger;
4
+ constructor(models: Function[], logger?: Console);
5
+ migrate(): Promise<void>;
6
+ private createTable;
7
+ private updateTable;
8
+ private getSchemaFromModel;
9
+ private getSqlTypeForClass;
10
+ private getExistingColumns;
11
+ }