@ghom/orm 1.9.2 → 2.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/.github/workflows/test.yml +9 -9
- package/biome.json +45 -0
- package/bun.lock +488 -0
- package/dist/app/backup.d.ts +3 -3
- package/dist/app/backup.js +21 -13
- package/dist/app/column.d.ts +225 -0
- package/dist/app/column.js +342 -0
- package/dist/app/orm.d.ts +42 -8
- package/dist/app/orm.js +68 -14
- package/dist/app/table.d.ts +52 -21
- package/dist/app/table.js +43 -21
- package/dist/app/util.d.ts +1 -1
- package/dist/app/util.js +6 -5
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/package.json +43 -47
- package/readme.md +19 -0
- package/tests/orm.test.ts +288 -0
- package/tests/tables/a.ts +16 -0
- package/tests/tables/b.ts +16 -0
- package/tests/tables/c.ts +14 -0
- package/tests/tables/d.ts +32 -0
- package/tsconfig.json +1 -0
- package/tests/tables/a.js +0 -25
- package/tests/tables/b.js +0 -30
- package/tests/tables/c.js +0 -17
- package/tests/test.js +0 -205
package/dist/app/orm.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Handler } from "@ghom/handler";
|
|
2
|
-
import { Knex } from "knex";
|
|
3
|
-
import { TextStyle } from "./util.js";
|
|
4
|
-
import { Table } from "./table.js";
|
|
5
2
|
import { CachedQuery } from "@ghom/query";
|
|
3
|
+
import { type Knex } from "knex";
|
|
4
|
+
import { Table } from "./table.js";
|
|
5
|
+
import { type TextStyle } from "./util.js";
|
|
6
6
|
export interface ILogger {
|
|
7
7
|
log: (message: string) => void;
|
|
8
8
|
error: (error: string | Error) => void;
|
|
@@ -44,13 +44,47 @@ export interface ORMConfig {
|
|
|
44
44
|
*/
|
|
45
45
|
caching?: number;
|
|
46
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* The main ORM class that manages database connections, tables, and caching.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* // With database connection
|
|
52
|
+
* const orm = new ORM({
|
|
53
|
+
* tableLocation: "./tables",
|
|
54
|
+
* database: { client: "sqlite3", connection: { filename: ":memory:" } }
|
|
55
|
+
* })
|
|
56
|
+
* await orm.init()
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* // Without database connection (for type exports only)
|
|
60
|
+
* const orm = new ORM(false)
|
|
61
|
+
* orm.isConnected // false
|
|
62
|
+
*/
|
|
47
63
|
export declare class ORM {
|
|
48
|
-
config: ORMConfig;
|
|
64
|
+
config: ORMConfig | false;
|
|
49
65
|
private _ready;
|
|
50
|
-
|
|
51
|
-
handler
|
|
52
|
-
_rawCache
|
|
53
|
-
|
|
66
|
+
_client?: Knex;
|
|
67
|
+
handler?: Handler<Table<any>>;
|
|
68
|
+
_rawCache?: CachedQuery<[raw: string], Knex.Raw>;
|
|
69
|
+
/**
|
|
70
|
+
* Creates a new ORM instance.
|
|
71
|
+
*
|
|
72
|
+
* @param config - The ORM configuration, or `false` to create an unconnected instance.
|
|
73
|
+
*
|
|
74
|
+
* When `false` is passed, the ORM will not connect to any database.
|
|
75
|
+
* This is useful for scenarios where you only need to export types or
|
|
76
|
+
* use the ORM structure without an actual database connection.
|
|
77
|
+
*
|
|
78
|
+
* Methods that require a database connection will throw an error
|
|
79
|
+
* if called on an unconnected ORM instance.
|
|
80
|
+
*/
|
|
81
|
+
constructor(config: ORMConfig | false);
|
|
82
|
+
private requireClient;
|
|
83
|
+
get client(): Knex;
|
|
84
|
+
/**
|
|
85
|
+
* Returns true if the ORM has a database client connected.
|
|
86
|
+
*/
|
|
87
|
+
get isConnected(): boolean;
|
|
54
88
|
get cachedTables(): Table<any>[];
|
|
55
89
|
get cachedTableNames(): string[];
|
|
56
90
|
hasCachedTable(name: string): boolean;
|
package/dist/app/orm.js
CHANGED
|
@@ -1,19 +1,49 @@
|
|
|
1
1
|
import url from "node:url";
|
|
2
2
|
import { Handler } from "@ghom/handler";
|
|
3
|
+
import { CachedQuery } from "@ghom/query";
|
|
3
4
|
import { default as knex } from "knex";
|
|
4
|
-
import {
|
|
5
|
+
import { backupTable, disableForeignKeys, enableForeignKeys, restoreBackup } from "./backup.js";
|
|
5
6
|
import { Table } from "./table.js";
|
|
6
|
-
import {
|
|
7
|
-
|
|
7
|
+
import { isCJS } from "./util.js";
|
|
8
|
+
/**
|
|
9
|
+
* The main ORM class that manages database connections, tables, and caching.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* // With database connection
|
|
13
|
+
* const orm = new ORM({
|
|
14
|
+
* tableLocation: "./tables",
|
|
15
|
+
* database: { client: "sqlite3", connection: { filename: ":memory:" } }
|
|
16
|
+
* })
|
|
17
|
+
* await orm.init()
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* // Without database connection (for type exports only)
|
|
21
|
+
* const orm = new ORM(false)
|
|
22
|
+
* orm.isConnected // false
|
|
23
|
+
*/
|
|
8
24
|
export class ORM {
|
|
9
25
|
config;
|
|
10
26
|
_ready = false;
|
|
11
|
-
|
|
27
|
+
_client;
|
|
12
28
|
handler;
|
|
13
29
|
_rawCache;
|
|
30
|
+
/**
|
|
31
|
+
* Creates a new ORM instance.
|
|
32
|
+
*
|
|
33
|
+
* @param config - The ORM configuration, or `false` to create an unconnected instance.
|
|
34
|
+
*
|
|
35
|
+
* When `false` is passed, the ORM will not connect to any database.
|
|
36
|
+
* This is useful for scenarios where you only need to export types or
|
|
37
|
+
* use the ORM structure without an actual database connection.
|
|
38
|
+
*
|
|
39
|
+
* Methods that require a database connection will throw an error
|
|
40
|
+
* if called on an unconnected ORM instance.
|
|
41
|
+
*/
|
|
14
42
|
constructor(config) {
|
|
15
43
|
this.config = config;
|
|
16
|
-
|
|
44
|
+
if (config === false)
|
|
45
|
+
return;
|
|
46
|
+
this._client = knex(config.database ?? {
|
|
17
47
|
client: "sqlite3",
|
|
18
48
|
useNullAsDefault: true,
|
|
19
49
|
connection: {
|
|
@@ -31,7 +61,23 @@ export class ORM {
|
|
|
31
61
|
});
|
|
32
62
|
this._rawCache = new CachedQuery(async (raw) => await this.raw(raw), config.caching ?? Infinity);
|
|
33
63
|
}
|
|
64
|
+
requireClient() {
|
|
65
|
+
if (!this._client)
|
|
66
|
+
throw new Error("ORM client is not initialized. Cannot use this method without a database connection.");
|
|
67
|
+
}
|
|
68
|
+
get client() {
|
|
69
|
+
this.requireClient();
|
|
70
|
+
return this._client;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Returns true if the ORM has a database client connected.
|
|
74
|
+
*/
|
|
75
|
+
get isConnected() {
|
|
76
|
+
return this._client !== undefined;
|
|
77
|
+
}
|
|
34
78
|
get cachedTables() {
|
|
79
|
+
if (!this.handler)
|
|
80
|
+
return [];
|
|
35
81
|
return [...this.handler.elements.values()];
|
|
36
82
|
}
|
|
37
83
|
get cachedTableNames() {
|
|
@@ -41,12 +87,14 @@ export class ORM {
|
|
|
41
87
|
return this.cachedTables.some((table) => table.options.name === name);
|
|
42
88
|
}
|
|
43
89
|
async hasTable(name) {
|
|
44
|
-
|
|
90
|
+
this.requireClient();
|
|
91
|
+
return this._client.schema.hasTable(name);
|
|
45
92
|
}
|
|
46
93
|
/**
|
|
47
94
|
* Handle the table files and create the tables in the database.
|
|
48
95
|
*/
|
|
49
96
|
async init() {
|
|
97
|
+
this.requireClient();
|
|
50
98
|
await this.handler.init();
|
|
51
99
|
try {
|
|
52
100
|
await enableForeignKeys(this);
|
|
@@ -55,10 +103,10 @@ export class ORM {
|
|
|
55
103
|
this.handler.elements.set("migration", new Table({
|
|
56
104
|
name: "migration",
|
|
57
105
|
priority: Infinity,
|
|
58
|
-
|
|
59
|
-
table.string(
|
|
60
|
-
|
|
61
|
-
},
|
|
106
|
+
columns: (col) => ({
|
|
107
|
+
table: col.string().unique(),
|
|
108
|
+
version: col.integer(),
|
|
109
|
+
}),
|
|
62
110
|
}));
|
|
63
111
|
const sortedTables = this.cachedTables.toSorted((a, b) => (b.options.priority ?? 0) - (a.options.priority ?? 0));
|
|
64
112
|
for (const table of sortedTables) {
|
|
@@ -67,22 +115,26 @@ export class ORM {
|
|
|
67
115
|
this._ready = true;
|
|
68
116
|
}
|
|
69
117
|
raw(sql) {
|
|
118
|
+
this.requireClient();
|
|
70
119
|
if (this._ready)
|
|
71
120
|
this.cache.invalidate();
|
|
72
|
-
return this.
|
|
121
|
+
return this._client.raw(sql);
|
|
73
122
|
}
|
|
74
123
|
cache = {
|
|
75
124
|
raw: (sql, anyDataUpdated) => {
|
|
125
|
+
this.requireClient();
|
|
76
126
|
if (anyDataUpdated)
|
|
77
127
|
this.cache.invalidate();
|
|
78
128
|
return this._rawCache.get(sql, sql);
|
|
79
129
|
},
|
|
80
130
|
invalidate: () => {
|
|
81
|
-
this._rawCache
|
|
131
|
+
this._rawCache?.invalidate();
|
|
82
132
|
this.cachedTables.forEach((table) => table.cache.invalidate());
|
|
83
133
|
},
|
|
84
134
|
};
|
|
85
135
|
clientBasedOperation(operation) {
|
|
136
|
+
if (this.config === false)
|
|
137
|
+
return undefined;
|
|
86
138
|
const client = (this.config.database?.client ?? "sqlite3");
|
|
87
139
|
return operation[client]?.();
|
|
88
140
|
}
|
|
@@ -91,7 +143,8 @@ export class ORM {
|
|
|
91
143
|
* The backup will be saved in the location specified in the config.
|
|
92
144
|
*/
|
|
93
145
|
async createBackup(dirname) {
|
|
94
|
-
|
|
146
|
+
this.requireClient();
|
|
147
|
+
for (const table of this.cachedTables) {
|
|
95
148
|
await backupTable(table, dirname);
|
|
96
149
|
}
|
|
97
150
|
console.log("Database backup created.");
|
|
@@ -101,8 +154,9 @@ export class ORM {
|
|
|
101
154
|
* @warning This will delete all the data in the tables.
|
|
102
155
|
*/
|
|
103
156
|
async restoreBackup(dirname) {
|
|
157
|
+
this.requireClient();
|
|
104
158
|
await disableForeignKeys(this, async (trx) => {
|
|
105
|
-
for (
|
|
159
|
+
for (const table of this.cachedTables) {
|
|
106
160
|
await restoreBackup(table, trx, dirname);
|
|
107
161
|
}
|
|
108
162
|
});
|
package/dist/app/table.d.ts
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
|
-
import { Knex } from "knex";
|
|
2
|
-
import { ORM } from "./orm.js";
|
|
3
1
|
import { CachedQuery } from "@ghom/query";
|
|
2
|
+
import type { Knex } from "knex";
|
|
3
|
+
import { type ColumnDef, type InferColumns } from "./column.js";
|
|
4
|
+
import type { ORM } from "./orm.js";
|
|
4
5
|
export interface MigrationData {
|
|
5
6
|
table: string;
|
|
6
7
|
version: number;
|
|
7
8
|
}
|
|
8
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Table options with typed columns.
|
|
11
|
+
* Type is automatically inferred from the column definitions.
|
|
12
|
+
*/
|
|
13
|
+
export interface TableOptions<Columns extends Record<string, ColumnDef<any, any>>> {
|
|
9
14
|
name: string;
|
|
10
15
|
description?: string;
|
|
11
16
|
priority?: number;
|
|
@@ -15,22 +20,48 @@ export interface TableOptions<Type extends object = object> {
|
|
|
15
20
|
*/
|
|
16
21
|
caching?: number;
|
|
17
22
|
migrations?: {
|
|
18
|
-
[version: number]: (
|
|
23
|
+
[version: number]: (builder: Knex.CreateTableBuilder) => void;
|
|
19
24
|
};
|
|
20
|
-
then?: (this: Table<
|
|
21
|
-
|
|
25
|
+
then?: (this: Table<Columns>, table: Table<Columns>) => unknown;
|
|
26
|
+
/**
|
|
27
|
+
* Typed columns definition with automatic type inference.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* columns: (col) => ({
|
|
31
|
+
* id: col.increments(),
|
|
32
|
+
* username: col.string().unique(),
|
|
33
|
+
* age: col.integer().nullable(),
|
|
34
|
+
* role: col.enum(["admin", "user"]),
|
|
35
|
+
* })
|
|
36
|
+
*/
|
|
37
|
+
columns: (col: typeof import("./column.js").col) => Columns;
|
|
22
38
|
}
|
|
23
|
-
|
|
24
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Represents a database table with typed columns.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* const userTable = new Table({
|
|
44
|
+
* name: "user",
|
|
45
|
+
* columns: (col) => ({
|
|
46
|
+
* id: col.increments(),
|
|
47
|
+
* username: col.string().unique(),
|
|
48
|
+
* age: col.integer().nullable(),
|
|
49
|
+
* }),
|
|
50
|
+
* })
|
|
51
|
+
* // Type is automatically inferred as { id: number; username: string; age: number | null }
|
|
52
|
+
*/
|
|
53
|
+
export declare class Table<Columns extends Record<string, ColumnDef<any, any>> = Record<string, ColumnDef<any, any>>> {
|
|
54
|
+
readonly options: TableOptions<Columns>;
|
|
25
55
|
orm?: ORM;
|
|
26
|
-
_whereCache?: CachedQuery<[
|
|
27
|
-
cb: (query: Table<Type>["query"]) => unknown
|
|
28
|
-
], unknown>;
|
|
56
|
+
_whereCache?: CachedQuery<[cb: (query: Table<Columns>["query"]) => unknown], unknown>;
|
|
29
57
|
_countCache?: CachedQuery<[where: string | null], number>;
|
|
30
|
-
constructor(options: TableOptions<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
58
|
+
constructor(options: TableOptions<Columns>);
|
|
59
|
+
/** The inferred TypeScript type for rows of this table */
|
|
60
|
+
readonly $type: InferColumns<Columns>;
|
|
61
|
+
private requireOrm;
|
|
62
|
+
get client(): Knex;
|
|
63
|
+
get query(): Knex.QueryBuilder<InferColumns<Columns>, {
|
|
64
|
+
_base: InferColumns<Columns>;
|
|
34
65
|
_hasSelection: false;
|
|
35
66
|
_keys: never;
|
|
36
67
|
_aliases: {};
|
|
@@ -39,16 +70,16 @@ export declare class Table<Type extends object = object> {
|
|
|
39
70
|
_unionProps: never;
|
|
40
71
|
}[]>;
|
|
41
72
|
get cache(): {
|
|
42
|
-
get: <Return>(id: string, cb: (table: Pick<Table<
|
|
43
|
-
set: <Return>(cb: (table: Pick<Table<
|
|
73
|
+
get: <Return>(id: string, cb: (table: Pick<Table<Columns>["query"], "select" | "count" | "avg" | "sum" | "countDistinct" | "avgDistinct" | "sumDistinct">) => Return) => Return;
|
|
74
|
+
set: <Return>(cb: (table: Pick<Table<Columns>["query"], "update" | "delete" | "insert" | "upsert" | "truncate" | "jsonInsert">) => Return) => Return;
|
|
44
75
|
count: (where?: string) => Promise<number>;
|
|
45
76
|
invalidate: () => void;
|
|
46
77
|
};
|
|
47
78
|
count(where?: string): Promise<number>;
|
|
48
|
-
hasColumn(name: keyof
|
|
49
|
-
getColumn(name: keyof
|
|
50
|
-
getColumns(): Promise<Record<keyof
|
|
51
|
-
getColumnNames(): Promise<Array<keyof
|
|
79
|
+
hasColumn(name: keyof InferColumns<Columns> & string): Promise<boolean>;
|
|
80
|
+
getColumn(name: keyof InferColumns<Columns> & string): Promise<Knex.ColumnInfo>;
|
|
81
|
+
getColumns(): Promise<Record<keyof InferColumns<Columns> & string, Knex.ColumnInfo>>;
|
|
82
|
+
getColumnNames(): Promise<Array<keyof InferColumns<Columns> & string>>;
|
|
52
83
|
isEmpty(): Promise<boolean>;
|
|
53
84
|
make(orm: ORM): Promise<this>;
|
|
54
85
|
private migrate;
|
package/dist/app/table.js
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
|
-
import { styled } from "./util.js";
|
|
2
1
|
import { CachedQuery } from "@ghom/query";
|
|
2
|
+
import { buildColumnsSchema, col } from "./column.js";
|
|
3
|
+
import { styled } from "./util.js";
|
|
4
|
+
/**
|
|
5
|
+
* Represents a database table with typed columns.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* const userTable = new Table({
|
|
9
|
+
* name: "user",
|
|
10
|
+
* columns: (col) => ({
|
|
11
|
+
* id: col.increments(),
|
|
12
|
+
* username: col.string().unique(),
|
|
13
|
+
* age: col.integer().nullable(),
|
|
14
|
+
* }),
|
|
15
|
+
* })
|
|
16
|
+
* // Type is automatically inferred as { id: number; username: string; age: number | null }
|
|
17
|
+
*/
|
|
3
18
|
export class Table {
|
|
4
19
|
options;
|
|
5
20
|
orm;
|
|
@@ -8,19 +23,23 @@ export class Table {
|
|
|
8
23
|
constructor(options) {
|
|
9
24
|
this.options = options;
|
|
10
25
|
}
|
|
11
|
-
|
|
26
|
+
requireOrm() {
|
|
12
27
|
if (!this.orm)
|
|
13
28
|
throw new Error("missing ORM");
|
|
29
|
+
if (!this.orm._client)
|
|
30
|
+
throw new Error("ORM client is not initialized");
|
|
31
|
+
}
|
|
32
|
+
get client() {
|
|
33
|
+
this.requireOrm();
|
|
14
34
|
return this.orm.client;
|
|
15
35
|
}
|
|
16
36
|
get query() {
|
|
17
|
-
return this.
|
|
37
|
+
return this.client(this.options.name);
|
|
18
38
|
}
|
|
19
39
|
get cache() {
|
|
20
40
|
if (!this._whereCache || !this._countCache)
|
|
21
41
|
throw new Error("missing cache");
|
|
22
|
-
|
|
23
|
-
throw new Error("missing ORM");
|
|
42
|
+
this.requireOrm();
|
|
24
43
|
return {
|
|
25
44
|
get: (id, cb) => {
|
|
26
45
|
return this._whereCache.get(id, cb);
|
|
@@ -42,18 +61,18 @@ export class Table {
|
|
|
42
61
|
}
|
|
43
62
|
async count(where) {
|
|
44
63
|
return this.query
|
|
45
|
-
.select(this.
|
|
64
|
+
.select(this.client.raw("count(*) as total"))
|
|
46
65
|
.whereRaw(where ?? "1=1")
|
|
47
66
|
.then((rows) => +(rows?.[0] ?? { total: 0 }).total);
|
|
48
67
|
}
|
|
49
68
|
async hasColumn(name) {
|
|
50
|
-
return this.
|
|
69
|
+
return this.client.schema.hasColumn(this.options.name, name);
|
|
51
70
|
}
|
|
52
71
|
async getColumn(name) {
|
|
53
|
-
return this.
|
|
72
|
+
return this.client(this.options.name).columnInfo(name);
|
|
54
73
|
}
|
|
55
74
|
async getColumns() {
|
|
56
|
-
return this.
|
|
75
|
+
return this.client(this.options.name).columnInfo();
|
|
57
76
|
}
|
|
58
77
|
async getColumnNames() {
|
|
59
78
|
return this.getColumns().then(Object.keys);
|
|
@@ -63,18 +82,22 @@ export class Table {
|
|
|
63
82
|
}
|
|
64
83
|
async make(orm) {
|
|
65
84
|
this.orm = orm;
|
|
66
|
-
this.
|
|
67
|
-
this.
|
|
85
|
+
this.requireOrm();
|
|
86
|
+
this._whereCache = new CachedQuery((cb) => cb(this.query), this.options.caching ?? this.orm.config.caching ?? Infinity);
|
|
87
|
+
this._countCache = new CachedQuery((where) => this.count(where ?? undefined), this.options.caching ?? this.orm.config.caching ?? Infinity);
|
|
68
88
|
const tableNameLog = `table ${styled(this.orm, this.options.name, "highlight")}${this.options.description
|
|
69
89
|
? ` ${styled(this.orm, this.options.description, "description")}`
|
|
70
90
|
: ""}`;
|
|
71
91
|
try {
|
|
72
|
-
await this.
|
|
92
|
+
await this.client.schema.createTable(this.options.name, (builder) => {
|
|
93
|
+
const columns = this.options.columns(col);
|
|
94
|
+
buildColumnsSchema(builder, columns);
|
|
95
|
+
});
|
|
73
96
|
this.orm.config.logger?.log(`created table ${tableNameLog}`);
|
|
74
97
|
}
|
|
75
98
|
catch (error) {
|
|
76
99
|
if (error.toString().includes("syntax error")) {
|
|
77
|
-
this.orm.config.logger?.error(`you need to implement the "
|
|
100
|
+
this.orm.config.logger?.error(`you need to implement the "columns" callback in options of your ${styled(this.orm, this.options.name, "highlight")} table!`);
|
|
78
101
|
throw error;
|
|
79
102
|
}
|
|
80
103
|
else {
|
|
@@ -91,8 +114,10 @@ export class Table {
|
|
|
91
114
|
this.orm.config.logger?.error(error);
|
|
92
115
|
throw error;
|
|
93
116
|
}
|
|
94
|
-
if ((await this.count()) === 0)
|
|
95
|
-
|
|
117
|
+
if ((await this.count()) === 0) {
|
|
118
|
+
const thenFn = this.options.then;
|
|
119
|
+
await thenFn?.bind(this)(this);
|
|
120
|
+
}
|
|
96
121
|
return this;
|
|
97
122
|
}
|
|
98
123
|
async migrate() {
|
|
@@ -101,7 +126,7 @@ export class Table {
|
|
|
101
126
|
const migrations = new Map(Object.entries(this.options.migrations)
|
|
102
127
|
.sort((a, b) => Number(a[0]) - Number(b[0]))
|
|
103
128
|
.map((entry) => [Number(entry[0]), entry[1]]));
|
|
104
|
-
const fromDatabase = await this.
|
|
129
|
+
const fromDatabase = await this.client("migration")
|
|
105
130
|
.where("table", this.options.name)
|
|
106
131
|
.first();
|
|
107
132
|
const data = fromDatabase || {
|
|
@@ -110,17 +135,14 @@ export class Table {
|
|
|
110
135
|
};
|
|
111
136
|
const baseVersion = data.version;
|
|
112
137
|
for (const [version, migration] of migrations) {
|
|
113
|
-
await this.
|
|
138
|
+
await this.client.schema.alterTable(this.options.name, (builder) => {
|
|
114
139
|
if (version <= data.version)
|
|
115
140
|
return;
|
|
116
141
|
migration(builder);
|
|
117
142
|
data.version = version;
|
|
118
143
|
});
|
|
119
144
|
}
|
|
120
|
-
await this.
|
|
121
|
-
.insert(data)
|
|
122
|
-
.onConflict("table")
|
|
123
|
-
.merge();
|
|
145
|
+
await this.client("migration").insert(data).onConflict("table").merge();
|
|
124
146
|
return baseVersion === data.version ? false : data.version;
|
|
125
147
|
}
|
|
126
148
|
}
|
package/dist/app/util.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export type TextStyle = Parameters<typeof util.styleText>[0];
|
|
|
4
4
|
export declare const DEFAULT_BACKUP_LOCATION: string;
|
|
5
5
|
export declare const DEFAULT_BACKUP_CHUNK_SIZE: number;
|
|
6
6
|
export declare const DEFAULT_LOGGER_HIGHLIGHT = "blueBright";
|
|
7
|
-
export declare const DEFAULT_LOGGER_DESCRIPTION = "
|
|
7
|
+
export declare const DEFAULT_LOGGER_DESCRIPTION = "gray";
|
|
8
8
|
export declare const DEFAULT_LOGGER_RAW_VALUE = "magentaBright";
|
|
9
9
|
declare let isCJS: boolean;
|
|
10
10
|
export { isCJS };
|
package/dist/app/util.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import util from "node:util";
|
|
2
|
-
import path from "node:path";
|
|
3
1
|
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import util from "node:util";
|
|
4
4
|
export const DEFAULT_BACKUP_LOCATION = path.join(process.cwd(), "backup");
|
|
5
5
|
export const DEFAULT_BACKUP_CHUNK_SIZE = 5 * 1024 * 1024; // 5MB
|
|
6
6
|
export const DEFAULT_LOGGER_HIGHLIGHT = "blueBright";
|
|
7
|
-
export const DEFAULT_LOGGER_DESCRIPTION = "
|
|
7
|
+
export const DEFAULT_LOGGER_DESCRIPTION = "gray";
|
|
8
8
|
export const DEFAULT_LOGGER_RAW_VALUE = "magentaBright";
|
|
9
9
|
let isCJS = false;
|
|
10
10
|
try {
|
|
11
11
|
const pack = JSON.parse(fs.readFileSync(path.join(process.cwd(), "package.json"), "utf8"));
|
|
12
|
-
isCJS = pack.type === "commonjs" || pack.type
|
|
12
|
+
isCJS = pack.type === "commonjs" || pack.type === void 0;
|
|
13
13
|
}
|
|
14
14
|
catch {
|
|
15
15
|
throw new Error("Missing package.json: Can't detect the type of modules.\n" +
|
|
@@ -18,7 +18,8 @@ catch {
|
|
|
18
18
|
}
|
|
19
19
|
export { isCJS };
|
|
20
20
|
export function styled(orm, message, style) {
|
|
21
|
-
|
|
21
|
+
const config = orm.config !== false ? orm.config : undefined;
|
|
22
|
+
return util.styleText(config?.loggerStyles?.[style] ??
|
|
22
23
|
(style === "highlight"
|
|
23
24
|
? DEFAULT_LOGGER_HIGHLIGHT
|
|
24
25
|
: style === "rawValue"
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,47 +1,43 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@ghom/orm",
|
|
3
|
-
"version": "
|
|
4
|
-
"license": "MIT",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "dist/index.js",
|
|
7
|
-
"types": "dist/index.d.ts",
|
|
8
|
-
"description": "TypeScript KnexJS ORM & handler",
|
|
9
|
-
"homepage": "https://github.com/GhomKrosmonaute/orm",
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"@types/
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
"url": "https://github.com/GhomKrosmonaute/orm.git",
|
|
45
|
-
"type": "git"
|
|
46
|
-
}
|
|
47
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@ghom/orm",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"description": "TypeScript KnexJS ORM & handler",
|
|
9
|
+
"homepage": "https://github.com/GhomKrosmonaute/orm",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"format": "biome format --write src",
|
|
12
|
+
"lint": "biome lint .",
|
|
13
|
+
"check": "biome check --write .",
|
|
14
|
+
"build": "rimraf dist && tsc",
|
|
15
|
+
"test": "bun test",
|
|
16
|
+
"prepublishOnly": "npm run check && npm run build && npm test"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@biomejs/biome": "^2.3.13",
|
|
20
|
+
"@types/bun": "^1.1.0",
|
|
21
|
+
"rimraf": "^6.0.1",
|
|
22
|
+
"typescript": "^5.2.2"
|
|
23
|
+
},
|
|
24
|
+
"optionalDependencies": {
|
|
25
|
+
"mysql2": "^3.6.2",
|
|
26
|
+
"pg": "^8.11.3",
|
|
27
|
+
"sqlite3": "^5.1.6"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@ghom/handler": "^3.1.0",
|
|
31
|
+
"@ghom/query": "1.0.0",
|
|
32
|
+
"csv-parser": "^3.0.0",
|
|
33
|
+
"json-2-csv": "^5.5.6",
|
|
34
|
+
"knex": "^3.0.1"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=22.0.0"
|
|
38
|
+
},
|
|
39
|
+
"repository": {
|
|
40
|
+
"url": "https://github.com/GhomKrosmonaute/orm.git",
|
|
41
|
+
"type": "git"
|
|
42
|
+
}
|
|
43
|
+
}
|
package/readme.md
CHANGED
|
@@ -41,6 +41,25 @@ const orm = new ORM({
|
|
|
41
41
|
await orm.init()
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
+
### Unconnected ORM
|
|
45
|
+
|
|
46
|
+
You can also create an ORM instance without connecting to a database. This is useful when you only need to export types or prepare the ORM structure for a future database connection.
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import { ORM } from "@ghom/orm"
|
|
50
|
+
|
|
51
|
+
const orm = new ORM(false)
|
|
52
|
+
|
|
53
|
+
orm.isConnected // false
|
|
54
|
+
orm.cachedTables // []
|
|
55
|
+
orm.cachedTableNames // []
|
|
56
|
+
|
|
57
|
+
// Methods requiring a database connection will throw an error
|
|
58
|
+
orm.init() // throws Error
|
|
59
|
+
orm.hasTable("user") // throws Error
|
|
60
|
+
orm.raw("SELECT 1") // throws Error
|
|
61
|
+
```
|
|
62
|
+
|
|
44
63
|
## Add tables
|
|
45
64
|
|
|
46
65
|
The tables are automatically loaded from the `location` directory.
|