@zuzjs/orm 0.1.1

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.
package/dist/bin.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/bin.js ADDED
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const path_1 = __importDefault(require("path"));
8
+ const picocolors_1 = __importDefault(require("picocolors"));
9
+ const commander_1 = require("commander");
10
+ // import fs, { readdirSync } from "fs";
11
+ // import { fileURLToPath } from "url";
12
+ const index_js_1 = require("./drivers/mysql/index.js");
13
+ const index_js_2 = require("./core/index.js");
14
+ commander_1.program
15
+ .option(`-v, --version`)
16
+ .option(`-c, --connection <VALUE>`, `Database Connection String`)
17
+ .option(`-p, --dist`);
18
+ commander_1.program.parse();
19
+ const { version, connection, dist: destination } = commander_1.program.opts();
20
+ if (version) {
21
+ console.log(`ZuzORM v0.1.1`);
22
+ process.exit(1);
23
+ }
24
+ if (connection) {
25
+ const dist = destination || path_1.default.join(`src`, `zorm`);
26
+ const _checkDist = (0, index_js_2.checkDirectory)(path_1.default.join(process.cwd(), dist), true);
27
+ if (!_checkDist.access) {
28
+ console.log(picocolors_1.default.red(`○ ${_checkDist.message}`));
29
+ process.exit(1);
30
+ }
31
+ if (connection.startsWith(`mysql`)) {
32
+ const driver = new index_js_1.MySqlDriver(decodeURIComponent(connection), dist);
33
+ driver.generate();
34
+ }
35
+ else {
36
+ console.log(`Only MySQL is supported for now`);
37
+ process.exit(1);
38
+ }
39
+ }
@@ -0,0 +1,7 @@
1
+ export declare const toPascalCase: (str: string) => string;
2
+ export declare const mapSqlTypeToTypescript: (sqlType: string) => string;
3
+ export declare const checkDirectory: (path: string, create: boolean) => {
4
+ access: boolean;
5
+ message?: string;
6
+ };
7
+ export declare const stackTrace: (_error: string, ...more: string[]) => Error;
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.stackTrace = exports.checkDirectory = exports.mapSqlTypeToTypescript = exports.toPascalCase = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const toPascalCase = (str) => {
9
+ return str.replace(/(^\w|_\w)/g, (match) => match.replace("_", "").toUpperCase());
10
+ };
11
+ exports.toPascalCase = toPascalCase;
12
+ const mapSqlTypeToTypescript = (sqlType) => {
13
+ if (sqlType.startsWith("int") || sqlType.startsWith("tinyint"))
14
+ return "number";
15
+ if (sqlType.startsWith("varchar") || sqlType.startsWith("text"))
16
+ return "string";
17
+ if (sqlType.startsWith("datetime") || sqlType.startsWith("timestamp"))
18
+ return "Date";
19
+ return "any";
20
+ };
21
+ exports.mapSqlTypeToTypescript = mapSqlTypeToTypescript;
22
+ const checkDirectory = (path, create) => {
23
+ try {
24
+ // Check if the path exists
25
+ if (!fs_1.default.existsSync(path)) {
26
+ if (create)
27
+ fs_1.default.mkdirSync(path, { recursive: true });
28
+ else
29
+ return { access: false, message: `${path} does not exist.\nRun \`zorm -c [DATABASE_CONNECTION_STRING]\`` };
30
+ }
31
+ // Check if it's a directory
32
+ const stat = fs_1.default.statSync(path);
33
+ if (!stat.isDirectory())
34
+ return { access: false, message: `${path} is not a folder.` };
35
+ // Check if it's writable
36
+ try {
37
+ fs_1.default.accessSync(path, fs_1.default.constants.W_OK);
38
+ }
39
+ catch (e) {
40
+ return { access: false, message: `${path} has no write permission.` };
41
+ }
42
+ return { access: true };
43
+ }
44
+ catch (err) {
45
+ return { access: false, message: `Destination folder is not accessible` };
46
+ }
47
+ };
48
+ exports.checkDirectory = checkDirectory;
49
+ const stackTrace = (_error, ...more) => {
50
+ const error = new Error([_error, ...more].join(` `));
51
+ const lines = [error.message];
52
+ // const lines : string[] = [`${pc.bgRed(pc.whiteBright(` Zorm `))} ${pc.red(error.message)}`]
53
+ // const regex = /\((\/.*\.[a-zA-Z0-9]+):\d+:\d+\)/;
54
+ // const regex = /(\/.*\.[a-zA-Z0-9]+):\d+:\d+/;
55
+ // error.stack?.split(`\n`)
56
+ // .forEach((line, index) => {
57
+ // if ( line.includes(process.cwd()) ){
58
+ // const match = line.match(regex)
59
+ // if ( match ){
60
+ // const [f, _line, _column] = match[0].split(`:`)
61
+ // const rawLine = fs.readFileSync(match[1], `utf8`).split(`\n`)[Number(_line)-1]
62
+ // const ls = f.split(`/`)
63
+ // lines.push(`${ls[ls.length-3]}/${ls[ls.length-2]}/${ls[ls.length-1]} - ZormError: ${_error}`)
64
+ // lines.push(`${pc.bgBlack(pc.white(_line))}\t${rawLine}`)
65
+ // }
66
+ // }
67
+ // })
68
+ return Error(lines.join(`\n`));
69
+ };
70
+ exports.stackTrace = stackTrace;
@@ -0,0 +1,38 @@
1
+ import { Pool } from 'mysql2/promise';
2
+ import { ConnectionDetails, ModelGenerator } from '../../types.js';
3
+ export declare const parseConnectionString: (connectionString: string) => {
4
+ user: string;
5
+ password: string;
6
+ host: string;
7
+ port: number;
8
+ database: string;
9
+ params: {
10
+ [k: string]: string;
11
+ };
12
+ };
13
+ export declare const MySQLErrorMap: Record<string, string>;
14
+ export declare class MySqlDriver implements ModelGenerator {
15
+ pool: Pool | null;
16
+ conn: ConnectionDetails;
17
+ dist: string | null;
18
+ constructor(connectionString: string, dist?: string);
19
+ connection(): ConnectionDetails;
20
+ parseConnectionString: (connectionString: string) => {
21
+ user: string;
22
+ password: string;
23
+ host: string;
24
+ port: number;
25
+ database: string;
26
+ params: {
27
+ [k: string]: string;
28
+ };
29
+ };
30
+ createPool(): void;
31
+ mapColumns(sqlType: string): {
32
+ tsType: string;
33
+ columnType: string;
34
+ length?: number;
35
+ };
36
+ formatDefault(value: any, tsType: string): string | number;
37
+ generate(): Promise<void>;
38
+ }
@@ -0,0 +1,181 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.MySqlDriver = exports.MySQLErrorMap = exports.parseConnectionString = void 0;
7
+ const promise_1 = __importDefault(require("mysql2/promise"));
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const picocolors_1 = __importDefault(require("picocolors"));
11
+ const index_js_1 = require("../../core/index.js");
12
+ const parseConnectionString = (connectionString) => {
13
+ const regex = /mysql:\/\/(?<user>[^:]+):(?<password>[^@]+)@(?<host>[^:/]+)(?::(?<port>\d+))?\/(?<database>[^?]+)(?:\?(?<params>.+))?/;
14
+ const match = connectionString.match(regex);
15
+ if (!match || !match.groups) {
16
+ throw new Error("Invalid MySQL connection string");
17
+ }
18
+ const { user, password, host, port, database, params } = match.groups;
19
+ const queryParams = params ? Object.fromEntries(new URLSearchParams(params)) : {};
20
+ return { user, password, host, port: port ? Number(port) : 3306, database, params: queryParams };
21
+ };
22
+ exports.parseConnectionString = parseConnectionString;
23
+ exports.MySQLErrorMap = {
24
+ ER_DUP_ENTRY: "DuplicateEntry",
25
+ ER_NO_REFERENCED_ROW: "ForeignKeyConstraintFails",
26
+ ER_NO_REFERENCED_ROW_2: "ForeignKeyConstraintFails",
27
+ ER_BAD_NULL_ERROR: "NullValueNotAllowed",
28
+ ER_PARSE_ERROR: "SQLSyntaxError",
29
+ ER_ACCESS_DENIED_ERROR: "AccessDenied",
30
+ ER_TABLE_EXISTS_ERROR: "TableAlreadyExists",
31
+ ER_NO_SUCH_TABLE: "TableNotFound",
32
+ ER_LOCK_WAIT_TIMEOUT: "LockWaitTimeout",
33
+ ER_LOCK_DEADLOCK: "DeadlockDetected",
34
+ ER_DATA_TOO_LONG: "DataTooLong",
35
+ ER_TRUNCATED_WRONG_VALUE: "InvalidDataFormat",
36
+ ER_WRONG_VALUE_COUNT_ON_ROW: "WrongValueCount",
37
+ ER_CANT_CREATE_TABLE: "TableCreationFailed",
38
+ ER_CANT_DROP_FIELD_OR_KEY: "CannotDropKey",
39
+ ER_ROW_IS_REFERENCED: "RowReferenced",
40
+ ER_ROW_IS_REFERENCED_2: "RowReferenced",
41
+ ER_PRIMARY_CANT_HAVE_NULL: "PrimaryKeyCannotBeNull",
42
+ ER_KEY_COLUMN_DOES_NOT_EXITS: "KeyColumnNotFound",
43
+ ER_UNKNOWN_COLUMN: "UnknownColumn",
44
+ ER_WRONG_DB_NAME: "InvalidDatabaseName",
45
+ ER_WRONG_TABLE_NAME: "InvalidTableName",
46
+ ER_UNKNOWN_PROCEDURE: "ProcedureNotFound",
47
+ ER_DUP_UNIQUE: "UniqueConstraintViolation",
48
+ ER_SP_DOES_NOT_EXIST: "StoredProcedureNotFound",
49
+ ER_BAD_FIELD_ERROR: "InvalidColumn",
50
+ };
51
+ class MySqlDriver {
52
+ pool;
53
+ conn;
54
+ dist;
55
+ constructor(connectionString, dist) {
56
+ this.pool = null;
57
+ this.dist = dist || null;
58
+ this.conn = this.parseConnectionString(connectionString);
59
+ }
60
+ connection() {
61
+ return this.conn;
62
+ }
63
+ parseConnectionString = (connectionString) => {
64
+ const regex = /mysql:\/\/(?<user>[^:]+):(?<password>[^@]+)@(?<host>[^:/]+)(?::(?<port>\d+))?\/(?<database>[^?]+)(?:\?(?<params>.+))?/;
65
+ const match = connectionString.match(regex);
66
+ if (!match || !match.groups) {
67
+ throw new Error("Invalid MySQL connection string");
68
+ }
69
+ const { user, password, host, port, database, params } = match.groups;
70
+ const queryParams = params ? Object.fromEntries(new URLSearchParams(params)) : {};
71
+ return { user, password, host, port: port ? Number(port) : 3306, database, params: queryParams };
72
+ };
73
+ createPool() {
74
+ if (!this.pool) {
75
+ try {
76
+ this.pool = promise_1.default.createPool({
77
+ user: this.conn.user,
78
+ password: this.conn.password,
79
+ host: this.conn.host,
80
+ port: this.conn.port,
81
+ database: this.conn.database,
82
+ ...this.conn.params
83
+ });
84
+ this.pool.getConnection()
85
+ .then((connection) => {
86
+ console.log(picocolors_1.default.green("✓ MySQL Connected..."));
87
+ connection.release();
88
+ })
89
+ .catch((err) => {
90
+ console.error(picocolors_1.default.red("○ Error while connecting to your MySQL Server with following error:"), err);
91
+ process.exit(1); // Exit process if connection fails
92
+ });
93
+ }
94
+ catch (error) {
95
+ console.error(picocolors_1.default.red("○ Error while connecting to your MySQL Server with following error:"), error);
96
+ process.exit(1);
97
+ }
98
+ }
99
+ }
100
+ mapColumns(sqlType) {
101
+ const typeMap = {
102
+ "int": { tsType: "number", columnType: "int" },
103
+ "tinyint": { tsType: "boolean", columnType: "tinyint" },
104
+ "smallint": { tsType: "number", columnType: "smallint" },
105
+ "mediumint": { tsType: "number", columnType: "mediumint" },
106
+ "bigint": { tsType: "string", columnType: "bigint" }, // bigint is safer as string
107
+ "decimal": { tsType: "string", columnType: "decimal" },
108
+ "float": { tsType: "number", columnType: "float" },
109
+ "double": { tsType: "number", columnType: "double" },
110
+ "varchar": { tsType: "string", columnType: "varchar", length: 255 },
111
+ "text": { tsType: "string", columnType: "text" },
112
+ "longtext": { tsType: "string", columnType: "longtext" },
113
+ "char": { tsType: "string", columnType: "char", length: 1 },
114
+ "datetime": { tsType: "Date", columnType: "datetime" },
115
+ "timestamp": { tsType: "Date", columnType: "timestamp" },
116
+ "date": { tsType: "Date", columnType: "date" },
117
+ "time": { tsType: "string", columnType: "time" },
118
+ "json": { tsType: "any", columnType: "json" },
119
+ };
120
+ const match = sqlType.match(/^(\w+)(?:\((\d+)\))?/);
121
+ if (!match)
122
+ return { tsType: "any", columnType: "varchar", length: 255 };
123
+ const baseType = match[1].toLowerCase();
124
+ const length = match[2] ? parseInt(match[2], 10) : undefined;
125
+ return typeMap[baseType] ? { ...typeMap[baseType], length: length || typeMap[baseType].length } : { tsType: "any", columnType: "varchar", length: 255 };
126
+ }
127
+ formatDefault(value, tsType) {
128
+ if (tsType === "number")
129
+ return Number(value);
130
+ if (tsType === "boolean")
131
+ return value === "1" ? "true" : "false";
132
+ return `"${value}"`;
133
+ }
134
+ async generate() {
135
+ const self = this;
136
+ self.createPool();
137
+ //Extract Tables
138
+ console.log(picocolors_1.default.cyan("○ Extract Tables..."));
139
+ const [tables] = await self.pool.execute("SHOW TABLES");
140
+ console.log(picocolors_1.default.cyan(`○ ${tables.length} Tables Found.`));
141
+ const tableNames = tables.map((row) => Object.values(row)[0]);
142
+ console.log(picocolors_1.default.yellow(`○ Generating Models...`));
143
+ for (const tableName of tableNames) {
144
+ // Get table structure
145
+ const [columns] = await self.pool.execute(`DESCRIBE ${tableName}`);
146
+ const entityCode = [
147
+ `import { Entity, PrimaryColumn, PrimaryGeneratedColumn, Column, BaseEntity } from "typeorm";\n`,
148
+ `@Entity({ name: "${tableName}" })`,
149
+ `export class ${(0, index_js_1.toPascalCase)(tableName)} extends BaseEntity {\n`
150
+ ];
151
+ for (const column of columns) {
152
+ // const { Field, Type, Key } = column;
153
+ const { Field, Type, Key, Null, Default, Extra } = column;
154
+ const { tsType, columnType, length } = this.mapColumns(Type);
155
+ // Handle primary key
156
+ if (Key === "PRI") {
157
+ entityCode.push(Extra.includes("auto_increment") ? `\t@PrimaryGeneratedColumn()` : `\t@PrimaryColumn()`);
158
+ }
159
+ else {
160
+ let columnDecorator = `\t@Column({ type: "${columnType}"`;
161
+ if (length)
162
+ columnDecorator += `, length: ${length}`;
163
+ if (Null === "YES")
164
+ columnDecorator += `, nullable: true`;
165
+ if (Default !== null)
166
+ columnDecorator += `, default: ${this.formatDefault(Default, tsType)}`;
167
+ columnDecorator += ` })`;
168
+ entityCode.push(columnDecorator);
169
+ }
170
+ entityCode.push(`\t${Field}!: ${tsType};\n`);
171
+ }
172
+ entityCode.push(`}`);
173
+ // Write entity file
174
+ fs_1.default.writeFileSync(path_1.default.join(this.dist, `${tableName}.ts`), entityCode.join(`\n`));
175
+ }
176
+ await self.pool.end();
177
+ console.log(picocolors_1.default.green(`✓ ${tables.length} Tables Processed.`));
178
+ process.exit(1);
179
+ }
180
+ }
181
+ exports.MySqlDriver = MySqlDriver;
@@ -0,0 +1,42 @@
1
+ import { Repository, ObjectLiteral, FindOptionsWhere } from "typeorm";
2
+ import { QueryAction } from "../types";
3
+ import { QueryResult } from "mysql2";
4
+ import { QueryDeepPartialEntity } from "typeorm/query-builder/QueryPartialEntity";
5
+ declare class ZormQueryBuilder<T extends ObjectLiteral, R = QueryResult> extends Promise<R> {
6
+ private repository;
7
+ private queryBuilder;
8
+ private entityAlias;
9
+ private action;
10
+ private queryValues;
11
+ private usePromise;
12
+ private whereCount;
13
+ constructor(repository: Repository<T>, _action: QueryAction, _usePromise?: boolean);
14
+ _create(): this;
15
+ upsert(): this;
16
+ _update(): this;
17
+ _delete(): this;
18
+ _getRawQuery(): [string, any[]];
19
+ with(data: QueryDeepPartialEntity<T> | QueryDeepPartialEntity<T[]>): this;
20
+ select(fields: (keyof T)[]): this;
21
+ where(condition: FindOptionsWhere<T>): this;
22
+ or(condition: FindOptionsWhere<T>): this;
23
+ orderBy(field: keyof T, direction?: "ASC" | "DESC"): this;
24
+ limit(n: number): this;
25
+ offset(n: number): this;
26
+ relations(relations: string[]): this;
27
+ innerJoin(relation: string, alias: string, condition?: string): this;
28
+ leftJoin(relation: string, alias: string, condition?: string): this;
29
+ groupBy(field: keyof T): this;
30
+ having(condition: string): this;
31
+ in(field: keyof T, values: any[]): this;
32
+ distinct(): this;
33
+ count(): Promise<number>;
34
+ sum(field: keyof T): Promise<number>;
35
+ avg(field: keyof T): Promise<number>;
36
+ min(field: keyof T): Promise<number>;
37
+ max(field: keyof T): Promise<number>;
38
+ rawQuery(sql: string, params?: any): Promise<any>;
39
+ execute(): Promise<R>;
40
+ then<TResult1 = R, TResult2 = never>(onfulfilled?: ((value: R) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
41
+ }
42
+ export default ZormQueryBuilder;
@@ -0,0 +1,224 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const index_js_1 = require("./mysql/index.js");
4
+ const core_1 = require("../core");
5
+ class ZormQueryBuilder extends Promise {
6
+ repository;
7
+ queryBuilder;
8
+ entityAlias;
9
+ action;
10
+ queryValues = null;
11
+ usePromise;
12
+ whereCount = 0;
13
+ constructor(repository, _action, _usePromise) {
14
+ super(() => { }); // Required for extending Promise
15
+ this.repository = repository;
16
+ this.entityAlias = repository.metadata.tableName;
17
+ this.queryBuilder = repository.createQueryBuilder(this.entityAlias);
18
+ this.action = _action;
19
+ this.usePromise = _usePromise || false;
20
+ }
21
+ _create() {
22
+ if (this.queryValues) {
23
+ this.queryBuilder = this.queryBuilder
24
+ .insert()
25
+ .into(this.entityAlias)
26
+ .values(this.queryValues);
27
+ }
28
+ else
29
+ throw (0, core_1.stackTrace)(`○ Values are missing. You forgot to call .with({ key: value })`);
30
+ return this;
31
+ }
32
+ upsert() {
33
+ if (this.queryValues) {
34
+ this.queryBuilder = this.repository.createQueryBuilder(this.entityAlias)
35
+ .insert()
36
+ .into(this.entityAlias)
37
+ .values(this.queryValues)
38
+ .orUpdate(Object.keys(this.queryValues));
39
+ }
40
+ else
41
+ throw (0, core_1.stackTrace)(`○ Values are missing. You forgot to call .with({ key: value })`);
42
+ return this;
43
+ }
44
+ _update() {
45
+ if (this.queryValues) {
46
+ if (this.whereCount > 0) {
47
+ this.queryBuilder = this.queryBuilder
48
+ .update()
49
+ .set(this.queryValues);
50
+ }
51
+ else {
52
+ throw (0, core_1.stackTrace)(`○ Update must have at least one WHERE condition. You forgot to call .where({ condition: value })`);
53
+ }
54
+ }
55
+ else
56
+ throw (0, core_1.stackTrace)(`○ Values are missing. You forgot to call .with({ key: value })`);
57
+ return this;
58
+ }
59
+ _delete() {
60
+ this.queryBuilder = this.queryBuilder.delete();
61
+ return this;
62
+ }
63
+ _getRawQuery() {
64
+ return this.queryBuilder.getQueryAndParameters();
65
+ }
66
+ with(data) {
67
+ this.queryValues = data;
68
+ return this;
69
+ }
70
+ select(fields) {
71
+ this.queryBuilder
72
+ .select(fields.map(field => `${this.entityAlias}.${String(field)}`));
73
+ return this;
74
+ }
75
+ where(condition) {
76
+ this.queryBuilder.where(condition);
77
+ this.whereCount++;
78
+ return this;
79
+ }
80
+ or(condition) {
81
+ this.queryBuilder.orWhere(condition);
82
+ return this;
83
+ }
84
+ orderBy(field, direction = "ASC") {
85
+ this.queryBuilder.orderBy(`${this.entityAlias}.${String(field)}`, direction);
86
+ return this;
87
+ }
88
+ limit(n) {
89
+ this.queryBuilder.limit(n);
90
+ return this;
91
+ }
92
+ offset(n) {
93
+ this.queryBuilder.offset(n);
94
+ return this;
95
+ }
96
+ relations(relations) {
97
+ relations.forEach(relation => this.queryBuilder.leftJoinAndSelect(`${this.entityAlias}.${relation}`, relation));
98
+ return this;
99
+ }
100
+ innerJoin(relation, alias, condition) {
101
+ if (condition) {
102
+ this.queryBuilder.innerJoin(`${this.entityAlias}.${relation}`, alias, condition);
103
+ }
104
+ else {
105
+ this.queryBuilder.innerJoin(`${this.entityAlias}.${relation}`, alias);
106
+ }
107
+ return this;
108
+ }
109
+ leftJoin(relation, alias, condition) {
110
+ if (condition) {
111
+ this.queryBuilder.leftJoin(`${this.entityAlias}.${relation}`, alias, condition);
112
+ }
113
+ else {
114
+ this.queryBuilder.leftJoin(`${this.entityAlias}.${relation}`, alias);
115
+ }
116
+ return this;
117
+ }
118
+ groupBy(field) {
119
+ this.queryBuilder.groupBy(`${this.entityAlias}.${String(field)}`);
120
+ return this;
121
+ }
122
+ having(condition) {
123
+ this.queryBuilder.having(condition);
124
+ return this;
125
+ }
126
+ in(field, values) {
127
+ this.queryBuilder.andWhere(`${this.entityAlias}.${String(field)} IN (:...values)`, { values });
128
+ return this;
129
+ }
130
+ distinct() {
131
+ this.queryBuilder.distinct(true);
132
+ return this;
133
+ }
134
+ async count() {
135
+ return await this.queryBuilder.getCount();
136
+ }
137
+ async sum(field) {
138
+ const result = await this.queryBuilder.select(`SUM(${this.entityAlias}.${String(field)})`, "sum").getRawOne();
139
+ return result.sum || 0;
140
+ }
141
+ async avg(field) {
142
+ const result = await this.queryBuilder.select(`AVG(${this.entityAlias}.${String(field)})`, "avg").getRawOne();
143
+ return result.avg || 0;
144
+ }
145
+ async min(field) {
146
+ const result = await this.queryBuilder.select(`MIN(${this.entityAlias}.${String(field)})`, "min").getRawOne();
147
+ return result.min || 0;
148
+ }
149
+ async max(field) {
150
+ const result = await this.queryBuilder.select(`MAX(${this.entityAlias}.${String(field)})`, "max").getRawOne();
151
+ return result.max || 0;
152
+ }
153
+ async rawQuery(sql, params) {
154
+ return await this.repository.query(sql, params);
155
+ }
156
+ async execute() {
157
+ try {
158
+ switch (this.action) {
159
+ case "upsert":
160
+ case "create":
161
+ this._create();
162
+ const _create = await this.queryBuilder.execute();
163
+ // console.log(_create)
164
+ return {
165
+ created: true,
166
+ id: _create.raw.insertId,
167
+ record: _create.generatedMaps[0],
168
+ records: _create.generatedMaps.length > 1 ? _create.generatedMaps : null
169
+ };
170
+ case "update":
171
+ this._update();
172
+ const _update = await this.queryBuilder.execute();
173
+ // console.log(`updated`, _update)
174
+ return { updated: true };
175
+ case "delete":
176
+ this._delete();
177
+ const _delete = await this.queryBuilder.execute();
178
+ return { deleted: true, count: _delete.affected || 0 };
179
+ case "select":
180
+ default:
181
+ const _select = await this.queryBuilder.getMany();
182
+ return {
183
+ hasRows: true,
184
+ count: _select.length,
185
+ row: _select[0],
186
+ rows: _select
187
+ };
188
+ }
189
+ }
190
+ catch (err) {
191
+ const _e = err;
192
+ const error = {
193
+ code: index_js_1.MySQLErrorMap[_e.code] || _e.code,
194
+ message: _e.message,
195
+ query: _e.query, // The SQL query that caused the error
196
+ values: _e.parameters, // Parameters used in the query
197
+ };
198
+ switch (this.action) {
199
+ case "upsert":
200
+ case "create":
201
+ const _c = {
202
+ created: false,
203
+ id: 0,
204
+ error
205
+ };
206
+ return this.usePromise ? Promise.reject(_c) : _c;
207
+ case "update":
208
+ const _u = { updated: false, error };
209
+ return this.usePromise ? Promise.reject(_u) : _u;
210
+ case "delete":
211
+ const _d = { deleted: false, error };
212
+ return this.usePromise ? Promise.reject(_d) : _d;
213
+ case "select":
214
+ default:
215
+ const _s = { hasRows: false, count: 0, row: null, rows: [], error };
216
+ return this.usePromise ? Promise.reject(_s) : _s;
217
+ }
218
+ }
219
+ }
220
+ then(onfulfilled, onrejected) {
221
+ return this.execute().then(onfulfilled, onrejected);
222
+ }
223
+ }
224
+ exports.default = ZormQueryBuilder;
@@ -0,0 +1,25 @@
1
+ import { EntitySchema, EntityTarget, MixedList, ObjectLiteral, Repository } from "typeorm";
2
+ import ZormQueryBuilder from "./drivers/queryBuilder.js";
3
+ import { InsertQueryResult, SelectQueryResult, UpdateQueryResult } from "./types.js";
4
+ declare class Zorm {
5
+ private static instance;
6
+ private dataSource;
7
+ private initialized;
8
+ private usePromise;
9
+ private constructor();
10
+ static get(connectionString: string, entitiesPath?: string | null, usePromise?: boolean): Zorm;
11
+ connect(entities: MixedList<string | Function | EntitySchema<any>>): Promise<void>;
12
+ /**
13
+ * Returns the appropriate QueryBuilder based on the database type.
14
+ */
15
+ private getQueryBuilder;
16
+ /**
17
+ * Get a repository for a specific entity
18
+ */
19
+ getRepository<T extends ObjectLiteral>(entity: EntityTarget<T>): Repository<T>;
20
+ create<T extends ObjectLiteral>(entity: EntityTarget<T>): ZormQueryBuilder<T, InsertQueryResult>;
21
+ find<T extends ObjectLiteral>(entity: EntityTarget<T>): ZormQueryBuilder<T, SelectQueryResult>;
22
+ update<T extends ObjectLiteral>(entity: EntityTarget<T>): ZormQueryBuilder<T, UpdateQueryResult>;
23
+ delete<T extends ObjectLiteral>(entity: EntityTarget<T>): ZormQueryBuilder<T, UpdateQueryResult>;
24
+ }
25
+ export default Zorm;
package/dist/index.js ADDED
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const path_1 = __importDefault(require("path"));
7
+ const typeorm_1 = require("typeorm");
8
+ const index_js_1 = require("./core/index.js");
9
+ const picocolors_1 = __importDefault(require("picocolors"));
10
+ const index_js_2 = require("./drivers/mysql/index.js");
11
+ const queryBuilder_js_1 = __importDefault(require("./drivers/queryBuilder.js"));
12
+ class Zorm {
13
+ static instance;
14
+ dataSource;
15
+ initialized = false;
16
+ usePromise = false;
17
+ constructor(connectionString, entitiesPath, usePromise) {
18
+ const _dist = entitiesPath || path_1.default.join(`src`, `zorm`);
19
+ const dist = path_1.default.join(process.cwd(), _dist);
20
+ const _checkDist = (0, index_js_1.checkDirectory)(dist, false);
21
+ this.usePromise = usePromise || false;
22
+ if (!_checkDist.access) {
23
+ console.log(picocolors_1.default.red(`○ ${_checkDist.message}`));
24
+ return;
25
+ }
26
+ if (connectionString.startsWith(`mysql`)) {
27
+ const driver = new index_js_2.MySqlDriver(decodeURIComponent(connectionString));
28
+ const conn = driver.connection();
29
+ this.dataSource = new typeorm_1.DataSource({
30
+ type: "mysql",
31
+ username: conn.user,
32
+ password: conn.password,
33
+ host: conn.host,
34
+ port: Number(conn.port),
35
+ database: conn.database
36
+ });
37
+ }
38
+ else {
39
+ console.log(`Only MySQL is supported for now`);
40
+ process.exit(1);
41
+ }
42
+ }
43
+ static get(connectionString, entitiesPath, usePromise) {
44
+ if (!Zorm.instance) {
45
+ Zorm.instance = new Zorm(connectionString, entitiesPath, usePromise);
46
+ }
47
+ return Zorm.instance;
48
+ }
49
+ async connect(entities) {
50
+ if (!this.initialized) {
51
+ try {
52
+ this.dataSource.setOptions({ entities });
53
+ await this.dataSource.initialize();
54
+ this.initialized = true;
55
+ console.log(picocolors_1.default.green("○ Zorm is connected"));
56
+ }
57
+ catch (e) {
58
+ console.log(picocolors_1.default.red("○ Error while connecting to your MySQL Server with following error:"), e);
59
+ }
60
+ }
61
+ }
62
+ /**
63
+ * Returns the appropriate QueryBuilder based on the database type.
64
+ */
65
+ getQueryBuilder(entity, action) {
66
+ const repository = this.getRepository(entity);
67
+ switch (this.dataSource.options.type) {
68
+ case "mysql":
69
+ case "mariadb":
70
+ case "postgres":
71
+ case "sqlite":
72
+ case "mssql":
73
+ case "oracle":
74
+ return new queryBuilder_js_1.default(repository, action, this.usePromise);
75
+ case "mongodb":
76
+ throw new Error("MongoDB does not support QueryBuilder. Use repository methods instead.");
77
+ default:
78
+ throw new Error(`Unsupported database type: ${this.dataSource.options.type}`);
79
+ }
80
+ }
81
+ /**
82
+ * Get a repository for a specific entity
83
+ */
84
+ getRepository(entity) {
85
+ return this.dataSource.getRepository(entity);
86
+ }
87
+ create(entity) {
88
+ return this.getQueryBuilder(entity, "create");
89
+ }
90
+ find(entity) {
91
+ return this.getQueryBuilder(entity, "select");
92
+ }
93
+ // upsert<T extends ObjectLiteral>(entity: EntityTarget<T>): ZormQueryBuilder<T, InsertQueryResult> {
94
+ // return this.getQueryBuilder(entity, "upsert");
95
+ // }
96
+ update(entity) {
97
+ return this.getQueryBuilder(entity, "update");
98
+ }
99
+ delete(entity) {
100
+ return this.getQueryBuilder(entity, "delete");
101
+ }
102
+ }
103
+ exports.default = Zorm;
@@ -0,0 +1,48 @@
1
+ import { ObjectLiteral } from "typeorm";
2
+ export type dynamicObject = {
3
+ [x: string]: any;
4
+ };
5
+ export interface ModelGenerator {
6
+ generate: () => void;
7
+ connection: () => ConnectionDetails;
8
+ mapColumns: (sqlType: string) => void;
9
+ }
10
+ export type ConnectionDetails = {
11
+ host: string;
12
+ port: string | number;
13
+ user: string;
14
+ password: string;
15
+ database: string;
16
+ params: dynamicObject;
17
+ };
18
+ export type QueryAction = "create" | "upsert" | "select" | "update" | "delete";
19
+ export type QueryResult = InsertQueryResult | SelectQueryResult | UpdateQueryResult | DeleteQueryResult;
20
+ export type QueryError = {
21
+ code: number | string;
22
+ message: string;
23
+ query: string;
24
+ values: string[];
25
+ };
26
+ export type InsertQueryResult = {
27
+ created: boolean;
28
+ id?: number;
29
+ record?: ObjectLiteral;
30
+ records?: ObjectLiteral[];
31
+ error?: QueryError;
32
+ };
33
+ export type SelectQueryResult = {
34
+ hasRows: boolean;
35
+ count?: number;
36
+ row?: any;
37
+ rows?: any[];
38
+ error?: QueryError;
39
+ };
40
+ export type UpdateQueryResult = {
41
+ updated: boolean;
42
+ error?: QueryError;
43
+ };
44
+ export type DeleteQueryResult = {
45
+ deleted: boolean;
46
+ count: number;
47
+ error?: QueryError;
48
+ };
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@zuzjs/orm",
3
+ "version": "0.1.1",
4
+ "keywords": [
5
+ "orm",
6
+ "zuz",
7
+ "zuz.js",
8
+ "zuz orm",
9
+ "zuzjs"
10
+ ],
11
+ "description": "ZuzJS ORM Library",
12
+ "author": "Zuz.js Team <support@zuz.com.pk>",
13
+ "license": "MIT",
14
+ "type": "commonjs",
15
+ "main": "dist/index.js",
16
+ "bin": {
17
+ "zorm": "dist/bin.js"
18
+ },
19
+ "exports": {
20
+ ".": "./dist/index.js"
21
+ },
22
+ "files": [
23
+ "dist"
24
+ ],
25
+ "scripts": {
26
+ "dev": "tsc -d -w -p tsconfig.json"
27
+ },
28
+ "engines": {
29
+ "node": ">=18.17.0"
30
+ },
31
+ "dependencies": {
32
+ "@types/md5": "^2.3.5",
33
+ "commander": "^13.1.0",
34
+ "md5": "^2.3.0",
35
+ "mysql2": "^3.12.0",
36
+ "nanoid": "^5.1.0",
37
+ "picocolors": "^1.1.1",
38
+ "reflect-metadata": "^0.2.2",
39
+ "typeorm": "^0.3.20"
40
+ },
41
+ "devDependencies": {
42
+ "husky": "^9.1.7",
43
+ "ts-node": "^10.9.2",
44
+ "typescript": "^5.7.3"
45
+ },
46
+ "peerDependencies": {
47
+ "reflect-metadata": "^0.2.2",
48
+ "typeorm": "^0.3.0"
49
+ }
50
+ }