@onebun/drizzle 0.1.9 → 0.1.10
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/package.json +2 -2
- package/src/builders/index.ts +9 -0
- package/src/builders/select-builder.ts +109 -0
- package/src/builders/transaction-client.ts +116 -0
- package/src/drizzle.service.ts +107 -62
- package/src/entity.decorator.ts +3 -6
- package/src/index.ts +7 -0
- package/src/repository.ts +17 -56
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onebun/drizzle",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "Drizzle ORM module for OneBun framework - SQLite and PostgreSQL support",
|
|
5
5
|
"license": "LGPL-3.0",
|
|
6
6
|
"author": "RemRyahirev",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"dev": "bun run --watch src/index.ts"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@onebun/core": "^0.1.
|
|
50
|
+
"@onebun/core": "^0.1.15",
|
|
51
51
|
"@onebun/envs": "^0.1.4",
|
|
52
52
|
"@onebun/logger": "^0.1.5",
|
|
53
53
|
"drizzle-orm": "^0.44.7",
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Universal Query Builders
|
|
3
|
+
*
|
|
4
|
+
* These builders provide type-safe database operations that infer
|
|
5
|
+
* the correct database type (SQLite or PostgreSQL) from table schemas.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export { UniversalSelectBuilder, UniversalSelectDistinctBuilder } from './select-builder';
|
|
9
|
+
export { UniversalTransactionClient } from './transaction-client';
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
// Drizzle ORM uses complex conditional types that require `any` for proper type inference
|
|
3
|
+
|
|
4
|
+
import type { DatabaseInstance } from '../types';
|
|
5
|
+
import type { PgTable } from 'drizzle-orm/pg-core';
|
|
6
|
+
import type { SQLiteSelectBase } from 'drizzle-orm/sqlite-core';
|
|
7
|
+
import type { SQLiteTable, SQLiteColumn } from 'drizzle-orm/sqlite-core';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* SQLite select query result type with proper typing for table columns
|
|
11
|
+
*/
|
|
12
|
+
type SQLiteSelectQueryResult<TTable extends SQLiteTable<any>> = SQLiteSelectBase<
|
|
13
|
+
TTable['_']['name'],
|
|
14
|
+
'sync',
|
|
15
|
+
void,
|
|
16
|
+
TTable['_']['columns'],
|
|
17
|
+
'single',
|
|
18
|
+
Record<TTable['_']['name'], 'not-null'>,
|
|
19
|
+
false,
|
|
20
|
+
never,
|
|
21
|
+
TTable['$inferSelect'][],
|
|
22
|
+
Record<keyof TTable['_']['columns'], SQLiteColumn>
|
|
23
|
+
>;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Universal Select Builder that works with any table type
|
|
27
|
+
*
|
|
28
|
+
* This builder allows using DrizzleService without generic type parameter.
|
|
29
|
+
* The result type is determined by the actual database operation at runtime,
|
|
30
|
+
* but the select result array element type is inferred from the table schema.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* // DrizzleService without generic
|
|
35
|
+
* const db = new DrizzleService();
|
|
36
|
+
*
|
|
37
|
+
* // Type of result elements is inferred from table schema
|
|
38
|
+
* const users = await db.select().from(usersTable);
|
|
39
|
+
* // users has type: UserRow[]
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export class UniversalSelectBuilder<TFields extends Record<string, unknown> | undefined = undefined> {
|
|
43
|
+
constructor(
|
|
44
|
+
private db: DatabaseInstance,
|
|
45
|
+
private fields?: TFields,
|
|
46
|
+
) {}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Select from a SQLite table
|
|
50
|
+
* Returns a chainable query builder that resolves to table row type
|
|
51
|
+
*/
|
|
52
|
+
from<TTable extends SQLiteTable<any>>(
|
|
53
|
+
table: TTable,
|
|
54
|
+
): SQLiteSelectQueryResult<TTable>;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Select from a PostgreSQL table
|
|
58
|
+
* Returns a chainable query builder that resolves to table row type
|
|
59
|
+
*/
|
|
60
|
+
from<TTable extends PgTable<any>>(
|
|
61
|
+
table: TTable,
|
|
62
|
+
): Promise<TTable['$inferSelect'][]> & {
|
|
63
|
+
where: (condition: any) => Promise<TTable['$inferSelect'][]> & { get: () => Promise<TTable['$inferSelect'] | undefined> };
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Implementation - runtime call to appropriate database
|
|
68
|
+
*/
|
|
69
|
+
from(table: SQLiteTable<any> | PgTable<any>): any {
|
|
70
|
+
const selectBuilder = this.fields
|
|
71
|
+
? (this.db as any).select(this.fields)
|
|
72
|
+
: (this.db as any).select();
|
|
73
|
+
|
|
74
|
+
return selectBuilder.from(table);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Universal SelectDistinct Builder
|
|
80
|
+
* Same as UniversalSelectBuilder but for DISTINCT queries
|
|
81
|
+
*/
|
|
82
|
+
export class UniversalSelectDistinctBuilder<TFields extends Record<string, unknown> | undefined = undefined> {
|
|
83
|
+
constructor(
|
|
84
|
+
private db: DatabaseInstance,
|
|
85
|
+
private fields?: TFields,
|
|
86
|
+
) {}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Select distinct from a SQLite table
|
|
90
|
+
*/
|
|
91
|
+
from<TTable extends SQLiteTable<any>>(
|
|
92
|
+
table: TTable,
|
|
93
|
+
): SQLiteSelectQueryResult<TTable>;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Select distinct from a PostgreSQL table
|
|
97
|
+
*/
|
|
98
|
+
from<TTable extends PgTable<any>>(
|
|
99
|
+
table: TTable,
|
|
100
|
+
): Promise<TTable['$inferSelect'][]>;
|
|
101
|
+
|
|
102
|
+
from(table: SQLiteTable<any> | PgTable<any>): any {
|
|
103
|
+
const selectBuilder = this.fields
|
|
104
|
+
? (this.db as any).selectDistinct(this.fields)
|
|
105
|
+
: (this.db as any).selectDistinct();
|
|
106
|
+
|
|
107
|
+
return selectBuilder.from(table);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/* eslint-disable
|
|
2
|
+
@typescript-eslint/no-explicit-any,
|
|
3
|
+
@typescript-eslint/explicit-module-boundary-types */
|
|
4
|
+
// Drizzle ORM uses complex conditional types that require `any` for proper type inference
|
|
5
|
+
// Method overloads define return types, so explicit return type on implementation is not needed
|
|
6
|
+
|
|
7
|
+
import type { DatabaseInstance } from '../types';
|
|
8
|
+
import type { BunSQLDatabase } from 'drizzle-orm/bun-sql';
|
|
9
|
+
import type { BunSQLiteDatabase } from 'drizzle-orm/bun-sqlite';
|
|
10
|
+
import type { PgTable } from 'drizzle-orm/pg-core';
|
|
11
|
+
import type { SQLiteTable } from 'drizzle-orm/sqlite-core';
|
|
12
|
+
|
|
13
|
+
import { UniversalSelectBuilder, UniversalSelectDistinctBuilder } from './select-builder';
|
|
14
|
+
|
|
15
|
+
// Type helpers for insert/update/delete return types
|
|
16
|
+
type SQLiteDb = BunSQLiteDatabase<Record<string, SQLiteTable>>;
|
|
17
|
+
type PgDb = BunSQLDatabase<Record<string, PgTable>>;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Universal Transaction Client
|
|
21
|
+
*
|
|
22
|
+
* Wraps the transaction object to provide methods with overloads
|
|
23
|
+
* that infer the correct type from table schemas.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* await db.transaction(async (tx) => {
|
|
28
|
+
* // tx has the same API as DrizzleService
|
|
29
|
+
* const users = await tx.select().from(usersTable);
|
|
30
|
+
* await tx.insert(usersTable).values({ name: 'John' });
|
|
31
|
+
* });
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export class UniversalTransactionClient {
|
|
35
|
+
constructor(private tx: DatabaseInstance) {}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Create a SELECT query
|
|
39
|
+
*/
|
|
40
|
+
select(): UniversalSelectBuilder;
|
|
41
|
+
select<TFields extends Record<string, unknown>>(fields: TFields): UniversalSelectBuilder<TFields>;
|
|
42
|
+
select<TFields extends Record<string, unknown>>(fields?: TFields) {
|
|
43
|
+
return new UniversalSelectBuilder(this.tx, fields);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Create a SELECT DISTINCT query
|
|
48
|
+
*/
|
|
49
|
+
selectDistinct(): UniversalSelectDistinctBuilder;
|
|
50
|
+
selectDistinct<TFields extends Record<string, unknown>>(fields: TFields): UniversalSelectDistinctBuilder<TFields>;
|
|
51
|
+
selectDistinct<TFields extends Record<string, unknown>>(fields?: TFields) {
|
|
52
|
+
return new UniversalSelectDistinctBuilder(this.tx, fields);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Create an INSERT query for SQLite table
|
|
57
|
+
*/
|
|
58
|
+
insert<TTable extends SQLiteTable<any>>(
|
|
59
|
+
table: TTable,
|
|
60
|
+
): ReturnType<SQLiteDb['insert']>;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Create an INSERT query for PostgreSQL table
|
|
64
|
+
*/
|
|
65
|
+
insert<TTable extends PgTable<any>>(
|
|
66
|
+
table: TTable,
|
|
67
|
+
): ReturnType<PgDb['insert']>;
|
|
68
|
+
|
|
69
|
+
insert(table: SQLiteTable<any> | PgTable<any>) {
|
|
70
|
+
return (this.tx as any).insert(table);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Create an UPDATE query for SQLite table
|
|
75
|
+
*/
|
|
76
|
+
update<TTable extends SQLiteTable<any>>(
|
|
77
|
+
table: TTable,
|
|
78
|
+
): ReturnType<SQLiteDb['update']>;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Create an UPDATE query for PostgreSQL table
|
|
82
|
+
*/
|
|
83
|
+
update<TTable extends PgTable<any>>(
|
|
84
|
+
table: TTable,
|
|
85
|
+
): ReturnType<PgDb['update']>;
|
|
86
|
+
|
|
87
|
+
update(table: SQLiteTable<any> | PgTable<any>) {
|
|
88
|
+
return (this.tx as any).update(table);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Create a DELETE query for SQLite table
|
|
93
|
+
*/
|
|
94
|
+
delete<TTable extends SQLiteTable<any>>(
|
|
95
|
+
table: TTable,
|
|
96
|
+
): ReturnType<SQLiteDb['delete']>;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Create a DELETE query for PostgreSQL table
|
|
100
|
+
*/
|
|
101
|
+
delete<TTable extends PgTable<any>>(
|
|
102
|
+
table: TTable,
|
|
103
|
+
): ReturnType<PgDb['delete']>;
|
|
104
|
+
|
|
105
|
+
delete(table: SQLiteTable<any> | PgTable<any>) {
|
|
106
|
+
return (this.tx as any).delete(table);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Get the raw transaction object for advanced usage
|
|
111
|
+
* Note: Returns union type - use type guards for specific database type
|
|
112
|
+
*/
|
|
113
|
+
getRawTransaction(): DatabaseInstance {
|
|
114
|
+
return this.tx;
|
|
115
|
+
}
|
|
116
|
+
}
|
package/src/drizzle.service.ts
CHANGED
|
@@ -18,10 +18,14 @@ import {
|
|
|
18
18
|
EnvParser,
|
|
19
19
|
} from '@onebun/envs';
|
|
20
20
|
|
|
21
|
+
import {
|
|
22
|
+
UniversalSelectBuilder,
|
|
23
|
+
UniversalSelectDistinctBuilder,
|
|
24
|
+
UniversalTransactionClient,
|
|
25
|
+
} from './builders';
|
|
21
26
|
import {
|
|
22
27
|
type DatabaseConnectionOptions,
|
|
23
28
|
type DatabaseInstance,
|
|
24
|
-
type DatabaseInstanceForType,
|
|
25
29
|
DatabaseType,
|
|
26
30
|
type DatabaseTypeLiteral,
|
|
27
31
|
type DrizzleModuleOptions,
|
|
@@ -29,6 +33,10 @@ import {
|
|
|
29
33
|
type PostgreSQLConnectionOptions,
|
|
30
34
|
} from './types';
|
|
31
35
|
|
|
36
|
+
// Type helpers for insert/update/delete return types
|
|
37
|
+
type SQLiteDb = BunSQLiteDatabase<Record<string, SQLiteTable>>;
|
|
38
|
+
type PgDb = BunSQLDatabase<Record<string, PgTable>>;
|
|
39
|
+
|
|
32
40
|
/**
|
|
33
41
|
* Default environment variable prefix
|
|
34
42
|
*/
|
|
@@ -170,23 +178,35 @@ interface BufferedLogEntry {
|
|
|
170
178
|
|
|
171
179
|
/**
|
|
172
180
|
* Drizzle service for database operations
|
|
173
|
-
*
|
|
181
|
+
*
|
|
182
|
+
* The service automatically infers database types from table schemas.
|
|
183
|
+
* No generic parameter is required - just use select(), insert(), update(), delete()
|
|
184
|
+
* with your table schemas and TypeScript will infer the correct types.
|
|
174
185
|
*
|
|
175
186
|
* @example
|
|
176
187
|
* ```typescript
|
|
177
|
-
* //
|
|
178
|
-
* const
|
|
179
|
-
* const
|
|
180
|
-
*
|
|
181
|
-
* //
|
|
182
|
-
*
|
|
183
|
-
*
|
|
188
|
+
* // Define tables with proper types
|
|
189
|
+
* const users = sqliteTable('users', { ... }); // SQLite table
|
|
190
|
+
* const orders = pgTable('orders', { ... }); // PostgreSQL table
|
|
191
|
+
*
|
|
192
|
+
* // Use DrizzleService without generic parameter
|
|
193
|
+
* @Service()
|
|
194
|
+
* class UserService extends BaseService {
|
|
195
|
+
* constructor(private db: DrizzleService) {
|
|
196
|
+
* super();
|
|
197
|
+
* }
|
|
198
|
+
*
|
|
199
|
+
* async findAll() {
|
|
200
|
+
* // TypeScript infers SQLite types from `users` table
|
|
201
|
+
* return this.db.select().from(users);
|
|
202
|
+
* }
|
|
203
|
+
* }
|
|
184
204
|
* ```
|
|
185
205
|
*/
|
|
186
206
|
@Service()
|
|
187
|
-
export class DrizzleService
|
|
207
|
+
export class DrizzleService extends BaseService {
|
|
188
208
|
private db: DatabaseInstance | null = null;
|
|
189
|
-
private dbType:
|
|
209
|
+
private dbType: DatabaseTypeLiteral | null = null;
|
|
190
210
|
private connectionOptions: DatabaseConnectionOptions | null = null;
|
|
191
211
|
private initialized = false;
|
|
192
212
|
private initPromise: Promise<void> | null = null;
|
|
@@ -412,7 +432,7 @@ export class DrizzleService<TDbType extends DatabaseTypeLiteral = DatabaseTypeLi
|
|
|
412
432
|
}
|
|
413
433
|
|
|
414
434
|
this.connectionOptions = options;
|
|
415
|
-
this.dbType = options.type
|
|
435
|
+
this.dbType = options.type;
|
|
416
436
|
|
|
417
437
|
if (options.type === DatabaseType.SQLITE) {
|
|
418
438
|
const sqliteOptions = options.options;
|
|
@@ -449,16 +469,20 @@ export class DrizzleService<TDbType extends DatabaseTypeLiteral = DatabaseTypeLi
|
|
|
449
469
|
}
|
|
450
470
|
|
|
451
471
|
/**
|
|
452
|
-
* Get database instance
|
|
453
|
-
*
|
|
472
|
+
* Get raw database instance
|
|
473
|
+
*
|
|
474
|
+
* Returns a union type - use isSQLite()/isPostgreSQL() type guards
|
|
475
|
+
* or getSQLiteDatabase()/getPostgreSQLDatabase() for specific types.
|
|
476
|
+
*
|
|
477
|
+
* For most use cases, prefer using select(), insert(), update(), delete()
|
|
478
|
+
* methods which automatically infer types from table schemas.
|
|
454
479
|
*/
|
|
455
|
-
getDatabase():
|
|
480
|
+
getDatabase(): DatabaseInstance {
|
|
456
481
|
if (!this.db || !this.dbType) {
|
|
457
482
|
throw new Error('Database not initialized. Call initialize() first.');
|
|
458
483
|
}
|
|
459
484
|
|
|
460
|
-
|
|
461
|
-
return this.db as DatabaseInstanceForType<TDbType>;
|
|
485
|
+
return this.db;
|
|
462
486
|
}
|
|
463
487
|
|
|
464
488
|
/**
|
|
@@ -595,10 +619,22 @@ export class DrizzleService<TDbType extends DatabaseTypeLiteral = DatabaseTypeLi
|
|
|
595
619
|
}
|
|
596
620
|
|
|
597
621
|
/**
|
|
598
|
-
* Execute a transaction with
|
|
622
|
+
* Execute a transaction with universal transaction client
|
|
623
|
+
*
|
|
624
|
+
* The transaction client provides the same API as DrizzleService
|
|
625
|
+
* with automatic type inference from table schemas.
|
|
626
|
+
*
|
|
627
|
+
* @example
|
|
628
|
+
* ```typescript
|
|
629
|
+
* await db.transaction(async (tx) => {
|
|
630
|
+
* const users = await tx.select().from(usersTable);
|
|
631
|
+
* await tx.insert(usersTable).values({ name: 'John' });
|
|
632
|
+
* await tx.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.id, 1));
|
|
633
|
+
* });
|
|
634
|
+
* ```
|
|
599
635
|
*/
|
|
600
636
|
async transaction<R>(
|
|
601
|
-
callback: (tx:
|
|
637
|
+
callback: (tx: UniversalTransactionClient) => Promise<R>,
|
|
602
638
|
): Promise<R> {
|
|
603
639
|
await this.waitForInit();
|
|
604
640
|
|
|
@@ -606,43 +642,42 @@ export class DrizzleService<TDbType extends DatabaseTypeLiteral = DatabaseTypeLi
|
|
|
606
642
|
throw new Error('Database not initialized. Call initialize() first.');
|
|
607
643
|
}
|
|
608
644
|
|
|
609
|
-
// Type assertion needed because TypeScript cannot infer methods from conditional types
|
|
610
|
-
// Runtime type is correct - this is a TypeScript limitation
|
|
611
645
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
612
|
-
return await (this.db as any).transaction(
|
|
646
|
+
return await (this.db as any).transaction(async (rawTx: DatabaseInstance) => {
|
|
647
|
+
const wrappedTx = new UniversalTransactionClient(rawTx);
|
|
648
|
+
|
|
649
|
+
return await callback(wrappedTx);
|
|
650
|
+
});
|
|
613
651
|
}
|
|
614
652
|
|
|
615
653
|
// ============================================
|
|
616
|
-
// Direct database operation methods
|
|
654
|
+
// Direct database operation methods
|
|
655
|
+
// These methods use universal builders that infer
|
|
656
|
+
// database type from table schemas
|
|
617
657
|
// ============================================
|
|
618
658
|
|
|
619
659
|
/**
|
|
620
660
|
* Create a SELECT query
|
|
621
661
|
*
|
|
662
|
+
* Returns a builder with from() method that infers the correct
|
|
663
|
+
* database type from the table schema.
|
|
664
|
+
*
|
|
622
665
|
* @example
|
|
623
666
|
* ```typescript
|
|
624
|
-
* // Select all columns
|
|
667
|
+
* // Select all columns - type inferred from table
|
|
625
668
|
* const users = await this.db.select().from(usersTable);
|
|
626
669
|
*
|
|
627
670
|
* // Select specific columns
|
|
628
671
|
* const names = await this.db.select({ name: usersTable.name }).from(usersTable);
|
|
629
672
|
* ```
|
|
630
673
|
*/
|
|
631
|
-
select():
|
|
632
|
-
select<
|
|
633
|
-
|
|
634
|
-
)
|
|
635
|
-
select<TSelection extends Record<string, unknown>>(
|
|
636
|
-
fields?: TSelection,
|
|
637
|
-
): ReturnType<DatabaseInstanceForType<TDbType>['select']> {
|
|
674
|
+
select(): UniversalSelectBuilder;
|
|
675
|
+
select<TFields extends Record<string, unknown>>(fields: TFields): UniversalSelectBuilder<TFields>;
|
|
676
|
+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
677
|
+
select<TFields extends Record<string, unknown>>(fields?: TFields) {
|
|
638
678
|
const db = this.getDatabase();
|
|
639
|
-
if (fields) {
|
|
640
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
641
|
-
return (db as any).select(fields);
|
|
642
|
-
}
|
|
643
679
|
|
|
644
|
-
|
|
645
|
-
return (db as any).select();
|
|
680
|
+
return new UniversalSelectBuilder(db, fields);
|
|
646
681
|
}
|
|
647
682
|
|
|
648
683
|
/**
|
|
@@ -654,25 +689,17 @@ export class DrizzleService<TDbType extends DatabaseTypeLiteral = DatabaseTypeLi
|
|
|
654
689
|
* const uniqueNames = await this.db.selectDistinct({ name: usersTable.name }).from(usersTable);
|
|
655
690
|
* ```
|
|
656
691
|
*/
|
|
657
|
-
selectDistinct():
|
|
658
|
-
selectDistinct<
|
|
659
|
-
|
|
660
|
-
)
|
|
661
|
-
selectDistinct<TSelection extends Record<string, unknown>>(
|
|
662
|
-
fields?: TSelection,
|
|
663
|
-
): ReturnType<DatabaseInstanceForType<TDbType>['selectDistinct']> {
|
|
692
|
+
selectDistinct(): UniversalSelectDistinctBuilder;
|
|
693
|
+
selectDistinct<TFields extends Record<string, unknown>>(fields: TFields): UniversalSelectDistinctBuilder<TFields>;
|
|
694
|
+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
695
|
+
selectDistinct<TFields extends Record<string, unknown>>(fields?: TFields) {
|
|
664
696
|
const db = this.getDatabase();
|
|
665
|
-
if (fields) {
|
|
666
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
667
|
-
return (db as any).selectDistinct(fields);
|
|
668
|
-
}
|
|
669
697
|
|
|
670
|
-
|
|
671
|
-
return (db as any).selectDistinct();
|
|
698
|
+
return new UniversalSelectDistinctBuilder(db, fields);
|
|
672
699
|
}
|
|
673
700
|
|
|
674
701
|
/**
|
|
675
|
-
* Create an INSERT query
|
|
702
|
+
* Create an INSERT query for SQLite table
|
|
676
703
|
*
|
|
677
704
|
* @example
|
|
678
705
|
* ```typescript
|
|
@@ -685,9 +712,15 @@ export class DrizzleService<TDbType extends DatabaseTypeLiteral = DatabaseTypeLi
|
|
|
685
712
|
* .returning();
|
|
686
713
|
* ```
|
|
687
714
|
*/
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
715
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
716
|
+
insert<TTable extends SQLiteTable<any>>(table: TTable): ReturnType<SQLiteDb['insert']>;
|
|
717
|
+
/**
|
|
718
|
+
* Create an INSERT query for PostgreSQL table
|
|
719
|
+
*/
|
|
720
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
721
|
+
insert<TTable extends PgTable<any>>(table: TTable): ReturnType<PgDb['insert']>;
|
|
722
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
|
|
723
|
+
insert(table: SQLiteTable<any> | PgTable<any>) {
|
|
691
724
|
const db = this.getDatabase();
|
|
692
725
|
|
|
693
726
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -695,7 +728,7 @@ export class DrizzleService<TDbType extends DatabaseTypeLiteral = DatabaseTypeLi
|
|
|
695
728
|
}
|
|
696
729
|
|
|
697
730
|
/**
|
|
698
|
-
* Create an UPDATE query
|
|
731
|
+
* Create an UPDATE query for SQLite table
|
|
699
732
|
*
|
|
700
733
|
* @example
|
|
701
734
|
* ```typescript
|
|
@@ -711,9 +744,15 @@ export class DrizzleService<TDbType extends DatabaseTypeLiteral = DatabaseTypeLi
|
|
|
711
744
|
* .returning();
|
|
712
745
|
* ```
|
|
713
746
|
*/
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
747
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
748
|
+
update<TTable extends SQLiteTable<any>>(table: TTable): ReturnType<SQLiteDb['update']>;
|
|
749
|
+
/**
|
|
750
|
+
* Create an UPDATE query for PostgreSQL table
|
|
751
|
+
*/
|
|
752
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
753
|
+
update<TTable extends PgTable<any>>(table: TTable): ReturnType<PgDb['update']>;
|
|
754
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
|
|
755
|
+
update(table: SQLiteTable<any> | PgTable<any>) {
|
|
717
756
|
const db = this.getDatabase();
|
|
718
757
|
|
|
719
758
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -721,7 +760,7 @@ export class DrizzleService<TDbType extends DatabaseTypeLiteral = DatabaseTypeLi
|
|
|
721
760
|
}
|
|
722
761
|
|
|
723
762
|
/**
|
|
724
|
-
* Create a DELETE query
|
|
763
|
+
* Create a DELETE query for SQLite table
|
|
725
764
|
*
|
|
726
765
|
* @example
|
|
727
766
|
* ```typescript
|
|
@@ -734,9 +773,15 @@ export class DrizzleService<TDbType extends DatabaseTypeLiteral = DatabaseTypeLi
|
|
|
734
773
|
* .returning();
|
|
735
774
|
* ```
|
|
736
775
|
*/
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
776
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
777
|
+
delete<TTable extends SQLiteTable<any>>(table: TTable): ReturnType<SQLiteDb['delete']>;
|
|
778
|
+
/**
|
|
779
|
+
* Create a DELETE query for PostgreSQL table
|
|
780
|
+
*/
|
|
781
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
782
|
+
delete<TTable extends PgTable<any>>(table: TTable): ReturnType<PgDb['delete']>;
|
|
783
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
|
|
784
|
+
delete(table: SQLiteTable<any> | PgTable<any>) {
|
|
740
785
|
const db = this.getDatabase();
|
|
741
786
|
|
|
742
787
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
package/src/entity.decorator.ts
CHANGED
|
@@ -24,7 +24,6 @@
|
|
|
24
24
|
*/
|
|
25
25
|
import type { DrizzleService } from './drizzle.service';
|
|
26
26
|
import type { BaseRepository } from './repository';
|
|
27
|
-
import type { InferDbTypeFromTable } from './types';
|
|
28
27
|
import type { PgTable } from 'drizzle-orm/pg-core';
|
|
29
28
|
import type { SQLiteTable } from 'drizzle-orm/sqlite-core';
|
|
30
29
|
|
|
@@ -32,8 +31,6 @@ import type { SQLiteTable } from 'drizzle-orm/sqlite-core';
|
|
|
32
31
|
const ENTITY_METADATA = new Map<Function, PgTable<any> | SQLiteTable<any>>();
|
|
33
32
|
|
|
34
33
|
export function Entity<TTable extends PgTable<any> | SQLiteTable<any>>(table: TTable) {
|
|
35
|
-
type TDbType = InferDbTypeFromTable<TTable>;
|
|
36
|
-
|
|
37
34
|
return <T extends new (...args: any[]) => BaseRepository<TTable>>(target: T) => {
|
|
38
35
|
// Store table schema in metadata
|
|
39
36
|
ENTITY_METADATA.set(target, table);
|
|
@@ -42,8 +39,8 @@ export function Entity<TTable extends PgTable<any> | SQLiteTable<any>>(table: TT
|
|
|
42
39
|
// The wrapped class only requires DrizzleService as constructor argument
|
|
43
40
|
class WrappedRepository extends target {
|
|
44
41
|
constructor(...args: any[]) {
|
|
45
|
-
// First argument should be DrizzleService
|
|
46
|
-
const drizzleService = args[0] as DrizzleService
|
|
42
|
+
// First argument should be DrizzleService (no generic parameter needed)
|
|
43
|
+
const drizzleService = args[0] as DrizzleService;
|
|
47
44
|
// Pass drizzleService and table to parent constructor
|
|
48
45
|
super(drizzleService, table);
|
|
49
46
|
}
|
|
@@ -57,7 +54,7 @@ export function Entity<TTable extends PgTable<any> | SQLiteTable<any>>(table: TT
|
|
|
57
54
|
});
|
|
58
55
|
|
|
59
56
|
// Return wrapped class - TypeScript will infer the correct constructor signature
|
|
60
|
-
return WrappedRepository as any as new (drizzleService: DrizzleService
|
|
57
|
+
return WrappedRepository as any as new (drizzleService: DrizzleService) => InstanceType<T>;
|
|
61
58
|
};
|
|
62
59
|
}
|
|
63
60
|
|
package/src/index.ts
CHANGED
|
@@ -25,6 +25,13 @@ export { DatabaseType } from './types';
|
|
|
25
25
|
export { DrizzleModule } from './drizzle.module';
|
|
26
26
|
export { DrizzleService } from './drizzle.service';
|
|
27
27
|
|
|
28
|
+
// Universal builders for type inference
|
|
29
|
+
export {
|
|
30
|
+
UniversalSelectBuilder,
|
|
31
|
+
UniversalSelectDistinctBuilder,
|
|
32
|
+
UniversalTransactionClient,
|
|
33
|
+
} from './builders';
|
|
34
|
+
|
|
28
35
|
// Repository
|
|
29
36
|
export { BaseRepository, type QueryBuilder } from './repository';
|
|
30
37
|
|
package/src/repository.ts
CHANGED
|
@@ -4,19 +4,18 @@
|
|
|
4
4
|
|
|
5
5
|
import { eq, type SQL } from 'drizzle-orm';
|
|
6
6
|
|
|
7
|
-
import type { IRepository } from './types';
|
|
8
|
-
import type { DatabaseTypeLiteral, DatabaseInstanceForType } from './types';
|
|
7
|
+
import type { IRepository, DatabaseInstance } from './types';
|
|
9
8
|
import type { PgTable } from 'drizzle-orm/pg-core';
|
|
10
9
|
import type { SQLiteTable } from 'drizzle-orm/sqlite-core';
|
|
11
10
|
|
|
12
11
|
|
|
12
|
+
import { UniversalTransactionClient } from './builders';
|
|
13
13
|
import { DrizzleService } from './drizzle.service';
|
|
14
14
|
import {
|
|
15
15
|
getPrimaryKeyColumn,
|
|
16
16
|
type SelectType,
|
|
17
17
|
type InsertType,
|
|
18
18
|
} from './schema-utils';
|
|
19
|
-
import { DatabaseType } from './types';
|
|
20
19
|
|
|
21
20
|
/**
|
|
22
21
|
* Query builder interface for type-safe database operations
|
|
@@ -90,55 +89,13 @@ export interface QueryBuilder {
|
|
|
90
89
|
* }
|
|
91
90
|
* ```
|
|
92
91
|
*/
|
|
93
|
-
/**
|
|
94
|
-
* Infer database type from table schema
|
|
95
|
-
*/
|
|
96
|
-
type InferDbTypeFromTable<TTable> =
|
|
97
|
-
TTable extends SQLiteTable<any>
|
|
98
|
-
? DatabaseType.SQLITE
|
|
99
|
-
: TTable extends PgTable<any>
|
|
100
|
-
? DatabaseType.POSTGRESQL
|
|
101
|
-
: never;
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Base repository class for Drizzle ORM
|
|
105
|
-
*
|
|
106
|
-
* Simplified version with single generic parameter - database type is automatically inferred from table schema
|
|
107
|
-
*
|
|
108
|
-
* @example
|
|
109
|
-
* ```typescript
|
|
110
|
-
* import { sqliteTable, integer, text } from 'drizzle-orm/sqlite-core';
|
|
111
|
-
* import { BaseRepository } from '@onebun/drizzle';
|
|
112
|
-
*
|
|
113
|
-
* const users = sqliteTable('users', {
|
|
114
|
-
* id: integer('id').primaryKey({ autoIncrement: true }),
|
|
115
|
-
* name: text('name').notNull(),
|
|
116
|
-
* });
|
|
117
|
-
*
|
|
118
|
-
* export class UserRepository extends BaseRepository<typeof users> {
|
|
119
|
-
* constructor(drizzleService: DrizzleService) {
|
|
120
|
-
* super(drizzleService, users);
|
|
121
|
-
* }
|
|
122
|
-
* }
|
|
123
|
-
* ```
|
|
124
|
-
*
|
|
125
|
-
* Advanced version with explicit database type (for edge cases):
|
|
126
|
-
* ```typescript
|
|
127
|
-
* export class UserRepository extends BaseRepository<DatabaseType.SQLITE, typeof users> {
|
|
128
|
-
* constructor(drizzleService: DrizzleService<DatabaseType.SQLITE>) {
|
|
129
|
-
* super(drizzleService, users);
|
|
130
|
-
* }
|
|
131
|
-
* }
|
|
132
|
-
* ```
|
|
133
|
-
*/
|
|
134
92
|
export class BaseRepository<
|
|
135
93
|
TTable extends PgTable<any> | SQLiteTable<any>,
|
|
136
|
-
TDbType extends DatabaseTypeLiteral = InferDbTypeFromTable<TTable>
|
|
137
94
|
> implements IRepository<SelectType<TTable>> {
|
|
138
95
|
/**
|
|
139
|
-
* Database instance
|
|
96
|
+
* Database instance
|
|
140
97
|
*/
|
|
141
|
-
protected readonly db:
|
|
98
|
+
protected readonly db: DatabaseInstance;
|
|
142
99
|
|
|
143
100
|
/**
|
|
144
101
|
* Table schema instance
|
|
@@ -146,20 +103,16 @@ export class BaseRepository<
|
|
|
146
103
|
protected readonly table: TTable;
|
|
147
104
|
|
|
148
105
|
/**
|
|
149
|
-
* DrizzleService instance
|
|
106
|
+
* DrizzleService instance
|
|
150
107
|
*/
|
|
151
|
-
protected readonly drizzleService: DrizzleService
|
|
108
|
+
protected readonly drizzleService: DrizzleService;
|
|
152
109
|
|
|
153
110
|
constructor(
|
|
154
|
-
drizzleService: DrizzleService
|
|
111
|
+
drizzleService: DrizzleService,
|
|
155
112
|
table: TTable,
|
|
156
113
|
) {
|
|
157
114
|
this.table = table;
|
|
158
|
-
|
|
159
|
-
// Type assertion is safe because TDbType is inferred from TTable
|
|
160
|
-
this.drizzleService = drizzleService as DrizzleService<TDbType>;
|
|
161
|
-
// getDatabase() returns correctly typed instance based on TDbType
|
|
162
|
-
// TDbType is automatically inferred from TTable
|
|
115
|
+
this.drizzleService = drizzleService;
|
|
163
116
|
this.db = this.drizzleService.getDatabase();
|
|
164
117
|
}
|
|
165
118
|
|
|
@@ -308,9 +261,17 @@ export class BaseRepository<
|
|
|
308
261
|
|
|
309
262
|
/**
|
|
310
263
|
* Execute a transaction
|
|
264
|
+
*
|
|
265
|
+
* @example
|
|
266
|
+
* ```typescript
|
|
267
|
+
* await userRepository.transaction(async (tx) => {
|
|
268
|
+
* const users = await tx.select().from(usersTable);
|
|
269
|
+
* await tx.insert(usersTable).values({ name: 'John' });
|
|
270
|
+
* });
|
|
271
|
+
* ```
|
|
311
272
|
*/
|
|
312
273
|
async transaction<R>(
|
|
313
|
-
callback: (tx:
|
|
274
|
+
callback: (tx: UniversalTransactionClient) => Promise<R>,
|
|
314
275
|
): Promise<R> {
|
|
315
276
|
return await this.drizzleService.transaction(callback);
|
|
316
277
|
}
|