@ghom/orm 1.5.1 → 1.6.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.
@@ -50,9 +50,23 @@ class ORM {
50
50
  pattern: /\.js$/,
51
51
  });
52
52
  }
53
+ get cachedTables() {
54
+ return [...this.handler.elements.values()];
55
+ }
56
+ get cachedTableNames() {
57
+ return this.cachedTables.map((table) => table.options.name);
58
+ }
59
+ hasCachedTable(name) {
60
+ return this.cachedTables.some((table) => table.options.name);
61
+ }
62
+ async hasTable(name) {
63
+ return this.database.schema.hasTable(name);
64
+ }
65
+ /**
66
+ * Handle the table files and create the tables in the database.
67
+ */
53
68
  async init() {
54
69
  await this.handler.init();
55
- const tables = [...this.handler.elements.values()];
56
70
  try {
57
71
  await this.database.raw("PRAGMA foreign_keys = ON;");
58
72
  }
@@ -67,11 +81,14 @@ class ORM {
67
81
  });
68
82
  migration.orm = this;
69
83
  await migration.make();
70
- for (const table of tables.sort((a, b) => (b.options.priority ?? 0) - (a.options.priority ?? 0))) {
84
+ for (const table of this.cachedTables.sort((a, b) => (b.options.priority ?? 0) - (a.options.priority ?? 0))) {
71
85
  table.orm = this;
72
86
  await table.make();
73
87
  }
74
88
  }
89
+ async raw(sql) {
90
+ return this.database.raw(sql);
91
+ }
75
92
  /**
76
93
  * Extract the database to a CSV file.
77
94
  */
@@ -17,9 +17,18 @@ class Table {
17
17
  get query() {
18
18
  return this.db(this.options.name);
19
19
  }
20
+ async count(where) {
21
+ return this.query
22
+ .select(this.db.raw("count(*) as total"))
23
+ .whereRaw(where ?? "1=1")
24
+ .then((rows) => rows[0].total);
25
+ }
20
26
  async hasColumn(name) {
21
27
  return this.db.schema.hasColumn(this.options.name, name);
22
28
  }
29
+ async getColumn(name) {
30
+ return this.db(this.options.name).columnInfo(name);
31
+ }
23
32
  async getColumns() {
24
33
  return this.db(this.options.name).columnInfo();
25
34
  }
@@ -27,10 +36,7 @@ class Table {
27
36
  return this.getColumns().then(Object.keys);
28
37
  }
29
38
  async isEmpty() {
30
- return this.query
31
- .select()
32
- .limit(1)
33
- .then((rows) => rows.length === 0);
39
+ return this.count().then((count) => count === 0);
34
40
  }
35
41
  async make() {
36
42
  if (!this.orm)
@@ -56,8 +62,10 @@ class Table {
56
62
  }
57
63
  catch (error) {
58
64
  this.orm.config.logger?.error(error);
65
+ throw error;
59
66
  }
60
- await this.options.then?.bind(this)(this);
67
+ if ((await this.count()) === 0)
68
+ await this.options.then?.bind(this)(this);
61
69
  return this;
62
70
  }
63
71
  async migrate() {
@@ -24,9 +24,23 @@ export class ORM {
24
24
  pattern: /\.js$/,
25
25
  });
26
26
  }
27
+ get cachedTables() {
28
+ return [...this.handler.elements.values()];
29
+ }
30
+ get cachedTableNames() {
31
+ return this.cachedTables.map((table) => table.options.name);
32
+ }
33
+ hasCachedTable(name) {
34
+ return this.cachedTables.some((table) => table.options.name);
35
+ }
36
+ async hasTable(name) {
37
+ return this.database.schema.hasTable(name);
38
+ }
39
+ /**
40
+ * Handle the table files and create the tables in the database.
41
+ */
27
42
  async init() {
28
43
  await this.handler.init();
29
- const tables = [...this.handler.elements.values()];
30
44
  try {
31
45
  await this.database.raw("PRAGMA foreign_keys = ON;");
32
46
  }
@@ -41,11 +55,14 @@ export class ORM {
41
55
  });
42
56
  migration.orm = this;
43
57
  await migration.make();
