@cheetah.js/orm 0.1.45 → 0.1.46

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 (52) hide show
  1. package/dist/SqlBuilder.d.ts +18 -33
  2. package/dist/SqlBuilder.js +99 -586
  3. package/dist/driver/bun-driver.base.d.ts +35 -1
  4. package/dist/driver/bun-driver.base.js +132 -0
  5. package/dist/driver/bun-mysql.driver.d.ts +13 -6
  6. package/dist/driver/bun-mysql.driver.js +47 -72
  7. package/dist/driver/bun-pg.driver.d.ts +12 -6
  8. package/dist/driver/bun-pg.driver.js +27 -59
  9. package/dist/driver/driver.interface.d.ts +1 -1
  10. package/dist/query/model-transformer.d.ts +21 -0
  11. package/dist/query/model-transformer.js +118 -0
  12. package/dist/query/sql-column-manager.d.ts +25 -0
  13. package/dist/query/sql-column-manager.js +124 -0
  14. package/dist/query/sql-condition-builder.d.ts +36 -0
  15. package/dist/query/sql-condition-builder.js +160 -0
  16. package/dist/query/sql-join-manager.d.ts +39 -0
  17. package/dist/query/sql-join-manager.js +224 -0
  18. package/dist/utils/value-processor.d.ts +14 -0
  19. package/dist/utils/value-processor.js +84 -0
  20. package/package.json +3 -3
  21. package/build.ts +0 -14
  22. package/cheetah.config.ts +0 -14
  23. package/dist/SqlBuilder.js.map +0 -1
  24. package/dist/bun/index.js +0 -233434
  25. package/dist/bun/index.js.map +0 -336
  26. package/dist/common/email.vo.js.map +0 -1
  27. package/dist/common/uuid.js.map +0 -1
  28. package/dist/common/value-object.js.map +0 -1
  29. package/dist/constants.js.map +0 -1
  30. package/dist/decorators/entity.decorator.js.map +0 -1
  31. package/dist/decorators/enum.decorator.js.map +0 -1
  32. package/dist/decorators/event-hook.decorator.js.map +0 -1
  33. package/dist/decorators/index.decorator.js.map +0 -1
  34. package/dist/decorators/one-many.decorator.js.map +0 -1
  35. package/dist/decorators/primary-key.decorator.js.map +0 -1
  36. package/dist/decorators/property.decorator.js.map +0 -1
  37. package/dist/domain/base-entity.js.map +0 -1
  38. package/dist/domain/collection.js.map +0 -1
  39. package/dist/domain/entities.js.map +0 -1
  40. package/dist/domain/reference.js.map +0 -1
  41. package/dist/driver/bun-driver.base.js.map +0 -1
  42. package/dist/driver/bun-mysql.driver.js.map +0 -1
  43. package/dist/driver/bun-pg.driver.js.map +0 -1
  44. package/dist/driver/driver.interface.js.map +0 -1
  45. package/dist/driver/pg-driver.d.ts +0 -63
  46. package/dist/driver/pg-driver.js +0 -335
  47. package/dist/driver/pg-driver.js.map +0 -1
  48. package/dist/entry.js.map +0 -1
  49. package/dist/index.js.map +0 -1
  50. package/dist/orm.js.map +0 -1
  51. package/dist/orm.service.js.map +0 -1
  52. package/dist/utils.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { SQL } from 'bun';
