@mantiq/database 0.1.4 → 0.3.0-rc.1

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.
@@ -1,6 +1,7 @@
1
- import type { DatabaseConnection } from '../contracts/Connection.ts'
2
1
  import type { SchemaBuilder } from '../schema/SchemaBuilder.ts'
3
2
  import type { EventDispatcher } from '@mantiq/core'
3
+ import type { DatabaseConnection } from '../contracts/Connection.ts'
4
+ import { BaseSQLConnection } from './BaseSQLConnection.ts'
4
5
  import { QueryBuilder } from '../query/Builder.ts'
5
6
  import { SQLiteGrammar } from './SQLiteGrammar.ts'
6
7
  import { SchemaBuilderImpl } from '../schema/SchemaBuilder.ts'
@@ -12,7 +13,7 @@ export interface SQLiteConfig {
12
13
  database: string // ':memory:' or file path
13
14
  }
14
15
 
15
- export class SQLiteConnection implements DatabaseConnection {
16
+ export class SQLiteConnection extends BaseSQLConnection {
16
17
  readonly _grammar = new SQLiteGrammar()
17
18
  private db: import('bun:sqlite').Database | null = null
18
19
  private config: SQLiteConfig
@@ -21,6 +22,7 @@ export class SQLiteConnection implements DatabaseConnection {
21
22
  static _dispatcher: EventDispatcher | null = null
22
23
 
23
24
  constructor(config: SQLiteConfig) {
25
+ super()
24
26
  this.config = config
25
27
  }
26
28
 
@@ -102,10 +104,6 @@ export class SQLiteConnection implements DatabaseConnection {
102
104
  await SQLiteConnection._dispatcher?.emit(new QueryExecuted(sql, bindings, time, 'sqlite'))
103
105
  }
104
106
 
105
- table(name: string): QueryBuilder {
106
- return new QueryBuilder(this, name)
107
- }
108
-
109
107
  schema(): SchemaBuilder {
110
108
  return new SchemaBuilderImpl(this)
111
109
  }
@@ -114,10 +112,6 @@ export class SQLiteConnection implements DatabaseConnection {
114
112
  return 'sqlite'
115
113
  }
116
114
 
117
- getTablePrefix(): string {
118
- return ''
119
- }
120
-
121
115
  close(): void {
122
116
  this.db?.close()
123
117
  this.db = null
@@ -1,10 +1,10 @@
1
- import { MantiqError } from '@mantiq/core'
2
-
3
- export class ConnectionError extends MantiqError {
1
+ export class ConnectionError extends Error {
4
2
  constructor(
3
+ message: string,
5
4
  public readonly driver: string,
6
- originalError: Error,
5
+ originalError?: Error,
7
6
  ) {
8
- super(`Failed to connect to ${driver} database: ${originalError.message}`)
7
+ super(originalError ? `${message}: ${originalError.message}` : message)
8
+ this.name = 'ConnectionError'
9
9
  }
10
10
  }
@@ -0,0 +1,6 @@
1
+ export class DriverNotSupportedError extends Error {
2
+ constructor(driver: string, feature: string) {
3
+ super(`"${feature}" is not supported by the ${driver} driver.`)
4
+ this.name = 'DriverNotSupportedError'
5
+ }
6
+ }
package/src/index.ts CHANGED
@@ -21,6 +21,7 @@ export type {
21
21
  export { QueryError } from './errors/QueryError.ts'
22
22
  export { ModelNotFoundError } from './errors/ModelNotFoundError.ts'
23
23
  export { ConnectionError } from './errors/ConnectionError.ts'
24
+ export { DriverNotSupportedError } from './errors/DriverNotSupportedError.ts'
24
25
 
25
26
  // ── Query Builder ─────────────────────────────────────────────────────────────
26
27
  export { QueryBuilder } from './query/Builder.ts'
@@ -34,6 +35,9 @@ export { PostgresGrammar } from './drivers/PostgresGrammar.ts'
34
35
  export { MySQLGrammar } from './drivers/MySQLGrammar.ts'
35
36
  export { MSSQLGrammar } from './drivers/MSSQLGrammar.ts'
36
37
 
38
+ // ── Base Connection ──────────────────────────────────────────────────────────
39
+ export { BaseSQLConnection } from './drivers/BaseSQLConnection.ts'
40
+
37
41
  // ── SQL Connections ───────────────────────────────────────────────────────────
38
42
  export { SQLiteConnection } from './drivers/SQLiteConnection.ts'
39
43
  export type { SQLiteConfig } from './drivers/SQLiteConnection.ts'
@@ -76,7 +80,8 @@ export type { ModelStatic } from './orm/Model.ts'
76
80
  export { ModelQueryBuilder } from './orm/ModelQueryBuilder.ts'
77
81
  export { Collection } from './orm/Collection.ts'
78
82
 
79
- // ── MongoDB Document ORM ──────────────────────────────────────────────────────
83
+ // ── MongoDB Document ORM (deprecated — use Model with MongoDB connection) ────
84
+ /** @deprecated Use Model with a MongoDB connection instead */
80
85
  export { Document } from './orm/Document.ts'
81
86
 
82
87
  // ── Seeders & Factories ───────────────────────────────────────────────────────
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — deprecated, replaced by Model with MongoDB connection
1
2
  import type { MongoDatabaseConnection, MongoFilter, MongoUpdateDoc, MongoPipelineStage } from '../contracts/MongoConnection.ts'
2
3
  import { ModelNotFoundError } from '../errors/ModelNotFoundError.ts'
3
4
 
@@ -27,34 +28,34 @@ export abstract class Document {
27
28
 
28
29
  // ── Static query API ───────────────────────────────────────────────────────
29
30
 
30
- static col<T extends Document>(this: new () => T): import('../contracts/MongoConnection.ts').MongoCollectionContract {
31
+ static col<T extends Document>(this: { new(): T } & typeof Document): import('../contracts/MongoConnection.ts').MongoCollectionContract {
31
32
  const ctor = this as unknown as typeof Document
32
33
  if (!ctor.connection) throw new Error(`No connection set on Document ${ctor.collection}`)
33
34
  return ctor.connection.collection(ctor.collection)
34
35
  }
35
36
 
36
- static async find<T extends Document>(this: new () => T, filter: MongoFilter = {}): Promise<T[]> {
37
+ static async find<T extends Document>(this: { new(): T } & typeof Document, filter: MongoFilter = {}): Promise<T[]> {
37
38
  const rows = await (this as unknown as typeof Document).col<T>().find(filter).get()
38
39
  return rows.map((r) => (this as unknown as typeof Document).hydrate<T>(this, r))
39
40
  }
40
41
 
41
- static async findOne<T extends Document>(this: new () => T, filter: MongoFilter = {}): Promise<T | null> {
42
+ static async findOne<T extends Document>(this: { new(): T } & typeof Document, filter: MongoFilter = {}): Promise<T | null> {
42
43
  const row = await (this as unknown as typeof Document).col<T>().findOne(filter)
43
44
  return row ? (this as unknown as typeof Document).hydrate<T>(this, row) : null
44
45
  }
45
46
 
46
- static async findById<T extends Document>(this: new () => T, id: any): Promise<T | null> {
47
+ static async findById<T extends Document>(this: { new(): T } & typeof Document, id: any): Promise<T | null> {
47
48
  const row = await (this as unknown as typeof Document).col<T>().findById(id)
48
49
  return row ? (this as unknown as typeof Document).hydrate<T>(this, row) : null
49
50
  }
50
51
 
51
- static async findByIdOrFail<T extends Document>(this: new () => T, id: any): Promise<T> {
52
+ static async findByIdOrFail<T extends Document>(this: { new(): T } & typeof Document, id: any): Promise<T> {
52
53
  const doc = await (this as unknown as typeof Document).findById<T>(id)
53
54
  if (!doc) throw new ModelNotFoundError((this as unknown as typeof Document).collection)
54
55
  return doc
55
56
  }
56
57
 
57
- static async create<T extends Document>(this: new () => T, data: Record<string, any>): Promise<T> {
58
+ static async create<T extends Document>(this: { new(): T } & typeof Document, data: Record<string, any>): Promise<T> {
58
59
  const col = (this as unknown as typeof Document).col<T>()
59
60
  const now = new Date()
60
61
  const doc = { ...data, createdAt: now, updatedAt: now }
@@ -63,7 +64,7 @@ export abstract class Document {
63
64
  }
64
65
 
65
66
  static async insertMany<T extends Document>(
66
- this: new () => T,
67
+ this: { new(): T } & typeof Document,
67
68
  docs: Record<string, any>[],
68
69
  ): Promise<T[]> {
69
70
  const col = (this as unknown as typeof Document).col<T>()
@@ -76,7 +77,7 @@ export abstract class Document {
76
77
  }
77
78
 
78
79
  static async updateOne<T extends Document>(
79
- this: new () => T,
80
+ this: { new(): T } & typeof Document,
80
81
  filter: MongoFilter,
81
82
  update: MongoUpdateDoc,
82
83
  ) {
@@ -88,7 +89,7 @@ export abstract class Document {
88
89
  }
89
90
 
90
91
  static async updateMany<T extends Document>(
91
- this: new () => T,
92
+ this: { new(): T } & typeof Document,
92
93
  filter: MongoFilter,
93
94
  update: MongoUpdateDoc,
94
95
  ) {
@@ -99,22 +100,22 @@ export abstract class Document {
99
100
  return (this as unknown as typeof Document).col<T>().updateMany(filter, merged)
100
101
  }
101
102
 
102
- static async deleteOne<T extends Document>(this: new () => T, filter: MongoFilter) {
103
+ static async deleteOne<T extends Document>(this: { new(): T } & typeof Document, filter: MongoFilter) {
103
104
  return (this as unknown as typeof Document).col<T>().deleteOne(filter)
104
105
  }
105
106
 
106
- static async deleteMany<T extends Document>(this: new () => T, filter: MongoFilter) {
107
+ static async deleteMany<T extends Document>(this: { new(): T } & typeof Document, filter: MongoFilter) {
107
108
  return (this as unknown as typeof Document).col<T>().deleteMany(filter)
108
109
  }
109
110
 
110
111
  static async aggregate<T extends Document>(
111
- this: new () => T,
112
+ this: { new(): T } & typeof Document,
112
113
  pipeline: MongoPipelineStage[],
113
114
  ): Promise<Record<string, any>[]> {
114
115
  return (this as unknown as typeof Document).col<T>().aggregate(pipeline)
115
116
  }
116
117
 
117
- static async count<T extends Document>(this: new () => T, filter: MongoFilter = {}): Promise<number> {
118
+ static async count<T extends Document>(this: { new(): T } & typeof Document, filter: MongoFilter = {}): Promise<number> {
118
119
  return (this as unknown as typeof Document).col<T>().count(filter)
119
120
  }
120
121
 
package/src/orm/Model.ts CHANGED
@@ -12,6 +12,8 @@ export interface ModelStatic<T extends Model> {
12
12
  connection: DatabaseConnection | null
13
13
  table: string
14
14
  primaryKey: string
15
+ incrementing: boolean
16
+ keyType: 'int' | 'string'
15
17
  fillable: string[]
16
18
  guarded: string[]
17
19
  hidden: string[]
@@ -56,6 +58,8 @@ export abstract class Model {
56
58
  static connection: DatabaseConnection | null = null
57
59
  static table: string = ''
58
60
  static primaryKey: string = 'id'
61
+ static incrementing = true
62
+ static keyType: 'int' | 'string' = 'int'
59
63
  static fillable: string[] = []
60
64
  static guarded: string[] = ['id']
61
65
  static hidden: string[] = []
@@ -469,8 +473,13 @@ export abstract class Model {
469
473
  if (!this._attributes['updated_at']) this._attributes['updated_at'] = now
470
474
  }
471
475
 
472
- const id = await ctor.connection.table(table).insertGetId(this._attributes)
473
- this._attributes[ctor.primaryKey] = Number(id)
476
+ if (ctor.incrementing) {
477
+ const id = await ctor.connection.table(table).insertGetId(this._attributes)
478
+ this._attributes[ctor.primaryKey] = Number(id)
479
+ } else {
480
+ // Non-incrementing (UUID) — id is already set in attributes
481
+ await ctor.connection.table(table).insert(this._attributes)
482
+ }
474
483
  this._original = { ...this._attributes }
475
484
  this._exists = true
476
485
 
@@ -3,6 +3,7 @@ import { ModelNotFoundError } from '../errors/ModelNotFoundError.ts'
3
3
  import { eagerLoadRelations, type EagerLoadSpec, normalizeEagerLoads } from './eagerLoad.ts'
4
4
  import type { Model } from './Model.ts'
5
5
  import type { DatabaseConnection } from '../contracts/Connection.ts'
6
+ import type { PaginationResult } from '../contracts/Paginator.ts'
6
7
 
7
8
  export class ModelQueryBuilder<T> extends QueryBuilder {
8
9
  private _eagerLoads: string[] = []
@@ -131,7 +132,7 @@ export class ModelQueryBuilder<T> extends QueryBuilder {
131
132
 
132
133
  // ── Hydrating read methods ─────────────────────────────────────────────────
133
134
 
134
- override async get(): Promise<T[]> {
135
+ override async get(): Promise<any[]> {
135
136
  this.applyGlobalScopes()
136
137
  const rows = await this.raw().get()
137
138
  const models = rows.map(this._hydrate)
@@ -148,7 +149,7 @@ export class ModelQueryBuilder<T> extends QueryBuilder {
148
149
  return models
149
150
  }
150
151
 
151
- override async first(): Promise<T | null> {
152
+ override async first(): Promise<any> {
152
153
  this.applyGlobalScopes()
153
154
  const row = await this.raw().first()
154
155
  if (!row) return null
@@ -166,13 +167,13 @@ export class ModelQueryBuilder<T> extends QueryBuilder {
166
167
  return model
167
168
  }
168
169
 
169
- async firstOrFail(): Promise<T> {
170
+ override async firstOrFail(): Promise<any> {
170
171
  const result = await this.first()
171
172
  if (!result) throw new ModelNotFoundError(this.state.table)
172
173
  return result
173
174
  }
174
175
 
175
- override async find(id: number | string): Promise<T | null> {
176
+ override async find(id: number | string): Promise<any> {
176
177
  this.applyGlobalScopes()
177
178
  const row = await this.raw().where('id', id).first()
178
179
  if (!row) return null
@@ -190,7 +191,7 @@ export class ModelQueryBuilder<T> extends QueryBuilder {
190
191
  return model
191
192
  }
192
193
 
193
- async findOrFail(id: number | string): Promise<T> {
194
+ async findOrFail(id: number | string): Promise<any> {
194
195
  const result = await this.find(id)
195
196
  if (!result) throw new ModelNotFoundError(this.state.table)
196
197
  return result
@@ -198,7 +199,7 @@ export class ModelQueryBuilder<T> extends QueryBuilder {
198
199
 
199
200
  // ── Pagination (hydrated) ─────────────────────────────────────────────────
200
201
 
201
- override async paginate(page = 1, perPage = 15) {
202
+ override async paginate(page = 1, perPage = 15): Promise<PaginationResult> {
202
203
  const total = await this.count()
203
204
  const lastPage = Math.max(1, Math.ceil(total / perPage))
204
205
  const currentPage = Math.min(page, lastPage)
@@ -381,7 +382,7 @@ export class ModelQueryBuilder<T> extends QueryBuilder {
381
382
  * @example
382
383
  * const user = await User.where('email', 'admin@example.com').sole()
383
384
  */
384
- async sole(): Promise<T> {
385
+ override async sole(): Promise<any> {
385
386
  this.applyGlobalScopes()
386
387
  const originalLimit = this.state.limitValue
387
388
  this.state.limitValue = 2
@@ -9,15 +9,15 @@ export type Operator = '=' | '!=' | '<>' | '<' | '>' | '<=' | '>=' | 'like' | 'n
9
9
  export interface WhereClause {
10
10
  type: 'basic' | 'in' | 'notIn' | 'null' | 'notNull' | 'between' | 'raw' | 'nested' | 'column'
11
11
  boolean: 'and' | 'or'
12
- column?: string
13
- operator?: string
12
+ column?: string | undefined
13
+ operator?: string | undefined
14
14
  value?: any
15
- values?: any[]
16
- range?: [any, any]
17
- sql?: string
18
- bindings?: any[]
19
- nested?: WhereClause[]
20
- secondColumn?: string
15
+ values?: any[] | undefined
16
+ range?: [any, any] | undefined
17
+ sql?: string | undefined
18
+ bindings?: any[] | undefined
19
+ nested?: WhereClause[] | undefined
20
+ secondColumn?: string | undefined
21
21
  }
22
22
 
23
23
  export interface JoinClause {
@@ -92,11 +92,10 @@ export class QueryBuilder {
92
92
  return this
93
93
  }
94
94
 
95
- // ── Where conditions ─────────────────────────────────────────────────────
95
+ // ── Where conditions ───────────────────────────────────────────────────────
96
96
 
97
97
  where(column: string | ((q: QueryBuilder) => void), operatorOrValue?: any, value?: any): this {
98
98
  if (typeof column === 'function') {
99
- const nested: WhereClause[] = []
100
99
  const sub = new QueryBuilder(this._connection, this.state.table)
101
100
  column(sub)
102
101
  this.state.wheres.push({ type: 'nested', boolean: 'and', nested: sub.state.wheres })
@@ -224,7 +223,7 @@ export class QueryBuilder {
224
223
  return this.whereRaw(`strftime('%H:%M:%S', ${column}) ${op} ?`, [val])
225
224
  }
226
225
 
227
- // ── Joins ─────────────────────────────────────────────────────────────────
226
+ // ── Joins ───────────────────────────────────────────────────────────────────
228
227
 
229
228
  join(table: string, first: string, operator: string, second: string): this {
230
229
  this.state.joins.push({ type: 'inner', table, first, operator, second })
@@ -241,7 +240,7 @@ export class QueryBuilder {
241
240
  return this
242
241
  }
243
242
 
244
- // ── Ordering / Grouping ───────────────────────────────────────────────────
243
+ // ── Ordering / Grouping ─────────────────────────────────────────────────────
245
244
 
246
245
  orderBy(column: string | Expression, direction: 'asc' | 'desc' = 'asc'): this {
247
246
  this.state.orders.push({ column, direction })
@@ -267,7 +266,7 @@ export class QueryBuilder {
267
266
  return this
268
267
  }
269
268
 
270
- // ── Pagination ────────────────────────────────────────────────────────────
269
+ // ── Pagination ──────────────────────────────────────────────────────────────
271
270
 
272
271
  limit(value: number): this {
273
272
  this.state.limitValue = value
@@ -282,11 +281,10 @@ export class QueryBuilder {
282
281
  take = this.limit
283
282
  skip = this.offset
284
283
 
285
- // ── Execution ─────────────────────────────────────────────────────────────
284
+ // ── Execution ───────────────────────────────────────────────────────────────
286
285
 
287
286
  async get(): Promise<Record<string, any>[]> {
288
- const { sql, bindings } = this.grammar().compileSelect(this.state)
289
- return this._connection.select(sql, bindings)
287
+ return this._connection.executeSelect(this.state)
290
288
  }
291
289
 
292
290
  async first(): Promise<Record<string, any> | null> {
@@ -315,8 +313,7 @@ export class QueryBuilder {
315
313
  }
316
314
 
317
315
  async exists(): Promise<boolean> {
318
- const row = await this.selectRaw('1 as exists_check').limit(1).first()
319
- return row !== null
316
+ return this._connection.executeExists(this.state)
320
317
  }
321
318
 
322
319
  async doesntExist(): Promise<boolean> {
@@ -333,52 +330,43 @@ export class QueryBuilder {
333
330
  return results[0]!
334
331
  }
335
332
 
336
- // ── Aggregates ────────────────────────────────────────────────────────────
333
+ // ── Aggregates ──────────────────────────────────────────────────────────────
337
334
 
338
335
  async count(column = '*'): Promise<number> {
339
- const row = await this.selectRaw(`COUNT(${column}) as aggregate`).first()
340
- return Number(row?.['aggregate'] ?? 0)
336
+ return this._connection.executeAggregate(this.state, 'count', column)
341
337
  }
342
338
 
343
339
  async sum(column: string): Promise<number> {
344
- const row = await this.selectRaw(`SUM(${column}) as aggregate`).first()
345
- return Number(row?.['aggregate'] ?? 0)
340
+ return this._connection.executeAggregate(this.state, 'sum', column)
346
341
  }
347
342
 
348
343
  async avg(column: string): Promise<number> {
349
- const row = await this.selectRaw(`AVG(${column}) as aggregate`).first()
350
- return Number(row?.['aggregate'] ?? 0)
344
+ return this._connection.executeAggregate(this.state, 'avg', column)
351
345
  }
352
346
 
353
347
  async min(column: string): Promise<any> {
354
- const row = await this.selectRaw(`MIN(${column}) as aggregate`).first()
355
- return row?.['aggregate']
348
+ return this._connection.executeAggregate(this.state, 'min', column)
356
349
  }
357
350
 
358
351
  async max(column: string): Promise<any> {
359
- const row = await this.selectRaw(`MAX(${column}) as aggregate`).first()
360
- return row?.['aggregate']
352
+ return this._connection.executeAggregate(this.state, 'max', column)
361
353
  }
362
354
 
363
- // ── Writes ────────────────────────────────────────────────────────────────
355
+ // ── Writes ──────────────────────────────────────────────────────────────────
364
356
 
365
357
  async insert(data: Record<string, any> | Record<string, any>[]): Promise<void> {
366
358
  const rows = Array.isArray(data) ? data : [data]
367
359
  for (const row of rows) {
368
- const { sql, bindings } = this.grammar().compileInsert(this.state.table, row)
369
- await this._connection.statement(sql, bindings)
360
+ await this._connection.executeInsert(this.state.table, row)
370
361
  }
371
362
  }
372
363
 
373
- async insertGetId(data: Record<string, any>): Promise<number> {
374
- const { sql, bindings } = this.grammar().compileInsertGetId(this.state.table, data)
375
- const id = await this._connection.insertGetId(sql, bindings)
376
- return Number(id)
364
+ async insertGetId(data: Record<string, any>): Promise<number | string> {
365
+ return this._connection.executeInsertGetId(this.state.table, data)
377
366
  }
378
367
 
379
368
  async update(data: Record<string, any>): Promise<number> {
380
- const { sql, bindings } = this.grammar().compileUpdate(this.state.table, this.state, data)
381
- return this._connection.statement(sql, bindings)
369
+ return this._connection.executeUpdate(this.state.table, this.state, data)
382
370
  }
383
371
 
384
372
  async updateOrInsert(
@@ -396,19 +384,19 @@ export class QueryBuilder {
396
384
  }
397
385
 
398
386
  async delete(): Promise<number> {
399
- const { sql, bindings } = this.grammar().compileDelete(this.state.table, this.state)
400
- return this._connection.statement(sql, bindings)
387
+ return this._connection.executeDelete(this.state.table, this.state)
401
388
  }
402
389
 
403
390
  async truncate(): Promise<void> {
404
- const sql = this.grammar().compileTruncate(this.state.table)
405
- await this._connection.statement(sql, [])
391
+ return this._connection.executeTruncate(this.state.table)
406
392
  }
407
393
 
408
- // ── Pagination ────────────────────────────────────────────────────────────
394
+ // ── Pagination ──────────────────────────────────────────────────────────────
409
395
 
410
396
  async paginate(page = 1, perPage = 15): Promise<PaginationResult> {
411
- const total = await this.clone().count()
397
+ const countQuery = this.clone()
398
+ countQuery.state.orders = []
399
+ const total = await countQuery.count()
412
400
  const lastPage = Math.max(1, Math.ceil(total / perPage))
413
401
  const currentPage = Math.min(page, lastPage)
414
402
  const data = await this.clone().limit(perPage).offset((currentPage - 1) * perPage).get()
@@ -417,14 +405,20 @@ export class QueryBuilder {
417
405
  return { data, total, perPage, currentPage, lastPage, from, to, hasMore: currentPage < lastPage }
418
406
  }
419
407
 
420
- // ── Utilities ─────────────────────────────────────────────────────────────
408
+ // ── Utilities ───────────────────────────────────────────────────────────────
421
409
 
410
+ /** Returns the SQL for this query. Only works on SQL connections. */
422
411
  toSql(): string {
423
- return this.grammar().compileSelect(this.state).sql
412
+ const grammar = this.getGrammar()
413
+ if (!grammar) throw new Error('toSql() is only available on SQL connections')
414
+ return grammar.compileSelect(this.state).sql
424
415
  }
425
416
 
417
+ /** Returns the bindings for this query. Only works on SQL connections. */
426
418
  getBindings(): any[] {
427
- return this.grammar().compileSelect(this.state).bindings
419
+ const grammar = this.getGrammar()
420
+ if (!grammar) throw new Error('getBindings() is only available on SQL connections')
421
+ return grammar.compileSelect(this.state).bindings
428
422
  }
429
423
 
430
424
  clone(): QueryBuilder {
@@ -445,10 +439,9 @@ export class QueryBuilder {
445
439
  return this.state
446
440
  }
447
441
 
448
- // ── Grammar (driver-specific SQL) ─────────────────────────────────────────
449
-
450
- protected grammar() {
451
- return (this._connection as any)._grammar as import('../contracts/Grammar.ts').Grammar
442
+ /** Returns the Grammar if this is a SQL connection, null otherwise. */
443
+ protected getGrammar(): import('../contracts/Grammar.ts').Grammar | null {
444
+ return (this._connection as any)._grammar ?? null
452
445
  }
453
446
  }
454
447
 
@@ -3,15 +3,15 @@ import { ColumnDefinition } from './ColumnDefinition.ts'
3
3
  export interface IndexDefinition {
4
4
  type: 'index' | 'unique' | 'primary'
5
5
  columns: string[]
6
- name?: string
6
+ name?: string | undefined
7
7
  }
8
8
 
9
9
  export interface ForeignKeyDefinition {
10
10
  column: string
11
11
  references: string
12
12
  on: string
13
- onDelete?: string
14
- onUpdate?: string
13
+ onDelete?: string | undefined
14
+ onUpdate?: string | undefined
15
15
  }
16
16
 
17
17
  export class Blueprint {
@@ -6,16 +6,16 @@ export class ColumnDefinition {
6
6
  private _index = false
7
7
  private _unsigned = false
8
8
  private _primary = false
9
- private _references: { table: string; column: string; onDelete?: string; onUpdate?: string } | null = null
9
+ private _references: { table: string; column: string; onDelete?: string | undefined; onUpdate?: string | undefined } | null = null
10
10
  private _comment: string | null = null
11
11
  private _after: string | null = null
12
12
 
13
13
  constructor(
14
14
  public readonly name: string,
15
15
  public readonly type: string,
16
- public readonly length?: number,
17
- public readonly precision?: number,
18
- public readonly scale?: number,
16
+ public readonly length?: number | undefined,
17
+ public readonly precision?: number | undefined,
18
+ public readonly scale?: number | undefined,
19
19
  ) {}
20
20
 
21
21
  nullable(): this {
@@ -49,7 +49,7 @@ export class ColumnDefinition {
49
49
  return this
50
50
  }
51
51
 
52
- references(column: string): this & { on(table: string): this } {
52
+ references(column: string): ColumnDefinition & { on(table: string): ColumnDefinition } {
53
53
  this._references = { table: '', column }
54
54
  const self = this as any
55
55
  self.on = (table: string) => {
@@ -210,9 +210,12 @@ export class SchemaBuilderImpl implements SchemaBuilder {
210
210
 
211
211
  let def = `${this.quoteCol(col.name)} ${typeSql}`
212
212
 
213
+ const isIntegerType = ['integer', 'bigInteger', 'tinyInteger', 'smallInteger', 'mediumInteger'].includes(col.type)
213
214
  if (isAutoIncrement || col.isPrimary()) {
214
- if (driver === 'sqlite') {
215
+ if (driver === 'sqlite' && (isAutoIncrement || isIntegerType)) {
215
216
  def += ' PRIMARY KEY AUTOINCREMENT'
217
+ } else if (driver === 'sqlite') {
218
+ def += ' PRIMARY KEY'
216
219
  } else if (driver === 'postgres') {
217
220
  // SERIAL/BIGSERIAL already implies sequence; just mark PRIMARY KEY
218
221
  def += ' PRIMARY KEY'
@@ -252,7 +255,7 @@ export class SchemaBuilderImpl implements SchemaBuilder {
252
255
  return def
253
256
  }
254
257
 
255
- private compileIndex(table: string, idx: { type: 'index' | 'unique' | 'primary'; columns: string[]; name?: string }): string {
258
+ private compileIndex(table: string, idx: { type: 'index' | 'unique' | 'primary'; columns: string[]; name?: string | undefined }): string {
256
259
  const cols = idx.columns.map((c) => this.quoteCol(c)).join(', ')
257
260
  const idxName = idx.name ?? `${table}_${idx.columns.join('_')}_${idx.type}`
258
261
 
package/src/types.d.ts ADDED
@@ -0,0 +1,21 @@
1
+ // Optional peer dependencies — ambient module declarations for dynamic imports
2
+ declare module 'pg' {
3
+ const pg: any
4
+ export default pg
5
+ }
6
+
7
+ declare module 'mysql2/promise' {
8
+ const mysql: any
9
+ export default mysql
10
+ export function createPool(config: any): any
11
+ }
12
+
13
+ declare module 'mssql' {
14
+ const mssql: any
15
+ export default mssql
16
+ }
17
+
18
+ declare module 'mongodb' {
19
+ export const MongoClient: any
20
+ export const ObjectId: any
21
+ }