forked from ericc-ch/copilot-api
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsqlite-adapter.ts
More file actions
108 lines (100 loc) · 3.13 KB
/
sqlite-adapter.ts
File metadata and controls
108 lines (100 loc) · 3.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-require-imports, unicorn/prefer-module */
/**
* Runtime-adaptive SQLite adapter.
*
* Uses `bun:sqlite` when running under Bun, `better-sqlite3` otherwise (Node.js).
* Exposes a unified interface that matches the subset we actually use.
*/
export interface DbStatement {
run(...params: Array<unknown>): { lastInsertRowid: number | bigint }
get(...params: Array<unknown>): unknown
all(...params: Array<unknown>): Array<unknown>
}
export interface DbInstance {
prepare(sql: string): DbStatement
exec(sql: string): void
pragma(pragma: string): unknown
transaction<F extends (...args: Array<never>) => unknown>(fn: F): F
close(): void
}
const isBun = typeof globalThis.Bun !== "undefined"
export function createDatabase(dbPath: string): DbInstance {
if (isBun) {
return createBunDatabase(dbPath)
}
return createBetterSqlite3Database(dbPath)
}
function createBunDatabase(dbPath: string): DbInstance {
// Dynamic import to avoid bundler resolving it on Node
const { Database } = require("bun:sqlite")
const db = new Database(dbPath, { create: true })
return {
prepare(sql: string): DbStatement {
const stmt = db.query(sql)
return {
run(...params: Array<unknown>) {
stmt.run(...params)
// bun:sqlite doesn't return lastInsertRowid from run(),
// but we can query it separately when needed
const row = db.query("SELECT last_insert_rowid() AS id").get()
return { lastInsertRowid: row?.id ?? 0 }
},
get(...params: Array<unknown>) {
return stmt.get(...params) ?? undefined
},
all(...params: Array<unknown>) {
return stmt.all(...params)
},
}
},
exec(sql: string) {
db.exec(sql)
},
pragma(pragma: string) {
// bun:sqlite uses db.query("PRAGMA ...").get() for read pragmas
if (pragma.includes("=")) {
db.run(`PRAGMA ${pragma}`)
return undefined
}
return db.query(`PRAGMA ${pragma}`).get()
},
transaction<F extends (...args: Array<never>) => unknown>(fn: F): F {
return db.transaction(fn)
},
close() {
db.close()
},
}
}
function createBetterSqlite3Database(dbPath: string): DbInstance {
const BetterSqlite3 = require("better-sqlite3")
const db = new BetterSqlite3(dbPath)
return {
prepare(sql: string): DbStatement {
const stmt = db.prepare(sql)
return {
run(...params: Array<unknown>) {
return stmt.run(...params)
},
get(...params: Array<unknown>) {
return stmt.get(...params) ?? undefined
},
all(...params: Array<unknown>) {
return stmt.all(...params)
},
}
},
exec(sql: string) {
db.exec(sql)
},
pragma(pragma: string) {
return db.pragma(pragma)
},
transaction<F extends (...args: Array<never>) => unknown>(fn: F): F {
return db.transaction(fn)
},
close() {
db.close()
},
}
}