@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 +2 -0
- package/dist/bin.js +39 -0
- package/dist/core/index.d.ts +7 -0
- package/dist/core/index.js +70 -0
- package/dist/drivers/mysql/index.d.ts +38 -0
- package/dist/drivers/mysql/index.js +181 -0
- package/dist/drivers/queryBuilder.d.ts +42 -0
- package/dist/drivers/queryBuilder.js +224 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +103 -0
- package/dist/types.d.ts +48 -0
- package/dist/types.js +2 -0
- package/package.json +50 -0
package/dist/bin.d.ts
ADDED
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;
|
package/dist/index.d.ts
ADDED
|
@@ -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;
|
package/dist/types.d.ts
ADDED
|
@@ -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
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
|
+
}
|