@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onebun/drizzle",
3
- "version": "0.1.9",
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.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,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
+ }
@@ -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 { PgTable } from 'drizzle-orm/pg-core';
12
- import type { SQLiteTable } from 'drizzle-orm/sqlite-core';
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
- * Generic type parameter TDbType specifies the database type (SQLITE or POSTGRESQL)
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
- * // 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
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<TDbType extends DatabaseTypeLiteral = DatabaseTypeLiteral> extends BaseService {
214
+ export class DrizzleService extends BaseService {
188
215
  private db: DatabaseInstance | null = null;
189
- private dbType: TDbType | null = null;
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 as TDbType;
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 with proper typing based on generic type parameter
453
- * Returns correctly typed database instance based on TDbType
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(): DatabaseInstanceForType<TDbType> {
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
- // Type assertion is safe because dbType matches TDbType
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 proper typing based on TDbType
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: DatabaseInstanceForType<TDbType>) => Promise<R>,
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(callback);
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 (proxies)
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(): 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']> {
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
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
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(): 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']> {
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
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
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 Parameters<DatabaseInstanceForType<TDbType>['insert']>[0]>(
689
- table: TTable,
690
- ): ReturnType<DatabaseInstanceForType<TDbType>['insert']> {
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 Parameters<DatabaseInstanceForType<TDbType>['update']>[0]>(
715
- table: TTable,
716
- ): ReturnType<DatabaseInstanceForType<TDbType>['update']> {
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 Parameters<DatabaseInstanceForType<TDbType>['delete']>[0]>(
738
- table: TTable,
739
- ): ReturnType<DatabaseInstanceForType<TDbType>['delete']> {
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
@@ -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
  }