@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.
Files changed (40) hide show
  1. package/.turbo/turbo-build.log +5 -0
  2. package/LICENSE +21 -0
  3. package/README.md +88 -0
  4. package/dist/DatabaseManager.d.ts +14 -0
  5. package/dist/DatabaseManager.d.ts.map +1 -0
  6. package/dist/DatabaseManager.js +76 -0
  7. package/dist/DatabaseManager.js.map +1 -0
  8. package/dist/config.d.ts +32 -0
  9. package/dist/config.d.ts.map +1 -0
  10. package/dist/config.js +2 -0
  11. package/dist/config.js.map +1 -0
  12. package/dist/index.d.ts +10 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +11 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/migrations/Migrator.d.ts +13 -0
  17. package/dist/migrations/Migrator.d.ts.map +1 -0
  18. package/dist/migrations/Migrator.js +32 -0
  19. package/dist/migrations/Migrator.js.map +1 -0
  20. package/dist/providers/DatabaseServiceProvider.d.ts +34 -0
  21. package/dist/providers/DatabaseServiceProvider.d.ts.map +1 -0
  22. package/dist/providers/DatabaseServiceProvider.js +42 -0
  23. package/dist/providers/DatabaseServiceProvider.js.map +1 -0
  24. package/dist/query/index.d.ts +9 -0
  25. package/dist/query/index.d.ts.map +1 -0
  26. package/dist/query/index.js +11 -0
  27. package/dist/query/index.js.map +1 -0
  28. package/dist/schema/Model.d.ts +38 -0
  29. package/dist/schema/Model.d.ts.map +1 -0
  30. package/dist/schema/Model.js +77 -0
  31. package/dist/schema/Model.js.map +1 -0
  32. package/package.json +65 -0
  33. package/src/DatabaseManager.ts +105 -0
  34. package/src/config.ts +35 -0
  35. package/src/index.ts +20 -0
  36. package/src/migrations/Migrator.ts +37 -0
  37. package/src/providers/DatabaseServiceProvider.ts +60 -0
  38. package/src/query/index.ts +81 -0
  39. package/src/schema/Model.ts +125 -0
  40. package/tsconfig.json +9 -0
@@ -0,0 +1,5 @@
1
+
2
+ 
3
+ > @pearl-framework/database@0.1.0 build /Users/sharvari/Desktop/Sharvari's Work Station/pearljs/packages/database
4
+ > tsc
5
+
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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"}
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":""}
@@ -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
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "rootDir": "./src",
5
+ "outDir": "./dist",
6
+ "types": ["node"]
7
+ },
8
+ "include": ["src/**/*"],
9
+ }