@cheetah.js/orm 0.1.8 → 0.1.10

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.
@@ -0,0 +1,340 @@
1
+ import { PropertyOptions } from '../decorators/property.decorator';
2
+ import { Collection } from '../domain/collection';
3
+ import { Reference } from '../domain/reference';
4
+ import { ValueObject } from '../common/value-object';
5
+
6
+ export interface DriverInterface {
7
+ connectionString: string;
8
+ executeStatement(statement: Statement<any>): Promise<{ query: any, startTime: number, sql: string }>;
9
+ connect(): Promise<void>;
10
+ disconnect(): Promise<void>;
11
+ executeSql(s: string): Promise<any>;
12
+ snapshot(tableName: string, options: any): Promise<SnapshotTable | undefined>;
13
+
14
+ getCreateTableInstruction(schema: string | undefined, tableName: string, creates: ColDiff[]): string;
15
+ getAlterTableFkInstruction(schema: string | undefined, tableName: string, colDiff: ColDiff, fk: ForeignKeyInfo) : string;
16
+ getCreateIndex(index: { name: string; properties?: string[] }, schema: string | undefined, tableName: string): string;
17
+ getAddColumn(schema: string | undefined, tableName: string, colName: string, colDiff: ColDiff, colDiffInstructions: string[]): void;
18
+ getDropColumn(colDiffInstructions: string[], schema: string | undefined, tableName: string, colName: string): void;
19
+
20
+ getDropIndex(index: {name: string; properties?: string[]}, schema: string | undefined, tableName: string): string;
21
+
22
+ getAlterTableType(schema: string | undefined, tableName: string, colName: string, colDiff: ColDiff): string;
23
+
24
+ getAlterTableDefaultInstruction(schema: string | undefined, tableName: string, colName: string, colDiff: ColDiff): string;
25
+
26
+ getAlterTablePrimaryKeyInstruction(schema: string | undefined, tableName: string, colName: string, colDiff: ColDiff): string;
27
+
28
+ getDropConstraint(param: {name: string}, schema: string | undefined, tableName: string): string;
29
+
30
+ getAddUniqueConstraint(schema: string | undefined, tableName: string, colName: string): string;
31
+
32
+ getAlterTableDropNullInstruction(schema: string | undefined, tableName: string, colName: string, colDiff: ColDiff): string;
33
+
34
+ getAlterTableDropNotNullInstruction(schema: string | undefined, tableName: string, colName: string, colDiff: ColDiff): string;
35
+
36
+ startTransaction(): Promise<void>;
37
+ commitTransaction(): Promise<void>;
38
+ rollbackTransaction(): Promise<void>;
39
+ }
40
+
41
+ // @ts-ignore
42
+ export type ValueOrInstance<T> = T extends ValueObject<any, any> ? T | T['value'] : NonNullable<T>;
43
+
44
+ export type SnapshotConstraintInfo = {
45
+ indexName: string;
46
+ consDef: string;
47
+ type: string;
48
+ }
49
+
50
+ export interface ConnectionSettings<T extends DriverInterface = DriverInterface> {
51
+ host?: string;
52
+ port?: number;
53
+ username?: string;
54
+ password?: string;
55
+ database?: string;
56
+ connectionString?: string;
57
+ ssl?: boolean;
58
+ driver: T;
59
+ entities?: Function[] | string;
60
+ migrationPath?: string;
61
+ }
62
+
63
+ export type ConditionOperators<T, C> = {
64
+ $ne?: T;
65
+ $in?: T[];
66
+ $nin?: T[];
67
+ $like?: string;
68
+ $gt?: T;
69
+ $gte?: T;
70
+ $lt?: T;
71
+ $lte?: T;
72
+
73
+ $and?: Condition<C>[];
74
+ $or?: Condition<C>[];
75
+ // adicione aqui os demais operadores que precisar
76
+ };
77
+
78
+ export type Condition<T> = {
79
+ [P in keyof T]?: T[P] | ConditionOperators<T[P], T>;
80
+ };
81
+
82
+ export type InstanceOf<T> = {
83
+ [key in keyof T]: T[key]
84
+ };
85
+
86
+ export type JoinStatement<T> = {
87
+ type: 'INNER' | 'LEFT' | 'RIGHT';
88
+ originalEntity?: Function;
89
+ originTable: string;
90
+ originSchema: string;
91
+ originAlias: string;
92
+ joinTable: string;
93
+ joinSchema: string;
94
+ joinAlias: string;
95
+ joinEntity?: Function;
96
+ joinWhere?: string;
97
+ joinProperty: string;
98
+ on: string;
99
+ propertyKey: string | symbol;
100
+ };
101
+
102
+ export type Statement<T> = {
103
+ statement?: 'select' | 'insert' | 'update' | 'delete';
104
+ table?: string;
105
+ alias?: string;
106
+ columns?: Array<keyof T>;
107
+ join?: JoinStatement<T>[];
108
+ where?: string;
109
+ values?: any;
110
+ groupBy?: string[];
111
+ orderBy?: string[];
112
+ limit?: number;
113
+ offset?: number;
114
+ }
115
+
116
+ interface SnapshotColumnInfo {
117
+ table_catalog: string;
118
+ table_schema: string;
119
+ table_name: string;
120
+ column_name: string;
121
+ ordinal_position: number;
122
+ column_default: string | null;
123
+ is_nullable: string;
124
+ data_type: string;
125
+ character_maximum_length: number | null;
126
+ character_octet_length: number | null;
127
+ numeric_precision: number | null;
128
+ numeric_scale: number | null;
129
+ datetime_precision: number | null;
130
+ character_set_name: string | null;
131
+ collation_name: string | null;
132
+ column_type: string;
133
+ column_key: string;
134
+ extra: string;
135
+ privileges: string;
136
+ column_comment: string;
137
+ }
138
+
139
+ export type SnapshotTable = {
140
+ tableName: string;
141
+ schema?: string;
142
+ columns: ColumnsInfo[];
143
+ indexes: SnapshotIndexInfo[];
144
+ foreignKeys?: ForeignKeyInfo[];
145
+ }
146
+
147
+ export type SnapshotIndexInfo = {
148
+ table: string;
149
+ indexName: string;
150
+ columnName: string;
151
+ }
152
+
153
+ export type ForeignKeyInfo = {
154
+ referencedTableName: string;
155
+ referencedColumnName: string;
156
+ }
157
+
158
+ export type ColumnsInfo = {
159
+ name: string;
160
+ type: string;
161
+ nullable?: boolean;
162
+ default?: string | null;
163
+ primary?: boolean;
164
+ unique?: boolean;
165
+ autoIncrement?: boolean;
166
+ length?: number;
167
+ foreignKeys?: ForeignKeyInfo[];
168
+ }
169
+
170
+ export type SqlActionType = 'CREATE' | 'DELETE' | 'ALTER' | 'INDEX';
171
+
172
+ export type ColDiff = {
173
+ actionType: SqlActionType;
174
+ colName: string;
175
+ colType?: string;
176
+ colLength?: number;
177
+ indexTables?: { name: string, properties?: string[] }[];
178
+ colChanges?: {
179
+ default?: string | null;
180
+ primary?: boolean;
181
+ unique?: boolean;
182
+ nullable?: boolean;
183
+ autoIncrement?: boolean;
184
+ foreignKeys?: ForeignKeyInfo[];
185
+ };
186
+ }
187
+
188
+ export type TableDiff = {
189
+ tableName: string;
190
+ schema?: string;
191
+ newTable?: boolean;
192
+ colDiffs: ColDiff[];
193
+ }
194
+
195
+ export declare const PrimaryKeyType: unique symbol;
196
+ export declare const PrimaryKeyProp: unique symbol;
197
+ type ReadonlyPrimary<T> = T extends any[] ? Readonly<T> : T;
198
+ export type Primary<T> = T extends {
199
+ [PrimaryKeyType]?: infer PK;
200
+ } ? ReadonlyPrimary<PK> : T extends {
201
+ _id?: infer PK;
202
+ } ? ReadonlyPrimary<PK> | string : T extends {
203
+ uuid?: infer PK;
204
+ } ? ReadonlyPrimary<PK> : T extends {
205
+ id?: infer PK;
206
+ } ? ReadonlyPrimary<PK> : never;
207
+ export type PrimaryProperty<T> = T extends {
208
+ [PrimaryKeyProp]?: infer PK;
209
+ } ? PK : T extends {
210
+ _id?: any;
211
+ } ? '_id' | string : T extends {
212
+ uuid?: any;
213
+ } ? 'uuid' : T extends {
214
+ id?: any;
215
+ } ? 'id' : never;
216
+ export type IPrimaryKeyValue = number | string | bigint | Date | {
217
+ toHexString(): string;
218
+ };
219
+ export type IPrimaryKey<T extends IPrimaryKeyValue = IPrimaryKeyValue> = T;
220
+ export type OperatorMap<T> = {
221
+ $and?: Query<T>[];
222
+ $or?: Query<T>[];
223
+ $eq?: ExpandScalar<T> | ExpandScalar<T>[];
224
+ $ne?: ExpandScalar<T>;
225
+ $in?: ExpandScalar<T>[];
226
+ $nin?: ExpandScalar<T>[];
227
+ $not?: Query<T>;
228
+ $gt?: ExpandScalar<T>;
229
+ $gte?: ExpandScalar<T>;
230
+ $lt?: ExpandScalar<T>;
231
+ $lte?: ExpandScalar<T>;
232
+ $like?: string;
233
+
234
+ };
235
+ export type ExcludeFunctions<T, K extends keyof T> = T[K] extends Function ? ValueOrInstance<T> : (K extends symbol ? never : K);
236
+ export type Scalar = boolean | number | string | bigint | symbol | Date | RegExp | Uint8Array | {
237
+ toHexString(): string;
238
+ };
239
+ //TODO: editar
240
+ export type ExpandProperty<T> = T extends (infer U)[] ? ValueOrInstance<U> : ValueOrInstance<T> ;
241
+ export type ExpandScalar<T> = null | ValueOrInstance<T> | (T extends string ? T | RegExp : T extends Date ? Date | string : T);
242
+ type ExpandObject<T> = T extends object ? T extends Scalar ? never : {
243
+ // @ts-ignore
244
+ -readonly [K in keyof T as ExcludeFunctions<T, K>]?: Query<ExpandProperty<T[K]>> | FilterValue<ExpandProperty<T[K]>> | null;
245
+ } : never;
246
+ export type EntityProps<T> = {
247
+ // @ts-ignore
248
+ -readonly [K in keyof T as ExcludeFunctions<T, K>]?: T[K];
249
+ };
250
+ export type Query<T> = T extends object ? T extends Scalar ? never : FilterQuery<T> : FilterValue<T>;
251
+ export type FilterValue2<T> = T | ExpandScalar<T> | Primary<T>;
252
+ export type FilterValue<T> = OperatorMap<FilterValue2<T>> | FilterValue2<T> | FilterValue2<T>[] | null;
253
+ export type EntityClass<T> = Function & { prototype: T };
254
+ export type EntityName<T> = string | EntityClass<T> | { name: string };
255
+ export type ObjectQuery<T> = ExpandObject<T> & OperatorMap<T>;
256
+ export type FilterQuery<T> = ValueOrInstance<T> | ObjectQuery<T> | NonNullable<ExpandScalar<Primary<T>>> | NonNullable<EntityProps<T> & OperatorMap<T>> | FilterQuery<T>[];
257
+
258
+ export type Relationship<T> = {
259
+ isRelation?: boolean;
260
+ relation: 'one-to-many' | 'many-to-one';
261
+ type: Function;
262
+ fkKey?: (string & keyof T) | ((e: T) => any);
263
+ entity: () => EntityName<T>;
264
+ originalEntity?: EntityName<T>;
265
+ propertyKey: string | symbol;
266
+ } & Partial<PropertyOptions>;
267
+
268
+ export type Cast<T, R> = T extends R ? T : R;
269
+ export type IsUnknown<T> = T extends unknown ? unknown extends T ? true : never : never;
270
+ export type IdentifiedReference<T, PK extends keyof T | unknown = PrimaryProperty<T>> = true extends IsUnknown<PK> ? Reference<T> : ({
271
+ [K in Cast<PK, keyof T>]: T[K];
272
+ } & Reference<T>);
273
+ export type Ref<T, PK extends keyof T | unknown = PrimaryProperty<T>> = IdentifiedReference<T, PK>;
274
+ type Loadable<T extends object> = Collection<T, any> | Reference<T> | readonly T[];
275
+ type ExtractType<T> = T extends Loadable<infer U> ? U : T;
276
+ type StringKeys<T, E extends string = never> = T extends Collection<any, any> ? `${Exclude<keyof ExtractType<T> | E, symbol>}` : T extends Ref<any> ? `${Exclude<keyof ExtractType<T> | E, symbol>}` : T extends object ? `${Exclude<keyof ExtractType<T> | E, symbol>}` : never;
277
+ type Defined<T> = Exclude<T, null | undefined>;
278
+ type GetStringKey<T, K extends StringKeys<T, string>, E extends string> = K extends keyof T ? ExtractType<T[K]> : (K extends E ? keyof T : never);
279
+ type Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
280
+ export type AutoPath<
281
+ O,
282
+ P extends string,
283
+ E extends string = never,
284
+ D extends Prev[number] = 5
285
+ > = [D] extends [never]
286
+ ? string
287
+ : P extends any
288
+ ? (P & `${string}.` extends never
289
+ ? P
290
+ : P & `${string}.`) extends infer Q
291
+ ? Q extends `${infer A}.${infer B}`
292
+ ? A extends StringKeys<O, E>
293
+ ? `${A}.${AutoPath<Defined<GetStringKey<O, A, E>>, B, E, Prev[D]>}`
294
+ : never
295
+ : Q extends StringKeys<O, E>
296
+ ? (Defined<GetStringKey<O, Q, E>> extends unknown
297
+ ? Exclude<P, `${string}.`>
298
+ : never) | (StringKeys<Defined<GetStringKey<O, Q, E>>, E> extends never
299
+ ? never
300
+ : `${Q}.`)
301
+ : keyof ExtractType<O>
302
+ : never
303
+ : never;
304
+
305
+ export declare enum QueryOrder {
306
+ ASC = "ASC",
307
+ ASC_NULLS_LAST = "ASC NULLS LAST",
308
+ ASC_NULLS_FIRST = "ASC NULLS FIRST",
309
+ DESC = "DESC",
310
+ DESC_NULLS_LAST = "DESC NULLS LAST",
311
+ DESC_NULLS_FIRST = "DESC NULLS FIRST",
312
+ asc = "asc",
313
+ asc_nulls_last = "asc nulls last",
314
+ asc_nulls_first = "asc nulls first",
315
+ desc = "desc",
316
+ desc_nulls_last = "desc nulls last",
317
+ desc_nulls_first = "desc nulls first"
318
+ }
319
+ export declare enum QueryOrderNumeric {
320
+ ASC = 1,
321
+ DESC = -1
322
+ }
323
+ export type QueryOrderKeysFlat = QueryOrder | QueryOrderNumeric | keyof typeof QueryOrder;
324
+ export type QueryOrderKeys<T> = QueryOrderKeysFlat | QueryOrderMap<T>;
325
+ export type QueryOrderMap<T> = {
326
+ // @ts-ignore
327
+ [K in keyof T as ExcludeFunctions<T, K>]?: QueryOrderKeys<ExpandProperty<T[K]>>;
328
+ };
329
+ export type EntityField<T, P extends string = never> = AutoPath<T, P, '*'>;
330
+ export interface FindOptions<T, P extends string = never> {
331
+ // populate?: readonly AutoPath<T, P>[] | boolean;
332
+ orderBy?: (QueryOrderMap<T> & {
333
+ 0?: never;
334
+ }) | QueryOrderMap<T>[];
335
+ // cache?: boolean | number | [string, number];
336
+ limit?: number;
337
+ offset?: number;
338
+ fields?: readonly EntityField<T, P>[];
339
+ }
340
+ export type FindOneOption<T, P extends string = never> = Omit<FindOptions<T, P>, 'limit'|'offset'>
@@ -0,0 +1,310 @@
1
+ import {
2
+ ColDiff,
3
+ ConnectionSettings,
4
+ DriverInterface, ForeignKeyInfo,
5
+ SnapshotConstraintInfo,
6
+ SnapshotIndexInfo,
7
+ SnapshotTable,
8
+ Statement,
9
+ } from './driver.interface';
10
+ import { Client } from 'pg'
11
+
12
+ export class PgDriver implements DriverInterface {
13
+ connectionString: string;
14
+
15
+ private client: Client;
16
+
17
+ constructor(options: ConnectionSettings) {
18
+ if (options.connectionString) {
19
+ this.connectionString = options.connectionString;
20
+ } else {
21
+ const {host, port, username, password, database} = options;
22
+ this.connectionString = `postgres://${username}:${password}@${host}:${port}/${database}`;
23
+ }
24
+
25
+ this.client = new Client({
26
+ connectionString: this.connectionString,
27
+ })
28
+ }
29
+
30
+ getCreateTableInstruction(schema: string | undefined, tableName: string, creates: ColDiff[]) {
31
+ return `CREATE TABLE "${schema}"."${tableName}" (${creates.map(colDiff => {
32
+ const isAutoIncrement = colDiff.colChanges?.autoIncrement;
33
+
34
+ let sql = `"${colDiff.colName}" ${isAutoIncrement ? 'SERIAL' : colDiff.colType + (colDiff.colLength ? `(${colDiff.colLength})` : '')}`;
35
+
36
+ if (!isAutoIncrement && !colDiff.colChanges?.nullable) {
37
+ sql += ' NOT NULL';
38
+ }
39
+
40
+ if (colDiff.colChanges?.primary) {
41
+ sql += ' PRIMARY KEY';
42
+ }
43
+
44
+ if (colDiff.colChanges?.unique) {
45
+ sql += ' UNIQUE';
46
+ }
47
+
48
+ if (colDiff.colChanges?.default) {
49
+ sql += ` DEFAULT ${colDiff.colChanges.default}`;
50
+ }
51
+
52
+ return sql;
53
+ })});`;
54
+ }
55
+ getAlterTableFkInstruction(schema: string | undefined, tableName: string, colDiff: ColDiff, fk: ForeignKeyInfo) {
56
+ return `ALTER TABLE "${schema}"."${tableName}" ADD CONSTRAINT "${tableName}_${colDiff.colName}_fk" FOREIGN KEY ("${colDiff.colName}") REFERENCES "${fk.referencedTableName}" ("${fk.referencedColumnName}");`;
57
+ }
58
+ getCreateIndex(index: { name: string; properties: string[] }, schema: string | undefined, tableName: string) {
59
+ return `CREATE INDEX "${index.name}" ON "${schema}"."${tableName}" (${index.properties.map(prop => `"${prop}"`).join(', ')});`;
60
+ }
61
+ getAddColumn(schema: string | undefined, tableName: string, colName: string, colDiff: ColDiff, colDiffInstructions: string[]): void{
62
+ let sql = `ALTER TABLE "${schema}"."${tableName}" ADD COLUMN "${colName}" ${colDiff.colType}${(colDiff.colLength ? `(${colDiff.colLength})` : '')}`
63
+
64
+ if (!colDiff.colChanges?.nullable) {
65
+ sql += ' NOT NULL';
66
+ }
67
+ if (colDiff.colChanges?.primary) {
68
+ sql += ' PRIMARY KEY';
69
+ }
70
+ if (colDiff.colChanges?.unique) {
71
+ sql += ' UNIQUE';
72
+ }
73
+ if (colDiff.colChanges?.default) {
74
+ sql += ` DEFAULT ${colDiff.colChanges.default}`;
75
+ }
76
+ colDiffInstructions.push(sql.concat(';'));
77
+
78
+ if (colDiff.colChanges?.foreignKeys) {
79
+ colDiff.colChanges.foreignKeys.forEach(fk => {
80
+ colDiffInstructions.push(`ALTER TABLE "${schema}"."${tableName}" ADD CONSTRAINT "${tableName}_${colName}_fk" FOREIGN KEY ("${colName}") REFERENCES "${fk.referencedTableName}" ("${fk.referencedColumnName}");`);
81
+ });
82
+ }
83
+ }
84
+ getDropColumn(colDiffInstructions: string[], schema: string | undefined, tableName: string, colName: string) {
85
+ colDiffInstructions.push(`ALTER TABLE "${schema}"."${tableName}" DROP COLUMN IF EXISTS "${colName}";`);
86
+ }
87
+ getDropIndex(index: { name: string; properties?: string[] }, schema: string | undefined, tableName: string) {
88
+ return `DROP INDEX "${index.name}" ON "${schema}"."${tableName}";`;
89
+ }
90
+ getAlterTableType(schema: string | undefined, tableName: string, colName: string, colDiff: ColDiff): string {
91
+ return `ALTER TABLE "${schema}"."${tableName}" ALTER COLUMN "${colName}" TYPE ${colDiff.colType}${(colDiff.colLength ? `(${colDiff.colLength})` : '')};`;
92
+ }
93
+ getAlterTableDefaultInstruction(schema: string | undefined, tableName: string, colName: string, colDiff: ColDiff): string {
94
+ return `ALTER TABLE "${schema}"."${tableName}" ALTER COLUMN "${colName}" SET DEFAULT ${colDiff.colChanges!.default};`;
95
+ }
96
+ getAlterTablePrimaryKeyInstruction(schema: string | undefined, tableName: string, colName: string, colDiff: ColDiff): string {
97
+ return `ALTER TABLE "${schema}"."${tableName}" ADD PRIMARY KEY ("${colName}");`
98
+ }
99
+ getDropConstraint(param: { name: string }, schema: string | undefined, tableName: string): string {
100
+ return `ALTER TABLE "${schema}"."${tableName}" DROP CONSTRAINT "${param.name}";`;
101
+ }
102
+ getAddUniqueConstraint(schema: string | undefined, tableName: string, colName: string): string {
103
+ return `ALTER TABLE "${schema}"."${tableName}" ADD UNIQUE ("${colName}");`
104
+ }
105
+ getAlterTableDropNullInstruction(schema: string | undefined, tableName: string, colName: string, colDiff: ColDiff): string {
106
+ return `ALTER TABLE "${schema}"."${tableName}" ALTER COLUMN "${colName}" DROP NOT NULL;`
107
+ }
108
+ getAlterTableDropNotNullInstruction(schema: string | undefined, tableName: string, colName: string, colDiff: ColDiff): string {
109
+ return `ALTER TABLE "${schema}"."${tableName}" ALTER COLUMN "${colName}" SET NOT NULL;`;
110
+ }
111
+
112
+ async startTransaction(): Promise<void> {
113
+ await this.client.query('BEGIN;');
114
+ }
115
+
116
+ async commitTransaction(): Promise<void> {
117
+ await this.client.query('COMMIT;');
118
+ }
119
+
120
+ async rollbackTransaction(): Promise<void> {
121
+ await this.client.query('ROLLBACK;');
122
+ }
123
+
124
+ async executeStatement(statement: Statement<any>): Promise<{ query: any, startTime: number, sql: string }>{
125
+ let {statement: statementType, table, columns, where, limit, alias} = statement;
126
+ let sql = '';
127
+
128
+ switch (statementType) {
129
+ case 'select':
130
+ sql = `SELECT ${columns ? columns.join(', ') : '*'} FROM ${table} ${alias}`;
131
+ break;
132
+ case 'insert': // TODO: Tratar corretamente os valores string, number
133
+ const fields = Object.keys(statement.values).map(v => `"${v}"`).join(', ');
134
+ const values = Object.values(statement.values).map(value => this.toDatabaseValue(value)).join(', ');
135
+
136
+ sql = `INSERT INTO ${table} (${fields}) VALUES (${values}) RETURNING ${statement.columns!.join(', ').replaceAll(`${alias}.`, '')}`;
137
+ break;
138
+ case 'update':
139
+ sql = `UPDATE ${table} as ${alias} SET ${Object.entries(statement.values).map(([key, value]) => `${key} = '${value}'`).join(', ')}`;
140
+ break;
141
+ case 'delete':
142
+ break;
143
+ }
144
+
145
+ if (statement.join) {
146
+ statement.join.forEach(join => {
147
+ sql += ` ${join.type} JOIN ${join.joinSchema}.${join.joinTable} ${join.joinAlias} ON ${join.on}`;
148
+ });
149
+ }
150
+
151
+ if (statementType !== 'insert') {
152
+ if (where) {
153
+ sql += ` WHERE ${where}`;
154
+ }
155
+
156
+ if (statement.orderBy) {
157
+ sql += ` ORDER BY ${statement.orderBy.join(', ')}`;
158
+ }
159
+
160
+ if (statement.offset) {
161
+ sql += ` OFFSET ${statement.offset}`;
162
+ }
163
+
164
+ if (limit) {
165
+ sql += ` LIMIT ${statement.limit}`;
166
+ }
167
+ }
168
+
169
+ const startTime = Date.now();
170
+ return {
171
+ query: await this.client.query(sql),
172
+ startTime,
173
+ sql
174
+ };
175
+ }
176
+
177
+
178
+ connect(): Promise<void> {
179
+ return this.client.connect()
180
+ }
181
+
182
+ disconnect(): Promise<void> {
183
+ return this.client.end()
184
+ }
185
+
186
+ executeSql(s: string) {
187
+ return this.client.query(s)
188
+ }
189
+
190
+ async snapshot(tableName: string, options: any): Promise<SnapshotTable | undefined> {
191
+ const schema = (options && options.schema) || 'public';
192
+ const sql = `SELECT * FROM information_schema.columns WHERE table_name = '${tableName}' AND table_schema = '${schema}'`;
193
+ const result = await this.client.query(sql);
194
+
195
+ if (!result.rows || result.rows.length === 0) {
196
+ return;
197
+ }
198
+
199
+ const indexes = await this.index(tableName, options) || [];
200
+ const constraints = await this.constraints(tableName, options) || [];
201
+
202
+ return {
203
+ tableName,
204
+ schema,
205
+ indexes,
206
+ columns: result.rows.map(row => {
207
+ // console.log(this.getForeignKeys(constraints, row), row.column_name)
208
+ return {
209
+ default: row.column_default,
210
+ length: row.character_maximum_length || row.numeric_precision || row.datetime_precision,
211
+ name: row.column_name,
212
+ nullable: row.is_nullable === 'YES',
213
+ primary: constraints.some(c => c.type === 'PRIMARY KEY' && c.consDef.includes(row.column_name)),
214
+ unique: constraints.some(c => (c.type === 'UNIQUE' || c.type === 'PRIMARY KEY') && c.consDef.includes(row.column_name)),
215
+ type: row.data_type,
216
+ foreignKeys: this.getForeignKeys(constraints, row)
217
+ }
218
+ })
219
+ }
220
+ }
221
+
222
+ private getForeignKeys(constraints: SnapshotConstraintInfo[], row: any) {
223
+ const name = row.column_name
224
+ return constraints.filter(c => c.type === 'FOREIGN KEY' && c.consDef.match(new RegExp(`FOREIGN KEY \\("${row.column_name}"\\)`))).map(c => {
225
+ const filter = c.consDef.match(/REFERENCES\s+"([^"]+)"\s*\(([^)]+)\)/);
226
+
227
+ if (!filter) throw new Error('Invalid constraint definition');
228
+
229
+ return {
230
+ referencedColumnName: filter[2].split(',')[0].trim(),
231
+ referencedTableName: filter[1],
232
+ }
233
+ });
234
+ }
235
+
236
+ async index(tableName: string, options: any): Promise<SnapshotIndexInfo[] | undefined> {
237
+ const schema = (options && options.schema) || 'public';
238
+ const result = await this.client.query(
239
+ `SELECT indexname AS index_name, indexdef AS column_name, tablename AS table_name
240
+ FROM pg_indexes
241
+ WHERE tablename = '${tableName}' AND schemaname = '${schema}'`
242
+ );
243
+
244
+ return result.rows.map(row => {
245
+ return {
246
+ table: tableName,
247
+ indexName: row.index_name,
248
+ columnName: row.column_name
249
+ }
250
+ });
251
+ }
252
+
253
+ async constraints(tableName: string, options: any): Promise<SnapshotConstraintInfo[] | undefined> {
254
+ const schema = (options && options.schema) || 'public';
255
+ const result = await this.client.query(
256
+ `SELECT
257
+ conname AS index_name,
258
+ pg_get_constraintdef(pg_constraint.oid) as consdef,
259
+ CASE contype
260
+ WHEN 'c' THEN 'CHECK'
261
+ WHEN 'f' THEN 'FOREIGN KEY'
262
+ WHEN 'p' THEN 'PRIMARY KEY'
263
+ WHEN 'u' THEN 'UNIQUE'
264
+ WHEN 't' THEN 'TRIGGER'
265
+ WHEN 'x' THEN 'EXCLUSION'
266
+ ELSE 'UNKNOWN'
267
+ END AS constraint_type
268
+ FROM pg_constraint
269
+ where conrelid = (
270
+ SELECT oid
271
+ FROM pg_class
272
+ WHERE relname = '${tableName}'
273
+ AND relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = '${schema}')
274
+ )
275
+ AND conkey @> ARRAY(
276
+ SELECT attnum
277
+ FROM pg_attribute
278
+ WHERE attrelid = conrelid
279
+ AND attname = '${schema}'
280
+ )`
281
+ );
282
+
283
+ return result.rows.map(row => {
284
+ return {
285
+ indexName: row.index_name,
286
+ type: row.constraint_type,
287
+ consDef: row.consdef
288
+ }
289
+ });
290
+ }
291
+
292
+ private toDatabaseValue(value: unknown) {
293
+ if (value instanceof Date) {
294
+ return `'${value.toISOString()}'`;
295
+ }
296
+
297
+ switch (typeof value) {
298
+ case 'string':
299
+ return `'${value}'`;
300
+ case 'number':
301
+ return value;
302
+ case 'boolean':
303
+ return value;
304
+ case 'object':
305
+ return `'${JSON.stringify(value)}'`;
306
+ default:
307
+ return `'${value}'`;
308
+ }
309
+ }
310
+ }
package/src/entry.ts ADDED
@@ -0,0 +1,6 @@
1
+ import { Cheetah } from '@cheetah.js/core';
2
+ import { Orm } from './orm';
3
+ import { OrmService } from './orm.service';
4
+ import { EntityStorage } from './domain/entities';
5
+
6
+ export const CheetahOrm = new Cheetah({exports: [Orm, OrmService, EntityStorage]})
package/src/index.ts ADDED
@@ -0,0 +1,15 @@
1
+ export * from './decorators/entity.decorator';
2
+ export * from './decorators/property.decorator';
3
+ export * from './decorators/primary-key.decorator';
4
+ export * from './decorators/one-many.decorator';
5
+ export * from './decorators/index.decorator';
6
+ export * from './orm'
7
+ export * from './orm.service'
8
+ export * from './domain/base-entity'
9
+ export * from './driver/pg-driver'
10
+ export * from './utils'
11
+ export * from './driver/driver.interface'
12
+ export * from './entry'
13
+ export * from './common/value-object'
14
+ export * from './common/email.vo'
15
+ export * from './common/uuid'