44
- for (const table of tables.sort((a, b) => (b.options.priority ?? 0) - (a.options.priority ?? 0))) {
58
+ for (const table of this.cachedTables.sort((a, b) => (b.options.priority ?? 0) - (a.options.priority ?? 0))) {
45
59
  table.orm = this;
46
60
  await table.make();
47
61
  }
48
62
  }
63
+ async raw(sql) {
64
+ return this.database.raw(sql);
65
+ }
49
66
  /**
50
67
  * Extract the database to a CSV file.
51
68
  */
@@ -13,9 +13,18 @@ export class Table {
13
13
  get query() {
14
14
  return this.db(this.options.name);
15
15
  }
16
+ async count(where) {
17
+ return this.query
18
+ .select(this.db.raw("count(*) as total"))
19
+ .whereRaw(where ?? "1=1")
20
+ .then((rows) => rows[0].total);
21
+ }
16
22
  async hasColumn(name) {
17
23
  return this.db.schema.hasColumn(this.options.name, name);
18
24
  }
25
+ async getColumn(name) {
26
+ return this.db(this.options.name).columnInfo(name);
27
+ }
19
28
  async getColumns() {
20
29
  return this.db(this.options.name).columnInfo();
21
30
  }
@@ -23,10 +32,7 @@ export class Table {
23
32
  return this.getColumns().then(Object.keys);
24
33
  }
25
34
  async isEmpty() {
26
- return this.query
27
- .select()
28
- .limit(1)
29
- .then((rows) => rows.length === 0);
35
+ return this.count().then((count) => count === 0);
30
36
  }
31
37
  async make() {
32
38
  if (!this.orm)
@@ -52,8 +58,10 @@ export class Table {
52
58
  }
53
59
  catch (error) {
54
60
  this.orm.config.logger?.error(error);
61
+ throw error;
55
62
  }
56
- await this.options.then?.bind(this)(this);
63
+ if ((await this.count()) === 0)
64
+ await this.options.then?.bind(this)(this);
57
65
  return this;
58
66
  }
59
67
  async migrate() {
@@ -33,7 +33,15 @@ export declare class ORM {
33
33
  database: Knex;
34
34
  handler: Handler<Table<any>>;
35
35
  constructor(config: ORMConfig);
36
+ get cachedTables(): Table<any>[];
37
+ get cachedTableNames(): string[];
38
+ hasCachedTable(name: string): boolean;
39
+ hasTable(name: string): Promise<boolean>;
40
+ /**
41
+ * Handle the table files and create the tables in the database.
42
+ */
36
43
  init(): Promise<void>;
44
+ raw(sql: Knex.Value): Promise<Knex.Raw>;
37
45
  /**
38
46
  * Extract the database to a CSV file.
39
47
  */
@@ -4,7 +4,7 @@ export interface MigrationData {
4
4
  table: string;
5
5
  version: number;
6
6
  }
7
- export interface TableOptions<Type extends {}> {
7
+ export interface TableOptions<Type extends object = object> {
8
8
  name: string;
9
9
  priority?: number;
10
10
  migrations?: {
@@ -13,7 +13,7 @@ export interface TableOptions<Type extends {}> {
13
13
  then?: (this: Table<Type>, table: Table<Type>) => unknown;
14
14
  setup: (table: Knex.CreateTableBuilder) => void;
15
15
  }
16
- export declare class Table<Type extends {}> {
16
+ export declare class Table<Type extends object = object> {
17
17
  readonly options: TableOptions<Type>;
18
18
  orm?: ORM;
19
19
  constructor(options: TableOptions<Type>);
@@ -27,9 +27,11 @@ export declare class Table<Type extends {}> {
27
27
  _intersectProps: {};
28
28
  _unionProps: never;
29
29
  }[]>;
30
+ count(where?: string): Promise<number>;
30
31
  hasColumn(name: keyof Type): Promise<boolean>;
31
- getColumns(): Promise<Record<string | number | symbol, Knex.ColumnInfo>>;
32
- getColumnNames(): Promise<string[]>;
32
+ getColumn(name: keyof Type): Promise<Knex.ColumnInfo>;
33
+ getColumns(): Promise<Record<keyof Type, Knex.ColumnInfo>>;
34
+ getColumnNames(): Promise<Array<keyof Type>>;
33
35
  isEmpty(): Promise<boolean>;
34
36
  make(): Promise<this>;
35
37
  private migrate;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ghom/orm",
3
- "version": "1.5.1",
3
+ "version": "1.6.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "dist/esm/index.js",
@@ -33,7 +33,7 @@
33
33
  "sqlite3": "^5.1.6"
34
34
  },
35
35
  "dependencies": {
36
- "@ghom/handler": "^1.3.0",
36
+ "@ghom/handler": "^2.0.0",
37
37
  "knex": "^2.4.2"
38
38
  }
39
39
  }
package/src/app/orm.ts CHANGED
@@ -43,8 +43,8 @@ export interface ORMConfig {
43
43
  }
44
44
 
45
45
  export class ORM {
46
- database: Knex
47
- handler: Handler<Table<any>>
46
+ public database: Knex
47
+ public handler: Handler<Table<any>>
48
48
 
49
49
  constructor(public config: ORMConfig) {
50
50
  this.database = knex(
@@ -56,6 +56,7 @@ export class ORM {
56
56
  },
57
57
  }
58
58
  )
59
+
59
60
  this.handler = new Handler(config.location, {
60
61
  loader: (filepath) =>
61
62
  import(isCJS ? filepath : url.pathToFileURL(filepath).href).then(
@@ -65,11 +66,28 @@ export class ORM {
65
66
  })
66
67
  }
67
68
 
69
+ get cachedTables() {
70
+ return [...this.handler.elements.values()]
71
+ }
72
+
73
+ get cachedTableNames() {
74
+ return this.cachedTables.map((table) => table.options.name)
75
+ }
76
+
77
+ hasCachedTable(name: string) {
78
+ return this.cachedTables.some((table) => table.options.name)
79
+ }
80
+
81
+ async hasTable(name: string): Promise<boolean> {
82
+ return this.database.schema.hasTable(name)
83
+ }
84
+
85
+ /**
86
+ * Handle the table files and create the tables in the database.
87
+ */
68
88
  async init() {
69
89
  await this.handler.init()
70
90
 
71
- const tables = [...this.handler.elements.values()]
72
-
73
91
  try {
74
92
  await this.database.raw("PRAGMA foreign_keys = ON;")
75
93
  } catch (error) {}
@@ -86,7 +104,7 @@ export class ORM {
86
104
  migration.orm = this
87
105
  await migration.make()
88
106
 
89
- for (const table of tables.sort(
107
+ for (const table of this.cachedTables.sort(
90
108
  (a, b) => (b.options.priority ?? 0) - (a.options.priority ?? 0)
91
109
  )) {
92
110
  table.orm = this
@@ -94,6 +112,10 @@ export class ORM {
94
112
  }
95
113
  }
96
114
 
115
+ async raw(sql: Knex.Value): Promise<Knex.Raw> {
116
+ return this.database.raw(sql)
117
+ }
118
+
97
119
  /**
98
120
  * Extract the database to a CSV file.
99
121
  */
package/src/app/table.ts CHANGED
@@ -7,7 +7,7 @@ export interface MigrationData {
7
7
  version: number
8
8
  }
9
9
 
10
- export interface TableOptions<Type extends {}> {
10
+ export interface TableOptions<Type extends object = object> {
11
11
  name: string
12
12
  priority?: number
13
13
  migrations?: { [version: number]: (table: Knex.CreateTableBuilder) => void }
@@ -15,7 +15,7 @@ export interface TableOptions<Type extends {}> {
15
15
  setup: (table: Knex.CreateTableBuilder) => void
16
16
  }
17
17
 
18
- export class Table<Type extends {}> {
18
+ export class Table<Type extends object = object> {
19
19
  orm?: ORM
20
20
 
21
21
  constructor(public readonly options: TableOptions<Type>) {}
@@ -29,23 +29,31 @@ export class Table<Type extends {}> {
29
29
  return this.db<Type>(this.options.name)
30
30
  }
31
31
 
32
+ async count(where?: string): Promise<number> {
33
+ return this.query
34
+ .select(this.db.raw("count(*) as total"))
35
+ .whereRaw(where ?? "1=1")
36
+ .then((rows) => (rows[0] as unknown as { total: number }).total)
37
+ }
38
+
32
39
  async hasColumn(name: keyof Type): Promise<boolean> {
33
40
  return this.db.schema.hasColumn(this.options.name, name as string)
34
41
  }
35
42
 
36
- async getColumns() {
43
+ async getColumn(name: keyof Type): Promise<Knex.ColumnInfo> {
44
+ return this.db(this.options.name).columnInfo(name)
45
+ }
46
+
47
+ async getColumns(): Promise<Record<keyof Type, Knex.ColumnInfo>> {
37
48
  return this.db(this.options.name).columnInfo()
38
49
  }
39
50
 
40
- async getColumnNames() {
41
- return this.getColumns().then(Object.keys)
51
+ async getColumnNames(): Promise<Array<keyof Type>> {
52
+ return this.getColumns().then(Object.keys) as Promise<Array<keyof Type>>
42
53
  }
43
54
 
44
55
  async isEmpty(): Promise<boolean> {
45
- return this.query
46
- .select()
47
- .limit(1)
48
- .then((rows) => rows.length === 0)
56
+ return this.count().then((count) => count === 0)
49
57
  }
50
58
 
51
59
  async make(): Promise<this> {
@@ -91,9 +99,11 @@ export class Table<Type extends {}> {
91
99
  }
92
100
  } catch (error: any) {
93
101
  this.orm.config.logger?.error(error)
102
+
103
+ throw error
94
104
  }
95
105
 
96
- await this.options.then?.bind(this)(this)
106
+ if ((await this.count()) === 0) await this.options.then?.bind(this)(this)
97
107
 
98
108
  return this
99
109
  }
package/tests/test.js CHANGED
@@ -20,18 +20,23 @@ beforeAll(async () => {
20
20
 
21
21
  describe("table management", () => {
22
22
  test("tables created", async () => {
23
- expect(await orm.database.schema.hasTable("migration")).toBeTruthy()
24
- expect(await orm.database.schema.hasTable("a")).toBeTruthy()
25
- expect(await orm.database.schema.hasTable("b")).toBeTruthy()
26
- expect(await orm.database.schema.hasTable("c")).toBeTruthy()
23
+ expect(await orm.hasTable("migration")).toBeTruthy()
24
+ expect(await orm.hasTable("a")).toBeTruthy()
25
+ expect(await orm.hasTable("b")).toBeTruthy()
26
+ expect(await orm.hasTable("c")).toBeTruthy()
27
+
28
+ expect(orm.hasCachedTable("migration")).toBeTruthy()
29
+ expect(orm.hasCachedTable("a")).toBeTruthy()
30
+ expect(orm.hasCachedTable("b")).toBeTruthy()
31
+ expect(orm.hasCachedTable("c")).toBeTruthy()
27
32
  })
28
33
 
29
34
  test("migrations ran", async () => {
30
- expect(await orm.database.schema.hasColumn("b", "c_id")).toBeTruthy()
35
+ expect(await b.hasColumn("c_id")).toBeTruthy()
31
36
  })
32
37
 
33
38
  test("then ran", async () => {
34
- const rows = await orm.database("a").select()
39
+ const rows = await a.query.select()
35
40
 
36
41
  expect(rows.length).toBe(1)
37
42
  })
@@ -103,6 +108,24 @@ describe("database migration", () => {
103
108
  })
104
109
  })
105
110
 
111
+ describe("table getters", () => {
112
+ test("table info", async () => {
113
+ expect(await a.getColumnNames()).toContain("id")
114
+ expect(await a.getColumnNames()).toContain("b_id")
115
+
116
+ expect(await b.getColumnNames()).toContain("id")
117
+ expect(await b.getColumnNames()).toContain("c_id")
118
+
119
+ expect(await c.getColumnNames()).toContain("id")
120
+ })
121
+
122
+ test("table names", async () => {
123
+ expect(orm.cachedTableNames).toContain("a")
124
+ expect(orm.cachedTableNames).toContain("b")
125
+ expect(orm.cachedTableNames).toContain("c")
126
+ })
127
+ })
128
+
106
129
  afterAll(async () => {
107
130
  await orm.database.destroy()
108
131
  fs.unlinkSync("a.csv")