2
- import { ConnectionSettings, DriverInterface } from './driver.interface';
2
+ import { ConnectionSettings, DriverInterface, Statement, SnapshotConstraintInfo, ColDiff } from './driver.interface';
3
3
  export declare abstract class BunDriverBase implements Partial<DriverInterface> {
4
4
  protected sql: SQL;
5
5
  connectionString: string;
@@ -7,6 +7,7 @@ export declare abstract class BunDriverBase implements Partial<DriverInterface>
7
7
  constructor(options: ConnectionSettings);
8
8
  protected buildConnectionString(options: ConnectionSettings): string;
9
9
  protected abstract getProtocol(): string;
10
+ protected abstract getIdentifierQuote(): string;
10
11
  connect(): Promise<void>;
11
12
  protected validateConnection(): Promise<void>;
12
13
  disconnect(): Promise<void>;
@@ -18,4 +19,37 @@ export declare abstract class BunDriverBase implements Partial<DriverInterface>
18
19
  protected buildOrderByClause(orderBy: string[] | undefined): string;
19
20
  protected buildLimitClause(limit: number | undefined): string;
20
21
  protected buildOffsetClause(offset: number | undefined): string;
22
+ protected quote(identifier: string): string;
23
+ protected buildTableIdentifier(schema: string | undefined, tableName: string): string;
24
+ protected buildColumnConstraints(colDiff: ColDiff): string;
25
+ protected abstract buildAutoIncrementType(colDiff: ColDiff): string;
26
+ protected abstract buildEnumType(schema: string | undefined, tableName: string, colDiff: ColDiff): {
27
+ beforeSql: string;
28
+ columnType: string;
29
+ };
30
+ protected abstract handleInsertReturn(statement: Statement<any>, result: any, sql: string, startTime: number): Promise<{
31
+ query: any;
32
+ startTime: number;
33
+ sql: string;
34
+ }>;
35
+ executeStatement(statement: Statement<any>): Promise<{
36
+ query: any;
37
+ startTime: number;
38
+ sql: string;
39
+ }>;
40
+ protected buildInsertSqlWithReturn(statement: Statement<any>): string;
41
+ protected abstract appendReturningClause(sql: string, statement: Statement<any>): string;
42
+ protected buildStatementSql(statement: Statement<any>): string;
43
+ protected buildBaseSql(statement: Statement<any>): string;
44
+ protected buildInsertSql(table: string, values: any, columns: string[] | undefined, alias: string): string;
45
+ protected buildUpdateSql(table: string, values: any, alias: string): string;
46
+ protected buildJoinClause(statement: Statement<any>): string;
47
+ protected buildWhereAndOrderClauses(statement: Statement<any>): string;
48
+ protected buildLimitAndOffsetClause(statement: Statement<any>): string;
49
+ protected getForeignKeysFromConstraints(constraints: SnapshotConstraintInfo[], row: any, columnNameField: string): any[];
50
+ protected isForeignKeyConstraint(constraint: SnapshotConstraintInfo, columnName: string): boolean;
51
+ protected parseForeignKeyDefinition(consDef: string): {
52
+ referencedColumnName: string;
53
+ referencedTableName: string;
54
+ };
21
55
  }
@@ -83,4 +83,136 @@ export class BunDriverBase {
83
83
  }
84
84
  return ` OFFSET ${offset}`;
85
85
  }
