@pearl-framework/database 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/.turbo/turbo-build.log +5 -0
- package/LICENSE +21 -0
- package/README.md +88 -0
- package/dist/DatabaseManager.d.ts +14 -0
- package/dist/DatabaseManager.d.ts.map +1 -0
- package/dist/DatabaseManager.js +76 -0
- package/dist/DatabaseManager.js.map +1 -0
- package/dist/config.d.ts +32 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +2 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/migrations/Migrator.d.ts +13 -0
- package/dist/migrations/Migrator.d.ts.map +1 -0
- package/dist/migrations/Migrator.js +32 -0
- package/dist/migrations/Migrator.js.map +1 -0
- package/dist/providers/DatabaseServiceProvider.d.ts +34 -0
- package/dist/providers/DatabaseServiceProvider.d.ts.map +1 -0
- package/dist/providers/DatabaseServiceProvider.js +42 -0
- package/dist/providers/DatabaseServiceProvider.js.map +1 -0
- package/dist/query/index.d.ts +9 -0
- package/dist/query/index.d.ts.map +1 -0
- package/dist/query/index.js +11 -0
- package/dist/query/index.js.map +1 -0
- package/dist/schema/Model.d.ts +38 -0
- package/dist/schema/Model.d.ts.map +1 -0
- package/dist/schema/Model.js +77 -0
- package/dist/schema/Model.js.map +1 -0
- package/package.json +65 -0
- package/src/DatabaseManager.ts +105 -0
- package/src/config.ts +35 -0
- package/src/index.ts +20 -0
- package/src/migrations/Migrator.ts +37 -0
- package/src/providers/DatabaseServiceProvider.ts +60 -0
- package/src/query/index.ts +81 -0
- package/src/schema/Model.ts +125 -0
- package/tsconfig.json +9 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Sharvari Divekar
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# @pearl-framework/database
|
|
2
|
+
|
|
3
|
+
> Drizzle ORM integration, Model helpers, and migrations for Pearl.js
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @pearl-framework/database drizzle-orm
|
|
9
|
+
pnpm add -D drizzle-kit
|
|
10
|
+
|
|
11
|
+
# Add your driver
|
|
12
|
+
pnpm add pg # PostgreSQL
|
|
13
|
+
pnpm add mysql2 # MySQL
|
|
14
|
+
pnpm add better-sqlite3 # SQLite
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
### Define a schema
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { pgTable, serial, varchar, timestamp } from '@pearl-framework/database'
|
|
23
|
+
|
|
24
|
+
export const users = pgTable('users', {
|
|
25
|
+
id: serial('id').primaryKey(),
|
|
26
|
+
name: varchar('name', { length: 255 }).notNull(),
|
|
27
|
+
email: varchar('email', { length: 255 }).notNull().unique(),
|
|
28
|
+
createdAt: timestamp('created_at').defaultNow(),
|
|
29
|
+
})
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Define a model
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import { Model } from '@pearl-framework/database'
|
|
36
|
+
|
|
37
|
+
export class User extends Model<typeof users> {
|
|
38
|
+
static table = users
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Query
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
const db = app.make(DatabaseManager).db
|
|
46
|
+
|
|
47
|
+
await User.all(db)
|
|
48
|
+
await User.find(db, 1)
|
|
49
|
+
await User.findOrFail(db, 1)
|
|
50
|
+
await User.create(db, { name: 'Sharvari', email: 'hi@pearl.dev' })
|
|
51
|
+
await User.update(db, 1, { name: 'Updated' })
|
|
52
|
+
await User.delete(db, 1)
|
|
53
|
+
await User.count(db)
|
|
54
|
+
|
|
55
|
+
// Raw Drizzle query
|
|
56
|
+
const admins = await db.select().from(users).where(eq(users.role, 'admin'))
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### DatabaseServiceProvider
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
export class AppDatabaseServiceProvider extends DatabaseServiceProvider {
|
|
63
|
+
protected config = {
|
|
64
|
+
connection: {
|
|
65
|
+
driver: 'postgres' as const,
|
|
66
|
+
host: process.env.DB_HOST!,
|
|
67
|
+
port: Number(process.env.DB_PORT),
|
|
68
|
+
user: process.env.DB_USER!,
|
|
69
|
+
password: process.env.DB_PASSWORD!,
|
|
70
|
+
database: process.env.DB_NAME!,
|
|
71
|
+
},
|
|
72
|
+
migrationsFolder: './database/migrations',
|
|
73
|
+
runMigrationsOnBoot: true,
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Migrations
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# Generate migrations from schema changes
|
|
82
|
+
npx drizzle-kit generate:pg --schema=./src/schema
|
|
83
|
+
|
|
84
|
+
# Apply migrations
|
|
85
|
+
pnpm pearl migrate
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { DatabaseConfig } from './config.js';
|
|
2
|
+
export type AnyDrizzleDb = any;
|
|
3
|
+
export declare class DatabaseManager {
|
|
4
|
+
private _db?;
|
|
5
|
+
private _config;
|
|
6
|
+
constructor(config: DatabaseConfig);
|
|
7
|
+
connect(): Promise<AnyDrizzleDb>;
|
|
8
|
+
get db(): AnyDrizzleDb;
|
|
9
|
+
disconnect(): Promise<void>;
|
|
10
|
+
private connectPostgres;
|
|
11
|
+
private connectMysql;
|
|
12
|
+
private connectSqlite;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=DatabaseManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DatabaseManager.d.ts","sourceRoot":"","sources":["../src/DatabaseManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAIjD,MAAM,MAAM,YAAY,GAAG,GAAG,CAAA;AAE9B,qBAAa,eAAe;IACxB,OAAO,CAAC,GAAG,CAAC,CAAc;IAC1B,OAAO,CAAC,OAAO,CAAgB;gBAEnB,MAAM,EAAE,cAAc;IAM5B,OAAO,IAAI,OAAO,CAAC,YAAY,CAAC;IAkBtC,IAAI,EAAE,IAAI,YAAY,CAQrB;IAEK,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;YAanB,eAAe;YAoBf,YAAY;YAkBZ,aAAa;CAS9B"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export class DatabaseManager {
|
|
2
|
+
_db;
|
|
3
|
+
_config;
|
|
4
|
+
constructor(config) {
|
|
5
|
+
this._config = config;
|
|
6
|
+
}
|
|
7
|
+
// ─── Connection ───────────────────────────────────────────────────────────
|
|
8
|
+
async connect() {
|
|
9
|
+
if (this._db)
|
|
10
|
+
return this._db;
|
|
11
|
+
switch (this._config.driver) {
|
|
12
|
+
case 'postgres':
|
|
13
|
+
this._db = await this.connectPostgres(this._config);
|
|
14
|
+
break;
|
|
15
|
+
case 'mysql':
|
|
16
|
+
this._db = await this.connectMysql(this._config);
|
|
17
|
+
break;
|
|
18
|
+
case 'sqlite':
|
|
19
|
+
this._db = await this.connectSqlite(this._config);
|
|
20
|
+
break;
|
|
21
|
+
}
|
|
22
|
+
return this._db;
|
|
23
|
+
}
|
|
24
|
+
get db() {
|
|
25
|
+
if (!this._db) {
|
|
26
|
+
throw new Error('Database not connected. Call DatabaseManager.connect() first, ' +
|
|
27
|
+
'or ensure DatabaseServiceProvider has booted.');
|
|
28
|
+
}
|
|
29
|
+
return this._db;
|
|
30
|
+
}
|
|
31
|
+
async disconnect() {
|
|
32
|
+
if (!this._db)
|
|
33
|
+
return;
|
|
34
|
+
if (this._config.driver === 'postgres') {
|
|
35
|
+
const { sql } = await import('drizzle-orm');
|
|
36
|
+
await this._db.execute(sql `SELECT 1`); // flush pending queries
|
|
37
|
+
}
|
|
38
|
+
this._db = undefined;
|
|
39
|
+
}
|
|
40
|
+
// ─── Driver connections ───────────────────────────────────────────────────
|
|
41
|
+
async connectPostgres(config) {
|
|
42
|
+
const { drizzle } = await import('drizzle-orm/node-postgres');
|
|
43
|
+
const { Pool } = await import('pg');
|
|
44
|
+
const pool = new Pool({
|
|
45
|
+
host: config.host,
|
|
46
|
+
port: config.port ?? 5432,
|
|
47
|
+
user: config.user,
|
|
48
|
+
password: config.password,
|
|
49
|
+
database: config.database,
|
|
50
|
+
ssl: config.ssl ?? false,
|
|
51
|
+
min: config.pool?.min ?? 2,
|
|
52
|
+
max: config.pool?.max ?? 10,
|
|
53
|
+
});
|
|
54
|
+
return drizzle(pool);
|
|
55
|
+
}
|
|
56
|
+
async connectMysql(config) {
|
|
57
|
+
const { drizzle } = await import('drizzle-orm/mysql2');
|
|
58
|
+
const mysql = await import('mysql2/promise');
|
|
59
|
+
const pool = mysql.createPool({
|
|
60
|
+
host: config.host,
|
|
61
|
+
port: config.port ?? 3306,
|
|
62
|
+
user: config.user,
|
|
63
|
+
password: config.password,
|
|
64
|
+
database: config.database,
|
|
65
|
+
connectionLimit: config.pool?.max ?? 10,
|
|
66
|
+
});
|
|
67
|
+
return drizzle(pool);
|
|
68
|
+
}
|
|
69
|
+
async connectSqlite(config) {
|
|
70
|
+
const { drizzle } = await import('drizzle-orm/better-sqlite3');
|
|
71
|
+
const Database = await import('better-sqlite3');
|
|
72
|
+
const client = new Database.default(config.filename);
|
|
73
|
+
return drizzle(client);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=DatabaseManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DatabaseManager.js","sourceRoot":"","sources":["../src/DatabaseManager.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export type DatabaseDriver = 'postgres' | 'mysql' | 'sqlite';
|
|
2
|
+
export interface PostgresConfig {
|
|
3
|
+
driver: 'postgres';
|
|
4
|
+
host: string;
|
|
5
|
+
port?: number;
|
|
6
|
+
user: string;
|
|
7
|
+
password: string;
|
|
8
|
+
database: string;
|
|
9
|
+
ssl?: boolean;
|
|
10
|
+
pool?: {
|
|
11
|
+
min?: number;
|
|
12
|
+
max?: number;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export interface MysqlConfig {
|
|
16
|
+
driver: 'mysql';
|
|
17
|
+
host: string;
|
|
18
|
+
port?: number;
|
|
19
|
+
user: string;
|
|
20
|
+
password: string;
|
|
21
|
+
database: string;
|
|
22
|
+
pool?: {
|
|
23
|
+
min?: number;
|
|
24
|
+
max?: number;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export interface SqliteConfig {
|
|
28
|
+
driver: 'sqlite';
|
|
29
|
+
filename: string;
|
|
30
|
+
}
|
|
31
|
+
export type DatabaseConfig = PostgresConfig | MysqlConfig | SqliteConfig;
|
|
32
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,OAAO,GAAG,QAAQ,CAAA;AAE5D,MAAM,WAAW,cAAc;IAC3B,MAAM,EAAE,UAAU,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,GAAG,CAAC,EAAE,OAAO,CAAA;IACb,IAAI,CAAC,EAAE;QACH,GAAG,CAAC,EAAE,MAAM,CAAA;QACZ,GAAG,CAAC,EAAE,MAAM,CAAA;KACf,CAAA;CACJ;AAED,MAAM,WAAW,WAAW;IACxB,MAAM,EAAE,OAAO,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,CAAC,EAAE;QACH,GAAG,CAAC,EAAE,MAAM,CAAA;QACZ,GAAG,CAAC,EAAE,MAAM,CAAA;KACf,CAAA;CACJ;AAED,MAAM,WAAW,YAAY;IACzB,MAAM,EAAE,QAAQ,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,MAAM,cAAc,GAAG,cAAc,GAAG,WAAW,GAAG,YAAY,CAAA"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":""}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { DatabaseManager } from './DatabaseManager.js';
|
|
2
|
+
export type { AnyDrizzleDb } from './DatabaseManager.js';
|
|
3
|
+
export type { DatabaseConfig, DatabaseDriver, PostgresConfig, MysqlConfig, SqliteConfig } from './config.js';
|
|
4
|
+
export { Model } from './schema/Model.js';
|
|
5
|
+
export { Migrator } from './migrations/Migrator.js';
|
|
6
|
+
export type { MigratorOptions } from './migrations/Migrator.js';
|
|
7
|
+
export { DatabaseServiceProvider } from './providers/DatabaseServiceProvider.js';
|
|
8
|
+
export type { DatabaseServiceConfig } from './providers/DatabaseServiceProvider.js';
|
|
9
|
+
export * from './query/index.js';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AACtD,YAAY,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAGxD,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,cAAc,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAG5G,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAGzC,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAA;AACnD,YAAY,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAG/D,OAAO,EAAE,uBAAuB,EAAE,MAAM,wCAAwC,CAAA;AAChF,YAAY,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAA;AAGnF,cAAc,kBAAkB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Core
|
|
2
|
+
export { DatabaseManager } from './DatabaseManager.js';
|
|
3
|
+
// Model
|
|
4
|
+
export { Model } from './schema/Model.js';
|
|
5
|
+
// Migrations
|
|
6
|
+
export { Migrator } from './migrations/Migrator.js';
|
|
7
|
+
// Service Provider
|
|
8
|
+
export { DatabaseServiceProvider } from './providers/DatabaseServiceProvider.js';
|
|
9
|
+
// Schema builders + query operators (Drizzle re-exports)
|
|
10
|
+
export * from './query/index.js';
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO;AACP,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAMtD,QAAQ;AACR,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAEzC,aAAa;AACb,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAA;AAGnD,mBAAmB;AACnB,OAAO,EAAE,uBAAuB,EAAE,MAAM,wCAAwC,CAAA;AAGhF,yDAAyD;AACzD,cAAc,kBAAkB,CAAA"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AnyDrizzleDb } from '../DatabaseManager.js';
|
|
2
|
+
import type { DatabaseConfig } from '../config.js';
|
|
3
|
+
export interface MigratorOptions {
|
|
4
|
+
migrationsFolder: string;
|
|
5
|
+
}
|
|
6
|
+
export declare class Migrator {
|
|
7
|
+
private readonly db;
|
|
8
|
+
private readonly config;
|
|
9
|
+
private readonly options;
|
|
10
|
+
constructor(db: AnyDrizzleDb, config: DatabaseConfig, options: MigratorOptions);
|
|
11
|
+
run(): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=Migrator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Migrator.d.ts","sourceRoot":"","sources":["../../src/migrations/Migrator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAElD,MAAM,WAAW,eAAe;IAC9B,gBAAgB,EAAE,MAAM,CAAA;CACzB;AAED,qBAAa,QAAQ;IAEb,OAAO,CAAC,QAAQ,CAAC,EAAE;IACnB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAFP,EAAE,EAAE,YAAY,EAChB,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,eAAe;IAGvC,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAqB7B"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
export class Migrator {
|
|
3
|
+
db;
|
|
4
|
+
config;
|
|
5
|
+
options;
|
|
6
|
+
constructor(db, config, options) {
|
|
7
|
+
this.db = db;
|
|
8
|
+
this.config = config;
|
|
9
|
+
this.options = options;
|
|
10
|
+
}
|
|
11
|
+
async run() {
|
|
12
|
+
const folder = resolve(this.options.migrationsFolder);
|
|
13
|
+
switch (this.config.driver) {
|
|
14
|
+
case 'postgres': {
|
|
15
|
+
const { migrate } = await import('drizzle-orm/node-postgres/migrator');
|
|
16
|
+
await migrate(this.db, { migrationsFolder: folder });
|
|
17
|
+
break;
|
|
18
|
+
}
|
|
19
|
+
case 'mysql': {
|
|
20
|
+
const { migrate } = await import('drizzle-orm/mysql2/migrator');
|
|
21
|
+
await migrate(this.db, { migrationsFolder: folder });
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
case 'sqlite': {
|
|
25
|
+
const { migrate } = await import('drizzle-orm/better-sqlite3/migrator');
|
|
26
|
+
await migrate(this.db, { migrationsFolder: folder });
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=Migrator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Migrator.js","sourceRoot":"","sources":["../../src/migrations/Migrator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAQnC,MAAM,OAAO,QAAQ;IAEI;IACA;IACA;IAHrB,YACqB,EAAgB,EAChB,MAAsB,EACtB,OAAwB;QAFxB,OAAE,GAAF,EAAE,CAAc;QAChB,WAAM,GAAN,MAAM,CAAgB;QACtB,YAAO,GAAP,OAAO,CAAiB;IAC1C,CAAC;IAEJ,KAAK,CAAC,GAAG;QACL,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAA;QAErD,QAAQ,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YAC7B,KAAK,UAAU,CAAC,CAAC,CAAC;gBACd,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC,oCAAoC,CAAC,CAAA;gBACtE,MAAM,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,gBAAgB,EAAE,MAAM,EAAE,CAAC,CAAA;gBACpD,MAAK;YACT,CAAC;YACD,KAAK,OAAO,CAAC,CAAC,CAAC;gBACX,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC,6BAA6B,CAAC,CAAA;gBAC/D,MAAM,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,gBAAgB,EAAE,MAAM,EAAE,CAAC,CAAA;gBACpD,MAAK;YACT,CAAC;YACD,KAAK,QAAQ,CAAC,CAAC,CAAC;gBACZ,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC,qCAAqC,CAAC,CAAA;gBACvE,MAAM,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,gBAAgB,EAAE,MAAM,EAAE,CAAC,CAAA;gBACpD,MAAK;YACT,CAAC;QACD,CAAC;IACL,CAAC;CACJ"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { ServiceProvider } from '@pearl-framework/core';
|
|
2
|
+
import type { DatabaseConfig } from '../config.js';
|
|
3
|
+
export interface DatabaseServiceConfig {
|
|
4
|
+
connection: DatabaseConfig;
|
|
5
|
+
migrationsFolder?: string;
|
|
6
|
+
runMigrationsOnBoot?: boolean;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* DatabaseServiceProvider connects to the database and optionally runs migrations on boot.
|
|
10
|
+
*
|
|
11
|
+
* Usage — extend this in your app:
|
|
12
|
+
*
|
|
13
|
+
* export class AppDatabaseServiceProvider extends DatabaseServiceProvider {
|
|
14
|
+
* protected config: DatabaseServiceConfig = {
|
|
15
|
+
* connection: {
|
|
16
|
+
* driver: 'postgres',
|
|
17
|
+
* host: env('DB_HOST'),
|
|
18
|
+
* port: env.number('DB_PORT', 5432),
|
|
19
|
+
* user: env('DB_USER'),
|
|
20
|
+
* password: env('DB_PASSWORD'),
|
|
21
|
+
* database: env('DB_NAME'),
|
|
22
|
+
* },
|
|
23
|
+
* migrationsFolder: './database/migrations',
|
|
24
|
+
* runMigrationsOnBoot: true,
|
|
25
|
+
* }
|
|
26
|
+
* }
|
|
27
|
+
*/
|
|
28
|
+
export declare class DatabaseServiceProvider extends ServiceProvider {
|
|
29
|
+
protected config: DatabaseServiceConfig;
|
|
30
|
+
register(): void;
|
|
31
|
+
boot(): Promise<void>;
|
|
32
|
+
shutdown(): Promise<void>;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=DatabaseServiceProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DatabaseServiceProvider.d.ts","sourceRoot":"","sources":["../../src/providers/DatabaseServiceProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAGvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAElD,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,cAAc,CAAA;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,mBAAmB,CAAC,EAAE,OAAO,CAAA;CAC9B;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,uBAAwB,SAAQ,eAAe;IACxD,SAAS,CAAC,MAAM,EAAG,qBAAqB,CAAA;IAExC,QAAQ,IAAI,IAAI;IAOD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAc9B,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAIlC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { ServiceProvider } from '@pearl-framework/core';
|
|
2
|
+
import { DatabaseManager } from '../DatabaseManager.js';
|
|
3
|
+
import { Migrator } from '../migrations/Migrator.js';
|
|
4
|
+
/**
|
|
5
|
+
* DatabaseServiceProvider connects to the database and optionally runs migrations on boot.
|
|
6
|
+
*
|
|
7
|
+
* Usage — extend this in your app:
|
|
8
|
+
*
|
|
9
|
+
* export class AppDatabaseServiceProvider extends DatabaseServiceProvider {
|
|
10
|
+
* protected config: DatabaseServiceConfig = {
|
|
11
|
+
* connection: {
|
|
12
|
+
* driver: 'postgres',
|
|
13
|
+
* host: env('DB_HOST'),
|
|
14
|
+
* port: env.number('DB_PORT', 5432),
|
|
15
|
+
* user: env('DB_USER'),
|
|
16
|
+
* password: env('DB_PASSWORD'),
|
|
17
|
+
* database: env('DB_NAME'),
|
|
18
|
+
* },
|
|
19
|
+
* migrationsFolder: './database/migrations',
|
|
20
|
+
* runMigrationsOnBoot: true,
|
|
21
|
+
* }
|
|
22
|
+
* }
|
|
23
|
+
*/
|
|
24
|
+
export class DatabaseServiceProvider extends ServiceProvider {
|
|
25
|
+
config;
|
|
26
|
+
register() {
|
|
27
|
+
this.container.singleton(DatabaseManager, () => new DatabaseManager(this.config.connection));
|
|
28
|
+
}
|
|
29
|
+
async boot() {
|
|
30
|
+
const manager = this.container.make(DatabaseManager);
|
|
31
|
+
await manager.connect();
|
|
32
|
+
if (this.config.runMigrationsOnBoot && this.config.migrationsFolder) {
|
|
33
|
+
const migrator = new Migrator(manager.db, this.config.connection, { migrationsFolder: this.config.migrationsFolder });
|
|
34
|
+
await migrator.run();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async shutdown() {
|
|
38
|
+
const manager = this.container.make(DatabaseManager);
|
|
39
|
+
await manager.disconnect();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=DatabaseServiceProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DatabaseServiceProvider.js","sourceRoot":"","sources":["../../src/providers/DatabaseServiceProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAA;AASpD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,OAAO,uBAAwB,SAAQ,eAAe;IAC9C,MAAM,CAAwB;IAExC,QAAQ;QACJ,IAAI,CAAC,SAAS,CAAC,SAAS,CACpB,eAAe,EACf,GAAG,EAAE,CAAC,IAAI,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CACpD,CAAA;IACL,CAAC;IAEQ,KAAK,CAAC,IAAI;QACf,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,eAAe,CAAC,CAAA;QACpD,MAAM,OAAO,CAAC,OAAO,EAAE,CAAA;QAEvB,IAAI,IAAI,CAAC,MAAM,CAAC,mBAAmB,IAAI,IAAI,CAAC,MAAM,CAAC,gBAAgB,EAAE,CAAC;YAClE,MAAM,QAAQ,GAAG,IAAI,QAAQ,CACzB,OAAO,CAAC,EAAE,EACV,IAAI,CAAC,MAAM,CAAC,UAAU,EACtB,EAAE,gBAAgB,EAAE,IAAI,CAAC,MAAM,CAAC,gBAAgB,EAAE,CACrD,CAAA;YACD,MAAM,QAAQ,CAAC,GAAG,EAAE,CAAA;QACxB,CAAC;IACL,CAAC;IAED,KAAK,CAAC,QAAQ;QACV,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,eAAe,CAAC,CAAA;QACpD,MAAM,OAAO,CAAC,UAAU,EAAE,CAAA;IAC9B,CAAC;CACJ"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Re-export Drizzle's schema builders and operators.
|
|
3
|
+
* Users import from @pearl-framework/database instead of drizzle-orm directly.
|
|
4
|
+
*/
|
|
5
|
+
export { pgTable, pgEnum, serial, integer, bigserial, bigint, varchar, text, boolean, timestamp, date, numeric, jsonb, uuid, primaryKey, index, uniqueIndex, } from 'drizzle-orm/pg-core';
|
|
6
|
+
export { mysqlTable, mysqlEnum, int, bigint as mysqlBigint, varchar as mysqlVarchar, text as mysqlText, boolean as mysqlBoolean, timestamp as mysqlTimestamp, datetime, decimal, json, primaryKey as mysqlPrimaryKey, index as mysqlIndex, uniqueIndex as mysqlUniqueIndex, } from 'drizzle-orm/mysql-core';
|
|
7
|
+
export { sqliteTable, integer as sqliteInteger, text as sqliteText, real, blob, primaryKey as sqlitePrimaryKey, index as sqliteIndex, uniqueIndex as sqliteUniqueIndex, } from 'drizzle-orm/sqlite-core';
|
|
8
|
+
export { eq, ne, gt, gte, lt, lte, and, or, not, isNull, isNotNull, inArray, notInArray, like, ilike, between, sql, count, sum, avg, min, max, asc, desc, } from 'drizzle-orm';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/query/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EACL,OAAO,EACP,MAAM,EACN,MAAM,EACN,OAAO,EACP,SAAS,EACT,MAAM,EACN,OAAO,EACP,IAAI,EACJ,OAAO,EACP,SAAS,EACT,IAAI,EACJ,OAAO,EACP,KAAK,EACL,IAAI,EACJ,UAAU,EACV,KAAK,EACL,WAAW,GACZ,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EACL,UAAU,EACV,SAAS,EACT,GAAG,EACH,MAAM,IAAI,WAAW,EACrB,OAAO,IAAI,YAAY,EACvB,IAAI,IAAI,SAAS,EACjB,OAAO,IAAI,YAAY,EACvB,SAAS,IAAI,cAAc,EAC3B,QAAQ,EACR,OAAO,EACP,IAAI,EACJ,UAAU,IAAI,eAAe,EAC7B,KAAK,IAAI,UAAU,EACnB,WAAW,IAAI,gBAAgB,GAChC,MAAM,wBAAwB,CAAA;AAE/B,OAAO,EACL,WAAW,EACX,OAAO,IAAI,aAAa,EACxB,IAAI,IAAI,UAAU,EAClB,IAAI,EACJ,IAAI,EACJ,UAAU,IAAI,gBAAgB,EAC9B,KAAK,IAAI,WAAW,EACpB,WAAW,IAAI,iBAAiB,GACjC,MAAM,yBAAyB,CAAA;AAGhC,OAAO,EACL,EAAE,EACF,EAAE,EACF,EAAE,EACF,GAAG,EACH,EAAE,EACF,GAAG,EACH,GAAG,EACH,EAAE,EACF,GAAG,EACH,MAAM,EACN,SAAS,EACT,OAAO,EACP,UAAU,EACV,IAAI,EACJ,KAAK,EACL,OAAO,EACP,GAAG,EACH,KAAK,EACL,GAAG,EACH,GAAG,EACH,GAAG,EACH,GAAG,EACH,GAAG,EACH,IAAI,GACL,MAAM,aAAa,CAAA"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Re-export Drizzle's schema builders and operators.
|
|
3
|
+
* Users import from @pearl-framework/database instead of drizzle-orm directly.
|
|
4
|
+
*/
|
|
5
|
+
// Schema builders
|
|
6
|
+
export { pgTable, pgEnum, serial, integer, bigserial, bigint, varchar, text, boolean, timestamp, date, numeric, jsonb, uuid, primaryKey, index, uniqueIndex, } from 'drizzle-orm/pg-core';
|
|
7
|
+
export { mysqlTable, mysqlEnum, int, bigint as mysqlBigint, varchar as mysqlVarchar, text as mysqlText, boolean as mysqlBoolean, timestamp as mysqlTimestamp, datetime, decimal, json, primaryKey as mysqlPrimaryKey, index as mysqlIndex, uniqueIndex as mysqlUniqueIndex, } from 'drizzle-orm/mysql-core';
|
|
8
|
+
export { sqliteTable, integer as sqliteInteger, text as sqliteText, real, blob, primaryKey as sqlitePrimaryKey, index as sqliteIndex, uniqueIndex as sqliteUniqueIndex, } from 'drizzle-orm/sqlite-core';
|
|
9
|
+
// Query operators
|
|
10
|
+
export { eq, ne, gt, gte, lt, lte, and, or, not, isNull, isNotNull, inArray, notInArray, like, ilike, between, sql, count, sum, avg, min, max, asc, desc, } from 'drizzle-orm';
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/query/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,kBAAkB;AAClB,OAAO,EACL,OAAO,EACP,MAAM,EACN,MAAM,EACN,OAAO,EACP,SAAS,EACT,MAAM,EACN,OAAO,EACP,IAAI,EACJ,OAAO,EACP,SAAS,EACT,IAAI,EACJ,OAAO,EACP,KAAK,EACL,IAAI,EACJ,UAAU,EACV,KAAK,EACL,WAAW,GACZ,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EACL,UAAU,EACV,SAAS,EACT,GAAG,EACH,MAAM,IAAI,WAAW,EACrB,OAAO,IAAI,YAAY,EACvB,IAAI,IAAI,SAAS,EACjB,OAAO,IAAI,YAAY,EACvB,SAAS,IAAI,cAAc,EAC3B,QAAQ,EACR,OAAO,EACP,IAAI,EACJ,UAAU,IAAI,eAAe,EAC7B,KAAK,IAAI,UAAU,EACnB,WAAW,IAAI,gBAAgB,GAChC,MAAM,wBAAwB,CAAA;AAE/B,OAAO,EACL,WAAW,EACX,OAAO,IAAI,aAAa,EACxB,IAAI,IAAI,UAAU,EAClB,IAAI,EACJ,IAAI,EACJ,UAAU,IAAI,gBAAgB,EAC9B,KAAK,IAAI,WAAW,EACpB,WAAW,IAAI,iBAAiB,GACjC,MAAM,yBAAyB,CAAA;AAEhC,kBAAkB;AAClB,OAAO,EACL,EAAE,EACF,EAAE,EACF,EAAE,EACF,GAAG,EACH,EAAE,EACF,GAAG,EACH,GAAG,EACH,EAAE,EACF,GAAG,EACH,MAAM,EACN,SAAS,EACT,OAAO,EACP,UAAU,EACV,IAAI,EACJ,KAAK,EACL,OAAO,EACP,GAAG,EACH,KAAK,EACL,GAAG,EACH,GAAG,EACH,GAAG,EACH,GAAG,EACH,GAAG,EACH,IAAI,GACL,MAAM,aAAa,CAAA"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type SQL } from 'drizzle-orm';
|
|
2
|
+
import type { AnyDrizzleDb } from '../DatabaseManager.js';
|
|
3
|
+
import type { PgTableWithColumns } from 'drizzle-orm/pg-core';
|
|
4
|
+
import type { MySqlTableWithColumns } from 'drizzle-orm/mysql-core';
|
|
5
|
+
import type { SQLiteTableWithColumns } from 'drizzle-orm/sqlite-core';
|
|
6
|
+
export type AnyTable = PgTableWithColumns<any> | MySqlTableWithColumns<any> | SQLiteTableWithColumns<any>;
|
|
7
|
+
/**
|
|
8
|
+
* Model is a thin static query helper that wraps a Drizzle table.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* // Define your schema with Drizzle
|
|
12
|
+
* export const users = pgTable('users', {
|
|
13
|
+
* id: serial('id').primaryKey(),
|
|
14
|
+
* name: varchar('name', { length: 255 }).notNull(),
|
|
15
|
+
* email: varchar('email', { length: 255 }).notNull().unique(),
|
|
16
|
+
* })
|
|
17
|
+
*
|
|
18
|
+
* // Define your model
|
|
19
|
+
* export class User extends Model<typeof users> {
|
|
20
|
+
* static table = users
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* // Use it
|
|
24
|
+
* const user = await User.find(db, 1)
|
|
25
|
+
* const all = await User.all(db)
|
|
26
|
+
*/
|
|
27
|
+
export declare class Model<TTable extends AnyTable> {
|
|
28
|
+
static table: AnyTable;
|
|
29
|
+
static all<T extends typeof Model>(this: T, db: AnyDrizzleDb): Promise<unknown[]>;
|
|
30
|
+
static find<T extends typeof Model>(this: T, db: AnyDrizzleDb, id: number | string): Promise<unknown | undefined>;
|
|
31
|
+
static findOrFail<T extends typeof Model>(this: T, db: AnyDrizzleDb, id: number | string): Promise<unknown>;
|
|
32
|
+
static where<T extends typeof Model>(this: T, db: AnyDrizzleDb, condition: SQL): Promise<unknown[]>;
|
|
33
|
+
static create<T extends typeof Model>(this: T, db: AnyDrizzleDb, data: Record<string, unknown>): Promise<unknown>;
|
|
34
|
+
static update<T extends typeof Model>(this: T, db: AnyDrizzleDb, id: number | string, data: Record<string, unknown>): Promise<unknown>;
|
|
35
|
+
static delete<T extends typeof Model>(this: T, db: AnyDrizzleDb, id: number | string): Promise<void>;
|
|
36
|
+
static count<T extends typeof Model>(this: T, db: AnyDrizzleDb): Promise<number>;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=Model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Model.d.ts","sourceRoot":"","sources":["../../src/schema/Model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAM,KAAK,GAAG,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAC7D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAA;AACnE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAA;AAErE,MAAM,MAAM,QAAQ,GAChB,kBAAkB,CAAC,GAAG,CAAC,GACvB,qBAAqB,CAAC,GAAG,CAAC,GAC1B,sBAAsB,CAAC,GAAG,CAAC,CAAA;AAE/B;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,KAAK,CAAC,MAAM,SAAS,QAAQ;IAEtC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAA;WAIT,GAAG,CAAC,CAAC,SAAS,OAAO,KAAK,EACnC,IAAI,EAAE,CAAC,EACP,EAAE,EAAE,YAAY,GACjB,OAAO,CAAC,OAAO,EAAE,CAAC;WAIR,IAAI,CAAC,CAAC,SAAS,OAAO,KAAK,EACpC,IAAI,EAAE,CAAC,EACP,EAAE,EAAE,YAAY,EAChB,EAAE,EAAE,MAAM,GAAG,MAAM,GACpB,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;WASlB,UAAU,CAAC,CAAC,SAAS,OAAO,KAAK,EAC1C,IAAI,EAAE,CAAC,EACP,EAAE,EAAE,YAAY,EAChB,EAAE,EAAE,MAAM,GAAG,MAAM,GACpB,OAAO,CAAC,OAAO,CAAC;WAMN,KAAK,CAAC,CAAC,SAAS,OAAO,KAAK,EACrC,IAAI,EAAE,CAAC,EACP,EAAE,EAAE,YAAY,EAChB,SAAS,EAAE,GAAG,GACf,OAAO,CAAC,OAAO,EAAE,CAAC;WAIR,MAAM,CAAC,CAAC,SAAS,OAAO,KAAK,EACtC,IAAI,EAAE,CAAC,EACP,EAAE,EAAE,YAAY,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,OAAO,CAAC;WAKN,MAAM,CAAC,CAAC,SAAS,OAAO,KAAK,EACtC,IAAI,EAAE,CAAC,EACP,EAAE,EAAE,YAAY,EAChB,EAAE,EAAE,MAAM,GAAG,MAAM,EACnB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,OAAO,CAAC;WAaN,MAAM,CAAC,CAAC,SAAS,OAAO,KAAK,EACtC,IAAI,EAAE,CAAC,EACP,EAAE,EAAE,YAAY,EAChB,EAAE,EAAE,MAAM,GAAG,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC;WAQH,KAAK,CAAC,CAAC,SAAS,OAAO,KAAK,EACrC,IAAI,EAAE,CAAC,EACP,EAAE,EAAE,YAAY,GACjB,OAAO,CAAC,MAAM,CAAC;CAOrB"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { eq } from 'drizzle-orm';
|
|
2
|
+
/**
|
|
3
|
+
* Model is a thin static query helper that wraps a Drizzle table.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* // Define your schema with Drizzle
|
|
7
|
+
* export const users = pgTable('users', {
|
|
8
|
+
* id: serial('id').primaryKey(),
|
|
9
|
+
* name: varchar('name', { length: 255 }).notNull(),
|
|
10
|
+
* email: varchar('email', { length: 255 }).notNull().unique(),
|
|
11
|
+
* })
|
|
12
|
+
*
|
|
13
|
+
* // Define your model
|
|
14
|
+
* export class User extends Model<typeof users> {
|
|
15
|
+
* static table = users
|
|
16
|
+
* }
|
|
17
|
+
*
|
|
18
|
+
* // Use it
|
|
19
|
+
* const user = await User.find(db, 1)
|
|
20
|
+
* const all = await User.all(db)
|
|
21
|
+
*/
|
|
22
|
+
export class Model {
|
|
23
|
+
// Subclasses set this to their Drizzle table definition
|
|
24
|
+
static table;
|
|
25
|
+
// ─── Queries ──────────────────────────────────────────────────────────────
|
|
26
|
+
static async all(db) {
|
|
27
|
+
return db.select().from(this.table);
|
|
28
|
+
}
|
|
29
|
+
static async find(db, id) {
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
31
|
+
const idCol = this.table.id;
|
|
32
|
+
if (!idCol)
|
|
33
|
+
throw new Error(`Table has no "id" column.`);
|
|
34
|
+
const rows = await db.select().from(this.table).where(eq(idCol, id)).limit(1);
|
|
35
|
+
return rows[0];
|
|
36
|
+
}
|
|
37
|
+
static async findOrFail(db, id) {
|
|
38
|
+
const row = await this.find(db, id);
|
|
39
|
+
if (!row)
|
|
40
|
+
throw new Error(`Record not found with id: ${id}`);
|
|
41
|
+
return row;
|
|
42
|
+
}
|
|
43
|
+
static async where(db, condition) {
|
|
44
|
+
return db.select().from(this.table).where(condition);
|
|
45
|
+
}
|
|
46
|
+
static async create(db, data) {
|
|
47
|
+
const rows = await db.insert(this.table).values(data).returning();
|
|
48
|
+
return rows[0];
|
|
49
|
+
}
|
|
50
|
+
static async update(db, id, data) {
|
|
51
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
52
|
+
const idCol = this.table.id;
|
|
53
|
+
if (!idCol)
|
|
54
|
+
throw new Error(`Table has no "id" column.`);
|
|
55
|
+
const rows = await db
|
|
56
|
+
.update(this.table)
|
|
57
|
+
.set(data)
|
|
58
|
+
.where(eq(idCol, id))
|
|
59
|
+
.returning();
|
|
60
|
+
return rows[0];
|
|
61
|
+
}
|
|
62
|
+
static async delete(db, id) {
|
|
63
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
64
|
+
const idCol = this.table.id;
|
|
65
|
+
if (!idCol)
|
|
66
|
+
throw new Error(`Table has no "id" column.`);
|
|
67
|
+
await db.delete(this.table).where(eq(idCol, id));
|
|
68
|
+
}
|
|
69
|
+
static async count(db) {
|
|
70
|
+
const { count } = await import('drizzle-orm');
|
|
71
|
+
const result = await db
|
|
72
|
+
.select({ count: count() })
|
|
73
|
+
.from(this.table);
|
|
74
|
+
return Number(result[0]?.count ?? 0);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=Model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Model.js","sourceRoot":"","sources":["../../src/schema/Model.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pearl-framework/database",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Pearl.js database — Drizzle ORM integration, migrations, and connection management",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"typesVersions": {
|
|
15
|
+
"*": {
|
|
16
|
+
"*": [
|
|
17
|
+
"./dist/index.d.ts"
|
|
18
|
+
]
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"drizzle-orm": "^0.30.0",
|
|
23
|
+
"drizzle-kit": "^0.20.0",
|
|
24
|
+
"@pearl-framework/core": "0.1.0"
|
|
25
|
+
},
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"pg": "^8.11.0",
|
|
28
|
+
"mysql2": "^3.9.0",
|
|
29
|
+
"better-sqlite3": "^9.4.0"
|
|
30
|
+
},
|
|
31
|
+
"peerDependenciesMeta": {
|
|
32
|
+
"pg": {
|
|
33
|
+
"optional": true
|
|
34
|
+
},
|
|
35
|
+
"mysql2": {
|
|
36
|
+
"optional": true
|
|
37
|
+
},
|
|
38
|
+
"better-sqlite3": {
|
|
39
|
+
"optional": true
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/node": "^20.0.0",
|
|
44
|
+
"@types/pg": "^8.11.0",
|
|
45
|
+
"@types/better-sqlite3": "^7.6.0",
|
|
46
|
+
"typescript": "^5.4.0",
|
|
47
|
+
"vitest": "^1.6.0"
|
|
48
|
+
},
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"access": "public",
|
|
51
|
+
"registry": "https://registry.npmjs.org"
|
|
52
|
+
},
|
|
53
|
+
"repository": {
|
|
54
|
+
"type": "git",
|
|
55
|
+
"url": "https://github.com/skd09/pearl.js.git",
|
|
56
|
+
"directory": "packages/database"
|
|
57
|
+
},
|
|
58
|
+
"scripts": {
|
|
59
|
+
"build": "tsc",
|
|
60
|
+
"dev": "tsc --watch",
|
|
61
|
+
"test": "vitest run",
|
|
62
|
+
"typecheck": "tsc --noEmit",
|
|
63
|
+
"clean": "rm -rf dist"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { DatabaseConfig } from './config.js'
|
|
2
|
+
|
|
3
|
+
// Use a union type for the Drizzle db instance since each driver returns different types
|
|
4
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5
|
+
export type AnyDrizzleDb = any
|
|
6
|
+
|
|
7
|
+
export class DatabaseManager {
|
|
8
|
+
private _db?: AnyDrizzleDb
|
|
9
|
+
private _config: DatabaseConfig
|
|
10
|
+
|
|
11
|
+
constructor(config: DatabaseConfig) {
|
|
12
|
+
this._config = config
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// ─── Connection ───────────────────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
async connect(): Promise<AnyDrizzleDb> {
|
|
18
|
+
if (this._db) return this._db
|
|
19
|
+
|
|
20
|
+
switch (this._config.driver) {
|
|
21
|
+
case 'postgres':
|
|
22
|
+
this._db = await this.connectPostgres(this._config)
|
|
23
|
+
break
|
|
24
|
+
case 'mysql':
|
|
25
|
+
this._db = await this.connectMysql(this._config)
|
|
26
|
+
break
|
|
27
|
+
case 'sqlite':
|
|
28
|
+
this._db = await this.connectSqlite(this._config)
|
|
29
|
+
break
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return this._db
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
get db(): AnyDrizzleDb {
|
|
36
|
+
if (!this._db) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
'Database not connected. Call DatabaseManager.connect() first, ' +
|
|
39
|
+
'or ensure DatabaseServiceProvider has booted.'
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
return this._db
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async disconnect(): Promise<void> {
|
|
46
|
+
if (!this._db) return
|
|
47
|
+
|
|
48
|
+
if (this._config.driver === 'postgres') {
|
|
49
|
+
const { sql } = await import('drizzle-orm')
|
|
50
|
+
await this._db.execute(sql`SELECT 1`) // flush pending queries
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
this._db = undefined
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ─── Driver connections ───────────────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
private async connectPostgres(
|
|
59
|
+
config: Extract<DatabaseConfig, { driver: 'postgres' }>
|
|
60
|
+
): Promise<AnyDrizzleDb> {
|
|
61
|
+
const { drizzle } = await import('drizzle-orm/node-postgres')
|
|
62
|
+
const { Pool } = await import('pg')
|
|
63
|
+
|
|
64
|
+
const pool = new Pool({
|
|
65
|
+
host: config.host,
|
|
66
|
+
port: config.port ?? 5432,
|
|
67
|
+
user: config.user,
|
|
68
|
+
password: config.password,
|
|
69
|
+
database: config.database,
|
|
70
|
+
ssl: config.ssl ?? false,
|
|
71
|
+
min: config.pool?.min ?? 2,
|
|
72
|
+
max: config.pool?.max ?? 10,
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
return drizzle(pool)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private async connectMysql(
|
|
79
|
+
config: Extract<DatabaseConfig, { driver: 'mysql' }>
|
|
80
|
+
): Promise<AnyDrizzleDb> {
|
|
81
|
+
const { drizzle } = await import('drizzle-orm/mysql2')
|
|
82
|
+
const mysql = await import('mysql2/promise')
|
|
83
|
+
|
|
84
|
+
const pool = mysql.createPool({
|
|
85
|
+
host: config.host,
|
|
86
|
+
port: config.port ?? 3306,
|
|
87
|
+
user: config.user,
|
|
88
|
+
password: config.password,
|
|
89
|
+
database: config.database,
|
|
90
|
+
connectionLimit: config.pool?.max ?? 10,
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
return drizzle(pool)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
private async connectSqlite(
|
|
97
|
+
config: Extract<DatabaseConfig, { driver: 'sqlite' }>
|
|
98
|
+
): Promise<AnyDrizzleDb> {
|
|
99
|
+
const { drizzle } = await import('drizzle-orm/better-sqlite3')
|
|
100
|
+
const Database = await import('better-sqlite3')
|
|
101
|
+
|
|
102
|
+
const client = new Database.default(config.filename)
|
|
103
|
+
return drizzle(client)
|
|
104
|
+
}
|
|
105
|
+
}
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export type DatabaseDriver = 'postgres' | 'mysql' | 'sqlite'
|
|
2
|
+
|
|
3
|
+
export interface PostgresConfig {
|
|
4
|
+
driver: 'postgres'
|
|
5
|
+
host: string
|
|
6
|
+
port?: number
|
|
7
|
+
user: string
|
|
8
|
+
password: string
|
|
9
|
+
database: string
|
|
10
|
+
ssl?: boolean
|
|
11
|
+
pool?: {
|
|
12
|
+
min?: number
|
|
13
|
+
max?: number
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface MysqlConfig {
|
|
18
|
+
driver: 'mysql'
|
|
19
|
+
host: string
|
|
20
|
+
port?: number
|
|
21
|
+
user: string
|
|
22
|
+
password: string
|
|
23
|
+
database: string
|
|
24
|
+
pool?: {
|
|
25
|
+
min?: number
|
|
26
|
+
max?: number
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface SqliteConfig {
|
|
31
|
+
driver: 'sqlite'
|
|
32
|
+
filename: string
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type DatabaseConfig = PostgresConfig | MysqlConfig | SqliteConfig
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Core
|
|
2
|
+
export { DatabaseManager } from './DatabaseManager.js'
|
|
3
|
+
export type { AnyDrizzleDb } from './DatabaseManager.js'
|
|
4
|
+
|
|
5
|
+
// Config
|
|
6
|
+
export type { DatabaseConfig, DatabaseDriver, PostgresConfig, MysqlConfig, SqliteConfig } from './config.js'
|
|
7
|
+
|
|
8
|
+
// Model
|
|
9
|
+
export { Model } from './schema/Model.js'
|
|
10
|
+
|
|
11
|
+
// Migrations
|
|
12
|
+
export { Migrator } from './migrations/Migrator.js'
|
|
13
|
+
export type { MigratorOptions } from './migrations/Migrator.js'
|
|
14
|
+
|
|
15
|
+
// Service Provider
|
|
16
|
+
export { DatabaseServiceProvider } from './providers/DatabaseServiceProvider.js'
|
|
17
|
+
export type { DatabaseServiceConfig } from './providers/DatabaseServiceProvider.js'
|
|
18
|
+
|
|
19
|
+
// Schema builders + query operators (Drizzle re-exports)
|
|
20
|
+
export * from './query/index.js'
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { resolve } from 'node:path'
|
|
2
|
+
import type { AnyDrizzleDb } from '../DatabaseManager.js'
|
|
3
|
+
import type { DatabaseConfig } from '../config.js'
|
|
4
|
+
|
|
5
|
+
export interface MigratorOptions {
|
|
6
|
+
migrationsFolder: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class Migrator {
|
|
10
|
+
constructor(
|
|
11
|
+
private readonly db: AnyDrizzleDb,
|
|
12
|
+
private readonly config: DatabaseConfig,
|
|
13
|
+
private readonly options: MigratorOptions,
|
|
14
|
+
) {}
|
|
15
|
+
|
|
16
|
+
async run(): Promise<void> {
|
|
17
|
+
const folder = resolve(this.options.migrationsFolder)
|
|
18
|
+
|
|
19
|
+
switch (this.config.driver) {
|
|
20
|
+
case 'postgres': {
|
|
21
|
+
const { migrate } = await import('drizzle-orm/node-postgres/migrator')
|
|
22
|
+
await migrate(this.db, { migrationsFolder: folder })
|
|
23
|
+
break
|
|
24
|
+
}
|
|
25
|
+
case 'mysql': {
|
|
26
|
+
const { migrate } = await import('drizzle-orm/mysql2/migrator')
|
|
27
|
+
await migrate(this.db, { migrationsFolder: folder })
|
|
28
|
+
break
|
|
29
|
+
}
|
|
30
|
+
case 'sqlite': {
|
|
31
|
+
const { migrate } = await import('drizzle-orm/better-sqlite3/migrator')
|
|
32
|
+
await migrate(this.db, { migrationsFolder: folder })
|
|
33
|
+
break
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { ServiceProvider } from '@pearl-framework/core'
|
|
2
|
+
import { DatabaseManager } from '../DatabaseManager.js'
|
|
3
|
+
import { Migrator } from '../migrations/Migrator.js'
|
|
4
|
+
import type { DatabaseConfig } from '../config.js'
|
|
5
|
+
|
|
6
|
+
export interface DatabaseServiceConfig {
|
|
7
|
+
connection: DatabaseConfig
|
|
8
|
+
migrationsFolder?: string
|
|
9
|
+
runMigrationsOnBoot?: boolean
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* DatabaseServiceProvider connects to the database and optionally runs migrations on boot.
|
|
14
|
+
*
|
|
15
|
+
* Usage — extend this in your app:
|
|
16
|
+
*
|
|
17
|
+
* export class AppDatabaseServiceProvider extends DatabaseServiceProvider {
|
|
18
|
+
* protected config: DatabaseServiceConfig = {
|
|
19
|
+
* connection: {
|
|
20
|
+
* driver: 'postgres',
|
|
21
|
+
* host: env('DB_HOST'),
|
|
22
|
+
* port: env.number('DB_PORT', 5432),
|
|
23
|
+
* user: env('DB_USER'),
|
|
24
|
+
* password: env('DB_PASSWORD'),
|
|
25
|
+
* database: env('DB_NAME'),
|
|
26
|
+
* },
|
|
27
|
+
* migrationsFolder: './database/migrations',
|
|
28
|
+
* runMigrationsOnBoot: true,
|
|
29
|
+
* }
|
|
30
|
+
* }
|
|
31
|
+
*/
|
|
32
|
+
export class DatabaseServiceProvider extends ServiceProvider {
|
|
33
|
+
protected config!: DatabaseServiceConfig
|
|
34
|
+
|
|
35
|
+
register(): void {
|
|
36
|
+
this.container.singleton(
|
|
37
|
+
DatabaseManager,
|
|
38
|
+
() => new DatabaseManager(this.config.connection),
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
override async boot(): Promise<void> {
|
|
43
|
+
const manager = this.container.make(DatabaseManager)
|
|
44
|
+
await manager.connect()
|
|
45
|
+
|
|
46
|
+
if (this.config.runMigrationsOnBoot && this.config.migrationsFolder) {
|
|
47
|
+
const migrator = new Migrator(
|
|
48
|
+
manager.db,
|
|
49
|
+
this.config.connection,
|
|
50
|
+
{ migrationsFolder: this.config.migrationsFolder },
|
|
51
|
+
)
|
|
52
|
+
await migrator.run()
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async shutdown(): Promise<void> {
|
|
57
|
+
const manager = this.container.make(DatabaseManager)
|
|
58
|
+
await manager.disconnect()
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Re-export Drizzle's schema builders and operators.
|
|
3
|
+
* Users import from @pearl-framework/database instead of drizzle-orm directly.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Schema builders
|
|
7
|
+
export {
|
|
8
|
+
pgTable,
|
|
9
|
+
pgEnum,
|
|
10
|
+
serial,
|
|
11
|
+
integer,
|
|
12
|
+
bigserial,
|
|
13
|
+
bigint,
|
|
14
|
+
varchar,
|
|
15
|
+
text,
|
|
16
|
+
boolean,
|
|
17
|
+
timestamp,
|
|
18
|
+
date,
|
|
19
|
+
numeric,
|
|
20
|
+
jsonb,
|
|
21
|
+
uuid,
|
|
22
|
+
primaryKey,
|
|
23
|
+
index,
|
|
24
|
+
uniqueIndex,
|
|
25
|
+
} from 'drizzle-orm/pg-core'
|
|
26
|
+
|
|
27
|
+
export {
|
|
28
|
+
mysqlTable,
|
|
29
|
+
mysqlEnum,
|
|
30
|
+
int,
|
|
31
|
+
bigint as mysqlBigint,
|
|
32
|
+
varchar as mysqlVarchar,
|
|
33
|
+
text as mysqlText,
|
|
34
|
+
boolean as mysqlBoolean,
|
|
35
|
+
timestamp as mysqlTimestamp,
|
|
36
|
+
datetime,
|
|
37
|
+
decimal,
|
|
38
|
+
json,
|
|
39
|
+
primaryKey as mysqlPrimaryKey,
|
|
40
|
+
index as mysqlIndex,
|
|
41
|
+
uniqueIndex as mysqlUniqueIndex,
|
|
42
|
+
} from 'drizzle-orm/mysql-core'
|
|
43
|
+
|
|
44
|
+
export {
|
|
45
|
+
sqliteTable,
|
|
46
|
+
integer as sqliteInteger,
|
|
47
|
+
text as sqliteText,
|
|
48
|
+
real,
|
|
49
|
+
blob,
|
|
50
|
+
primaryKey as sqlitePrimaryKey,
|
|
51
|
+
index as sqliteIndex,
|
|
52
|
+
uniqueIndex as sqliteUniqueIndex,
|
|
53
|
+
} from 'drizzle-orm/sqlite-core'
|
|
54
|
+
|
|
55
|
+
// Query operators
|
|
56
|
+
export {
|
|
57
|
+
eq,
|
|
58
|
+
ne,
|
|
59
|
+
gt,
|
|
60
|
+
gte,
|
|
61
|
+
lt,
|
|
62
|
+
lte,
|
|
63
|
+
and,
|
|
64
|
+
or,
|
|
65
|
+
not,
|
|
66
|
+
isNull,
|
|
67
|
+
isNotNull,
|
|
68
|
+
inArray,
|
|
69
|
+
notInArray,
|
|
70
|
+
like,
|
|
71
|
+
ilike,
|
|
72
|
+
between,
|
|
73
|
+
sql,
|
|
74
|
+
count,
|
|
75
|
+
sum,
|
|
76
|
+
avg,
|
|
77
|
+
min,
|
|
78
|
+
max,
|
|
79
|
+
asc,
|
|
80
|
+
desc,
|
|
81
|
+
} from 'drizzle-orm'
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { eq, type SQL } from 'drizzle-orm'
|
|
2
|
+
import type { AnyDrizzleDb } from '../DatabaseManager.js'
|
|
3
|
+
import type { PgTableWithColumns } from 'drizzle-orm/pg-core'
|
|
4
|
+
import type { MySqlTableWithColumns } from 'drizzle-orm/mysql-core'
|
|
5
|
+
import type { SQLiteTableWithColumns } from 'drizzle-orm/sqlite-core'
|
|
6
|
+
|
|
7
|
+
export type AnyTable =
|
|
8
|
+
| PgTableWithColumns<any> // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
9
|
+
| MySqlTableWithColumns<any> // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
10
|
+
| SQLiteTableWithColumns<any> // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Model is a thin static query helper that wraps a Drizzle table.
|
|
14
|
+
*
|
|
15
|
+
* Usage:
|
|
16
|
+
* // Define your schema with Drizzle
|
|
17
|
+
* export const users = pgTable('users', {
|
|
18
|
+
* id: serial('id').primaryKey(),
|
|
19
|
+
* name: varchar('name', { length: 255 }).notNull(),
|
|
20
|
+
* email: varchar('email', { length: 255 }).notNull().unique(),
|
|
21
|
+
* })
|
|
22
|
+
*
|
|
23
|
+
* // Define your model
|
|
24
|
+
* export class User extends Model<typeof users> {
|
|
25
|
+
* static table = users
|
|
26
|
+
* }
|
|
27
|
+
*
|
|
28
|
+
* // Use it
|
|
29
|
+
* const user = await User.find(db, 1)
|
|
30
|
+
* const all = await User.all(db)
|
|
31
|
+
*/
|
|
32
|
+
export class Model<TTable extends AnyTable> {
|
|
33
|
+
// Subclasses set this to their Drizzle table definition
|
|
34
|
+
static table: AnyTable
|
|
35
|
+
|
|
36
|
+
// ─── Queries ──────────────────────────────────────────────────────────────
|
|
37
|
+
|
|
38
|
+
static async all<T extends typeof Model>(
|
|
39
|
+
this: T,
|
|
40
|
+
db: AnyDrizzleDb,
|
|
41
|
+
): Promise<unknown[]> {
|
|
42
|
+
return db.select().from(this.table)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
static async find<T extends typeof Model>(
|
|
46
|
+
this: T,
|
|
47
|
+
db: AnyDrizzleDb,
|
|
48
|
+
id: number | string,
|
|
49
|
+
): Promise<unknown | undefined> {
|
|
50
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
51
|
+
const idCol = (this.table as any).id
|
|
52
|
+
if (!idCol) throw new Error(`Table has no "id" column.`)
|
|
53
|
+
|
|
54
|
+
const rows = await db.select().from(this.table).where(eq(idCol, id)).limit(1)
|
|
55
|
+
return rows[0]
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
static async findOrFail<T extends typeof Model>(
|
|
59
|
+
this: T,
|
|
60
|
+
db: AnyDrizzleDb,
|
|
61
|
+
id: number | string,
|
|
62
|
+
): Promise<unknown> {
|
|
63
|
+
const row = await this.find(db, id)
|
|
64
|
+
if (!row) throw new Error(`Record not found with id: ${id}`)
|
|
65
|
+
return row
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
static async where<T extends typeof Model>(
|
|
69
|
+
this: T,
|
|
70
|
+
db: AnyDrizzleDb,
|
|
71
|
+
condition: SQL,
|
|
72
|
+
): Promise<unknown[]> {
|
|
73
|
+
return db.select().from(this.table).where(condition)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
static async create<T extends typeof Model>(
|
|
77
|
+
this: T,
|
|
78
|
+
db: AnyDrizzleDb,
|
|
79
|
+
data: Record<string, unknown>,
|
|
80
|
+
): Promise<unknown> {
|
|
81
|
+
const rows = await db.insert(this.table).values(data).returning()
|
|
82
|
+
return rows[0]
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
static async update<T extends typeof Model>(
|
|
86
|
+
this: T,
|
|
87
|
+
db: AnyDrizzleDb,
|
|
88
|
+
id: number | string,
|
|
89
|
+
data: Record<string, unknown>,
|
|
90
|
+
): Promise<unknown> {
|
|
91
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
92
|
+
const idCol = (this.table as any).id
|
|
93
|
+
if (!idCol) throw new Error(`Table has no "id" column.`)
|
|
94
|
+
|
|
95
|
+
const rows = await db
|
|
96
|
+
.update(this.table)
|
|
97
|
+
.set(data)
|
|
98
|
+
.where(eq(idCol, id))
|
|
99
|
+
.returning()
|
|
100
|
+
return rows[0]
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
static async delete<T extends typeof Model>(
|
|
104
|
+
this: T,
|
|
105
|
+
db: AnyDrizzleDb,
|
|
106
|
+
id: number | string,
|
|
107
|
+
): Promise<void> {
|
|
108
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
109
|
+
const idCol = (this.table as any).id
|
|
110
|
+
if (!idCol) throw new Error(`Table has no "id" column.`)
|
|
111
|
+
|
|
112
|
+
await db.delete(this.table).where(eq(idCol, id))
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
static async count<T extends typeof Model>(
|
|
116
|
+
this: T,
|
|
117
|
+
db: AnyDrizzleDb,
|
|
118
|
+
): Promise<number> {
|
|
119
|
+
const { count } = await import('drizzle-orm')
|
|
120
|
+
const result = await db
|
|
121
|
+
.select({ count: count() })
|
|
122
|
+
.from(this.table)
|
|
123
|
+
return Number(result[0]?.count ?? 0)
|
|
124
|
+
}
|
|
125
|
+
}
|