@onebun/drizzle 0.1.9 → 0.1.11
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 +109 -0
- package/src/drizzle.service.ts +110 -64
- 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.11",
|
|
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,109 @@
|
|
|
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 { BunSQLQueryResultHKT } from 'drizzle-orm/bun-sql/session';
|
|
9
|
+
import type {
|
|
10
|
+
PgDeleteBase,
|
|
11
|
+
PgInsertBuilder,
|
|
12
|
+
PgTable,
|
|
13
|
+
PgUpdateBuilder,
|
|
14
|
+
} from 'drizzle-orm/pg-core';
|
|
15
|
+
import type {
|
|
16
|
+
SQLiteDeleteBase,
|
|
17
|
+
SQLiteInsertBuilder,
|
|
18
|
+
SQLiteTable,
|
|
19
|
+
SQLiteUpdateBuilder,
|
|
20
|
+
} from 'drizzle-orm/sqlite-core';
|
|
21
|
+
|
|
22
|
+
import { UniversalSelectBuilder, UniversalSelectDistinctBuilder } from './select-builder';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Universal Transaction Client
|
|
26
|
+
*
|
|
27
|
+
* Wraps the transaction object to provide methods with overloads
|
|
28
|
+
* that infer the correct type from table schemas.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* await db.transaction(async (tx) => {
|
|
33
|
+
* // tx has the same API as DrizzleService
|
|
34
|
+
* const users = await tx.select().from(usersTable);
|
|
35
|
+
* await tx.insert(usersTable).values({ name: 'John' });
|
|
36
|
+
* });
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export class UniversalTransactionClient {
|
|
40
|
+
constructor(private tx: DatabaseInstance) {}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Create a SELECT query
|
|
44
|
+
*/
|
|
45
|
+
select(): UniversalSelectBuilder;
|
|
46
|
+
select<TFields extends Record<string, unknown>>(fields: TFields): UniversalSelectBuilder<TFields>;
|
|
47
|
+
select<TFields extends Record<string, unknown>>(fields?: TFields) {
|
|
48
|
+
return new UniversalSelectBuilder(this.tx, fields);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Create a SELECT DISTINCT query
|
|
53
|
+
*/
|
|
54
|
+
selectDistinct(): UniversalSelectDistinctBuilder;
|
|
55
|
+
selectDistinct<TFields extends Record<string, unknown>>(fields: TFields): UniversalSelectDistinctBuilder<TFields>;
|
|
56
|
+
selectDistinct<TFields extends Record<string, unknown>>(fields?: TFields) {
|
|
57
|
+
return new UniversalSelectDistinctBuilder(this.tx, fields);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Create an INSERT query for SQLite table
|
|
62
|
+
*/
|
|
63
|
+
insert<TTable extends SQLiteTable>(table: TTable): SQLiteInsertBuilder<TTable, 'sync', void>;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Create an INSERT query for PostgreSQL table
|
|
67
|
+
*/
|
|
68
|
+
insert<TTable extends PgTable>(table: TTable): PgInsertBuilder<TTable, BunSQLQueryResultHKT>;
|
|
69
|
+
|
|
70
|
+
insert(table: SQLiteTable | PgTable) {
|
|
71
|
+
return (this.tx as any).insert(table);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Create an UPDATE query for SQLite table
|
|
76
|
+
*/
|
|
77
|
+
update<TTable extends SQLiteTable>(table: TTable): SQLiteUpdateBuilder<TTable, 'sync', void>;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Create an UPDATE query for PostgreSQL table
|
|
81
|
+
*/
|
|
82
|
+
update<TTable extends PgTable>(table: TTable): PgUpdateBuilder<TTable, BunSQLQueryResultHKT>;
|
|
83
|
+
|
|
84
|
+
update(table: SQLiteTable | PgTable) {
|
|
85
|
+
return (this.tx as any).update(table);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Create a DELETE query for SQLite table
|
|
90
|
+
*/
|
|
91
|
+
delete<TTable extends SQLiteTable>(table: TTable): SQLiteDeleteBase<TTable, 'sync', void>;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Create a DELETE query for PostgreSQL table
|
|
95
|
+
*/
|
|
96
|
+
delete<TTable extends PgTable>(table: TTable): PgDeleteBase<TTable, BunSQLQueryResultHKT>;
|
|
97
|
+
|
|
98
|
+
delete(table: SQLiteTable | PgTable) {
|
|
99
|
+
return (this.tx as any).delete(table);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Get the raw transaction object for advanced usage
|
|
104
|
+
* Note: Returns union type - use type guards for specific database type
|
|
105
|
+
*/
|
|
106
|
+
getRawTransaction(): DatabaseInstance {
|
|
107
|
+
return this.tx;
|
|
108
|
+
}
|
|
109
|
+
}
|
package/src/drizzle.service.ts
CHANGED
|
@@ -7,9 +7,20 @@ import { migrate } from 'drizzle-orm/bun-sqlite/migrator';
|
|
|
7
7
|
import { Effect } from 'effect';
|
|
8
8
|
|
|
9
9
|
import type { BunSQLDatabase } from 'drizzle-orm/bun-sql';
|
|
10
|
+
import type { BunSQLQueryResultHKT } from 'drizzle-orm/bun-sql/session';
|
|
10
11
|
import type { BunSQLiteDatabase } from 'drizzle-orm/bun-sqlite';
|
|
11
|
-
import type {
|
|
12
|
-
|
|
12
|
+
import type {
|
|
13
|
+
PgDeleteBase,
|
|
14
|
+
PgInsertBuilder,
|
|
15
|
+
PgTable,
|
|
16
|
+
PgUpdateBuilder,
|
|
17
|
+
} from 'drizzle-orm/pg-core';
|
|
18
|
+
import type {
|
|
19
|
+
SQLiteDeleteBase,
|
|
20
|
+
SQLiteInsertBuilder,
|
|
21
|
+
SQLiteTable,
|
|
22
|
+
SQLiteUpdateBuilder,
|
|
23
|
+
} from 'drizzle-orm/sqlite-core';
|
|
13
24
|
|
|
14
25
|
import { BaseService, Service } from '@onebun/core';
|
|
15
26
|
import {
|
|
@@ -18,10 +29,14 @@ import {
|
|
|
18
29
|
EnvParser,
|
|
19
30
|
} from '@onebun/envs';
|
|
20
31
|
|
|
32
|
+
import {
|
|
33
|
+
UniversalSelectBuilder,
|
|
34
|
+
UniversalSelectDistinctBuilder,
|
|
35
|
+
UniversalTransactionClient,
|
|
36
|
+
} from './builders';
|
|
21
37
|
import {
|
|
22
38
|
type DatabaseConnectionOptions,
|
|
23
39
|
type DatabaseInstance,
|
|
24
|
-
type DatabaseInstanceForType,
|
|
25
40
|
DatabaseType,
|
|
26
41
|
type DatabaseTypeLiteral,
|
|
27
42
|
type DrizzleModuleOptions,
|
|
@@ -170,23 +185,35 @@ interface BufferedLogEntry {
|
|
|
170
185
|
|
|
171
186
|
/**
|
|
172
187
|
* Drizzle service for database operations
|
|
173
|
-
*
|
|
188
|
+
*
|
|
189
|
+
* The service automatically infers database types from table schemas.
|
|
190
|
+
* No generic parameter is required - just use select(), insert(), update(), delete()
|
|
191
|
+
* with your table schemas and TypeScript will infer the correct types.
|
|
174
192
|
*
|
|
175
193
|
* @example
|
|
176
194
|
* ```typescript
|
|
177
|
-
* //
|
|
178
|
-
* const
|
|
179
|
-
* const
|
|
180
|
-
*
|
|
181
|
-
* //
|
|
182
|
-
*
|
|
183
|
-
*
|
|
195
|
+
* // Define tables with proper types
|
|
196
|
+
* const users = sqliteTable('users', { ... }); // SQLite table
|
|
197
|
+
* const orders = pgTable('orders', { ... }); // PostgreSQL table
|
|
198
|
+
*
|
|
199
|
+
* // Use DrizzleService without generic parameter
|
|
200
|
+
* @Service()
|
|
201
|
+
* class UserService extends BaseService {
|
|
202
|
+
* constructor(private db: DrizzleService) {
|
|
203
|
+
* super();
|
|
204
|
+
* }
|
|
205
|
+
*
|
|
206
|
+
* async findAll() {
|
|
207
|
+
* // TypeScript infers SQLite types from `users` table
|
|
208
|
+
* return this.db.select().from(users);
|
|
209
|
+
* }
|
|
210
|
+
* }
|
|
184
211
|
* ```
|
|
185
212
|
*/
|
|
186
213
|
@Service()
|
|
187
|
-
export class DrizzleService
|
|
214
|
+
export class DrizzleService extends BaseService {
|
|
188
215
|
private db: DatabaseInstance | null = null;
|
|
189
|
-
private dbType:
|
|
216
|
+
private dbType: DatabaseTypeLiteral | null = null;
|
|
190
217
|
private connectionOptions: DatabaseConnectionOptions | null = null;
|
|
191
218
|
private initialized = false;
|
|
192
219
|
private initPromise: Promise<void> | null = null;
|
|
@@ -412,7 +439,7 @@ export class DrizzleService<TDbType extends DatabaseTypeLiteral = DatabaseTypeLi
|
|
|
412
439
|
}
|
|
413
440
|
|
|
414
441
|
this.connectionOptions = options;
|
|
415
|
-
this.dbType = options.type
|
|
442
|
+
this.dbType = options.type;
|
|
416
443
|
|
|
417
444
|
if (options.type === DatabaseType.SQLITE) {
|
|
418
445
|
const sqliteOptions = options.options;
|
|
@@ -449,16 +476,20 @@ export class DrizzleService<TDbType extends DatabaseTypeLiteral = DatabaseTypeLi
|
|
|
449
476
|
}
|
|
450
477
|
|
|
451
478
|
/**
|
|
452
|
-
* Get database instance
|
|
453
|
-
*
|
|
479
|
+
* Get raw database instance
|
|
480
|
+
*
|
|
481
|
+
* Returns a union type - use isSQLite()/isPostgreSQL() type guards
|
|
482
|
+
* or getSQLiteDatabase()/getPostgreSQLDatabase() for specific types.
|
|
483
|
+
*
|
|
484
|
+
* For most use cases, prefer using select(), insert(), update(), delete()
|
|
485
|
+
* methods which automatically infer types from table schemas.
|
|
454
486
|
*/
|
|
455
|
-
getDatabase():
|
|
487
|
+
getDatabase(): DatabaseInstance {
|
|
456
488
|
if (!this.db || !this.dbType) {
|
|
457
489
|
throw new Error('Database not initialized. Call initialize() first.');
|
|
458
490
|
}
|
|
459
491
|
|
|
460
|
-
|
|
461
|
-
return this.db as DatabaseInstanceForType<TDbType>;
|
|
492
|
+
return this.db;
|
|
462
493
|
}
|
|
463
494
|
|
|
464
495
|
/**
|
|
@@ -595,10 +626,22 @@ export class DrizzleService<TDbType extends DatabaseTypeLiteral = DatabaseTypeLi
|
|
|
595
626
|
}
|
|
596
627
|
|
|
597
628
|
/**
|
|
598
|
-
* Execute a transaction with
|
|
629
|
+
* Execute a transaction with universal transaction client
|
|
630
|
+
*
|
|
631
|
+
* The transaction client provides the same API as DrizzleService
|
|
632
|
+
* with automatic type inference from table schemas.
|
|
633
|
+
*
|
|
634
|
+
* @example
|
|
635
|
+
* ```typescript
|
|
636
|
+
* await db.transaction(async (tx) => {
|
|
637
|
+
* const users = await tx.select().from(usersTable);
|
|
638
|
+
* await tx.insert(usersTable).values({ name: 'John' });
|
|
639
|
+
* await tx.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.id, 1));
|
|
640
|
+
* });
|
|
641
|
+
* ```
|
|
599
642
|
*/
|
|
600
643
|
async transaction<R>(
|
|
601
|
-
callback: (tx:
|
|
644
|
+
callback: (tx: UniversalTransactionClient) => Promise<R>,
|
|
602
645
|
): Promise<R> {
|
|
603
646
|
await this.waitForInit();
|
|
604
647
|
|
|
@@ -606,43 +649,42 @@ export class DrizzleService<TDbType extends DatabaseTypeLiteral = DatabaseTypeLi
|
|
|
606
649
|
throw new Error('Database not initialized. Call initialize() first.');
|
|
607
650
|
}
|
|
608
651
|
|
|
609
|
-
// Type assertion needed because TypeScript cannot infer methods from conditional types
|
|
610
|
-
// Runtime type is correct - this is a TypeScript limitation
|
|
611
652
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
612
|
-
return await (this.db as any).transaction(
|
|
653
|
+
return await (this.db as any).transaction(async (rawTx: DatabaseInstance) => {
|
|
654
|
+
const wrappedTx = new UniversalTransactionClient(rawTx);
|
|
655
|
+
|
|
656
|
+
return await callback(wrappedTx);
|
|
657
|
+
});
|
|
613
658
|
}
|
|
614
659
|
|
|
615
660
|
// ============================================
|
|
616
|
-
// Direct database operation methods
|
|
661
|
+
// Direct database operation methods
|
|
662
|
+
// These methods use universal builders that infer
|
|
663
|
+
// database type from table schemas
|
|
617
664
|
// ============================================
|
|
618
665
|
|
|
619
666
|
/**
|
|
620
667
|
* Create a SELECT query
|
|
621
668
|
*
|
|
669
|
+
* Returns a builder with from() method that infers the correct
|
|
670
|
+
* database type from the table schema.
|
|
671
|
+
*
|
|
622
672
|
* @example
|
|
623
673
|
* ```typescript
|
|
624
|
-
* // Select all columns
|
|
674
|
+
* // Select all columns - type inferred from table
|
|
625
675
|
* const users = await this.db.select().from(usersTable);
|
|
626
676
|
*
|
|
627
677
|
* // Select specific columns
|
|
628
678
|
* const names = await this.db.select({ name: usersTable.name }).from(usersTable);
|
|
629
679
|
* ```
|
|
630
680
|
*/
|
|
631
|
-
select():
|
|
632
|
-
select<
|
|
633
|
-
|
|
634
|
-
)
|
|
635
|
-
select<TSelection extends Record<string, unknown>>(
|
|
636
|
-
fields?: TSelection,
|
|
637
|
-
): ReturnType<DatabaseInstanceForType<TDbType>['select']> {
|
|
681
|
+
select(): UniversalSelectBuilder;
|
|
682
|
+
select<TFields extends Record<string, unknown>>(fields: TFields): UniversalSelectBuilder<TFields>;
|
|
683
|
+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
684
|
+
select<TFields extends Record<string, unknown>>(fields?: TFields) {
|
|
638
685
|
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
686
|
|
|
644
|
-
|
|
645
|
-
return (db as any).select();
|
|
687
|
+
return new UniversalSelectBuilder(db, fields);
|
|
646
688
|
}
|
|
647
689
|
|
|
648
690
|
/**
|
|
@@ -654,25 +696,17 @@ export class DrizzleService<TDbType extends DatabaseTypeLiteral = DatabaseTypeLi
|
|
|
654
696
|
* const uniqueNames = await this.db.selectDistinct({ name: usersTable.name }).from(usersTable);
|
|
655
697
|
* ```
|
|
656
698
|
*/
|
|
657
|
-
selectDistinct():
|
|
658
|
-
selectDistinct<
|
|
659
|
-
|
|
660
|
-
)
|
|
661
|
-
selectDistinct<TSelection extends Record<string, unknown>>(
|
|
662
|
-
fields?: TSelection,
|
|
663
|
-
): ReturnType<DatabaseInstanceForType<TDbType>['selectDistinct']> {
|
|
699
|
+
selectDistinct(): UniversalSelectDistinctBuilder;
|
|
700
|
+
selectDistinct<TFields extends Record<string, unknown>>(fields: TFields): UniversalSelectDistinctBuilder<TFields>;
|
|
701
|
+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
702
|
+
selectDistinct<TFields extends Record<string, unknown>>(fields?: TFields) {
|
|
664
703
|
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
704
|
|
|
670
|
-
|
|
671
|
-
return (db as any).selectDistinct();
|
|
705
|
+
return new UniversalSelectDistinctBuilder(db, fields);
|
|
672
706
|
}
|
|
673
707
|
|
|
674
708
|
/**
|
|
675
|
-
* Create an INSERT query
|
|
709
|
+
* Create an INSERT query for SQLite table
|
|
676
710
|
*
|
|
677
711
|
* @example
|
|
678
712
|
* ```typescript
|
|
@@ -685,9 +719,13 @@ export class DrizzleService<TDbType extends DatabaseTypeLiteral = DatabaseTypeLi
|
|
|
685
719
|
* .returning();
|
|
686
720
|
* ```
|
|
687
721
|
*/
|
|
688
|
-
insert<TTable extends
|
|
689
|
-
|
|
690
|
-
|
|
722
|
+
insert<TTable extends SQLiteTable>(table: TTable): SQLiteInsertBuilder<TTable, 'sync', void>;
|
|
723
|
+
/**
|
|
724
|
+
* Create an INSERT query for PostgreSQL table
|
|
725
|
+
*/
|
|
726
|
+
insert<TTable extends PgTable>(table: TTable): PgInsertBuilder<TTable, BunSQLQueryResultHKT>;
|
|
727
|
+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
728
|
+
insert(table: SQLiteTable | PgTable) {
|
|
691
729
|
const db = this.getDatabase();
|
|
692
730
|
|
|
693
731
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -695,7 +733,7 @@ export class DrizzleService<TDbType extends DatabaseTypeLiteral = DatabaseTypeLi
|
|
|
695
733
|
}
|
|
696
734
|
|
|
697
735
|
/**
|
|
698
|
-
* Create an UPDATE query
|
|
736
|
+
* Create an UPDATE query for SQLite table
|
|
699
737
|
*
|
|
700
738
|
* @example
|
|
701
739
|
* ```typescript
|
|
@@ -711,9 +749,13 @@ export class DrizzleService<TDbType extends DatabaseTypeLiteral = DatabaseTypeLi
|
|
|
711
749
|
* .returning();
|
|
712
750
|
* ```
|
|
713
751
|
*/
|
|
714
|
-
update<TTable extends
|
|
715
|
-
|
|
716
|
-
|
|
752
|
+
update<TTable extends SQLiteTable>(table: TTable): SQLiteUpdateBuilder<TTable, 'sync', void>;
|
|
753
|
+
/**
|
|
754
|
+
* Create an UPDATE query for PostgreSQL table
|
|
755
|
+
*/
|
|
756
|
+
update<TTable extends PgTable>(table: TTable): PgUpdateBuilder<TTable, BunSQLQueryResultHKT>;
|
|
757
|
+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
758
|
+
update(table: SQLiteTable | PgTable) {
|
|
717
759
|
const db = this.getDatabase();
|
|
718
760
|
|
|
719
761
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -721,7 +763,7 @@ export class DrizzleService<TDbType extends DatabaseTypeLiteral = DatabaseTypeLi
|
|
|
721
763
|
}
|
|
722
764
|
|
|
723
765
|
/**
|
|
724
|
-
* Create a DELETE query
|
|
766
|
+
* Create a DELETE query for SQLite table
|
|
725
767
|
*
|
|
726
768
|
* @example
|
|
727
769
|
* ```typescript
|
|
@@ -734,9 +776,13 @@ export class DrizzleService<TDbType extends DatabaseTypeLiteral = DatabaseTypeLi
|
|
|
734
776
|
* .returning();
|
|
735
777
|
* ```
|
|
736
778
|
*/
|
|
737
|
-
delete<TTable extends
|
|
738
|
-
|
|
739
|
-
|
|
779
|
+
delete<TTable extends SQLiteTable>(table: TTable): SQLiteDeleteBase<TTable, 'sync', void>;
|
|
780
|
+
/**
|
|
781
|
+
* Create a DELETE query for PostgreSQL table
|
|
782
|
+
*/
|
|
783
|
+
delete<TTable extends PgTable>(table: TTable): PgDeleteBase<TTable, BunSQLQueryResultHKT>;
|
|
784
|
+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
785
|
+
delete(table: SQLiteTable | PgTable) {
|
|
740
786
|
const db = this.getDatabase();
|
|
741
787
|
|
|
742
788
|
// 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
|
}
|