86
+ quote(identifier) {
87
+ const q = this.getIdentifierQuote();
88
+ return `${q}${identifier}${q}`;
89
+ }
90
+ buildTableIdentifier(schema, tableName) {
91
+ return schema
92
+ ? `${this.quote(schema)}.${this.quote(tableName)}`
93
+ : this.quote(tableName);
94
+ }
95
+ buildColumnConstraints(colDiff) {
96
+ const parts = [];
97
+ if (!colDiff.colChanges?.nullable) {
98
+ parts.push('NOT NULL');
99
+ }
100
+ if (colDiff.colChanges?.primary) {
101
+ parts.push('PRIMARY KEY');
102
+ }
103
+ if (colDiff.colChanges?.unique) {
104
+ parts.push('UNIQUE');
105
+ }
106
+ if (colDiff.colChanges?.default) {
107
+ parts.push(`DEFAULT ${colDiff.colChanges.default}`);
108
+ }
109
+ return parts.length > 0 ? ' ' + parts.join(' ') : '';
110
+ }
111
+ async executeStatement(statement) {
112
+ const startTime = Date.now();
113
+ if (statement.statement === 'insert') {
114
+ const sql = this.buildInsertSqlWithReturn(statement);
115
+ const result = await this.sql.unsafe(sql);
116
+ return this.handleInsertReturn(statement, result, sql, startTime);
117
+ }
118
+ const sql = this.buildStatementSql(statement);
119
+ const result = await this.sql.unsafe(sql);
120
+ return {
121
+ query: { rows: Array.isArray(result) ? result : [] },
122
+ startTime,
123
+ sql,
124
+ };
125
+ }
126
+ buildInsertSqlWithReturn(statement) {
127
+ const baseSql = this.buildInsertSql(statement.table, statement.values, statement.columns, statement.alias);
128
+ return this.appendReturningClause(baseSql, statement);
129
+ }
130
+ buildStatementSql(statement) {
131
+ let sql = this.buildBaseSql(statement);
132
+ sql += this.buildJoinClause(statement);
133
+ sql += this.buildWhereAndOrderClauses(statement);
134
+ return sql;
135
+ }
136
+ buildBaseSql(statement) {
137
+ const { statement: type, table, columns, values, alias } = statement;
138
+ switch (type) {
139
+ case 'select':
140
+ return `SELECT ${columns ? columns.join(', ') : '*'} FROM ${table} ${alias}`;
141
+ case 'insert':
142
+ return this.buildInsertSql(table, values, columns, alias);
143
+ case 'update':
144
+ return this.buildUpdateSql(table, values, alias);
145
+ default:
146
+ return '';
147
+ }
148
+ }
149
+ buildInsertSql(table, values, columns, alias) {
150
+ const q = this.getIdentifierQuote();
151
+ const fields = Object.keys(values)
152
+ .map((v) => `${q}${v}${q}`)
153
+ .join(', ');
154
+ const vals = Object.values(values)
155
+ .map((value) => this.toDatabaseValue(value))
156
+ .join(', ');
157
+ return `INSERT INTO ${table} (${fields}) VALUES (${vals})`;
158
+ }
159
+ buildUpdateSql(table, values, alias) {
160
+ const sets = Object.entries(values)
161
+ .map(([key, value]) => `${key} = ${this.toDatabaseValue(value)}`)
162
+ .join(', ');
163
+ return `UPDATE ${table} as ${alias} SET ${sets}`;
164
+ }
165
+ buildJoinClause(statement) {
166
+ if (!statement.join)
167
+ return '';
168
+ return statement.join
169
+ .map((join) => {
170
+ const table = `${join.joinSchema}.${join.joinTable}`;
171
+ return ` ${join.type} JOIN ${table} ${join.joinAlias} ON ${join.on}`;
172
+ })
173
+ .join('');
174
+ }
175
+ buildWhereAndOrderClauses(statement) {
176
+ if (statement.statement === 'insert')
177
+ return '';
178
+ let sql = this.buildWhereClause(statement.where);
179
+ sql += this.buildOrderByClause(statement.orderBy);
180
+ sql += this.buildLimitAndOffsetClause(statement);
181
+ return sql;
182
+ }
183
+ buildLimitAndOffsetClause(statement) {
184
+ const { offset, limit } = statement;
185
+ if (offset && limit) {
186
+ return this.buildOffsetClause(offset) + this.buildLimitClause(limit);
187
+ }
188
+ if (limit) {
189
+ return this.buildLimitClause(limit);
190
+ }
191
+ return '';
192
+ }
193
+ getForeignKeysFromConstraints(constraints, row, columnNameField) {
194
+ const columnName = row[columnNameField];
195
+ return constraints
196
+ .filter((c) => this.isForeignKeyConstraint(c, columnName))
197
+ .map((c) => this.parseForeignKeyDefinition(c.consDef));
198
+ }
199
+ isForeignKeyConstraint(constraint, columnName) {
200
+ return (constraint.type === 'FOREIGN KEY' &&
201
+ constraint.consDef.includes(columnName));
202
+ }
203
+ parseForeignKeyDefinition(consDef) {
204
+ const q = this.getIdentifierQuote();
205
+ const pattern = new RegExp(`REFERENCES\\s+${q}([^${q}]+)${q}\\s*\\(([^)]+)\\)`);
206
+ const filter = consDef.match(pattern);
207
+ if (!filter) {
208
+ throw new Error('Invalid constraint definition');
209
+ }
210
+ return {
211
+ referencedColumnName: filter[2]
212
+ .split(',')[0]
213
+ .trim()
214
+ .replace(new RegExp(q, 'g'), ''),
215
+ referencedTableName: filter[1],
216
+ };
217
+ }
86
218
  }
@@ -4,6 +4,19 @@ export declare class BunMysqlDriver extends BunDriverBase implements DriverInter
4
4
  readonly dbType: "mysql";
5
5
  constructor(options: ConnectionSettings);
6
6
  protected getProtocol(): string;
7
+ protected getIdentifierQuote(): string;
8
+ protected buildAutoIncrementType(colDiff: ColDiff): string;
9
+ protected buildEnumType(schema: string | undefined, tableName: string, colDiff: ColDiff): {
10
+ beforeSql: string;
11
+ columnType: string;
12
+ };
13
+ protected appendReturningClause(sql: string, statement: Statement<any>): string;
14
+ protected handleInsertReturn(statement: Statement<any>, result: any, sql: string, startTime: number): Promise<{
15
+ query: any;
16
+ startTime: number;
17
+ sql: string;
18
+ }>;
19
+ protected buildLimitAndOffsetClause(statement: Statement<any>): string;
7
20
  getCreateTableInstruction(schema: string | undefined, tableName: string, creates: ColDiff[]): string;
