@bunnykit/orm 0.1.0
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/LICENSE +21 -0
- package/README.md +904 -0
- package/dist/bin/bunny.d.ts +2 -0
- package/dist/bin/bunny.js +108 -0
- package/dist/src/connection/Connection.d.ts +13 -0
- package/dist/src/connection/Connection.js +49 -0
- package/dist/src/index.d.ts +20 -0
- package/dist/src/index.js +18 -0
- package/dist/src/migration/Migration.d.ts +4 -0
- package/dist/src/migration/Migration.js +2 -0
- package/dist/src/migration/MigrationCreator.d.ts +5 -0
- package/dist/src/migration/MigrationCreator.js +39 -0
- package/dist/src/migration/Migrator.d.ts +21 -0
- package/dist/src/migration/Migrator.js +137 -0
- package/dist/src/model/BelongsToMany.d.ts +27 -0
- package/dist/src/model/BelongsToMany.js +118 -0
- package/dist/src/model/Model.d.ts +166 -0
- package/dist/src/model/Model.js +763 -0
- package/dist/src/model/MorphMap.d.ts +7 -0
- package/dist/src/model/MorphMap.js +12 -0
- package/dist/src/model/MorphRelations.d.ts +81 -0
- package/dist/src/model/MorphRelations.js +296 -0
- package/dist/src/model/Observer.d.ts +19 -0
- package/dist/src/model/Observer.js +21 -0
- package/dist/src/query/Builder.d.ts +106 -0
- package/dist/src/query/Builder.js +466 -0
- package/dist/src/schema/Blueprint.d.ts +66 -0
- package/dist/src/schema/Blueprint.js +200 -0
- package/dist/src/schema/Schema.d.ts +16 -0
- package/dist/src/schema/Schema.js +135 -0
- package/dist/src/schema/grammars/Grammar.d.ts +26 -0
- package/dist/src/schema/grammars/Grammar.js +95 -0
- package/dist/src/schema/grammars/MySqlGrammar.d.ts +18 -0
- package/dist/src/schema/grammars/MySqlGrammar.js +96 -0
- package/dist/src/schema/grammars/PostgresGrammar.d.ts +16 -0
- package/dist/src/schema/grammars/PostgresGrammar.js +88 -0
- package/dist/src/schema/grammars/SQLiteGrammar.d.ts +16 -0
- package/dist/src/schema/grammars/SQLiteGrammar.js +108 -0
- package/dist/src/typegen/TypeGenerator.d.ts +29 -0
- package/dist/src/typegen/TypeGenerator.js +171 -0
- package/dist/src/typegen/TypeMapper.d.ts +4 -0
- package/dist/src/typegen/TypeMapper.js +27 -0
- package/dist/src/types/index.d.ts +53 -0
- package/dist/src/types/index.js +1 -0
- package/dist/src/utils.d.ts +1 -0
- package/dist/src/utils.js +6 -0
- package/package.json +62 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { Grammar } from "./Grammar.js";
|
|
2
|
+
export class PostgresGrammar extends Grammar {
|
|
3
|
+
wrappers = { prefix: '"', suffix: '"' };
|
|
4
|
+
getType(column) {
|
|
5
|
+
switch (column.type) {
|
|
6
|
+
case "string":
|
|
7
|
+
return `VARCHAR(${column.length || 255})`;
|
|
8
|
+
case "text":
|
|
9
|
+
return "TEXT";
|
|
10
|
+
case "integer":
|
|
11
|
+
return "INTEGER";
|
|
12
|
+
case "bigInteger":
|
|
13
|
+
return "BIGINT";
|
|
14
|
+
case "smallInteger":
|
|
15
|
+
return "SMALLINT";
|
|
16
|
+
case "tinyInteger":
|
|
17
|
+
return "SMALLINT";
|
|
18
|
+
case "float":
|
|
19
|
+
return `REAL`;
|
|
20
|
+
case "double":
|
|
21
|
+
return `DOUBLE PRECISION`;
|
|
22
|
+
case "decimal":
|
|
23
|
+
return `DECIMAL(${column.precision || 8}, ${column.scale || 2})`;
|
|
24
|
+
case "boolean":
|
|
25
|
+
return "BOOLEAN";
|
|
26
|
+
case "date":
|
|
27
|
+
return "DATE";
|
|
28
|
+
case "dateTime":
|
|
29
|
+
return "TIMESTAMP(0) WITHOUT TIME ZONE";
|
|
30
|
+
case "time":
|
|
31
|
+
return "TIME(0) WITHOUT TIME ZONE";
|
|
32
|
+
case "timestamp":
|
|
33
|
+
return "TIMESTAMP(0) WITHOUT TIME ZONE";
|
|
34
|
+
case "json":
|
|
35
|
+
return "JSON";
|
|
36
|
+
case "jsonb":
|
|
37
|
+
return "JSONB";
|
|
38
|
+
case "binary":
|
|
39
|
+
return "BYTEA";
|
|
40
|
+
case "uuid":
|
|
41
|
+
return "UUID";
|
|
42
|
+
case "enum":
|
|
43
|
+
return `VARCHAR(255)`;
|
|
44
|
+
default:
|
|
45
|
+
return "TEXT";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
modifyAutoIncrement(column) {
|
|
49
|
+
if (column.autoIncrement) {
|
|
50
|
+
return column.type === "bigInteger" ? " GENERATED ALWAYS AS IDENTITY" : " GENERATED ALWAYS AS IDENTITY";
|
|
51
|
+
}
|
|
52
|
+
return "";
|
|
53
|
+
}
|
|
54
|
+
getColumn(_blueprint, column) {
|
|
55
|
+
let sql = `${this.wrap(column.name)} ${this.getType(column)}`;
|
|
56
|
+
if (!column.nullable)
|
|
57
|
+
sql += " NOT NULL";
|
|
58
|
+
if (column.default !== undefined)
|
|
59
|
+
sql += ` DEFAULT ${this.getDefaultValue(column.default)}`;
|
|
60
|
+
if (column.autoIncrement)
|
|
61
|
+
sql += this.modifyAutoIncrement(column);
|
|
62
|
+
if (column.unique)
|
|
63
|
+
sql += " UNIQUE";
|
|
64
|
+
if (column.primary)
|
|
65
|
+
sql += " PRIMARY KEY";
|
|
66
|
+
return sql;
|
|
67
|
+
}
|
|
68
|
+
compileColumnRename(table, from, to) {
|
|
69
|
+
return `ALTER TABLE ${this.wrap(table)} RENAME COLUMN ${this.wrap(from)} TO ${this.wrap(to)}`;
|
|
70
|
+
}
|
|
71
|
+
compileDropColumn(table, columns) {
|
|
72
|
+
return `ALTER TABLE ${this.wrap(table)} ${columns.map((col) => `DROP COLUMN ${this.wrap(col)}`).join(", ")}`;
|
|
73
|
+
}
|
|
74
|
+
compileIndex(table, index) {
|
|
75
|
+
const type = index.unique ? "UNIQUE INDEX" : "INDEX";
|
|
76
|
+
return `CREATE ${type} ${this.wrap(index.name)} ON ${this.wrap(table)} (${this.wrapArray(index.columns).join(", ")})`;
|
|
77
|
+
}
|
|
78
|
+
compileCreate(blueprint, table) {
|
|
79
|
+
const columns = this.getColumns(blueprint).map((col) => ` ${col}`).join(",\n");
|
|
80
|
+
const sql = `CREATE TABLE ${this.wrap(table)} (\n${columns}\n)`;
|
|
81
|
+
return sql;
|
|
82
|
+
}
|
|
83
|
+
compileCreateIfNotExists(blueprint, table) {
|
|
84
|
+
const columns = this.getColumns(blueprint).map((col) => ` ${col}`).join(",\n");
|
|
85
|
+
const sql = `CREATE TABLE IF NOT EXISTS ${this.wrap(table)} (\n${columns}\n)`;
|
|
86
|
+
return sql;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Grammar } from "./Grammar.js";
|
|
2
|
+
import type { ColumnDefinition } from "../../types/index.js";
|
|
3
|
+
export declare class SQLiteGrammar extends Grammar {
|
|
4
|
+
protected wrappers: {
|
|
5
|
+
prefix: string;
|
|
6
|
+
suffix: string;
|
|
7
|
+
};
|
|
8
|
+
protected getType(column: ColumnDefinition): string;
|
|
9
|
+
protected modifyAutoIncrement(column: ColumnDefinition): string;
|
|
10
|
+
protected getColumn(_blueprint: any, column: ColumnDefinition): string;
|
|
11
|
+
compileColumnRename(table: string, from: string, to: string): string;
|
|
12
|
+
compileDropColumn(table: string, columns: string[]): string | string[];
|
|
13
|
+
compileForeignKeys(blueprint: any, table: string): string[];
|
|
14
|
+
compileCreate(blueprint: any, table: string): string;
|
|
15
|
+
compileCreateIfNotExists(blueprint: any, table: string): string;
|
|
16
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { Grammar } from "./Grammar.js";
|
|
2
|
+
export class SQLiteGrammar extends Grammar {
|
|
3
|
+
wrappers = { prefix: '"', suffix: '"' };
|
|
4
|
+
getType(column) {
|
|
5
|
+
switch (column.type) {
|
|
6
|
+
case "string":
|
|
7
|
+
return "TEXT";
|
|
8
|
+
case "text":
|
|
9
|
+
return "TEXT";
|
|
10
|
+
case "integer":
|
|
11
|
+
return "INTEGER";
|
|
12
|
+
case "bigInteger":
|
|
13
|
+
return "INTEGER";
|
|
14
|
+
case "smallInteger":
|
|
15
|
+
return "INTEGER";
|
|
16
|
+
case "tinyInteger":
|
|
17
|
+
return "INTEGER";
|
|
18
|
+
case "float":
|
|
19
|
+
case "double":
|
|
20
|
+
case "decimal":
|
|
21
|
+
return "REAL";
|
|
22
|
+
case "boolean":
|
|
23
|
+
return "INTEGER";
|
|
24
|
+
case "date":
|
|
25
|
+
case "dateTime":
|
|
26
|
+
case "time":
|
|
27
|
+
case "timestamp":
|
|
28
|
+
return "TEXT";
|
|
29
|
+
case "json":
|
|
30
|
+
case "jsonb":
|
|
31
|
+
return "TEXT";
|
|
32
|
+
case "binary":
|
|
33
|
+
return "BLOB";
|
|
34
|
+
case "uuid":
|
|
35
|
+
return "TEXT";
|
|
36
|
+
case "enum":
|
|
37
|
+
return "TEXT";
|
|
38
|
+
default:
|
|
39
|
+
return "TEXT";
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
modifyAutoIncrement(column) {
|
|
43
|
+
if (column.autoIncrement)
|
|
44
|
+
return " PRIMARY KEY AUTOINCREMENT";
|
|
45
|
+
return "";
|
|
46
|
+
}
|
|
47
|
+
getColumn(_blueprint, column) {
|
|
48
|
+
let sql = `${this.wrap(column.name)} ${this.getType(column)}`;
|
|
49
|
+
if (column.autoIncrement)
|
|
50
|
+
sql += this.modifyAutoIncrement(column);
|
|
51
|
+
if (!column.nullable)
|
|
52
|
+
sql += " NOT NULL";
|
|
53
|
+
if (column.default !== undefined)
|
|
54
|
+
sql += ` DEFAULT ${this.getDefaultValue(column.default)}`;
|
|
55
|
+
if (column.unique && !column.autoIncrement)
|
|
56
|
+
sql += " UNIQUE";
|
|
57
|
+
return sql;
|
|
58
|
+
}
|
|
59
|
+
compileColumnRename(table, from, to) {
|
|
60
|
+
return `ALTER TABLE ${this.wrap(table)} RENAME COLUMN ${this.wrap(from)} TO ${this.wrap(to)}`;
|
|
61
|
+
}
|
|
62
|
+
compileDropColumn(table, columns) {
|
|
63
|
+
// SQLite 3.35.0+ supports dropping columns.
|
|
64
|
+
return columns.map((col) => `ALTER TABLE ${this.wrap(table)} DROP COLUMN ${this.wrap(col)}`);
|
|
65
|
+
}
|
|
66
|
+
compileForeignKeys(blueprint, table) {
|
|
67
|
+
// SQLite supports foreign keys inside CREATE TABLE only.
|
|
68
|
+
// For simplicity, ALTER TABLE ADD CONSTRAINT is not supported in SQLite.
|
|
69
|
+
// We'll add inline foreign keys in CREATE TABLE by overriding compileCreate.
|
|
70
|
+
return [];
|
|
71
|
+
}
|
|
72
|
+
compileCreate(blueprint, table) {
|
|
73
|
+
const columns = this.getColumns(blueprint).map((col) => ` ${col}`).join(",\n");
|
|
74
|
+
const fks = blueprint.foreignKeys
|
|
75
|
+
.map((fk) => {
|
|
76
|
+
let sql = ` FOREIGN KEY (${this.wrapArray(fk.columns).join(", ")}) REFERENCES ${this.wrap(fk.onTable)} (${this.wrapArray(fk.references).join(", ")})`;
|
|
77
|
+
if (fk.onDelete)
|
|
78
|
+
sql += ` ON DELETE ${fk.onDelete}`;
|
|
79
|
+
if (fk.onUpdate)
|
|
80
|
+
sql += ` ON UPDATE ${fk.onUpdate}`;
|
|
81
|
+
return sql;
|
|
82
|
+
})
|
|
83
|
+
.join(",\n");
|
|
84
|
+
let sql = `CREATE TABLE ${this.wrap(table)} (\n${columns}`;
|
|
85
|
+
if (fks)
|
|
86
|
+
sql += `,\n${fks}`;
|
|
87
|
+
sql += "\n)";
|
|
88
|
+
return sql;
|
|
89
|
+
}
|
|
90
|
+
compileCreateIfNotExists(blueprint, table) {
|
|
91
|
+
const columns = this.getColumns(blueprint).map((col) => ` ${col}`).join(",\n");
|
|
92
|
+
const fks = blueprint.foreignKeys
|
|
93
|
+
.map((fk) => {
|
|
94
|
+
let sql = ` FOREIGN KEY (${this.wrapArray(fk.columns).join(", ")}) REFERENCES ${this.wrap(fk.onTable)} (${this.wrapArray(fk.references).join(", ")})`;
|
|
95
|
+
if (fk.onDelete)
|
|
96
|
+
sql += ` ON DELETE ${fk.onDelete}`;
|
|
97
|
+
if (fk.onUpdate)
|
|
98
|
+
sql += ` ON UPDATE ${fk.onUpdate}`;
|
|
99
|
+
return sql;
|
|
100
|
+
})
|
|
101
|
+
.join(",\n");
|
|
102
|
+
let sql = `CREATE TABLE IF NOT EXISTS ${this.wrap(table)} (\n${columns}`;
|
|
103
|
+
if (fks)
|
|
104
|
+
sql += `,\n${fks}`;
|
|
105
|
+
sql += "\n)";
|
|
106
|
+
return sql;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Connection } from "../connection/Connection.js";
|
|
2
|
+
export interface ModelDeclaration {
|
|
3
|
+
path: string;
|
|
4
|
+
className?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface TypeGeneratorOptions {
|
|
7
|
+
outDir: string;
|
|
8
|
+
stubs?: boolean;
|
|
9
|
+
declarations?: boolean;
|
|
10
|
+
modelDeclarations?: Record<string, string | ModelDeclaration>;
|
|
11
|
+
modelDirectory?: string;
|
|
12
|
+
modelImportPrefix?: string;
|
|
13
|
+
singularModels?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare class TypeGenerator {
|
|
16
|
+
private connection;
|
|
17
|
+
private options;
|
|
18
|
+
constructor(connection: Connection, options: TypeGeneratorOptions);
|
|
19
|
+
generate(): Promise<void>;
|
|
20
|
+
private getModelDeclaration;
|
|
21
|
+
private getConventionModelDeclaration;
|
|
22
|
+
private toModelClassName;
|
|
23
|
+
private singularizeTable;
|
|
24
|
+
private singularizeWord;
|
|
25
|
+
private getTables;
|
|
26
|
+
private getCurrentDatabase;
|
|
27
|
+
private getColumns;
|
|
28
|
+
private toClassName;
|
|
29
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { TypeMapper } from "./TypeMapper.js";
|
|
4
|
+
import { snakeCase } from "../utils.js";
|
|
5
|
+
export class TypeGenerator {
|
|
6
|
+
connection;
|
|
7
|
+
options;
|
|
8
|
+
constructor(connection, options) {
|
|
9
|
+
this.connection = connection;
|
|
10
|
+
this.options = options;
|
|
11
|
+
}
|
|
12
|
+
async generate() {
|
|
13
|
+
await mkdir(this.options.outDir, { recursive: true });
|
|
14
|
+
const tables = await this.getTables();
|
|
15
|
+
for (const table of tables) {
|
|
16
|
+
const columns = await this.getColumns(table);
|
|
17
|
+
const className = this.toClassName(table);
|
|
18
|
+
const interfaceName = `${className}Attributes`;
|
|
19
|
+
const lines = [];
|
|
20
|
+
const declarationOnly = this.options.declarations ?? !this.options.stubs;
|
|
21
|
+
if (!declarationOnly) {
|
|
22
|
+
lines.push(`import { Model } from "@bunnykit/orm";`);
|
|
23
|
+
lines.push("");
|
|
24
|
+
}
|
|
25
|
+
lines.push(`export interface ${interfaceName} {`);
|
|
26
|
+
for (const col of columns) {
|
|
27
|
+
const tsType = TypeMapper.sqlToTsType(col.type, col.nullable);
|
|
28
|
+
lines.push(` ${col.name}${col.nullable ? "?" : ""}: ${tsType};`);
|
|
29
|
+
}
|
|
30
|
+
lines.push("}");
|
|
31
|
+
lines.push("");
|
|
32
|
+
const modelDeclaration = this.getModelDeclaration(table, className);
|
|
33
|
+
if (declarationOnly && modelDeclaration) {
|
|
34
|
+
lines.push(`declare module "${modelDeclaration.path}" {`);
|
|
35
|
+
lines.push(` interface ${modelDeclaration.className} extends ${interfaceName} {}`);
|
|
36
|
+
lines.push("}");
|
|
37
|
+
lines.push("");
|
|
38
|
+
}
|
|
39
|
+
if (!declarationOnly && this.options.stubs) {
|
|
40
|
+
lines.push(`export class ${className}Base extends Model<${interfaceName}> {`);
|
|
41
|
+
lines.push(` static table = "${table}";`);
|
|
42
|
+
lines.push("");
|
|
43
|
+
for (const col of columns) {
|
|
44
|
+
const tsType = TypeMapper.sqlToTsType(col.type, col.nullable);
|
|
45
|
+
lines.push(` get ${col.name}(): ${tsType} {`);
|
|
46
|
+
lines.push(` return this.getAttribute("${col.name}");`);
|
|
47
|
+
lines.push(` }`);
|
|
48
|
+
lines.push(` set ${col.name}(value: ${tsType}) {`);
|
|
49
|
+
lines.push(` this.setAttribute("${col.name}", value);`);
|
|
50
|
+
lines.push(` }`);
|
|
51
|
+
lines.push("");
|
|
52
|
+
}
|
|
53
|
+
lines.push("}");
|
|
54
|
+
}
|
|
55
|
+
const fileName = `${snakeCase(className)}.${declarationOnly ? "d.ts" : "ts"}`;
|
|
56
|
+
const filePath = join(this.options.outDir, fileName);
|
|
57
|
+
await writeFile(filePath, lines.join("\n") + "\n", "utf-8");
|
|
58
|
+
}
|
|
59
|
+
const declarationOnly = this.options.declarations ?? !this.options.stubs;
|
|
60
|
+
const indexLines = tables.map((table) => {
|
|
61
|
+
const className = this.toClassName(table);
|
|
62
|
+
const fileName = snakeCase(className);
|
|
63
|
+
return `export * from "./${fileName}";`;
|
|
64
|
+
});
|
|
65
|
+
await writeFile(join(this.options.outDir, `index.${declarationOnly ? "d.ts" : "ts"}`), indexLines.join("\n") + "\n", "utf-8");
|
|
66
|
+
}
|
|
67
|
+
getModelDeclaration(table, fallbackClassName) {
|
|
68
|
+
const declaration = this.options.modelDeclarations?.[table];
|
|
69
|
+
if (!declaration)
|
|
70
|
+
return this.getConventionModelDeclaration(table);
|
|
71
|
+
if (typeof declaration === "string") {
|
|
72
|
+
return { path: declaration, className: this.toModelClassName(table, fallbackClassName) };
|
|
73
|
+
}
|
|
74
|
+
return { path: declaration.path, className: declaration.className || this.toModelClassName(table, fallbackClassName) };
|
|
75
|
+
}
|
|
76
|
+
getConventionModelDeclaration(table) {
|
|
77
|
+
const prefix = this.options.modelImportPrefix || this.options.modelDirectory;
|
|
78
|
+
if (!prefix)
|
|
79
|
+
return null;
|
|
80
|
+
const className = this.toModelClassName(table);
|
|
81
|
+
return {
|
|
82
|
+
path: `${prefix.replace(/\/$/, "")}/${className}`,
|
|
83
|
+
className,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
toModelClassName(table, fallback) {
|
|
87
|
+
if (this.options.singularModels === false) {
|
|
88
|
+
return fallback || this.toClassName(table);
|
|
89
|
+
}
|
|
90
|
+
return this.toClassName(this.singularizeTable(table));
|
|
91
|
+
}
|
|
92
|
+
singularizeTable(table) {
|
|
93
|
+
return table
|
|
94
|
+
.split("_")
|
|
95
|
+
.map((part) => this.singularizeWord(part))
|
|
96
|
+
.join("_");
|
|
97
|
+
}
|
|
98
|
+
singularizeWord(word) {
|
|
99
|
+
if (word.endsWith("ies") && word.length > 3)
|
|
100
|
+
return `${word.slice(0, -3)}y`;
|
|
101
|
+
if (word.endsWith("ses") || word.endsWith("xes") || word.endsWith("zes") || word.endsWith("ches") || word.endsWith("shes")) {
|
|
102
|
+
return word.slice(0, -2);
|
|
103
|
+
}
|
|
104
|
+
if (word.endsWith("s") && !word.endsWith("ss"))
|
|
105
|
+
return word.slice(0, -1);
|
|
106
|
+
return word;
|
|
107
|
+
}
|
|
108
|
+
async getTables() {
|
|
109
|
+
const driver = this.connection.getDriverName();
|
|
110
|
+
let sql;
|
|
111
|
+
if (driver === "sqlite") {
|
|
112
|
+
sql = `SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '%_inner_sequence' AND name != 'migrations'`;
|
|
113
|
+
}
|
|
114
|
+
else if (driver === "mysql") {
|
|
115
|
+
sql = "SHOW TABLES";
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
sql = `SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_type = 'BASE TABLE'`;
|
|
119
|
+
}
|
|
120
|
+
const rows = await this.connection.query(sql);
|
|
121
|
+
if (driver === "sqlite") {
|
|
122
|
+
return rows.map((r) => r.name);
|
|
123
|
+
}
|
|
124
|
+
else if (driver === "mysql") {
|
|
125
|
+
const key = Object.keys(rows[0] ?? {})[0] ?? "Tables_in_" + (await this.getCurrentDatabase());
|
|
126
|
+
return rows.map((r) => r[key]);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
return rows.map((r) => r.table_name);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
async getCurrentDatabase() {
|
|
133
|
+
const rows = await this.connection.query("SELECT DATABASE() as db");
|
|
134
|
+
return rows[0]?.db || "";
|
|
135
|
+
}
|
|
136
|
+
async getColumns(table) {
|
|
137
|
+
const driver = this.connection.getDriverName();
|
|
138
|
+
if (driver === "sqlite") {
|
|
139
|
+
const rows = await this.connection.query(`PRAGMA table_info(${table})`);
|
|
140
|
+
return rows.map((r) => ({
|
|
141
|
+
name: r.name,
|
|
142
|
+
type: r.type,
|
|
143
|
+
nullable: !r.notnull,
|
|
144
|
+
default: r.dflt_value,
|
|
145
|
+
}));
|
|
146
|
+
}
|
|
147
|
+
if (driver === "mysql") {
|
|
148
|
+
const rows = await this.connection.query(`SHOW COLUMNS FROM ${table}`);
|
|
149
|
+
return rows.map((r) => ({
|
|
150
|
+
name: r.Field,
|
|
151
|
+
type: r.Type,
|
|
152
|
+
nullable: r.Null === "YES",
|
|
153
|
+
default: r.Default,
|
|
154
|
+
}));
|
|
155
|
+
}
|
|
156
|
+
// postgres
|
|
157
|
+
const rows = await this.connection.query(`SELECT column_name, data_type, is_nullable, column_default FROM information_schema.columns WHERE table_name = '${table}' AND table_schema = 'public' ORDER BY ordinal_position`);
|
|
158
|
+
return rows.map((r) => ({
|
|
159
|
+
name: r.column_name,
|
|
160
|
+
type: r.data_type,
|
|
161
|
+
nullable: r.is_nullable === "YES",
|
|
162
|
+
default: r.column_default,
|
|
163
|
+
}));
|
|
164
|
+
}
|
|
165
|
+
toClassName(table) {
|
|
166
|
+
return table
|
|
167
|
+
.split("_")
|
|
168
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
169
|
+
.join("");
|
|
170
|
+
}
|
|
171
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export class TypeMapper {
|
|
2
|
+
static sqlToTsType(sqlType, nullable) {
|
|
3
|
+
const base = this.mapBaseType(sqlType);
|
|
4
|
+
return nullable && base !== "any" ? `${base} | null` : base;
|
|
5
|
+
}
|
|
6
|
+
static mapBaseType(sqlType) {
|
|
7
|
+
const t = sqlType.toLowerCase();
|
|
8
|
+
// Integers & numbers
|
|
9
|
+
if (/int|serial|float|double|real|decimal|numeric/.test(t)) {
|
|
10
|
+
return "number";
|
|
11
|
+
}
|
|
12
|
+
// Boolean
|
|
13
|
+
if (/bool/.test(t)) {
|
|
14
|
+
return "boolean";
|
|
15
|
+
}
|
|
16
|
+
// JSON
|
|
17
|
+
if (/json/.test(t)) {
|
|
18
|
+
return "any";
|
|
19
|
+
}
|
|
20
|
+
// Binary / BLOB
|
|
21
|
+
if (/blob|bytea|binary|varbinary/.test(t)) {
|
|
22
|
+
return "Buffer";
|
|
23
|
+
}
|
|
24
|
+
// Default to string for everything else (varchar, text, char, date, enum, uuid, etc.)
|
|
25
|
+
return "string";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export type ColumnType = "string" | "text" | "integer" | "bigInteger" | "smallInteger" | "tinyInteger" | "float" | "double" | "decimal" | "boolean" | "date" | "dateTime" | "time" | "timestamp" | "json" | "jsonb" | "binary" | "uuid" | "enum";
|
|
2
|
+
export interface ColumnDefinition {
|
|
3
|
+
name: string;
|
|
4
|
+
type: ColumnType;
|
|
5
|
+
length?: number;
|
|
6
|
+
precision?: number;
|
|
7
|
+
scale?: number;
|
|
8
|
+
nullable: boolean;
|
|
9
|
+
default?: any;
|
|
10
|
+
autoIncrement: boolean;
|
|
11
|
+
primary: boolean;
|
|
12
|
+
unique: boolean;
|
|
13
|
+
index: boolean;
|
|
14
|
+
unsigned: boolean;
|
|
15
|
+
values?: string[];
|
|
16
|
+
comment?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface IndexDefinition {
|
|
19
|
+
name: string;
|
|
20
|
+
columns: string[];
|
|
21
|
+
unique: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface ForeignKeyDefinition {
|
|
24
|
+
name?: string;
|
|
25
|
+
columns: string[];
|
|
26
|
+
references: string[];
|
|
27
|
+
onTable: string;
|
|
28
|
+
onDelete?: string;
|
|
29
|
+
onUpdate?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface WhereClause {
|
|
32
|
+
type: "basic" | "in" | "null" | "raw" | "between" | "column" | "exists";
|
|
33
|
+
column: string;
|
|
34
|
+
operator?: string;
|
|
35
|
+
value?: any;
|
|
36
|
+
boolean: "and" | "or";
|
|
37
|
+
scope?: string;
|
|
38
|
+
}
|
|
39
|
+
export interface OrderClause {
|
|
40
|
+
column: string;
|
|
41
|
+
direction: "asc" | "desc";
|
|
42
|
+
}
|
|
43
|
+
export type ConnectionConfig = {
|
|
44
|
+
url: string;
|
|
45
|
+
} | {
|
|
46
|
+
driver: "sqlite" | "mysql" | "postgres";
|
|
47
|
+
host?: string;
|
|
48
|
+
port?: number;
|
|
49
|
+
database?: string;
|
|
50
|
+
username?: string;
|
|
51
|
+
password?: string;
|
|
52
|
+
filename?: string;
|
|
53
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function snakeCase(str: string): string;
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bunnykit/orm",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "An Eloquent-inspired ORM for Bun's native SQL client supporting SQLite, MySQL, and PostgreSQL.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"packageManager": "bun@1.3.12",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"bun",
|
|
9
|
+
"orm",
|
|
10
|
+
"eloquent",
|
|
11
|
+
"sqlite",
|
|
12
|
+
"mysql",
|
|
13
|
+
"postgresql",
|
|
14
|
+
"database"
|
|
15
|
+
],
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/bunnykit/orm.git"
|
|
19
|
+
},
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/bunnykit/orm/issues"
|
|
22
|
+
},
|
|
23
|
+
"homepage": "https://github.com/bunnykit/orm#readme",
|
|
24
|
+
"main": "dist/src/index.js",
|
|
25
|
+
"module": "dist/src/index.js",
|
|
26
|
+
"types": "dist/src/index.d.ts",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/src/index.d.ts",
|
|
30
|
+
"import": "./dist/src/index.js"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"type": "module",
|
|
34
|
+
"bin": {
|
|
35
|
+
"bunny": "dist/bin/bunny.js"
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"bun": ">=1.1.0"
|
|
39
|
+
},
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"preinstall": "bun -e \"const ua = process.env.npm_config_user_agent || ''; if (!ua.startsWith('bun/')) { console.error('@bunnykit/orm is Bun-only. Install it with: bun add @bunnykit/orm'); process.exit(1); }\"",
|
|
45
|
+
"build": "tsc",
|
|
46
|
+
"prepack": "bun run build",
|
|
47
|
+
"migrate": "bun run bin/bunny.ts migrate",
|
|
48
|
+
"migrate:make": "bun run bin/bunny.ts migrate:make",
|
|
49
|
+
"migrate:rollback": "bun run bin/bunny.ts migrate:rollback",
|
|
50
|
+
"migrate:status": "bun run bin/bunny.ts migrate:status"
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {},
|
|
53
|
+
"files": [
|
|
54
|
+
"dist",
|
|
55
|
+
"README.md",
|
|
56
|
+
"LICENSE"
|
|
57
|
+
],
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@types/bun": "latest",
|
|
60
|
+
"typescript": "^5.4.0"
|
|
61
|
+
}
|
|
62
|
+
}
|