@better-auth/kysely-adapter 1.5.0-beta.9
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/.turbo/turbo-build.log +16 -0
- package/LICENSE.md +20 -0
- package/dist/bun-sqlite-dialect-deBGXcrd.mjs +155 -0
- package/dist/index.d.mts +49 -0
- package/dist/index.mjs +364 -0
- package/dist/node-sqlite-dialect.d.mts +28 -0
- package/dist/node-sqlite-dialect.mjs +155 -0
- package/package.json +44 -0
- package/src/bun-sqlite-dialect.ts +300 -0
- package/src/dialect.ts +148 -0
- package/src/index.ts +5 -0
- package/src/kysely-adapter.test.ts +24 -0
- package/src/kysely-adapter.ts +639 -0
- package/src/node-sqlite-dialect.ts +302 -0
- package/src/types.ts +1 -0
- package/tsconfig.json +9 -0
- package/tsdown.config.ts +7 -0
- package/vitest.config.ts +3 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { CompiledQuery, DEFAULT_MIGRATION_LOCK_TABLE, DEFAULT_MIGRATION_TABLE, DefaultQueryCompiler, sql } from "kysely";
|
|
2
|
+
|
|
3
|
+
//#region src/node-sqlite-dialect.ts
|
|
4
|
+
var NodeSqliteAdapter = class {
|
|
5
|
+
get supportsCreateIfNotExists() {
|
|
6
|
+
return true;
|
|
7
|
+
}
|
|
8
|
+
get supportsTransactionalDdl() {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
get supportsReturning() {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
async acquireMigrationLock() {}
|
|
15
|
+
async releaseMigrationLock() {}
|
|
16
|
+
get supportsOutput() {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
var NodeSqliteDriver = class {
|
|
21
|
+
#config;
|
|
22
|
+
#connectionMutex = new ConnectionMutex();
|
|
23
|
+
#db;
|
|
24
|
+
#connection;
|
|
25
|
+
constructor(config) {
|
|
26
|
+
this.#config = { ...config };
|
|
27
|
+
}
|
|
28
|
+
async init() {
|
|
29
|
+
this.#db = this.#config.database;
|
|
30
|
+
this.#connection = new NodeSqliteConnection(this.#db);
|
|
31
|
+
if (this.#config.onCreateConnection) await this.#config.onCreateConnection(this.#connection);
|
|
32
|
+
}
|
|
33
|
+
async acquireConnection() {
|
|
34
|
+
await this.#connectionMutex.lock();
|
|
35
|
+
return this.#connection;
|
|
36
|
+
}
|
|
37
|
+
async beginTransaction(connection) {
|
|
38
|
+
await connection.executeQuery(CompiledQuery.raw("begin"));
|
|
39
|
+
}
|
|
40
|
+
async commitTransaction(connection) {
|
|
41
|
+
await connection.executeQuery(CompiledQuery.raw("commit"));
|
|
42
|
+
}
|
|
43
|
+
async rollbackTransaction(connection) {
|
|
44
|
+
await connection.executeQuery(CompiledQuery.raw("rollback"));
|
|
45
|
+
}
|
|
46
|
+
async releaseConnection() {
|
|
47
|
+
this.#connectionMutex.unlock();
|
|
48
|
+
}
|
|
49
|
+
async destroy() {
|
|
50
|
+
this.#db?.close();
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
var NodeSqliteConnection = class {
|
|
54
|
+
#db;
|
|
55
|
+
constructor(db) {
|
|
56
|
+
this.#db = db;
|
|
57
|
+
}
|
|
58
|
+
executeQuery(compiledQuery) {
|
|
59
|
+
const { sql: sql$1, parameters } = compiledQuery;
|
|
60
|
+
const rows = this.#db.prepare(sql$1).all(...parameters);
|
|
61
|
+
return Promise.resolve({ rows });
|
|
62
|
+
}
|
|
63
|
+
async *streamQuery() {
|
|
64
|
+
throw new Error("Streaming query is not supported by SQLite driver.");
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
var ConnectionMutex = class {
|
|
68
|
+
#promise;
|
|
69
|
+
#resolve;
|
|
70
|
+
async lock() {
|
|
71
|
+
while (this.#promise !== void 0) await this.#promise;
|
|
72
|
+
this.#promise = new Promise((resolve) => {
|
|
73
|
+
this.#resolve = resolve;
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
unlock() {
|
|
77
|
+
const resolve = this.#resolve;
|
|
78
|
+
this.#promise = void 0;
|
|
79
|
+
this.#resolve = void 0;
|
|
80
|
+
resolve?.();
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
var NodeSqliteIntrospector = class {
|
|
84
|
+
#db;
|
|
85
|
+
constructor(db) {
|
|
86
|
+
this.#db = db;
|
|
87
|
+
}
|
|
88
|
+
async getSchemas() {
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
async getTables(options = { withInternalKyselyTables: false }) {
|
|
92
|
+
let query = this.#db.selectFrom("sqlite_schema").where("type", "=", "table").where("name", "not like", "sqlite_%").select("name").$castTo();
|
|
93
|
+
if (!options.withInternalKyselyTables) query = query.where("name", "!=", DEFAULT_MIGRATION_TABLE).where("name", "!=", DEFAULT_MIGRATION_LOCK_TABLE);
|
|
94
|
+
const tables = await query.execute();
|
|
95
|
+
return Promise.all(tables.map(({ name }) => this.#getTableMetadata(name)));
|
|
96
|
+
}
|
|
97
|
+
async getMetadata(options) {
|
|
98
|
+
return { tables: await this.getTables(options) };
|
|
99
|
+
}
|
|
100
|
+
async #getTableMetadata(table) {
|
|
101
|
+
const db = this.#db;
|
|
102
|
+
const autoIncrementCol = (await db.selectFrom("sqlite_master").where("name", "=", table).select("sql").$castTo().execute())[0]?.sql?.split(/[\(\),]/)?.find((it) => it.toLowerCase().includes("autoincrement"))?.split(/\s+/)?.[0]?.replace(/["`]/g, "");
|
|
103
|
+
return {
|
|
104
|
+
name: table,
|
|
105
|
+
columns: (await db.selectFrom(sql`pragma_table_info(${table})`.as("table_info")).select([
|
|
106
|
+
"name",
|
|
107
|
+
"type",
|
|
108
|
+
"notnull",
|
|
109
|
+
"dflt_value"
|
|
110
|
+
]).execute()).map((col) => ({
|
|
111
|
+
name: col.name,
|
|
112
|
+
dataType: col.type,
|
|
113
|
+
isNullable: !col.notnull,
|
|
114
|
+
isAutoIncrementing: col.name === autoIncrementCol,
|
|
115
|
+
hasDefaultValue: col.dflt_value != null
|
|
116
|
+
})),
|
|
117
|
+
isView: true
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
var NodeSqliteQueryCompiler = class extends DefaultQueryCompiler {
|
|
122
|
+
getCurrentParameterPlaceholder() {
|
|
123
|
+
return "?";
|
|
124
|
+
}
|
|
125
|
+
getLeftIdentifierWrapper() {
|
|
126
|
+
return "\"";
|
|
127
|
+
}
|
|
128
|
+
getRightIdentifierWrapper() {
|
|
129
|
+
return "\"";
|
|
130
|
+
}
|
|
131
|
+
getAutoIncrement() {
|
|
132
|
+
return "autoincrement";
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
var NodeSqliteDialect = class {
|
|
136
|
+
#config;
|
|
137
|
+
constructor(config) {
|
|
138
|
+
this.#config = { ...config };
|
|
139
|
+
}
|
|
140
|
+
createDriver() {
|
|
141
|
+
return new NodeSqliteDriver(this.#config);
|
|
142
|
+
}
|
|
143
|
+
createQueryCompiler() {
|
|
144
|
+
return new NodeSqliteQueryCompiler();
|
|
145
|
+
}
|
|
146
|
+
createAdapter() {
|
|
147
|
+
return new NodeSqliteAdapter();
|
|
148
|
+
}
|
|
149
|
+
createIntrospector(db) {
|
|
150
|
+
return new NodeSqliteIntrospector(db);
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
//#endregion
|
|
155
|
+
export { NodeSqliteDialect };
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@better-auth/kysely-adapter",
|
|
3
|
+
"version": "1.5.0-beta.9",
|
|
4
|
+
"description": "Kysely adapter for Better Auth",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/better-auth/better-auth.git",
|
|
9
|
+
"directory": "packages/kysely-adapter"
|
|
10
|
+
},
|
|
11
|
+
"main": "./dist/index.mjs",
|
|
12
|
+
"module": "./dist/index.mjs",
|
|
13
|
+
"types": "./dist/index.d.mts",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"dev-source": "./src/index.ts",
|
|
17
|
+
"types": "./dist/index.d.mts",
|
|
18
|
+
"default": "./dist/index.mjs"
|
|
19
|
+
},
|
|
20
|
+
"./node-sqlite-dialect": {
|
|
21
|
+
"dev-source": "./src/node-sqlite-dialect.ts",
|
|
22
|
+
"types": "./dist/node-sqlite-dialect.d.mts",
|
|
23
|
+
"default": "./dist/node-sqlite-dialect.mjs"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"@better-auth/utils": "^0.3.0",
|
|
28
|
+
"kysely": "^0.27.0 || ^0.28.0",
|
|
29
|
+
"@better-auth/core": "1.5.0-beta.9"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@better-auth/utils": "^0.3.0",
|
|
33
|
+
"kysely": "^0.28.10",
|
|
34
|
+
"tsdown": "^0.19.0",
|
|
35
|
+
"typescript": "^5.9.3",
|
|
36
|
+
"@better-auth/core": "1.5.0-beta.9"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsdown",
|
|
40
|
+
"dev": "tsdown --watch",
|
|
41
|
+
"test": "vitest",
|
|
42
|
+
"typecheck": "tsc --noEmit"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @see {@link https://github.com/dylanblokhuis/kysely-bun-sqlite} - Fork of the original kysely-bun-sqlite package by @dylanblokhuis
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { Database } from "bun:sqlite";
|
|
6
|
+
import type {
|
|
7
|
+
DatabaseConnection,
|
|
8
|
+
DatabaseIntrospector,
|
|
9
|
+
DatabaseMetadata,
|
|
10
|
+
DatabaseMetadataOptions,
|
|
11
|
+
Dialect,
|
|
12
|
+
DialectAdapter,
|
|
13
|
+
DialectAdapterBase,
|
|
14
|
+
Driver,
|
|
15
|
+
Kysely,
|
|
16
|
+
QueryCompiler,
|
|
17
|
+
QueryResult,
|
|
18
|
+
SchemaMetadata,
|
|
19
|
+
TableMetadata,
|
|
20
|
+
} from "kysely";
|
|
21
|
+
import {
|
|
22
|
+
CompiledQuery,
|
|
23
|
+
DEFAULT_MIGRATION_LOCK_TABLE,
|
|
24
|
+
DEFAULT_MIGRATION_TABLE,
|
|
25
|
+
DefaultQueryCompiler,
|
|
26
|
+
sql,
|
|
27
|
+
} from "kysely";
|
|
28
|
+
|
|
29
|
+
class BunSqliteAdapter implements DialectAdapterBase {
|
|
30
|
+
get supportsCreateIfNotExists(): boolean {
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
get supportsTransactionalDdl(): boolean {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
get supportsReturning(): boolean {
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async acquireMigrationLock(): Promise<void> {
|
|
43
|
+
// SQLite only has one connection that's reserved by the migration system
|
|
44
|
+
// for the whole time between acquireMigrationLock and releaseMigrationLock.
|
|
45
|
+
// We don't need to do anything here.
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async releaseMigrationLock(): Promise<void> {
|
|
49
|
+
// SQLite only has one connection that's reserved by the migration system
|
|
50
|
+
// for the whole time between acquireMigrationLock and releaseMigrationLock.
|
|
51
|
+
// We don't need to do anything here.
|
|
52
|
+
}
|
|
53
|
+
get supportsOutput(): boolean {
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Config for the SQLite dialect.
|
|
60
|
+
*/
|
|
61
|
+
export interface BunSqliteDialectConfig {
|
|
62
|
+
/**
|
|
63
|
+
* An sqlite Database instance or a function that returns one.
|
|
64
|
+
*/
|
|
65
|
+
database: Database;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Called once when the first query is executed.
|
|
69
|
+
*/
|
|
70
|
+
onCreateConnection?:
|
|
71
|
+
| ((connection: DatabaseConnection) => Promise<void>)
|
|
72
|
+
| undefined;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
class BunSqliteDriver implements Driver {
|
|
76
|
+
readonly #config: BunSqliteDialectConfig;
|
|
77
|
+
readonly #connectionMutex = new ConnectionMutex();
|
|
78
|
+
|
|
79
|
+
#db?: Database;
|
|
80
|
+
#connection?: DatabaseConnection;
|
|
81
|
+
|
|
82
|
+
constructor(config: BunSqliteDialectConfig) {
|
|
83
|
+
this.#config = { ...config };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async init(): Promise<void> {
|
|
87
|
+
this.#db = this.#config.database;
|
|
88
|
+
|
|
89
|
+
this.#connection = new BunSqliteConnection(this.#db);
|
|
90
|
+
|
|
91
|
+
if (this.#config.onCreateConnection) {
|
|
92
|
+
await this.#config.onCreateConnection(this.#connection);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async acquireConnection(): Promise<DatabaseConnection> {
|
|
97
|
+
// SQLite only has one single connection. We use a mutex here to wait
|
|
98
|
+
// until the single connection has been released.
|
|
99
|
+
await this.#connectionMutex.lock();
|
|
100
|
+
return this.#connection!;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async beginTransaction(connection: DatabaseConnection): Promise<void> {
|
|
104
|
+
await connection.executeQuery(CompiledQuery.raw("begin"));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async commitTransaction(connection: DatabaseConnection): Promise<void> {
|
|
108
|
+
await connection.executeQuery(CompiledQuery.raw("commit"));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async rollbackTransaction(connection: DatabaseConnection): Promise<void> {
|
|
112
|
+
await connection.executeQuery(CompiledQuery.raw("rollback"));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async releaseConnection(): Promise<void> {
|
|
116
|
+
this.#connectionMutex.unlock();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async destroy(): Promise<void> {
|
|
120
|
+
this.#db?.close();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
class BunSqliteConnection implements DatabaseConnection {
|
|
125
|
+
readonly #db: Database;
|
|
126
|
+
|
|
127
|
+
constructor(db: Database) {
|
|
128
|
+
this.#db = db;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
executeQuery<O>(compiledQuery: CompiledQuery): Promise<QueryResult<O>> {
|
|
132
|
+
const { sql, parameters } = compiledQuery;
|
|
133
|
+
const stmt = this.#db.prepare(sql);
|
|
134
|
+
|
|
135
|
+
return Promise.resolve({
|
|
136
|
+
rows: stmt.all(parameters as any) as O[],
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async *streamQuery() {
|
|
141
|
+
throw new Error("Streaming query is not supported by SQLite driver.");
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
class ConnectionMutex {
|
|
146
|
+
#promise?: Promise<void>;
|
|
147
|
+
#resolve?: () => void;
|
|
148
|
+
|
|
149
|
+
async lock(): Promise<void> {
|
|
150
|
+
while (this.#promise !== undefined) {
|
|
151
|
+
await this.#promise;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
this.#promise = new Promise((resolve) => {
|
|
155
|
+
this.#resolve = resolve;
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
unlock(): void {
|
|
160
|
+
const resolve = this.#resolve;
|
|
161
|
+
|
|
162
|
+
this.#promise = undefined;
|
|
163
|
+
this.#resolve = undefined;
|
|
164
|
+
|
|
165
|
+
resolve?.();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
class BunSqliteIntrospector implements DatabaseIntrospector {
|
|
170
|
+
readonly #db: Kysely<unknown>;
|
|
171
|
+
|
|
172
|
+
constructor(db: Kysely<unknown>) {
|
|
173
|
+
this.#db = db;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async getSchemas(): Promise<SchemaMetadata[]> {
|
|
177
|
+
// Sqlite doesn't support schemas.
|
|
178
|
+
return [];
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async getTables(
|
|
182
|
+
options: DatabaseMetadataOptions = { withInternalKyselyTables: false },
|
|
183
|
+
): Promise<TableMetadata[]> {
|
|
184
|
+
let query = this.#db
|
|
185
|
+
// @ts-expect-error
|
|
186
|
+
.selectFrom("sqlite_schema")
|
|
187
|
+
// @ts-expect-error
|
|
188
|
+
.where("type", "=", "table")
|
|
189
|
+
// @ts-expect-error
|
|
190
|
+
.where("name", "not like", "sqlite_%")
|
|
191
|
+
.select("name")
|
|
192
|
+
.$castTo<{ name: string }>();
|
|
193
|
+
|
|
194
|
+
if (!options.withInternalKyselyTables) {
|
|
195
|
+
query = query
|
|
196
|
+
// @ts-expect-error
|
|
197
|
+
.where("name", "!=", DEFAULT_MIGRATION_TABLE)
|
|
198
|
+
// @ts-expect-error
|
|
199
|
+
.where("name", "!=", DEFAULT_MIGRATION_LOCK_TABLE);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const tables = await query.execute();
|
|
203
|
+
return Promise.all(tables.map(({ name }) => this.#getTableMetadata(name)));
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async getMetadata(
|
|
207
|
+
options?: DatabaseMetadataOptions | undefined,
|
|
208
|
+
): Promise<DatabaseMetadata> {
|
|
209
|
+
return {
|
|
210
|
+
tables: await this.getTables(options),
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async #getTableMetadata(table: string): Promise<TableMetadata> {
|
|
215
|
+
const db = this.#db;
|
|
216
|
+
|
|
217
|
+
// Get the SQL that was used to create the table.
|
|
218
|
+
const createSql = await db
|
|
219
|
+
// @ts-expect-error
|
|
220
|
+
.selectFrom("sqlite_master")
|
|
221
|
+
// @ts-expect-error
|
|
222
|
+
.where("name", "=", table)
|
|
223
|
+
.select("sql")
|
|
224
|
+
.$castTo<{ sql: string | undefined }>()
|
|
225
|
+
.execute();
|
|
226
|
+
|
|
227
|
+
// Try to find the name of the column that has `autoincrement` 🤦
|
|
228
|
+
const autoIncrementCol = createSql[0]?.sql
|
|
229
|
+
?.split(/[\(\),]/)
|
|
230
|
+
?.find((it) => it.toLowerCase().includes("autoincrement"))
|
|
231
|
+
?.split(/\s+/)?.[0]
|
|
232
|
+
?.replace(/["`]/g, "");
|
|
233
|
+
|
|
234
|
+
const columns = await db
|
|
235
|
+
.selectFrom(
|
|
236
|
+
sql<{
|
|
237
|
+
name: string;
|
|
238
|
+
type: string;
|
|
239
|
+
notnull: 0 | 1;
|
|
240
|
+
dflt_value: any;
|
|
241
|
+
}>`pragma_table_info(${table})`.as("table_info"),
|
|
242
|
+
)
|
|
243
|
+
.select(["name", "type", "notnull", "dflt_value"])
|
|
244
|
+
.execute();
|
|
245
|
+
|
|
246
|
+
return {
|
|
247
|
+
name: table,
|
|
248
|
+
columns: columns.map((col) => ({
|
|
249
|
+
name: col.name,
|
|
250
|
+
dataType: col.type,
|
|
251
|
+
isNullable: !col.notnull,
|
|
252
|
+
isAutoIncrementing: col.name === autoIncrementCol,
|
|
253
|
+
hasDefaultValue: col.dflt_value != null,
|
|
254
|
+
})),
|
|
255
|
+
isView: true,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
class BunSqliteQueryCompiler extends DefaultQueryCompiler {
|
|
261
|
+
protected override getCurrentParameterPlaceholder() {
|
|
262
|
+
return "?";
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
protected override getLeftIdentifierWrapper(): string {
|
|
266
|
+
return '"';
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
protected override getRightIdentifierWrapper(): string {
|
|
270
|
+
return '"';
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
protected override getAutoIncrement() {
|
|
274
|
+
return "autoincrement";
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export class BunSqliteDialect implements Dialect {
|
|
279
|
+
readonly #config: BunSqliteDialectConfig;
|
|
280
|
+
|
|
281
|
+
constructor(config: BunSqliteDialectConfig) {
|
|
282
|
+
this.#config = { ...config };
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
createDriver(): Driver {
|
|
286
|
+
return new BunSqliteDriver(this.#config);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
createQueryCompiler(): QueryCompiler {
|
|
290
|
+
return new BunSqliteQueryCompiler();
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
createAdapter(): DialectAdapter {
|
|
294
|
+
return new BunSqliteAdapter();
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
createIntrospector(db: Kysely<any>): DatabaseIntrospector {
|
|
298
|
+
return new BunSqliteIntrospector(db);
|
|
299
|
+
}
|
|
300
|
+
}
|
package/src/dialect.ts
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import type { BetterAuthOptions } from "@better-auth/core";
|
|
2
|
+
import type { Dialect } from "kysely";
|
|
3
|
+
import {
|
|
4
|
+
Kysely,
|
|
5
|
+
MssqlDialect,
|
|
6
|
+
MysqlDialect,
|
|
7
|
+
PostgresDialect,
|
|
8
|
+
SqliteDialect,
|
|
9
|
+
} from "kysely";
|
|
10
|
+
import type { KyselyDatabaseType } from "./types";
|
|
11
|
+
|
|
12
|
+
export function getKyselyDatabaseType(
|
|
13
|
+
db: BetterAuthOptions["database"],
|
|
14
|
+
): KyselyDatabaseType | null {
|
|
15
|
+
if (!db) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
if ("dialect" in db) {
|
|
19
|
+
return getKyselyDatabaseType(db.dialect as Dialect);
|
|
20
|
+
}
|
|
21
|
+
if ("createDriver" in db) {
|
|
22
|
+
if (db instanceof SqliteDialect) {
|
|
23
|
+
return "sqlite";
|
|
24
|
+
}
|
|
25
|
+
if (db instanceof MysqlDialect) {
|
|
26
|
+
return "mysql";
|
|
27
|
+
}
|
|
28
|
+
if (db instanceof PostgresDialect) {
|
|
29
|
+
return "postgres";
|
|
30
|
+
}
|
|
31
|
+
if (db instanceof MssqlDialect) {
|
|
32
|
+
return "mssql";
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if ("aggregate" in db) {
|
|
36
|
+
return "sqlite";
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if ("getConnection" in db) {
|
|
40
|
+
return "mysql";
|
|
41
|
+
}
|
|
42
|
+
if ("connect" in db) {
|
|
43
|
+
return "postgres";
|
|
44
|
+
}
|
|
45
|
+
if ("fileControl" in db) {
|
|
46
|
+
return "sqlite";
|
|
47
|
+
}
|
|
48
|
+
if ("open" in db && "close" in db && "prepare" in db) {
|
|
49
|
+
return "sqlite";
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const createKyselyAdapter = async (config: BetterAuthOptions) => {
|
|
55
|
+
const db = config.database;
|
|
56
|
+
|
|
57
|
+
if (!db) {
|
|
58
|
+
return {
|
|
59
|
+
kysely: null,
|
|
60
|
+
databaseType: null,
|
|
61
|
+
transaction: undefined,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if ("db" in db) {
|
|
66
|
+
return {
|
|
67
|
+
kysely: db.db,
|
|
68
|
+
databaseType: db.type,
|
|
69
|
+
transaction: db.transaction,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if ("dialect" in db) {
|
|
74
|
+
return {
|
|
75
|
+
kysely: new Kysely<any>({ dialect: db.dialect }),
|
|
76
|
+
databaseType: db.type,
|
|
77
|
+
transaction: db.transaction,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let dialect: Dialect | undefined = undefined;
|
|
82
|
+
|
|
83
|
+
const databaseType = getKyselyDatabaseType(db);
|
|
84
|
+
|
|
85
|
+
if ("createDriver" in db) {
|
|
86
|
+
dialect = db;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if ("aggregate" in db && !("createSession" in db)) {
|
|
90
|
+
dialect = new SqliteDialect({
|
|
91
|
+
database: db,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if ("getConnection" in db) {
|
|
96
|
+
// @ts-expect-error - mysql2/promise
|
|
97
|
+
dialect = new MysqlDialect(db);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if ("connect" in db) {
|
|
101
|
+
dialect = new PostgresDialect({
|
|
102
|
+
pool: db,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if ("fileControl" in db) {
|
|
107
|
+
const { BunSqliteDialect } = await import("./bun-sqlite-dialect");
|
|
108
|
+
dialect = new BunSqliteDialect({
|
|
109
|
+
database: db,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if ("createSession" in db && typeof window === "undefined") {
|
|
114
|
+
let DatabaseSync: typeof import("node:sqlite").DatabaseSync | undefined =
|
|
115
|
+
undefined;
|
|
116
|
+
try {
|
|
117
|
+
const nodeSqlite: string = "node:sqlite";
|
|
118
|
+
// Ignore both Vite and Webpack for dynamic import as they both try to pre-bundle 'node:sqlite' which might fail
|
|
119
|
+
// It's okay because we are in a try-catch block
|
|
120
|
+
({ DatabaseSync } = await import(
|
|
121
|
+
/* @vite-ignore */
|
|
122
|
+
/* webpackIgnore: true */
|
|
123
|
+
nodeSqlite
|
|
124
|
+
));
|
|
125
|
+
} catch (error: unknown) {
|
|
126
|
+
if (
|
|
127
|
+
error !== null &&
|
|
128
|
+
typeof error === "object" &&
|
|
129
|
+
"code" in error &&
|
|
130
|
+
error.code !== "ERR_UNKNOWN_BUILTIN_MODULE"
|
|
131
|
+
) {
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (DatabaseSync && db instanceof DatabaseSync) {
|
|
136
|
+
const { NodeSqliteDialect } = await import("./node-sqlite-dialect");
|
|
137
|
+
dialect = new NodeSqliteDialect({
|
|
138
|
+
database: db,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
kysely: dialect ? new Kysely<any>({ dialect }) : null,
|
|
145
|
+
databaseType,
|
|
146
|
+
transaction: undefined,
|
|
147
|
+
};
|
|
148
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Kysely, SqliteDialect } from "kysely";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { kyselyAdapter } from "./kysely-adapter";
|
|
4
|
+
|
|
5
|
+
describe("kysely-adapter", () => {
|
|
6
|
+
it("should create kysely adapter", () => {
|
|
7
|
+
const db = new Kysely({
|
|
8
|
+
dialect: new SqliteDialect({
|
|
9
|
+
database: {
|
|
10
|
+
close: () => {},
|
|
11
|
+
prepare: () =>
|
|
12
|
+
({
|
|
13
|
+
all: () => [],
|
|
14
|
+
run: () => {},
|
|
15
|
+
get: () => {},
|
|
16
|
+
iterate: () => [],
|
|
17
|
+
}) as any,
|
|
18
|
+
} as any,
|
|
19
|
+
}),
|
|
20
|
+
});
|
|
21
|
+
const adapter = kyselyAdapter(db);
|
|
22
|
+
expect(adapter).toBeDefined();
|
|
23
|
+
});
|
|
24
|
+
});
|