@ghom/orm 1.0.0 → 1.1.2
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/app/orm.d.ts +5 -1
- package/dist/app/orm.js +12 -8
- package/dist/app/table.d.ts +5 -1
- package/dist/app/table.js +28 -13
- package/package.json +3 -1
- package/src/app/orm.ts +26 -19
- package/src/app/table.ts +41 -14
- package/tests/.env +1 -0
- package/tests/tables/a.js +14 -3
- package/tests/tables/b.js +14 -3
- package/tests/tables/c.js +5 -2
- package/tests/test.js +15 -1
package/dist/app/orm.d.ts
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { Handler } from "@ghom/handler";
|
|
2
2
|
import { Knex } from "knex";
|
|
3
|
+
export interface ORMLogger {
|
|
4
|
+
log: (message: string, section?: string) => void;
|
|
5
|
+
error: (text: string | Error, _path: string, full?: boolean) => void;
|
|
6
|
+
}
|
|
3
7
|
/**
|
|
4
8
|
* @property tablePath - path to directory that contains js files of tables
|
|
5
9
|
* @property verbose - show console logs or not
|
|
6
10
|
*/
|
|
7
11
|
export interface ORMConfig {
|
|
8
|
-
|
|
12
|
+
logger?: ORMLogger;
|
|
9
13
|
tablePath: string;
|
|
10
14
|
}
|
|
11
15
|
export declare class ORM extends Handler {
|
package/dist/app/orm.js
CHANGED
|
@@ -48,21 +48,25 @@ class ORM extends handler_1.Handler {
|
|
|
48
48
|
const tables = await Promise.all(pathList.map(async (filepath) => {
|
|
49
49
|
return Promise.resolve().then(() => __importStar(require(filepath))).then((file) => file.default);
|
|
50
50
|
}));
|
|
51
|
-
|
|
51
|
+
const migration = new table_1.Table({
|
|
52
52
|
name: "migration",
|
|
53
53
|
priority: Infinity,
|
|
54
54
|
setup: (table) => {
|
|
55
55
|
table.string("table").unique().notNullable();
|
|
56
56
|
table.integer("version").notNullable();
|
|
57
57
|
},
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
58
|
+
});
|
|
59
|
+
migration.orm = this;
|
|
60
|
+
await migration.make();
|
|
61
|
+
for (const table of tables.sort((a, b) => (b.options.priority ?? 0) - (a.options.priority ?? 0))) {
|
|
62
|
+
table.orm = this;
|
|
63
|
+
await table.make();
|
|
64
|
+
}
|
|
65
65
|
});
|
|
66
|
+
try {
|
|
67
|
+
await this.db.raw("PRAGMA foreign_keys = ON;");
|
|
68
|
+
}
|
|
69
|
+
catch (error) { }
|
|
66
70
|
await this.load();
|
|
67
71
|
}
|
|
68
72
|
}
|
package/dist/app/table.d.ts
CHANGED
|
@@ -10,13 +10,15 @@ export interface TableOptions<Type> {
|
|
|
10
10
|
migrations?: {
|
|
11
11
|
[version: number]: (table: Knex.CreateTableBuilder) => void;
|
|
12
12
|
};
|
|
13
|
+
then?: (this: Table<Type>, table: Table<Type>) => unknown;
|
|
13
14
|
setup: (table: Knex.CreateTableBuilder) => void;
|
|
14
15
|
}
|
|
15
16
|
export declare class Table<Type> {
|
|
16
17
|
readonly options: TableOptions<Type>;
|
|
17
18
|
orm?: ORM;
|
|
18
19
|
constructor(options: TableOptions<Type>);
|
|
19
|
-
private get
|
|
20
|
+
private get filepath();
|
|
21
|
+
private get logger();
|
|
20
22
|
get db(): Knex<any, Record<string, any>[]>;
|
|
21
23
|
get query(): Knex.QueryBuilder<Type, {
|
|
22
24
|
_base: Type;
|
|
@@ -27,6 +29,8 @@ export declare class Table<Type> {
|
|
|
27
29
|
_intersectProps: {};
|
|
28
30
|
_unionProps: never;
|
|
29
31
|
}[]>;
|
|
32
|
+
hasColumn(name: keyof Type): Promise<boolean>;
|
|
33
|
+
isEmpty(): Promise<boolean>;
|
|
30
34
|
make(): Promise<this>;
|
|
31
35
|
private migrate;
|
|
32
36
|
}
|
package/dist/app/table.js
CHANGED
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.Table = void 0;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
4
9
|
class Table {
|
|
5
10
|
constructor(options) {
|
|
6
11
|
this.options = options;
|
|
7
12
|
}
|
|
8
|
-
get
|
|
13
|
+
get filepath() {
|
|
9
14
|
if (!this.orm)
|
|
10
15
|
throw new Error("missing ORM");
|
|
11
|
-
return this.orm.ormConfig.
|
|
16
|
+
return path_1.default.relative(process.cwd(), path_1.default.join(this.orm.ormConfig.tablePath, this.options.name + ".ts"));
|
|
17
|
+
}
|
|
18
|
+
get logger() {
|
|
19
|
+
if (!this.orm)
|
|
20
|
+
throw new Error("missing ORM");
|
|
21
|
+
return this.orm.ormConfig.logger;
|
|
12
22
|
}
|
|
13
23
|
get db() {
|
|
14
24
|
if (!this.orm)
|
|
@@ -18,34 +28,39 @@ class Table {
|
|
|
18
28
|
get query() {
|
|
19
29
|
return this.db(this.options.name);
|
|
20
30
|
}
|
|
31
|
+
async hasColumn(name) {
|
|
32
|
+
return this.db.schema.hasColumn(this.options.name, name);
|
|
33
|
+
}
|
|
34
|
+
async isEmpty() {
|
|
35
|
+
return this.query
|
|
36
|
+
.select()
|
|
37
|
+
.limit(1)
|
|
38
|
+
.then((rows) => rows.length === 0);
|
|
39
|
+
}
|
|
21
40
|
async make() {
|
|
22
41
|
try {
|
|
23
42
|
await this.db.schema.createTable(this.options.name, this.options.setup);
|
|
24
|
-
|
|
25
|
-
console.log(`created table ${this.options.name}`);
|
|
43
|
+
this.logger?.log(`created table ${chalk_1.default.blueBright(this.options.name)}`);
|
|
26
44
|
}
|
|
27
45
|
catch (error) {
|
|
28
46
|
if (error.toString().includes("syntax error")) {
|
|
29
|
-
|
|
30
|
-
console.error(`you need to implement the "setup" method in options of your ${this.options.name} table!`);
|
|
47
|
+
this.logger?.error(`you need to implement the "setup" method in options of your ${chalk_1.default.blueBright(this.options.name)} table!`, this.filepath);
|
|
31
48
|
throw error;
|
|
32
49
|
}
|
|
33
50
|
else {
|
|
34
|
-
|
|
35
|
-
console.log(`loaded table ${this.options.name}`);
|
|
51
|
+
this.logger?.log(`loaded table ${chalk_1.default.blueBright(this.options.name)}`);
|
|
36
52
|
}
|
|
37
53
|
}
|
|
38
54
|
try {
|
|
39
55
|
const migrated = await this.migrate();
|
|
40
56
|
if (migrated !== false) {
|
|
41
|
-
|
|
42
|
-
console.log(`migrated table ${this.options.name} to version ${migrated}`);
|
|
57
|
+
this.logger?.log(`migrated table ${chalk_1.default.blueBright(this.options.name)} to version ${chalk_1.default.magentaBright(migrated)}`);
|
|
43
58
|
}
|
|
44
59
|
}
|
|
45
60
|
catch (error) {
|
|
46
|
-
|
|
47
|
-
console.error(error);
|
|
61
|
+
this.logger?.error(error, this.filepath);
|
|
48
62
|
}
|
|
63
|
+
await this.options.then?.bind(this)(this);
|
|
49
64
|
return this;
|
|
50
65
|
}
|
|
51
66
|
async migrate() {
|
|
@@ -59,7 +74,7 @@ class Table {
|
|
|
59
74
|
.first();
|
|
60
75
|
const data = fromDatabase || {
|
|
61
76
|
table: this.options.name,
|
|
62
|
-
version: -
|
|
77
|
+
version: -Infinity,
|
|
63
78
|
};
|
|
64
79
|
const baseVersion = data.version;
|
|
65
80
|
await this.db.schema.alterTable(this.options.name, (builder) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ghom/orm",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -17,12 +17,14 @@
|
|
|
17
17
|
"devDependencies": {
|
|
18
18
|
"@vscode/sqlite3": "^5.0.7",
|
|
19
19
|
"better-sqlite3": "^7.5.0",
|
|
20
|
+
"dotenv": "^16.0.0",
|
|
20
21
|
"jest": "^27.5.1",
|
|
21
22
|
"prettier": "^2.5.1",
|
|
22
23
|
"typescript": "^4.5.5"
|
|
23
24
|
},
|
|
24
25
|
"dependencies": {
|
|
25
26
|
"@ghom/handler": "^1.1.0",
|
|
27
|
+
"chalk": "^4.1.2",
|
|
26
28
|
"knex": "^1.0.3"
|
|
27
29
|
}
|
|
28
30
|
}
|
package/src/app/orm.ts
CHANGED
|
@@ -2,12 +2,17 @@ import { Handler } from "@ghom/handler"
|
|
|
2
2
|
import { Knex, default as knex } from "knex"
|
|
3
3
|
import { MigrationData, Table } from "./table"
|
|
4
4
|
|
|
5
|
+
export interface ORMLogger {
|
|
6
|
+
log: (message: string, section?: string) => void
|
|
7
|
+
error: (text: string | Error, _path: string, full?: boolean) => void
|
|
8
|
+
}
|
|
9
|
+
|
|
5
10
|
/**
|
|
6
11
|
* @property tablePath - path to directory that contains js files of tables
|
|
7
12
|
* @property verbose - show console logs or not
|
|
8
13
|
*/
|
|
9
14
|
export interface ORMConfig {
|
|
10
|
-
|
|
15
|
+
logger?: ORMLogger
|
|
11
16
|
tablePath: string
|
|
12
17
|
}
|
|
13
18
|
|
|
@@ -43,28 +48,30 @@ export class ORM extends Handler {
|
|
|
43
48
|
})
|
|
44
49
|
)
|
|
45
50
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
})
|
|
55
|
-
)
|
|
51
|
+
const migration = new Table<MigrationData>({
|
|
52
|
+
name: "migration",
|
|
53
|
+
priority: Infinity,
|
|
54
|
+
setup: (table) => {
|
|
55
|
+
table.string("table").unique().notNullable()
|
|
56
|
+
table.integer("version").notNullable()
|
|
57
|
+
},
|
|
58
|
+
})
|
|
56
59
|
|
|
57
|
-
|
|
60
|
+
migration.orm = this
|
|
61
|
+
await migration.make()
|
|
58
62
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
)
|
|
63
|
+
for (const table of tables.sort(
|
|
64
|
+
(a, b) => (b.options.priority ?? 0) - (a.options.priority ?? 0)
|
|
65
|
+
)) {
|
|
66
|
+
table.orm = this
|
|
67
|
+
await table.make()
|
|
68
|
+
}
|
|
66
69
|
})
|
|
67
70
|
|
|
71
|
+
try {
|
|
72
|
+
await this.db.raw("PRAGMA foreign_keys = ON;")
|
|
73
|
+
} catch (error) {}
|
|
74
|
+
|
|
68
75
|
await this.load()
|
|
69
76
|
}
|
|
70
77
|
}
|
package/src/app/table.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import path from "path"
|
|
2
|
+
import chalk from "chalk"
|
|
1
3
|
import { Knex } from "knex"
|
|
2
4
|
import { ORM } from "./orm.js"
|
|
3
5
|
|
|
@@ -10,6 +12,7 @@ export interface TableOptions<Type> {
|
|
|
10
12
|
name: string
|
|
11
13
|
priority?: number
|
|
12
14
|
migrations?: { [version: number]: (table: Knex.CreateTableBuilder) => void }
|
|
15
|
+
then?: (this: Table<Type>, table: Table<Type>) => unknown
|
|
13
16
|
setup: (table: Knex.CreateTableBuilder) => void
|
|
14
17
|
}
|
|
15
18
|
|
|
@@ -18,9 +21,17 @@ export class Table<Type> {
|
|
|
18
21
|
|
|
19
22
|
constructor(public readonly options: TableOptions<Type>) {}
|
|
20
23
|
|
|
21
|
-
private get
|
|
24
|
+
private get filepath() {
|
|
22
25
|
if (!this.orm) throw new Error("missing ORM")
|
|
23
|
-
return
|
|
26
|
+
return path.relative(
|
|
27
|
+
process.cwd(),
|
|
28
|
+
path.join(this.orm.ormConfig.tablePath, this.options.name + ".ts")
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
private get logger() {
|
|
33
|
+
if (!this.orm) throw new Error("missing ORM")
|
|
34
|
+
return this.orm.ormConfig.logger
|
|
24
35
|
}
|
|
25
36
|
|
|
26
37
|
get db() {
|
|
@@ -32,20 +43,33 @@ export class Table<Type> {
|
|
|
32
43
|
return this.db<Type>(this.options.name)
|
|
33
44
|
}
|
|
34
45
|
|
|
46
|
+
async hasColumn(name: keyof Type): Promise<boolean> {
|
|
47
|
+
return this.db.schema.hasColumn(this.options.name, name as string)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async isEmpty(): Promise<boolean> {
|
|
51
|
+
return this.query
|
|
52
|
+
.select()
|
|
53
|
+
.limit(1)
|
|
54
|
+
.then((rows) => rows.length === 0)
|
|
55
|
+
}
|
|
56
|
+
|
|
35
57
|
async make(): Promise<this> {
|
|
36
58
|
try {
|
|
37
59
|
await this.db.schema.createTable(this.options.name, this.options.setup)
|
|
38
|
-
|
|
60
|
+
this.logger?.log(`created table ${chalk.blueBright(this.options.name)}`)
|
|
39
61
|
} catch (error: any) {
|
|
40
62
|
if (error.toString().includes("syntax error")) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
)
|
|
63
|
+
this.logger?.error(
|
|
64
|
+
`you need to implement the "setup" method in options of your ${chalk.blueBright(
|
|
65
|
+
this.options.name
|
|
66
|
+
)} table!`,
|
|
67
|
+
this.filepath
|
|
68
|
+
)
|
|
45
69
|
|
|
46
70
|
throw error
|
|
47
71
|
} else {
|
|
48
|
-
|
|
72
|
+
this.logger?.log(`loaded table ${chalk.blueBright(this.options.name)}`)
|
|
49
73
|
}
|
|
50
74
|
}
|
|
51
75
|
|
|
@@ -53,15 +77,18 @@ export class Table<Type> {
|
|
|
53
77
|
const migrated = await this.migrate()
|
|
54
78
|
|
|
55
79
|
if (migrated !== false) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
)
|
|
80
|
+
this.logger?.log(
|
|
81
|
+
`migrated table ${chalk.blueBright(
|
|
82
|
+
this.options.name
|
|
83
|
+
)} to version ${chalk.magentaBright(migrated)}`
|
|
84
|
+
)
|
|
60
85
|
}
|
|
61
86
|
} catch (error: any) {
|
|
62
|
-
|
|
87
|
+
this.logger?.error(error, this.filepath)
|
|
63
88
|
}
|
|
64
89
|
|
|
90
|
+
await this.options.then?.bind(this)(this)
|
|
91
|
+
|
|
65
92
|
return this
|
|
66
93
|
}
|
|
67
94
|
|
|
@@ -83,7 +110,7 @@ export class Table<Type> {
|
|
|
83
110
|
|
|
84
111
|
const data = fromDatabase || {
|
|
85
112
|
table: this.options.name,
|
|
86
|
-
version: -
|
|
113
|
+
version: -Infinity,
|
|
87
114
|
}
|
|
88
115
|
|
|
89
116
|
const baseVersion = data.version
|
package/tests/.env
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
DEBUG=knex*
|
package/tests/tables/a.js
CHANGED
|
@@ -3,8 +3,19 @@ const { Table } = require("../../dist/index")
|
|
|
3
3
|
module.exports = new Table({
|
|
4
4
|
name: "a",
|
|
5
5
|
priority: 0,
|
|
6
|
-
setup
|
|
7
|
-
table.increments("id").primary()
|
|
8
|
-
table
|
|
6
|
+
setup(table) {
|
|
7
|
+
table.increments("id").primary().notNullable()
|
|
8
|
+
table
|
|
9
|
+
.integer("b_id")
|
|
10
|
+
.references("id")
|
|
11
|
+
.inTable("b")
|
|
12
|
+
.onDelete("cascade")
|
|
13
|
+
.notNullable()
|
|
14
|
+
},
|
|
15
|
+
async then({ query }) {
|
|
16
|
+
await query.insert({
|
|
17
|
+
id: 0,
|
|
18
|
+
b_id: 0,
|
|
19
|
+
})
|
|
9
20
|
},
|
|
10
21
|
})
|
package/tests/tables/b.js
CHANGED
|
@@ -4,10 +4,21 @@ module.exports = new Table({
|
|
|
4
4
|
name: "b",
|
|
5
5
|
migrations: {
|
|
6
6
|
0: (table) =>
|
|
7
|
-
table
|
|
7
|
+
table
|
|
8
|
+
.integer("c_id")
|
|
9
|
+
.references("id")
|
|
10
|
+
.inTable("c")
|
|
11
|
+
.onDelete("cascade")
|
|
12
|
+
.notNullable(),
|
|
8
13
|
},
|
|
9
14
|
priority: 1,
|
|
10
|
-
setup
|
|
11
|
-
table.increments("id").primary()
|
|
15
|
+
setup(table) {
|
|
16
|
+
table.increments("id").primary().notNullable()
|
|
17
|
+
},
|
|
18
|
+
async then({ query }) {
|
|
19
|
+
await query.insert({
|
|
20
|
+
id: 0,
|
|
21
|
+
c_id: 0,
|
|
22
|
+
})
|
|
12
23
|
},
|
|
13
24
|
})
|
package/tests/tables/c.js
CHANGED
|
@@ -3,7 +3,10 @@ const { Table } = require("../../dist/index")
|
|
|
3
3
|
module.exports = new Table({
|
|
4
4
|
name: "c",
|
|
5
5
|
priority: 2,
|
|
6
|
-
setup
|
|
7
|
-
table.increments("id").primary()
|
|
6
|
+
setup(table) {
|
|
7
|
+
table.increments("id").primary().notNullable()
|
|
8
|
+
},
|
|
9
|
+
async then({ query }) {
|
|
10
|
+
await query.insert({ id: 0 })
|
|
8
11
|
},
|
|
9
12
|
})
|
package/tests/test.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
require("dotenv").config({ path: "./.env" })
|
|
1
2
|
const path = require("path")
|
|
2
3
|
const { ORM } = require("../dist/index")
|
|
4
|
+
const a = require("./tables/a")
|
|
5
|
+
const b = require("./tables/b")
|
|
6
|
+
const c = require("./tables/c")
|
|
3
7
|
|
|
4
8
|
const orm = new ORM({
|
|
5
9
|
tablePath: path.join(__dirname, "tables"),
|
|
6
|
-
|
|
10
|
+
logger: console,
|
|
7
11
|
})
|
|
8
12
|
|
|
9
13
|
beforeAll(async () => {
|
|
@@ -21,6 +25,16 @@ test("migrations ran", async () => {
|
|
|
21
25
|
expect(await orm.db.schema.hasColumn("b", "c_id")).toBeTruthy()
|
|
22
26
|
})
|
|
23
27
|
|
|
28
|
+
test("then ran", async () => {
|
|
29
|
+
const rows = await orm.db("a").select()
|
|
30
|
+
expect(rows.length).toBe(1)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
test("cascade delete", async () => {
|
|
34
|
+
await c.query.del()
|
|
35
|
+
expect(await a.isEmpty()).toBeTruthy()
|
|
36
|
+
})
|
|
37
|
+
|
|
24
38
|
afterAll(async () => {
|
|
25
39
|
await orm.db.destroy()
|
|
26
40
|
})
|