Replace better-sqlite3 with built-in SQLite drivers
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
import Database from 'better-sqlite3';
|
||||
import { Kysely, MysqlDialect, PostgresDialect, SqliteDialect } from 'kysely';
|
||||
import { createPool as createMysqlPool } from 'mysql2';
|
||||
import { Pool as PostgresPool } from 'pg';
|
||||
@@ -44,16 +43,183 @@ export interface DatabaseConnection {
|
||||
destroy: () => Promise<void>;
|
||||
}
|
||||
|
||||
interface BunSqliteStatement {
|
||||
columnNames: ReadonlyArray<string>;
|
||||
all(parameters?: ReadonlyArray<unknown>): unknown[];
|
||||
run(parameters?: ReadonlyArray<unknown>): {
|
||||
changes: number | bigint;
|
||||
lastInsertRowid: number | bigint;
|
||||
};
|
||||
iterate(parameters?: ReadonlyArray<unknown>): IterableIterator<unknown>;
|
||||
}
|
||||
|
||||
interface BunSqliteDatabase {
|
||||
close(): void;
|
||||
exec(sql: string): void;
|
||||
prepare(sql: string): BunSqliteStatement;
|
||||
}
|
||||
|
||||
interface BunSqliteModule {
|
||||
Database: {
|
||||
open(filename: string, flags?: number): BunSqliteDatabase;
|
||||
};
|
||||
constants: {
|
||||
SQLITE_OPEN_CREATE: number;
|
||||
SQLITE_OPEN_MEMORY: number;
|
||||
SQLITE_OPEN_READONLY: number;
|
||||
SQLITE_OPEN_READWRITE: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface NodeSqliteStatement {
|
||||
all(...parameters: ReadonlyArray<unknown>): unknown[];
|
||||
columns(): ReadonlyArray<unknown>;
|
||||
iterate(...parameters: ReadonlyArray<unknown>): IterableIterator<unknown>;
|
||||
run(...parameters: ReadonlyArray<unknown>): {
|
||||
changes: number | bigint;
|
||||
lastInsertRowid: number | bigint;
|
||||
};
|
||||
}
|
||||
|
||||
interface NodeSqliteDatabase {
|
||||
close(): void;
|
||||
exec(sql: string): void;
|
||||
prepare(sql: string): NodeSqliteStatement;
|
||||
}
|
||||
|
||||
interface NodeSqliteModule {
|
||||
DatabaseSync: new (
|
||||
filename: string,
|
||||
options?: {
|
||||
readOnly?: boolean;
|
||||
},
|
||||
) => NodeSqliteDatabase;
|
||||
}
|
||||
|
||||
interface KyselyCompatibleSqliteStatement {
|
||||
readonly reader: boolean;
|
||||
all(parameters: ReadonlyArray<unknown>): unknown[];
|
||||
run(parameters: ReadonlyArray<unknown>): {
|
||||
changes: number | bigint;
|
||||
lastInsertRowid: number | bigint;
|
||||
};
|
||||
iterate(parameters: ReadonlyArray<unknown>): IterableIterator<unknown>;
|
||||
}
|
||||
|
||||
interface KyselyCompatibleSqliteDatabase {
|
||||
close(): void;
|
||||
prepare(sql: string): KyselyCompatibleSqliteStatement;
|
||||
}
|
||||
|
||||
const BUN_SQLITE_MODULE = 'bun:sqlite';
|
||||
const NODE_SQLITE_MODULE = 'node:sqlite';
|
||||
|
||||
function createUnsupportedSqliteRuntimeError(): IdentityDBConfigurationError {
|
||||
return new IdentityDBConfigurationError(
|
||||
'SQLite connections now require a runtime with a built-in SQLite driver. Use Bun for bun:sqlite support, or Node 22+ for node:sqlite support.',
|
||||
);
|
||||
}
|
||||
|
||||
function isBunRuntime(): boolean {
|
||||
return typeof globalThis === 'object' && 'Bun' in globalThis;
|
||||
}
|
||||
|
||||
async function createBunSqliteDatabase(
|
||||
config: SqliteConnectionConfig,
|
||||
bunSqliteModule?: BunSqliteModule,
|
||||
): Promise<KyselyCompatibleSqliteDatabase> {
|
||||
const bunSqlite = bunSqliteModule
|
||||
?? ((await import(BUN_SQLITE_MODULE).catch(() => {
|
||||
throw createUnsupportedSqliteRuntimeError();
|
||||
})) as BunSqliteModule);
|
||||
|
||||
const flags = config.readonly
|
||||
? bunSqlite.constants.SQLITE_OPEN_READONLY
|
||||
: bunSqlite.constants.SQLITE_OPEN_READWRITE
|
||||
| bunSqlite.constants.SQLITE_OPEN_CREATE
|
||||
| (config.filename === ':memory:' ? bunSqlite.constants.SQLITE_OPEN_MEMORY : 0);
|
||||
|
||||
const database = bunSqlite.Database.open(config.filename, flags);
|
||||
database.exec('PRAGMA foreign_keys = ON');
|
||||
|
||||
return {
|
||||
close() {
|
||||
database.close();
|
||||
},
|
||||
prepare(sql: string): KyselyCompatibleSqliteStatement {
|
||||
const statement = database.prepare(sql);
|
||||
|
||||
return {
|
||||
reader: statement.columnNames.length > 0,
|
||||
all(parameters) {
|
||||
return statement.all(Array.from(parameters));
|
||||
},
|
||||
run(parameters) {
|
||||
return statement.run(Array.from(parameters));
|
||||
},
|
||||
iterate(parameters) {
|
||||
return statement.iterate(Array.from(parameters));
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function createNodeSqliteDatabase(
|
||||
config: SqliteConnectionConfig,
|
||||
nodeSqlite: NodeSqliteModule,
|
||||
): KyselyCompatibleSqliteDatabase {
|
||||
const database = new nodeSqlite.DatabaseSync(config.filename, {
|
||||
readOnly: config.readonly ?? false,
|
||||
});
|
||||
|
||||
database.exec('PRAGMA foreign_keys = ON');
|
||||
|
||||
return {
|
||||
close() {
|
||||
database.close();
|
||||
},
|
||||
prepare(sql: string): KyselyCompatibleSqliteStatement {
|
||||
const statement = database.prepare(sql);
|
||||
|
||||
return {
|
||||
reader: statement.columns().length > 0,
|
||||
all(parameters) {
|
||||
return statement.all(...parameters);
|
||||
},
|
||||
run(parameters) {
|
||||
return statement.run(...parameters);
|
||||
},
|
||||
iterate(parameters) {
|
||||
return statement.iterate(...parameters);
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async function createSqliteDatabase(
|
||||
config: SqliteConnectionConfig,
|
||||
): Promise<KyselyCompatibleSqliteDatabase> {
|
||||
if (isBunRuntime()) {
|
||||
return createBunSqliteDatabase(config);
|
||||
}
|
||||
|
||||
const nodeSqlite = await import(NODE_SQLITE_MODULE).catch(() => null);
|
||||
|
||||
if (nodeSqlite) {
|
||||
return createNodeSqliteDatabase(config, nodeSqlite as NodeSqliteModule);
|
||||
}
|
||||
|
||||
throw createUnsupportedSqliteRuntimeError();
|
||||
}
|
||||
|
||||
export async function createDatabase(
|
||||
config: IdentityDBConnectionConfig,
|
||||
): Promise<DatabaseConnection> {
|
||||
switch (config.client) {
|
||||
case 'sqlite': {
|
||||
const sqlite = new Database(config.filename, {
|
||||
readonly: config.readonly ?? false,
|
||||
});
|
||||
|
||||
sqlite.pragma('foreign_keys = ON');
|
||||
const sqlite = await createSqliteDatabase(config);
|
||||
|
||||
const db = new Kysely<IdentityDatabaseSchema>({
|
||||
dialect: new SqliteDialect({
|
||||
@@ -66,7 +232,6 @@ export async function createDatabase(
|
||||
db,
|
||||
destroy: async () => {
|
||||
await db.destroy();
|
||||
sqlite.close();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user