@ghom/orm 1.0.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/dist/app/orm.d.ts +20 -0
- package/dist/app/orm.js +69 -0
- package/dist/app/table.d.ts +32 -0
- package/dist/app/table.js +80 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +14 -0
- package/package.json +28 -0
- package/readme.md +1 -0
- package/src/app/orm.ts +70 -0
- package/src/app/table.ts +106 -0
- package/src/index.ts +2 -0
- package/tests/tables/a.js +10 -0
- package/tests/tables/b.js +13 -0
- package/tests/tables/c.js +9 -0
- package/tests/test.js +26 -0
- package/tsconfig.json +13 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Handler } from "@ghom/handler";
|
|
2
|
+
import { Knex } from "knex";
|
|
3
|
+
/**
|
|
4
|
+
* @property tablePath - path to directory that contains js files of tables
|
|
5
|
+
* @property verbose - show console logs or not
|
|
6
|
+
*/
|
|
7
|
+
export interface ORMConfig {
|
|
8
|
+
verbose?: boolean;
|
|
9
|
+
tablePath: string;
|
|
10
|
+
}
|
|
11
|
+
export declare class ORM extends Handler {
|
|
12
|
+
db: Knex;
|
|
13
|
+
ormConfig: ORMConfig;
|
|
14
|
+
/**
|
|
15
|
+
* @param ormConfig configuration for table handler or just tablePath (path to directory that contains js files of tables)
|
|
16
|
+
* @param knexConfig configuration for connect to database
|
|
17
|
+
*/
|
|
18
|
+
constructor(ormConfig: ORMConfig | string, knexConfig?: Knex.Config);
|
|
19
|
+
init(): Promise<void>;
|
|
20
|
+
}
|
package/dist/app/orm.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
+
}) : (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
o[k2] = m[k];
|
|
8
|
+
}));
|
|
9
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
10
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11
|
+
}) : function(o, v) {
|
|
12
|
+
o["default"] = v;
|
|
13
|
+
});
|
|
14
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
15
|
+
if (mod && mod.__esModule) return mod;
|
|
16
|
+
var result = {};
|
|
17
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
18
|
+
__setModuleDefault(result, mod);
|
|
19
|
+
return result;
|
|
20
|
+
};
|
|
21
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
22
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
23
|
+
};
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.ORM = void 0;
|
|
26
|
+
const handler_1 = require("@ghom/handler");
|
|
27
|
+
const knex_1 = __importDefault(require("knex"));
|
|
28
|
+
const table_1 = require("./table");
|
|
29
|
+
class ORM extends handler_1.Handler {
|
|
30
|
+
/**
|
|
31
|
+
* @param ormConfig configuration for table handler or just tablePath (path to directory that contains js files of tables)
|
|
32
|
+
* @param knexConfig configuration for connect to database
|
|
33
|
+
*/
|
|
34
|
+
constructor(ormConfig, knexConfig = {
|
|
35
|
+
client: "sqlite3",
|
|
36
|
+
useNullAsDefault: true,
|
|
37
|
+
connection: {
|
|
38
|
+
filename: ":memory:",
|
|
39
|
+
},
|
|
40
|
+
}) {
|
|
41
|
+
super(typeof ormConfig === "string" ? ormConfig : ormConfig.tablePath);
|
|
42
|
+
this.ormConfig =
|
|
43
|
+
typeof ormConfig === "string" ? { tablePath: ormConfig } : ormConfig;
|
|
44
|
+
this.db = (0, knex_1.default)(knexConfig);
|
|
45
|
+
}
|
|
46
|
+
async init() {
|
|
47
|
+
this.once("finish", async (pathList) => {
|
|
48
|
+
const tables = await Promise.all(pathList.map(async (filepath) => {
|
|
49
|
+
return Promise.resolve().then(() => __importStar(require(filepath))).then((file) => file.default);
|
|
50
|
+
}));
|
|
51
|
+
tables.unshift(new table_1.Table({
|
|
52
|
+
name: "migration",
|
|
53
|
+
priority: Infinity,
|
|
54
|
+
setup: (table) => {
|
|
55
|
+
table.string("table").unique().notNullable();
|
|
56
|
+
table.integer("version").notNullable();
|
|
57
|
+
},
|
|
58
|
+
}));
|
|
59
|
+
tables.forEach((table) => (table.orm = this));
|
|
60
|
+
return Promise.all(tables
|
|
61
|
+
.sort((a, b) => {
|
|
62
|
+
return (b.options.priority ?? 0) - (a.options.priority ?? 0);
|
|
63
|
+
})
|
|
64
|
+
.map((table) => table.make()));
|
|
65
|
+
});
|
|
66
|
+
await this.load();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
exports.ORM = ORM;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Knex } from "knex";
|
|
2
|
+
import { ORM } from "./orm.js";
|
|
3
|
+
export interface MigrationData {
|
|
4
|
+
table: string;
|
|
5
|
+
version: number;
|
|
6
|
+
}
|
|
7
|
+
export interface TableOptions<Type> {
|
|
8
|
+
name: string;
|
|
9
|
+
priority?: number;
|
|
10
|
+
migrations?: {
|
|
11
|
+
[version: number]: (table: Knex.CreateTableBuilder) => void;
|
|
12
|
+
};
|
|
13
|
+
setup: (table: Knex.CreateTableBuilder) => void;
|
|
14
|
+
}
|
|
15
|
+
export declare class Table<Type> {
|
|
16
|
+
readonly options: TableOptions<Type>;
|
|
17
|
+
orm?: ORM;
|
|
18
|
+
constructor(options: TableOptions<Type>);
|
|
19
|
+
private get verbose();
|
|
20
|
+
get db(): Knex<any, Record<string, any>[]>;
|
|
21
|
+
get query(): Knex.QueryBuilder<Type, {
|
|
22
|
+
_base: Type;
|
|
23
|
+
_hasSelection: false;
|
|
24
|
+
_keys: never;
|
|
25
|
+
_aliases: {};
|
|
26
|
+
_single: false;
|
|
27
|
+
_intersectProps: {};
|
|
28
|
+
_unionProps: never;
|
|
29
|
+
}[]>;
|
|
30
|
+
make(): Promise<this>;
|
|
31
|
+
private migrate;
|
|
32
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Table = void 0;
|
|
4
|
+
class Table {
|
|
5
|
+
constructor(options) {
|
|
6
|
+
this.options = options;
|
|
7
|
+
}
|
|
8
|
+
get verbose() {
|
|
9
|
+
if (!this.orm)
|
|
10
|
+
throw new Error("missing ORM");
|
|
11
|
+
return this.orm.ormConfig.verbose;
|
|
12
|
+
}
|
|
13
|
+
get db() {
|
|
14
|
+
if (!this.orm)
|
|
15
|
+
throw new Error("missing ORM");
|
|
16
|
+
return this.orm.db;
|
|
17
|
+
}
|
|
18
|
+
get query() {
|
|
19
|
+
return this.db(this.options.name);
|
|
20
|
+
}
|
|
21
|
+
async make() {
|
|
22
|
+
try {
|
|
23
|
+
await this.db.schema.createTable(this.options.name, this.options.setup);
|
|
24
|
+
if (this.verbose)
|
|
25
|
+
console.log(`created table ${this.options.name}`);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
if (error.toString().includes("syntax error")) {
|
|
29
|
+
if (this.verbose)
|
|
30
|
+
console.error(`you need to implement the "setup" method in options of your ${this.options.name} table!`);
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
if (this.verbose)
|
|
35
|
+
console.log(`loaded table ${this.options.name}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
const migrated = await this.migrate();
|
|
40
|
+
if (migrated !== false) {
|
|
41
|
+
if (this.verbose)
|
|
42
|
+
console.log(`migrated table ${this.options.name} to version ${migrated}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
if (this.verbose)
|
|
47
|
+
console.error(error);
|
|
48
|
+
}
|
|
49
|
+
return this;
|
|
50
|
+
}
|
|
51
|
+
async migrate() {
|
|
52
|
+
if (!this.options.migrations)
|
|
53
|
+
return false;
|
|
54
|
+
const migrations = new Map(Object.entries(this.options.migrations)
|
|
55
|
+
.sort((a, b) => Number(a[0]) - Number(b[0]))
|
|
56
|
+
.map((entry) => [Number(entry[0]), entry[1]]));
|
|
57
|
+
const fromDatabase = await this.db("migration")
|
|
58
|
+
.where("table", this.options.name)
|
|
59
|
+
.first();
|
|
60
|
+
const data = fromDatabase || {
|
|
61
|
+
table: this.options.name,
|
|
62
|
+
version: -1,
|
|
63
|
+
};
|
|
64
|
+
const baseVersion = data.version;
|
|
65
|
+
await this.db.schema.alterTable(this.options.name, (builder) => {
|
|
66
|
+
migrations.forEach((migration, version) => {
|
|
67
|
+
if (version <= data.version)
|
|
68
|
+
return;
|
|
69
|
+
migration(builder);
|
|
70
|
+
data.version = version;
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
await this.db("migration")
|
|
74
|
+
.insert(data)
|
|
75
|
+
.onConflict("table")
|
|
76
|
+
.merge();
|
|
77
|
+
return baseVersion === data.version ? false : data.version;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
exports.Table = Table;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
+
}) : (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
o[k2] = m[k];
|
|
8
|
+
}));
|
|
9
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
10
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
11
|
+
};
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
__exportStar(require("./app/orm"), exports);
|
|
14
|
+
__exportStar(require("./app/table"), exports);
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ghom/orm",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"description": "TypeScript KnexJS ORM & handler",
|
|
8
|
+
"prettier": {
|
|
9
|
+
"semi": false
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"format": "prettier --write src tsconfig.*",
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"test": "npm run build && jest tests/test.js --detectOpenHandles",
|
|
15
|
+
"prepublishOnly": "npm run format && npm test"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@vscode/sqlite3": "^5.0.7",
|
|
19
|
+
"better-sqlite3": "^7.5.0",
|
|
20
|
+
"jest": "^27.5.1",
|
|
21
|
+
"prettier": "^2.5.1",
|
|
22
|
+
"typescript": "^4.5.5"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@ghom/handler": "^1.1.0",
|
|
26
|
+
"knex": "^1.0.3"
|
|
27
|
+
}
|
|
28
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# TypeScript KnexJS ORM & handler
|
package/src/app/orm.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Handler } from "@ghom/handler"
|
|
2
|
+
import { Knex, default as knex } from "knex"
|
|
3
|
+
import { MigrationData, Table } from "./table"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @property tablePath - path to directory that contains js files of tables
|
|
7
|
+
* @property verbose - show console logs or not
|
|
8
|
+
*/
|
|
9
|
+
export interface ORMConfig {
|
|
10
|
+
verbose?: boolean
|
|
11
|
+
tablePath: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class ORM extends Handler {
|
|
15
|
+
db: Knex
|
|
16
|
+
ormConfig: ORMConfig
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param ormConfig configuration for table handler or just tablePath (path to directory that contains js files of tables)
|
|
20
|
+
* @param knexConfig configuration for connect to database
|
|
21
|
+
*/
|
|
22
|
+
constructor(
|
|
23
|
+
ormConfig: ORMConfig | string,
|
|
24
|
+
knexConfig: Knex.Config = {
|
|
25
|
+
client: "sqlite3",
|
|
26
|
+
useNullAsDefault: true,
|
|
27
|
+
connection: {
|
|
28
|
+
filename: ":memory:",
|
|
29
|
+
},
|
|
30
|
+
}
|
|
31
|
+
) {
|
|
32
|
+
super(typeof ormConfig === "string" ? ormConfig : ormConfig.tablePath)
|
|
33
|
+
this.ormConfig =
|
|
34
|
+
typeof ormConfig === "string" ? { tablePath: ormConfig } : ormConfig
|
|
35
|
+
this.db = knex(knexConfig)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async init() {
|
|
39
|
+
this.once("finish", async (pathList) => {
|
|
40
|
+
const tables: Table<any>[] = await Promise.all(
|
|
41
|
+
pathList.map(async (filepath) => {
|
|
42
|
+
return import(filepath).then((file) => file.default)
|
|
43
|
+
})
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
tables.unshift(
|
|
47
|
+
new Table<MigrationData>({
|
|
48
|
+
name: "migration",
|
|
49
|
+
priority: Infinity,
|
|
50
|
+
setup: (table) => {
|
|
51
|
+
table.string("table").unique().notNullable()
|
|
52
|
+
table.integer("version").notNullable()
|
|
53
|
+
},
|
|
54
|
+
})
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
tables.forEach((table) => (table.orm = this))
|
|
58
|
+
|
|
59
|
+
return Promise.all(
|
|
60
|
+
tables
|
|
61
|
+
.sort((a, b) => {
|
|
62
|
+
return (b.options.priority ?? 0) - (a.options.priority ?? 0)
|
|
63
|
+
})
|
|
64
|
+
.map((table) => table.make())
|
|
65
|
+
)
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
await this.load()
|
|
69
|
+
}
|
|
70
|
+
}
|
package/src/app/table.ts
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { Knex } from "knex"
|
|
2
|
+
import { ORM } from "./orm.js"
|
|
3
|
+
|
|
4
|
+
export interface MigrationData {
|
|
5
|
+
table: string
|
|
6
|
+
version: number
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface TableOptions<Type> {
|
|
10
|
+
name: string
|
|
11
|
+
priority?: number
|
|
12
|
+
migrations?: { [version: number]: (table: Knex.CreateTableBuilder) => void }
|
|
13
|
+
setup: (table: Knex.CreateTableBuilder) => void
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class Table<Type> {
|
|
17
|
+
orm?: ORM
|
|
18
|
+
|
|
19
|
+
constructor(public readonly options: TableOptions<Type>) {}
|
|
20
|
+
|
|
21
|
+
private get verbose() {
|
|
22
|
+
if (!this.orm) throw new Error("missing ORM")
|
|
23
|
+
return this.orm.ormConfig.verbose
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get db() {
|
|
27
|
+
if (!this.orm) throw new Error("missing ORM")
|
|
28
|
+
return this.orm.db
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
get query() {
|
|
32
|
+
return this.db<Type>(this.options.name)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async make(): Promise<this> {
|
|
36
|
+
try {
|
|
37
|
+
await this.db.schema.createTable(this.options.name, this.options.setup)
|
|
38
|
+
if (this.verbose) console.log(`created table ${this.options.name}`)
|
|
39
|
+
} catch (error: any) {
|
|
40
|
+
if (error.toString().includes("syntax error")) {
|
|
41
|
+
if (this.verbose)
|
|
42
|
+
console.error(
|
|
43
|
+
`you need to implement the "setup" method in options of your ${this.options.name} table!`
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
throw error
|
|
47
|
+
} else {
|
|
48
|
+
if (this.verbose) console.log(`loaded table ${this.options.name}`)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
const migrated = await this.migrate()
|
|
54
|
+
|
|
55
|
+
if (migrated !== false) {
|
|
56
|
+
if (this.verbose)
|
|
57
|
+
console.log(
|
|
58
|
+
`migrated table ${this.options.name} to version ${migrated}`
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
} catch (error: any) {
|
|
62
|
+
if (this.verbose) console.error(error)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return this
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private async migrate(): Promise<false | number> {
|
|
69
|
+
if (!this.options.migrations) return false
|
|
70
|
+
|
|
71
|
+
const migrations = new Map<
|
|
72
|
+
number,
|
|
73
|
+
(table: Knex.CreateTableBuilder) => void
|
|
74
|
+
>(
|
|
75
|
+
Object.entries(this.options.migrations)
|
|
76
|
+
.sort((a, b) => Number(a[0]) - Number(b[0]))
|
|
77
|
+
.map((entry) => [Number(entry[0]), entry[1]])
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
const fromDatabase = await this.db<MigrationData>("migration")
|
|
81
|
+
.where("table", this.options.name)
|
|
82
|
+
.first()
|
|
83
|
+
|
|
84
|
+
const data = fromDatabase || {
|
|
85
|
+
table: this.options.name,
|
|
86
|
+
version: -1,
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const baseVersion = data.version
|
|
90
|
+
|
|
91
|
+
await this.db.schema.alterTable(this.options.name, (builder) => {
|
|
92
|
+
migrations.forEach((migration, version) => {
|
|
93
|
+
if (version <= data.version) return
|
|
94
|
+
migration(builder)
|
|
95
|
+
data.version = version
|
|
96
|
+
})
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
await this.db<MigrationData>("migration")
|
|
100
|
+
.insert(data)
|
|
101
|
+
.onConflict("table")
|
|
102
|
+
.merge()
|
|
103
|
+
|
|
104
|
+
return baseVersion === data.version ? false : data.version
|
|
105
|
+
}
|
|
106
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const { Table } = require("../../dist/index")
|
|
2
|
+
|
|
3
|
+
module.exports = new Table({
|
|
4
|
+
name: "b",
|
|
5
|
+
migrations: {
|
|
6
|
+
0: (table) =>
|
|
7
|
+
table.integer("c_id").references("id").inTable("c").notNullable(),
|
|
8
|
+
},
|
|
9
|
+
priority: 1,
|
|
10
|
+
setup: (table) => {
|
|
11
|
+
table.increments("id").primary()
|
|
12
|
+
},
|
|
13
|
+
})
|
package/tests/test.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const path = require("path")
|
|
2
|
+
const { ORM } = require("../dist/index")
|
|
3
|
+
|
|
4
|
+
const orm = new ORM({
|
|
5
|
+
tablePath: path.join(__dirname, "tables"),
|
|
6
|
+
verbose: false,
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
beforeAll(async () => {
|
|
10
|
+
await orm.init()
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
test("tables created", async () => {
|
|
14
|
+
expect(await orm.db.schema.hasTable("migration")).toBeTruthy()
|
|
15
|
+
expect(await orm.db.schema.hasTable("a")).toBeTruthy()
|
|
16
|
+
expect(await orm.db.schema.hasTable("b")).toBeTruthy()
|
|
17
|
+
expect(await orm.db.schema.hasTable("c")).toBeTruthy()
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
test("migrations ran", async () => {
|
|
21
|
+
expect(await orm.db.schema.hasColumn("b", "c_id")).toBeTruthy()
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
afterAll(async () => {
|
|
25
|
+
await orm.db.destroy()
|
|
26
|
+
})
|
package/tsconfig.json
ADDED