@zuzjs/orm 0.3.7 → 0.3.8
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.cjs +4 -0
- package/dist/bin.d.cts +1 -0
- package/dist/bin.d.ts +0 -1
- package/dist/bin.js +2 -25
- package/dist/chunk-3TCXY5YG.js +16 -0
- package/dist/chunk-ZWZYE56D.cjs +17 -0
- package/dist/index.cjs +2 -0
- package/dist/index.d.cts +386 -0
- package/dist/index.d.ts +298 -6
- package/dist/index.js +1 -232
- package/package.json +56 -48
- package/dist/core/cli.d.ts +0 -5
- package/dist/core/cli.js +0 -78
- package/dist/core/index.d.ts +0 -8
- package/dist/core/index.js +0 -72
- package/dist/drivers/expressionBuilder.d.ts +0 -32
- package/dist/drivers/expressionBuilder.js +0 -100
- package/dist/drivers/mysql/index.d.ts +0 -39
- package/dist/drivers/mysql/index.js +0 -336
- package/dist/drivers/queryBuilder.d.ts +0 -162
- package/dist/drivers/queryBuilder.js +0 -518
- package/dist/types.d.ts +0 -104
- package/dist/types.js +0 -14
package/dist/core/cli.js
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.Pull = exports.buildEntities = void 0;
|
|
40
|
-
const fs_1 = __importStar(require("fs"));
|
|
41
|
-
const path_1 = __importDefault(require("path"));
|
|
42
|
-
const picocolors_1 = __importDefault(require("picocolors"));
|
|
43
|
-
const index_js_1 = require("./index.js");
|
|
44
|
-
const mysql_1 = require("../drivers/mysql");
|
|
45
|
-
const buildEntities = (connection, dist) => {
|
|
46
|
-
dist = dist || path_1.default.join(`src`, `zorm`);
|
|
47
|
-
const _checkDist = (0, index_js_1.checkDirectory)(path_1.default.join(process.cwd(), dist), true);
|
|
48
|
-
if (!_checkDist.access) {
|
|
49
|
-
console.log(picocolors_1.default.red(`○ ${_checkDist.message}`));
|
|
50
|
-
process.exit(1);
|
|
51
|
-
}
|
|
52
|
-
if (connection.startsWith(`mysql`)) {
|
|
53
|
-
const driver = new mysql_1.MySqlDriver(decodeURIComponent(connection), dist);
|
|
54
|
-
driver.generate();
|
|
55
|
-
}
|
|
56
|
-
else {
|
|
57
|
-
console.log(`○ Only MySQL is supported for now`);
|
|
58
|
-
process.exit(1);
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
exports.buildEntities = buildEntities;
|
|
62
|
-
/**
|
|
63
|
-
* Pull using DATABASE_URL from .env in project directory
|
|
64
|
-
**/
|
|
65
|
-
const Pull = () => {
|
|
66
|
-
const env = path_1.default.join(process.cwd(), `.env`);
|
|
67
|
-
if (!(0, fs_1.existsSync)(env)) {
|
|
68
|
-
console.log(picocolors_1.default.red(`○ ".env" not exists. Create .env and add DATABASE_URL="connection_string"`));
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
const raw = fs_1.default.readFileSync(env, `utf8`).split(`\n`).filter((line) => line.startsWith(`DATABASE_URL`));
|
|
72
|
-
if (raw.length == 0) {
|
|
73
|
-
console.log(picocolors_1.default.red(`○ DATABASE_URL not found in ".env". Add DATABASE_URL="connection_string"`));
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
(0, exports.buildEntities)(raw[0].trim().replace(/DATABASE_URL=|"|"/g, ``));
|
|
77
|
-
};
|
|
78
|
-
exports.Pull = Pull;
|
package/dist/core/index.d.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
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;
|
|
8
|
-
export declare const isNumber: (val: any) => boolean;
|
package/dist/core/index.js
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
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.isNumber = 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;
|
|
71
|
-
const isNumber = (val) => /^[+-]?\d+(\.\d+)?$/.test(val);
|
|
72
|
-
exports.isNumber = isNumber;
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { ObjectLiteral } from "typeorm";
|
|
2
|
-
declare class ZormExprBuilder<T extends ObjectLiteral> {
|
|
3
|
-
private _parts;
|
|
4
|
-
private _params;
|
|
5
|
-
private _paramIdx;
|
|
6
|
-
private _alias;
|
|
7
|
-
private static globalParamIdx;
|
|
8
|
-
constructor(alias?: string, parent?: {
|
|
9
|
-
params: Record<string, any>;
|
|
10
|
-
idx: number;
|
|
11
|
-
});
|
|
12
|
-
field(col: keyof T | string): this;
|
|
13
|
-
equals(value: any): this;
|
|
14
|
-
append(extra: string): this;
|
|
15
|
-
wrap(wrapper: (expr: string) => string): this;
|
|
16
|
-
exists(sub: (q: ZormExprBuilder<T>) => ZormExprBuilder<T>): this;
|
|
17
|
-
select(expr: string): this;
|
|
18
|
-
from(table: string, alias: string): this;
|
|
19
|
-
where(cond: Record<string, any>): this;
|
|
20
|
-
or(): this;
|
|
21
|
-
group(): this;
|
|
22
|
-
fromUnixTime(): this;
|
|
23
|
-
date(): this;
|
|
24
|
-
substring(column: keyof T | string, delimiter: string, index: number): this;
|
|
25
|
-
toExpression(): {
|
|
26
|
-
expression: string;
|
|
27
|
-
param: Record<string, any>;
|
|
28
|
-
};
|
|
29
|
-
buildExpression(): string;
|
|
30
|
-
buildParams(): Record<string, any>;
|
|
31
|
-
}
|
|
32
|
-
export default ZormExprBuilder;
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
class ZormExprBuilder {
|
|
4
|
-
_parts = [];
|
|
5
|
-
_params = {};
|
|
6
|
-
_paramIdx = 0;
|
|
7
|
-
_alias;
|
|
8
|
-
static globalParamIdx = 0;
|
|
9
|
-
constructor(alias, parent) {
|
|
10
|
-
this._alias = alias ? `${alias}.` : ``;
|
|
11
|
-
this._paramIdx = parent ? parent.idx : 0;
|
|
12
|
-
this._params = parent ? parent.params : {};
|
|
13
|
-
}
|
|
14
|
-
field(col) {
|
|
15
|
-
this._parts.push(`${this._alias}${String(col)}`);
|
|
16
|
-
return this;
|
|
17
|
-
}
|
|
18
|
-
equals(value) {
|
|
19
|
-
const key = `p${this._paramIdx++}`;
|
|
20
|
-
this.append(` = :${key}`);
|
|
21
|
-
// this._parts[this._parts.length - 1] += ` = :${key}`;
|
|
22
|
-
this._params[key] = value;
|
|
23
|
-
return this;
|
|
24
|
-
}
|
|
25
|
-
append(extra) {
|
|
26
|
-
if (this._parts.length === 0)
|
|
27
|
-
throw new Error("Cannot append to empty expression");
|
|
28
|
-
this._parts[this._parts.length - 1] += extra;
|
|
29
|
-
return this;
|
|
30
|
-
}
|
|
31
|
-
wrap(wrapper) {
|
|
32
|
-
if (this._parts.length === 0)
|
|
33
|
-
throw new Error("Cannot wrap empty expression");
|
|
34
|
-
this._parts[this._parts.length - 1] = wrapper(this._parts[this._parts.length - 1]);
|
|
35
|
-
return this;
|
|
36
|
-
}
|
|
37
|
-
exists(sub) {
|
|
38
|
-
const subQ = new ZormExprBuilder(undefined, { params: this._params, idx: this._paramIdx });
|
|
39
|
-
sub(subQ);
|
|
40
|
-
// const { expression, param } = subQ.toExpression();
|
|
41
|
-
this._parts.push(`EXISTS (${subQ.buildExpression()})`);
|
|
42
|
-
// Object.assign(this._params, param);
|
|
43
|
-
this._paramIdx = subQ._paramIdx;
|
|
44
|
-
return this;
|
|
45
|
-
}
|
|
46
|
-
select(expr) {
|
|
47
|
-
this._parts.push(`SELECT ${expr}`);
|
|
48
|
-
return this;
|
|
49
|
-
}
|
|
50
|
-
from(table, alias) {
|
|
51
|
-
this._parts.push(`FROM ${table} ${alias}`);
|
|
52
|
-
this._alias = alias;
|
|
53
|
-
return this;
|
|
54
|
-
}
|
|
55
|
-
where(cond) {
|
|
56
|
-
const whereParts = [];
|
|
57
|
-
for (const [k, v] of Object.entries(cond)) {
|
|
58
|
-
const key = `p${this._paramIdx++}`;
|
|
59
|
-
whereParts.push(`${k} = :${key}`);
|
|
60
|
-
this._params[key] = v;
|
|
61
|
-
}
|
|
62
|
-
this._parts.push(`WHERE ${whereParts.join(' AND ')}`);
|
|
63
|
-
return this;
|
|
64
|
-
}
|
|
65
|
-
// or(sub: ZormExprBuilder<any> | { expression: string; param: Record<string, any> }): this {
|
|
66
|
-
// const { expression, param } = 'toExpression' in sub ? sub.toExpression() : sub;
|
|
67
|
-
// this._parts.push(this._parts.length === 0 ? expression : `OR ${expression}`);
|
|
68
|
-
// Object.assign(this._params, param);
|
|
69
|
-
// return this;
|
|
70
|
-
// }
|
|
71
|
-
or() {
|
|
72
|
-
this._parts.push(`OR`);
|
|
73
|
-
return this;
|
|
74
|
-
}
|
|
75
|
-
group() {
|
|
76
|
-
return this.wrap(e => `(${e})`);
|
|
77
|
-
}
|
|
78
|
-
fromUnixTime() {
|
|
79
|
-
this.wrap(expr => `FROM_UNIXTIME(${expr})`);
|
|
80
|
-
return this;
|
|
81
|
-
}
|
|
82
|
-
date() {
|
|
83
|
-
this.wrap(expr => `DATE(${expr})`);
|
|
84
|
-
return this;
|
|
85
|
-
}
|
|
86
|
-
substring(column, delimiter, index) {
|
|
87
|
-
this._parts = [`SUBSTRING_INDEX(${this._alias}${String(column)}, '${delimiter}', ${index})`];
|
|
88
|
-
return this;
|
|
89
|
-
}
|
|
90
|
-
toExpression() {
|
|
91
|
-
return { expression: this._parts.join(' '), param: this._params };
|
|
92
|
-
}
|
|
93
|
-
buildExpression() {
|
|
94
|
-
return this.toExpression().expression;
|
|
95
|
-
}
|
|
96
|
-
buildParams() {
|
|
97
|
-
return this.toExpression().param;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
exports.default = ZormExprBuilder;
|
|
@@ -1,39 +0,0 @@
|
|
|
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
|
-
enumValues?: string[];
|
|
36
|
-
};
|
|
37
|
-
formatDefault(value: any, tsType: string): string | number;
|
|
38
|
-
generate(): Promise<void>;
|
|
39
|
-
}
|
|
@@ -1,336 +0,0 @@
|
|
|
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 fs_1 = __importDefault(require("fs"));
|
|
8
|
-
const promise_1 = __importDefault(require("mysql2/promise"));
|
|
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: "number", columnType: "bigint" }, // bigint is safer as string
|
|
107
|
-
"decimal": { tsType: "number", 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 enumMatch = sqlType.match(/^enum\((.*)\)$/i);
|
|
121
|
-
if (enumMatch) {
|
|
122
|
-
const enumValues = enumMatch[1]
|
|
123
|
-
.split(",")
|
|
124
|
-
.map((val) => val.trim().replace(/^'|'$/g, "")); // Remove single quotes
|
|
125
|
-
return { tsType: `"${enumValues.join('" | "')}"`, columnType: "enum", enumValues };
|
|
126
|
-
}
|
|
127
|
-
const match = sqlType.match(/^(\w+)(?:\((\d+)\))?/);
|
|
128
|
-
if (!match)
|
|
129
|
-
return { tsType: "any", columnType: "varchar", length: 255 };
|
|
130
|
-
const baseType = match[1].toLowerCase();
|
|
131
|
-
const length = match[2] ? parseInt(match[2], 10) : undefined;
|
|
132
|
-
return typeMap[baseType] ? { ...typeMap[baseType], length: length || typeMap[baseType].length } : { tsType: "any", columnType: "varchar", length: 255 };
|
|
133
|
-
}
|
|
134
|
-
formatDefault(value, tsType) {
|
|
135
|
-
if (tsType === "number")
|
|
136
|
-
return Number(value);
|
|
137
|
-
if (tsType === "boolean")
|
|
138
|
-
return value === "1" ? "true" : "false";
|
|
139
|
-
return `"${value}"`;
|
|
140
|
-
}
|
|
141
|
-
async generate() {
|
|
142
|
-
const self = this;
|
|
143
|
-
self.createPool();
|
|
144
|
-
const numberTypes = [`int`, `bigint`, `tinyint`, `decimal`];
|
|
145
|
-
//Extract Tables
|
|
146
|
-
console.log(picocolors_1.default.cyan("○ Extract Tables..."));
|
|
147
|
-
const [tables] = await self.pool.execute("SHOW TABLES");
|
|
148
|
-
console.log(picocolors_1.default.cyan(`○ ${tables.length} Tables Found.`));
|
|
149
|
-
const tableNames = tables.map((row) => Object.values(row)[0]);
|
|
150
|
-
console.log(picocolors_1.default.yellow(`○ Generating Models...`));
|
|
151
|
-
// Fetch foreign keys
|
|
152
|
-
const foreignKeys = {};
|
|
153
|
-
for (const tableName of tableNames) {
|
|
154
|
-
const [fkResults] = await this.pool.execute(`SELECT COLUMN_NAME, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME
|
|
155
|
-
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
|
|
156
|
-
WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? AND REFERENCED_TABLE_NAME IS NOT NULL`, [this.conn.database, tableName]);
|
|
157
|
-
foreignKeys[tableName] = fkResults;
|
|
158
|
-
}
|
|
159
|
-
// Track inverse relations: { tableName: { fkColumn: targetTable } }
|
|
160
|
-
const inverseRelations = {};
|
|
161
|
-
// Populate inverse map
|
|
162
|
-
for (const [tableName, fks] of Object.entries(foreignKeys)) {
|
|
163
|
-
for (const fk of fks) {
|
|
164
|
-
const targetTable = fk.REFERENCED_TABLE_NAME;
|
|
165
|
-
const fkColumn = fk.COLUMN_NAME;
|
|
166
|
-
const fkPropName = `fk${(0, index_js_1.toPascalCase)(targetTable)}`;
|
|
167
|
-
if (!inverseRelations[targetTable]) {
|
|
168
|
-
inverseRelations[targetTable] = [];
|
|
169
|
-
}
|
|
170
|
-
inverseRelations[targetTable].push({
|
|
171
|
-
fkColumn,
|
|
172
|
-
fkPropName,
|
|
173
|
-
targetTable: tableName,
|
|
174
|
-
targetEntity: (0, index_js_1.toPascalCase)(tableName),
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
// Detect 2 Column Junction Tables for ManyToMany
|
|
179
|
-
const junctionTables = {};
|
|
180
|
-
for (const tableName of tableNames) {
|
|
181
|
-
const [cols] = await this.pool.execute(`SELECT COLUMN_NAME, COLUMN_KEY, EXTRA FROM INFORMATION_SCHEMA.COLUMNS
|
|
182
|
-
WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?`, [this.conn.database, tableName]);
|
|
183
|
-
const fks = foreignKeys[String(tableName)] || [];
|
|
184
|
-
if (fks.length !== 2)
|
|
185
|
-
continue;
|
|
186
|
-
// Must have exactly 2 columns
|
|
187
|
-
if (cols.length !== 2)
|
|
188
|
-
continue;
|
|
189
|
-
// Both columns must be primary key
|
|
190
|
-
if (cols.filter(c => c.COLUMN_KEY !== 'PRI').length > 0)
|
|
191
|
-
continue;
|
|
192
|
-
const [left, right] = fks;
|
|
193
|
-
const [t1, t2] = [left.REFERENCED_TABLE_NAME, right.REFERENCED_TABLE_NAME].sort();
|
|
194
|
-
const key = `${t1}_${t2}`;
|
|
195
|
-
if (!junctionTables[key]) {
|
|
196
|
-
junctionTables[key] = {
|
|
197
|
-
left: t1,
|
|
198
|
-
right: t2,
|
|
199
|
-
leftCol: left.COLUMN_NAME,
|
|
200
|
-
rightCol: right.COLUMN_NAME
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
for (const tableName of tableNames) {
|
|
205
|
-
const enums = [];
|
|
206
|
-
const imports = [];
|
|
207
|
-
const _imports = [`Entity`, `BaseEntity`];
|
|
208
|
-
// Get table structure
|
|
209
|
-
const [columns] = await this.pool.execute(`SELECT COLUMN_NAME as \`Field\`, COLUMN_TYPE as \`Type\`, COLUMN_KEY as \`Key\`, IS_NULLABLE as \`Null\`, COLUMN_DEFAULT as \`Default\`, EXTRA as \`Extra\`, COLUMN_COMMENT as \`Comment\`
|
|
210
|
-
FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? ORDER BY ORDINAL_POSITION ASC`, [this.conn.database, tableName]);
|
|
211
|
-
const entityCode = [];
|
|
212
|
-
let hasPrimary = false;
|
|
213
|
-
for (const column of columns) {
|
|
214
|
-
// console.log(tableName, column)
|
|
215
|
-
const { Field, Type, Key, Null, Default, Extra, Comment } = column;
|
|
216
|
-
const { tsType, columnType, length, enumValues } = this.mapColumns(Type);
|
|
217
|
-
let enumName = null;
|
|
218
|
-
if (columnType === "enum" && enumValues) {
|
|
219
|
-
enumName = (0, index_js_1.toPascalCase)(Field);
|
|
220
|
-
enums.push(`export enum ${enumName} { ${enumValues.map(v => {
|
|
221
|
-
return `${(0, index_js_1.isNumber)(v) ? `val${v}` : (0, index_js_1.toPascalCase)(v)} = "${v}"`;
|
|
222
|
-
}).join(", ")} }`);
|
|
223
|
-
}
|
|
224
|
-
// Handle primary key
|
|
225
|
-
if (Key === "PRI") {
|
|
226
|
-
const _priColumn = Extra.includes("auto_increment") ? `PrimaryGeneratedColumn` : `PrimaryColumn`;
|
|
227
|
-
if (!_imports.includes(_priColumn))
|
|
228
|
-
_imports.push(_priColumn);
|
|
229
|
-
entityCode.push(`\t@${_priColumn}()`);
|
|
230
|
-
hasPrimary = true;
|
|
231
|
-
}
|
|
232
|
-
else {
|
|
233
|
-
// const hasForeignKey = foreignKeys[tableName as string].find((fk) => fk.COLUMN_NAME === Field);
|
|
234
|
-
// let columnDecorator = hasForeignKey ? `\t@JoinColumn({ type: "${columnType}"` : `\t@Column({ type: "${columnType}"`;
|
|
235
|
-
let columnDecorator = `\t@Column({ type: "${columnType}"`;
|
|
236
|
-
if (!_imports.includes(`Column`))
|
|
237
|
-
_imports.push(`Column`);
|
|
238
|
-
if (length)
|
|
239
|
-
columnDecorator += `, length: ${length}`;
|
|
240
|
-
if (Null === "YES")
|
|
241
|
-
columnDecorator += `, nullable: true`;
|
|
242
|
-
if (enumName)
|
|
243
|
-
columnDecorator += `, enum: ${enumName}`;
|
|
244
|
-
if (Default !== null)
|
|
245
|
-
columnDecorator += `, default: ${this.formatDefault(Default, tsType)}`;
|
|
246
|
-
columnDecorator += ` })`;
|
|
247
|
-
entityCode.push(columnDecorator);
|
|
248
|
-
}
|
|
249
|
-
if (Comment && Comment.length > 0) {
|
|
250
|
-
entityCode.push(`\t/** @comment ${Comment} */`);
|
|
251
|
-
}
|
|
252
|
-
entityCode.push(`\t${Field}!: ${enumName ? enumName : Key == `PRI` && numberTypes.includes(Type) ? `number` : numberTypes.includes(Type) ? `number` : tsType};\n`);
|
|
253
|
-
}
|
|
254
|
-
// Add foreign key relationships
|
|
255
|
-
if (foreignKeys[tableName]) {
|
|
256
|
-
for (const fk of foreignKeys[tableName] || []) {
|
|
257
|
-
const relatedEntity = (0, index_js_1.toPascalCase)(fk.REFERENCED_TABLE_NAME);
|
|
258
|
-
if (imports.includes(`import { ${relatedEntity} } from "./${fk.REFERENCED_TABLE_NAME}";`) === false) {
|
|
259
|
-
entityCode.push(`\t@OneToOne(() => ${relatedEntity})`);
|
|
260
|
-
entityCode.push(`\t@JoinColumn({ name: "${fk.COLUMN_NAME}" })`);
|
|
261
|
-
entityCode.push(`\tfk${relatedEntity}!: ${relatedEntity};\n`);
|
|
262
|
-
imports.push(`import { ${relatedEntity} } from "./${fk.REFERENCED_TABLE_NAME}";`);
|
|
263
|
-
if (!_imports.includes(`OneToOne`))
|
|
264
|
-
_imports.push(`OneToOne`);
|
|
265
|
-
if (!_imports.includes(`JoinColumn`))
|
|
266
|
-
_imports.push(`JoinColumn`);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
// Add OneToMany Relations
|
|
271
|
-
const inverse = inverseRelations[String(tableName)] || [];
|
|
272
|
-
for (const rel of inverse) {
|
|
273
|
-
const propName = rel.targetTable.endsWith('s') ? rel.targetTable : `${rel.targetTable}s`;
|
|
274
|
-
if (entityCode.some(line => line.includes(` ${propName}!:`)))
|
|
275
|
-
continue;
|
|
276
|
-
const importLine = `import { ${rel.targetEntity} } from "./${rel.targetTable}";`;
|
|
277
|
-
if (!imports.includes(importLine))
|
|
278
|
-
imports.push(importLine);
|
|
279
|
-
if (!_imports.includes('OneToMany'))
|
|
280
|
-
_imports.push('OneToMany');
|
|
281
|
-
// CORRECT: Inverse is fk + referenced table (e.g. fkUsers)
|
|
282
|
-
entityCode.push(`\t@OneToMany(() => ${rel.targetEntity}, r => r.${rel.fkPropName})`);
|
|
283
|
-
entityCode.push(`\tfk${(0, index_js_1.toPascalCase)(propName)}!: ${rel.targetEntity}[];\n`);
|
|
284
|
-
// entityCode.push(`\t@OneToMany(() => ${targetEntity}, r => r.${rel.fkColumn})`);
|
|
285
|
-
// entityCode.push(`\tfk${toPascalCase(propName)}!: ${targetEntity}[];\n`);
|
|
286
|
-
}
|
|
287
|
-
// Add Many-to-Many Relations
|
|
288
|
-
const junctions = Object.values(junctionTables)
|
|
289
|
-
.filter(j => j.left === tableName || j.right === tableName);
|
|
290
|
-
for (const j of junctions) {
|
|
291
|
-
const targetTable = j.left === tableName ? j.right : j.left;
|
|
292
|
-
const targetEntity = (0, index_js_1.toPascalCase)(targetTable);
|
|
293
|
-
const propName = targetTable.endsWith('s') ? targetTable : `${targetTable}s`;
|
|
294
|
-
if (entityCode.some(line => line.includes(`@${propName}`)))
|
|
295
|
-
continue;
|
|
296
|
-
const importLine = `import { ${targetEntity} } from "./${targetTable}";`;
|
|
297
|
-
if (!imports.includes(importLine)) {
|
|
298
|
-
imports.push(importLine);
|
|
299
|
-
}
|
|
300
|
-
if (!_imports.includes('ManyToMany'))
|
|
301
|
-
_imports.push('ManyToMany');
|
|
302
|
-
if (!_imports.includes('JoinTable'))
|
|
303
|
-
_imports.push('JoinTable');
|
|
304
|
-
entityCode.push(`\t@ManyToMany(() => ${targetEntity})`);
|
|
305
|
-
entityCode.push(`\t@JoinTable()`);
|
|
306
|
-
entityCode.push(`\tom${propName}!: ${targetEntity}[];\n`);
|
|
307
|
-
}
|
|
308
|
-
const Code = [
|
|
309
|
-
`/**`,
|
|
310
|
-
`* AutoGenerated by @zuzjs/orm.`,
|
|
311
|
-
`* @ ${new Date().toString().split(` GMT`)[0].trim()}`,
|
|
312
|
-
`*/`,
|
|
313
|
-
`import { ${_imports.join(`, `)} } from "@zuzjs/orm";`,
|
|
314
|
-
imports.length > 0 ? imports.join(`\n`) : ``,
|
|
315
|
-
enums.length > 0 ? enums.join(`\n`) : ``,
|
|
316
|
-
`${enums.length > 0 || imports.length > 0 ? `\n` : ``}@Entity({ name: "${tableName}" })`,
|
|
317
|
-
`export class ${(0, index_js_1.toPascalCase)(tableName)} extends BaseEntity {\n`,
|
|
318
|
-
...entityCode,
|
|
319
|
-
`}`
|
|
320
|
-
];
|
|
321
|
-
if (!hasPrimary) {
|
|
322
|
-
console.log(picocolors_1.default.bgRed(picocolors_1.default.whiteBright(` WARNING `)), picocolors_1.default.yellow(`○ "${tableName}" does not have a primary column. Primary column is required.`));
|
|
323
|
-
}
|
|
324
|
-
// Write entity file
|
|
325
|
-
fs_1.default.writeFileSync(path_1.default.join(this.dist, `${tableName}.ts`), Code.join(`\n`));
|
|
326
|
-
}
|
|
327
|
-
// Write entry file i.e index.ts
|
|
328
|
-
const entry = tableNames
|
|
329
|
-
.map(tableName => `import { ${(0, index_js_1.toPascalCase)(tableName)} } from "./${tableName}";`);
|
|
330
|
-
entry.push(`import Zorm from "@zuzjs/orm";`, `import de from "dotenv";`, `de.config()`, `const zormEntities = [${tableNames.map(t => (0, index_js_1.toPascalCase)(t)).join(`, `)}];`, `const zorm = Zorm.get(`, `\tprocess.env.DATABASE_URL!,`, `\tzormEntities`, `);`, `zorm.connect(zormEntities);`, `export default zorm`, `export { ${tableNames.map(t => (0, index_js_1.toPascalCase)(t)).join(`, `)} }`);
|
|
331
|
-
fs_1.default.writeFileSync(path_1.default.join(this.dist, `index.ts`), entry.join(`\n`));
|
|
332
|
-
await self.pool.end();
|
|
333
|
-
console.log(picocolors_1.default.green(`✓ ${tables.length} Tables Processed.`));
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
exports.MySqlDriver = MySqlDriver;
|