@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onebun/drizzle",
3
- "version": "0.1.9",
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.14",
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
+ }
@@ -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
- * Generic type parameter TDbType specifies the database type (SQLITE or POSTGRESQL)
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
- * // For SQLite
178
- * const drizzleService = new DrizzleService<DatabaseType.SQLITE>(...);
179
- * const db = drizzleService.getDatabase(); // Type: BunSQLiteDatabase
180
- *
181
- * // For PostgreSQL
182
- * const drizzleService = new DrizzleService<DatabaseType.POSTGRESQL>(...);
183
- * const db = drizzleService.getDatabase(); // Type: BunSQLDatabase
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<TDbType extends DatabaseTypeLiteral = DatabaseTypeLiteral> extends BaseService {
207
+ export class DrizzleService extends BaseService {
188
208
  private db: DatabaseInstance | null = null;
189
- private dbType: TDbType | null = null;
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 as TDbType;
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 with proper typing based on generic type parameter
453
- * Returns correctly typed database instance based on TDbType
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(): DatabaseInstanceForType<TDbType> {
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
- // Type assertion is safe because dbType matches TDbType
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 proper typing based on TDbType
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: DatabaseInstanceForType<TDbType>) => Promise<R>,
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(callback);
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 (proxies)
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(): ReturnType<DatabaseInstanceForType<TDbType>['select']>;
632
- select<TSelection extends Record<string, unknown>>(
633
- fields: TSelection,
634
- ): ReturnType<DatabaseInstanceForType<TDbType>['select']>;
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
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
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(): ReturnType<DatabaseInstanceForType<TDbType>['selectDistinct']>;
658
- selectDistinct<TSelection extends Record<string, unknown>>(
659
- fields: TSelection,
660
- ): ReturnType<DatabaseInstanceForType<TDbType>['selectDistinct']>;
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
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
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
- insert<TTable extends Parameters<DatabaseInstanceForType<TDbType>['insert']>[0]>(
689
- table: TTable,
690
- ): ReturnType<DatabaseInstanceForType<TDbType>['insert']> {
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
- update<TTable extends Parameters<DatabaseInstanceForType<TDbType>['update']>[0]>(
715
- table: TTable,
716
- ): ReturnType<DatabaseInstanceForType<TDbType>['update']> {
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
- delete<TTable extends Parameters<DatabaseInstanceForType<TDbType>['delete']>[0]>(
738
- table: TTable,
739
- ): ReturnType<DatabaseInstanceForType<TDbType>['delete']> {
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
@@ -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 with correct type (inferred from table)
46
- const drizzleService = args[0] as DrizzleService<TDbType>;
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<TDbType>) => InstanceType<T>;
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 with proper typing based on TDbType
96
+ * Database instance
140
97
  */
141
- protected readonly db: DatabaseInstanceForType<TDbType>;
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 (type inferred from table)
106
+ * DrizzleService instance
150
107
  */
151
- protected readonly drizzleService: DrizzleService<TDbType>;
108
+ protected readonly drizzleService: DrizzleService;
152
109
 
153
110
  constructor(
154
- drizzleService: DrizzleService<DatabaseTypeLiteral>,
111
+ drizzleService: DrizzleService,
155
112
  table: TTable,
156
113
  ) {
157
114
  this.table = table;
158
- // Store drizzleService - type is inferred from table schema
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: DatabaseInstanceForType<TDbType>) => Promise<R>,
274
+ callback: (tx: UniversalTransactionClient) => Promise<R>,
314
275
  ): Promise<R> {
315
276
  return await this.drizzleService.transaction(callback);
316
277
  }