8
21
  getAlterTableFkInstruction(schema: string | undefined, tableName: string, colDiff: ColDiff, fk: ForeignKeyInfo): string;
9
22
  getCreateIndex(index: {
@@ -29,14 +42,8 @@ export declare class BunMysqlDriver extends BunDriverBase implements DriverInter
29
42
  getDropTypeEnumInstruction(param: {
30
43
  name: string;
31
44
  }, schema: string | undefined, tableName: string): string;
32
- executeStatement(statement: Statement<any>): Promise<{
33
- query: any;
34
- startTime: number;
35
- sql: string;
36
- }>;
37
45
  snapshot(tableName: string, options: any): Promise<SnapshotTable | undefined>;
38
46
  private parseEnumValues;
39
- private getForeignKeys;
40
47
  index(tableName: string, options: any): Promise<SnapshotIndexInfo[] | undefined>;
41
48
  constraints(tableName: string, options: any): Promise<SnapshotConstraintInfo[] | undefined>;
42
49
  }
@@ -7,6 +7,52 @@ export class BunMysqlDriver extends BunDriverBase {
7
7
  getProtocol() {
8
8
  return 'mysql';
9
9
  }
10
+ getIdentifierQuote() {
11
+ return '`';
12
+ }
13
+ buildAutoIncrementType(colDiff) {
14
+ return 'INT AUTO_INCREMENT';
15
+ }
16
+ buildEnumType(schema, tableName, colDiff) {
17
+ const enumValues = colDiff.colChanges.enumItems
18
+ .map((item) => `'${item}'`)
19
+ .join(', ');
20
+ return {
21
+ beforeSql: '',
22
+ columnType: `ENUM(${enumValues})`,
23
+ };
24
+ }
25
+ appendReturningClause(sql, statement) {
26
+ return sql;
27
+ }
28
+ async handleInsertReturn(statement, result, sql, startTime) {
29
+ if (!statement.columns) {
30
+ return {
31
+ query: { rows: Array.isArray(result) ? result : [] },
32
+ startTime,
33
+ sql,
34
+ };
35
+ }
36
+ const insertId = result.lastInsertRowid;
37
+ const cols = statement.columns.join(', ').replaceAll(`${statement.alias}.`, '');
38
+ const selectSql = `SELECT ${cols} FROM ${statement.table} WHERE id = ${insertId}`;
39
+ const selectResult = await this.sql.unsafe(selectSql);
40
+ return {
41
+ query: { rows: Array.isArray(selectResult) ? selectResult : [] },
42
+ startTime,
43
+ sql,
44
+ };
45
+ }
46
+ buildLimitAndOffsetClause(statement) {
47
+ const { offset, limit } = statement;
48
+ if (offset && limit) {
49
+ return ` LIMIT ${offset}, ${limit}`;
50
+ }
51
+ if (limit) {
52
+ return this.buildLimitClause(limit);
53
+ }
54
+ return '';
55
+ }
10
56
  getCreateTableInstruction(schema, tableName, creates) {
11
57
  let beforeSql = ``;
12
58
  const st = `CREATE TABLE \`${schema}\`.\`${tableName}\` (${creates
@@ -114,63 +160,6 @@ export class BunMysqlDriver extends BunDriverBase {
114
160
  getDropTypeEnumInstruction(param, schema, tableName) {
115
161
  return '';
116
162
  }
117
- async executeStatement(statement) {
118
- let { statement: statementType, table, columns, where, limit, alias } = statement;
119
- let sql = '';
120
- switch (statementType) {
121
- case 'select':
122
- sql = `SELECT ${columns ? columns.join(', ') : '*'} FROM ${table} ${alias}`;
123
- break;
124
- case 'insert':
125
- const fields = Object.keys(statement.values)
126
- .map((v) => `\`${v}\``)
127
- .join(', ');
128
- const values = Object.values(statement.values)
129
- .map((value) => this.toDatabaseValue(value))
130
- .join(', ');
131
- sql = `INSERT INTO ${table} (${fields}) VALUES (${values})`;
132
- break;
133
- case 'update':
134
- sql = `UPDATE ${table} as ${alias} SET ${Object.entries(statement.values)
135
- .map(([key, value]) => `${key} = ${this.toDatabaseValue(value)}`)
136
- .join(', ')}`;
137
- break;
138
- case 'delete':
139
- break;
140
- }
141
- if (statement.join) {
142
- statement.join.forEach((join) => {
143
- sql += ` ${join.type} JOIN ${join.joinSchema}.${join.joinTable} ${join.joinAlias} ON ${join.on}`;
144
- });
145
- }
146
- if (statementType !== 'insert') {
147
- sql += this.buildWhereClause(where);
148
- sql += this.buildOrderByClause(statement.orderBy);
149
- if (statement.offset && limit) {
150
- sql += ` LIMIT ${statement.offset}, ${limit}`;
151
- }
152
- else if (limit) {
153
- sql += this.buildLimitClause(limit);
154
- }
155
- }
156
- const startTime = Date.now();
157
- const result = await this.sql.unsafe(sql);
158
- if (statementType === 'insert' && statement.columns) {
159
- const insertId = result.lastInsertRowid;
160
- const selectSql = `SELECT ${statement.columns.join(', ').replaceAll(`${alias}.`, '')} FROM ${table} WHERE id = ${insertId}`;
161
- const insertResult = await this.sql.unsafe(selectSql);
162
- return {
163
- query: { rows: Array.isArray(insertResult) ? insertResult : [] },
164
- startTime,
165
- sql,
166
- };
167
- }
168
- return {
169
- query: { rows: Array.isArray(result) ? result : [] },
170
- startTime,
171
- sql,
172
- };
173
- }
174
163
  async snapshot(tableName, options) {
175
164
  const schema = (options && options.schema) || 'information_schema';
176
165
  const sql = `SELECT * FROM information_schema.columns WHERE table_name = '${tableName}' AND table_schema = DATABASE()`;
@@ -195,7 +184,7 @@ export class BunMysqlDriver extends BunDriverBase {
195
184
  primary: row.COLUMN_KEY === 'PRI',
196
185
  unique: row.COLUMN_KEY === 'UNI' || row.COLUMN_KEY === 'PRI',
197
186
  type: row.DATA_TYPE,
198
- foreignKeys: this.getForeignKeys(constraints, row),
187
+ foreignKeys: this.getForeignKeysFromConstraints(constraints, row, 'COLUMN_NAME'),
199
188
  isEnum: row.DATA_TYPE === 'enum',
200
189
  enumItems: row.DATA_TYPE === 'enum' ? this.parseEnumValues(row.COLUMN_TYPE) : undefined,
201
190
  precision: row.NUMERIC_PRECISION,
@@ -212,20 +201,6 @@ export class BunMysqlDriver extends BunDriverBase {
212
201
  }
213
202
  return match[1].split(',').map((v) => v.trim().replace(/'/g, ''));
214
203
  }
215
- getForeignKeys(constraints, row) {
216
- return constraints
217
- .filter((c) => c.type === 'FOREIGN KEY' && c.consDef.includes(row.COLUMN_NAME))
218
- .map((c) => {
219
- const filter = c.consDef.match(/REFERENCES\s+`([^`]+)`\s*\(([^)]+)\)/);
220
- if (!filter) {
221
- throw new Error('Invalid constraint definition');
222
- }
223
- return {
224
- referencedColumnName: filter[2].split(',')[0].trim().replace(/`/g, ''),
225
- referencedTableName: filter[1],
226
- };
227
- });
228
- }
229
204
  async index(tableName, options) {
230
205
  const result = await this.sql.unsafe(`SHOW INDEX FROM \`${tableName}\` FROM \`${options.schema || 'information_schema'}\``);
231
206
  return result
@@ -4,6 +4,18 @@ export declare class BunPgDriver extends BunDriverBase implements DriverInterfac
4
4
  readonly dbType: "postgres";
5
5
  constructor(options: ConnectionSettings);
6
6
  protected getProtocol(): string;
7
+ protected getIdentifierQuote(): string;
8
+ protected buildAutoIncrementType(colDiff: ColDiff): string;
9
+ protected buildEnumType(schema: string | undefined, tableName: string, colDiff: ColDiff): {
10
+ beforeSql: string;
11
+ columnType: string;
12
+ };
13
+ protected appendReturningClause(sql: string, statement: Statement<any>): string;
14
+ protected handleInsertReturn(statement: Statement<any>, result: any, sql: string, startTime: number): Promise<{
15
+ query: any;
16
+ startTime: number;
17
+ sql: string;
18
+ }>;
7
19
  getCreateTableInstruction(schema: string | undefined, tableName: string, creates: ColDiff[]): string;
8
20
  getAlterTableFkInstruction(schema: string | undefined, tableName: string, colDiff: ColDiff, fk: ForeignKeyInfo): string;
9
21
  getCreateIndex(index: {
@@ -29,13 +41,7 @@ export declare class BunPgDriver extends BunDriverBase implements DriverInterfac
29
41
  getDropTypeEnumInstruction(param: {
30
42
  name: string;
31
43
  }, schema: string | undefined, tableName: string): string;
32
- executeStatement(statement: Statement<any>): Promise<{
33
- query: any;
34
- startTime: number;
35
- sql: string;
36
- }>;
37
44
  snapshot(tableName: string, options: any): Promise<SnapshotTable | undefined>;
38
- private getForeignKeys;
39
45
  index(tableName: string, options: any): Promise<SnapshotIndexInfo[] | undefined>;
40
46
  constraints(tableName: string, options: any): Promise<SnapshotConstraintInfo[] | undefined>;
41
47
  private getEnums;
@@ -7,6 +7,32 @@ export class BunPgDriver extends BunDriverBase {
7
7
  getProtocol() {
8
8
  return 'postgres';
9
9
  }
10
+ getIdentifierQuote() {
11
+ return '"';
12
+ }
13
+ buildAutoIncrementType(colDiff) {
14
+ return 'SERIAL';
15
+ }
16
+ buildEnumType(schema, tableName, colDiff) {
17
+ const enumName = `${schema}_${tableName}_${colDiff.colName}_enum`;
18
+ const enumValues = colDiff.colChanges.enumItems
19
+ .map((item) => `'${item}'`)
20
+ .join(', ');
21
+ const beforeSql = `CREATE TYPE "${enumName}" AS ENUM (${enumValues});`;
22
+ const columnType = `"${enumName}"`;
23
+ return { beforeSql, columnType };
24
+ }
25
+ appendReturningClause(sql, statement) {
26
+ const cols = statement.columns.join(', ').replaceAll(`${statement.alias}.`, '');
27
+ return `${sql} RETURNING ${cols}`;
28
+ }
29
+ async handleInsertReturn(statement, result, sql, startTime) {
30
+ return {
31
+ query: { rows: Array.isArray(result) ? result : [] },
32
+ startTime,
33
+ sql,
34
+ };
35
+ }
10
36
  getCreateTableInstruction(schema, tableName, creates) {
11
37
  let beforeSql = ``;
12
38
  const st = `CREATE TABLE "${schema}"."${tableName}" (${creates
@@ -106,49 +132,6 @@ export class BunPgDriver extends BunDriverBase {
106
132
  getDropTypeEnumInstruction(param, schema, tableName) {
107
133
  return `DROP TYPE IF EXISTS "${param.name}";`;
108
134
  }
109
- async executeStatement(statement) {
110
- let { statement: statementType, table, columns, where, limit, alias } = statement;
111
- let sql = '';
112
- switch (statementType) {
113
- case 'select':
114
- sql = `SELECT ${columns ? columns.join(', ') : '*'} FROM ${table} ${alias}`;
115
- break;
116
- case 'insert':
117
- const fields = Object.keys(statement.values)
118
- .map((v) => `"${v}"`)
119
- .join(', ');
120
- const values = Object.values(statement.values)
121
- .map((value) => this.toDatabaseValue(value))
122
- .join(', ');
123
- sql = `INSERT INTO ${table} (${fields}) VALUES (${values}) RETURNING ${statement.columns.join(', ').replaceAll(`${alias}.`, '')}`;
124
- break;
125
- case 'update':
126
- sql = `UPDATE ${table} as ${alias} SET ${Object.entries(statement.values)
127
- .map(([key, value]) => `${key} = ${this.toDatabaseValue(value)}`)
128
- .join(', ')}`;
129
- break;
130
- case 'delete':
131
- break;
132
- }
133
- if (statement.join) {
134
- statement.join.forEach((join) => {
135
- sql += ` ${join.type} JOIN ${join.joinSchema}.${join.joinTable} ${join.joinAlias} ON ${join.on}`;
136
- });
137
- }
138
- if (statementType !== 'insert') {
139
- sql += this.buildWhereClause(where);
140
- sql += this.buildOrderByClause(statement.orderBy);
141
- sql += this.buildOffsetClause(statement.offset);
142
- sql += this.buildLimitClause(limit);
143
- }
144
- const startTime = Date.now();
145
- const result = await this.sql.unsafe(sql);
146
- return {
147
- query: { rows: Array.isArray(result) ? result : [] },
148
- startTime,
149
- sql,
150
- };
151
- }
152
135
  async snapshot(tableName, options) {
153
136
  const schema = (options && options.schema) || 'public';
154
137
  const sql = `SELECT * FROM information_schema.columns WHERE table_name = '${tableName}' AND table_schema = '${schema}'`;
@@ -182,7 +165,7 @@ export class BunPgDriver extends BunDriverBase {
182
165
  unique: constraints.some((c) => (c.type === 'UNIQUE' || c.type === 'PRIMARY KEY') &&
183
166
  c.consDef.includes(row.column_name)),
184
167
  type: row.data_type,
185
- foreignKeys: this.getForeignKeys(constraints, row),
168
+ foreignKeys: this.getForeignKeysFromConstraints(constraints, row, 'column_name'),
186
169
  isEnum: row.data_type === 'USER-DEFINED',
187
170
  enumItems: row.data_type === 'USER-DEFINED'
188
171
  ? enums[`${schema}_${tableName}_${row.column_name}_enum`]
@@ -194,21 +177,6 @@ export class BunPgDriver extends BunDriverBase {
194
177
  }),
195
178
  };
196
179
  }
197
- getForeignKeys(constraints, row) {
198
- return constraints
199
- .filter((c) => c.type === 'FOREIGN KEY' &&
200
- c.consDef.match(new RegExp(`FOREIGN KEY \\("${row.column_name}"\\)`)))
201
- .map((c) => {
202
- const filter = c.consDef.match(/REFERENCES\s+"([^"]+)"\s*\(([^)]+)\)/);
203
- if (!filter) {
204
- throw new Error('Invalid constraint definition');
205
- }
206
- return {
207
- referencedColumnName: filter[2].split(',')[0].trim(),
208
- referencedTableName: filter[1],
209
- };
210
- });
211
- }
212
180
  async index(tableName, options) {
213
181
  const schema = (options && options.schema) || 'public';
214
182
  let result = await this.sql.unsafe(`SELECT indexname AS index_name, indexdef AS column_name, tablename AS table_name
@@ -108,7 +108,7 @@ export type Statement<T> = {
108
108
  table?: string;
109
109
  alias?: string;
110
110
  customSchema?: string;
111
- columns?: Array<keyof T>;
111
+ columns?: string[];
112
112
  join?: JoinStatement<T>[];
113
113
  selectJoin?: Statement<T>[];
114
114
  strategy?: 'select' | 'joined';
@@ -0,0 +1,21 @@
1
+ import { Statement } from '../driver/driver.interface';
2
+ import { EntityStorage } from '../domain/entities';
3
+ export declare class ModelTransformer {
4
+ private entityStorage;
5
+ constructor(entityStorage: EntityStorage);
6
+ transform<T>(model: any, statement: Statement<any>, data: any): T;
7
+ private createInstances;
8
+ private createInstance;
9
+ private addJoinedInstances;
10
+ private buildOptionsMap;
11
+ private populateProperties;
12
+ private parseColumnKey;
13
+ private setPropertyValue;
14
+ private findPropertyByColumnName;
15
+ private isValueObjectType;
16
+ private linkJoinedEntities;
17
+ private linkSingleJoin;
18
+ private findRelationProperty;
19
+ private attachJoinedEntity;
20
+ private appendToArray;
21
+ }
@@ -0,0 +1,118 @@
1
+ import { ValueObject } from '../common/value-object';
2
+ import { extendsFrom } from '../utils';
3
+ export class ModelTransformer {
4
+ constructor(entityStorage) {
5
+ this.entityStorage = entityStorage;
6
+ }
7
+ transform(model, statement, data) {
8
+ const instanceMap = this.createInstances(model, statement);
9
+ const optionsMap = this.buildOptionsMap(instanceMap);
10
+ this.populateProperties(data, instanceMap, optionsMap);
11
+ this.linkJoinedEntities(statement, instanceMap, optionsMap);
12
+ return instanceMap[statement.alias];
13
+ }
14
+ createInstances(model, statement) {
15
+ const instance = this.createInstance(model);
16
+ const instanceMap = {
17
+ [statement.alias]: instance,
18
+ };
19
+ if (statement.join) {
20
+ this.addJoinedInstances(statement, instanceMap);
21
+ }
22
+ return instanceMap;
23
+ }
24
+ createInstance(model) {
25
+ const instance = new model();
26
+ instance.$_isPersisted = true;
27
+ return instance;
28
+ }
29
+ addJoinedInstances(statement, instanceMap) {
30
+ statement.join.forEach(join => {
31
+ const joinInstance = this.createInstance(join.joinEntity);
32
+ instanceMap[join.joinAlias] = joinInstance;
33
+ });
34
+ }
35
+ buildOptionsMap(instanceMap) {
36
+ const optionsMap = new Map();
37
+ for (const [alias, instance] of Object.entries(instanceMap)) {
38
+ const options = this.entityStorage.get(instance.constructor);
39
+ if (options) {
40
+ optionsMap.set(alias, options);
41
+ }
42
+ }
43
+ return optionsMap;
44
+ }
45
+ populateProperties(data, instanceMap, optionsMap) {
46
+ Object.entries(data).forEach(([key, value]) => {
47
+ const { alias, propertyName } = this.parseColumnKey(key);
48
+ const entity = instanceMap[alias];
49
+ if (!entity) {
50
+ return;
51
+ }
52
+ this.setPropertyValue(entity, propertyName, value, optionsMap.get(alias));
53
+ });
54
+ }
55
+ parseColumnKey(key) {
56
+ const index = key.indexOf('_');
57
+ return {
58
+ alias: key.substring(0, index),
59
+ propertyName: key.substring(index + 1),
60
+ };
61
+ }
62
+ setPropertyValue(entity, columnName, value, options) {
63
+ const propertyInfo = this.findPropertyByColumnName(columnName, options);
64
+ if (!propertyInfo) {
65
+ return;
66
+ }
67
+ const { key, property } = propertyInfo;
68
+ if (this.isValueObjectType(property.type)) {
69
+ entity[key] = new property.type(value);
70
+ return;
71
+ }
72
+ entity[key] = value;
73
+ }
74
+ findPropertyByColumnName(columnName, options) {
75
+ const entry = Object.entries(options.properties).find(([_, prop]) => prop.options.columnName === columnName);
76
+ if (!entry) {
77
+ return null;
78
+ }
79
+ return { key: entry[0], property: entry[1] };
80
+ }
81
+ isValueObjectType(type) {
82
+ return extendsFrom(ValueObject, type?.prototype);
83
+ }
84
+ linkJoinedEntities(statement, instanceMap, optionsMap) {
85
+ if (!statement.join) {
86
+ return;
87
+ }
88
+ statement.join.forEach(join => {
89
+ this.linkSingleJoin(join, instanceMap, optionsMap);
90
+ });
91
+ }
92
+ linkSingleJoin(join, instanceMap, optionsMap) {
93
+ const { joinAlias, originAlias, propertyKey } = join;
94
+ const originEntity = instanceMap[originAlias];
95
+ const joinEntity = instanceMap[joinAlias];
96
+ if (!originEntity || !joinEntity) {
97
+ return;
98
+ }
99
+ const property = this.findRelationProperty(originAlias, propertyKey, optionsMap);
100
+ if (property) {
101
+ this.attachJoinedEntity(originEntity, joinEntity, propertyKey, property);
102
+ }
103
+ }
104
+ findRelationProperty(alias, propertyKey, optionsMap) {
105
+ return optionsMap.get(alias)?.relations.find(rel => rel.propertyKey === propertyKey);
106
+ }
107
+ attachJoinedEntity(originEntity, joinEntity, propertyKey, property) {
108
+ if (property.type === Array) {
109
+ originEntity[propertyKey] = this.appendToArray(originEntity[propertyKey], joinEntity);
110
+ }
111
+ else {
112
+ originEntity[propertyKey] = joinEntity;
113
+ }
114
+ }
115
+ appendToArray(existingArray, newItem) {
116
+ return existingArray ? [...existingArray, newItem] : [newItem];
117
+ }
118
+ }