mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-15 20:17:22 +00:00
feat: enhance SQLite connection configurations to allow WAL
Updated the Bun and Node SQLite connection implementations to support additional configuration options, including `onCreateConnection`. Introduced tests for connection creation to validate database instance types and ensure proper callback execution. Improved type exports for better integration with existing code.
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
import { connectionTestSuite } from "data/connection/connection-test-suite";
|
||||
import { bunSqlite } from "./BunSqliteConnection";
|
||||
import { bunTestRunner } from "adapter/bun/test";
|
||||
import { describe } from "bun:test";
|
||||
import { describe, test, mock, expect } from "bun:test";
|
||||
import { Database } from "bun:sqlite";
|
||||
import { GenericSqliteConnection } from "data/connection/sqlite/GenericSqliteConnection";
|
||||
|
||||
describe("BunSqliteConnection", () => {
|
||||
connectionTestSuite(bunTestRunner, {
|
||||
@@ -12,4 +13,20 @@ describe("BunSqliteConnection", () => {
|
||||
}),
|
||||
rawDialectDetails: [],
|
||||
});
|
||||
|
||||
test("onCreateConnection", async () => {
|
||||
const called = mock(() => null);
|
||||
|
||||
const conn = bunSqlite({
|
||||
onCreateConnection: (db) => {
|
||||
expect(db).toBeInstanceOf(Database);
|
||||
called();
|
||||
},
|
||||
});
|
||||
await conn.ping();
|
||||
|
||||
expect(conn).toBeInstanceOf(GenericSqliteConnection);
|
||||
expect(conn.db).toBeInstanceOf(Database);
|
||||
expect(called).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,25 +1,36 @@
|
||||
import { Database } from "bun:sqlite";
|
||||
import { genericSqlite, type GenericSqliteConnection } from "bknd";
|
||||
import {
|
||||
genericSqlite,
|
||||
type GenericSqliteConnection,
|
||||
type GenericSqliteConnectionConfig,
|
||||
} from "bknd";
|
||||
import { omitKeys } from "bknd/utils";
|
||||
|
||||
export type BunSqliteConnection = GenericSqliteConnection<Database>;
|
||||
export type BunSqliteConnectionConfig = {
|
||||
database: Database;
|
||||
};
|
||||
export type BunSqliteConnectionConfig = Omit<
|
||||
GenericSqliteConnectionConfig<Database>,
|
||||
"name" | "supports"
|
||||
> &
|
||||
({ database?: Database; url?: never } | { url?: string; database?: never });
|
||||
|
||||
export function bunSqlite(config?: BunSqliteConnectionConfig | { url: string }) {
|
||||
let db: Database;
|
||||
export function bunSqlite(config?: BunSqliteConnectionConfig) {
|
||||
let db: Database | undefined;
|
||||
if (config) {
|
||||
if ("database" in config) {
|
||||
if ("database" in config && config.database) {
|
||||
db = config.database;
|
||||
} else {
|
||||
} else if (config.url) {
|
||||
db = new Database(config.url);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
|
||||
if (!db) {
|
||||
db = new Database(":memory:");
|
||||
}
|
||||
|
||||
return genericSqlite("bun-sqlite", db, (utils) => {
|
||||
//const fn = cache ? "query" : "prepare";
|
||||
return genericSqlite(
|
||||
"bun-sqlite",
|
||||
db,
|
||||
(utils) => {
|
||||
const getStmt = (sql: string) => db.prepare(sql);
|
||||
|
||||
return {
|
||||
@@ -36,5 +47,7 @@ export function bunSqlite(config?: BunSqliteConnectionConfig | { url: string })
|
||||
}),
|
||||
close: () => db.close(),
|
||||
};
|
||||
});
|
||||
},
|
||||
omitKeys(config ?? ({} as any), ["database", "url", "name", "supports"]),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,19 +1,29 @@
|
||||
import { genericSqlite } from "bknd";
|
||||
import {
|
||||
genericSqlite,
|
||||
type GenericSqliteConnection,
|
||||
type GenericSqliteConnectionConfig,
|
||||
} from "bknd";
|
||||
import { DatabaseSync } from "node:sqlite";
|
||||
import { omitKeys } from "bknd/utils";
|
||||
|
||||
export type NodeSqliteConnectionConfig = {
|
||||
database: DatabaseSync;
|
||||
};
|
||||
export type NodeSqliteConnection = GenericSqliteConnection<DatabaseSync>;
|
||||
export type NodeSqliteConnectionConfig = Omit<
|
||||
GenericSqliteConnectionConfig<DatabaseSync>,
|
||||
"name" | "supports"
|
||||
> &
|
||||
({ database?: DatabaseSync; url?: never } | { url?: string; database?: never });
|
||||
|
||||
export function nodeSqlite(config?: NodeSqliteConnectionConfig | { url: string }) {
|
||||
let db: DatabaseSync;
|
||||
export function nodeSqlite(config?: NodeSqliteConnectionConfig) {
|
||||
let db: DatabaseSync | undefined;
|
||||
if (config) {
|
||||
if ("database" in config) {
|
||||
if ("database" in config && config.database) {
|
||||
db = config.database;
|
||||
} else {
|
||||
} else if (config.url) {
|
||||
db = new DatabaseSync(config.url);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
|
||||
if (!db) {
|
||||
db = new DatabaseSync(":memory:");
|
||||
}
|
||||
|
||||
@@ -21,11 +31,7 @@ export function nodeSqlite(config?: NodeSqliteConnectionConfig | { url: string }
|
||||
"node-sqlite",
|
||||
db,
|
||||
(utils) => {
|
||||
const getStmt = (sql: string) => {
|
||||
const stmt = db.prepare(sql);
|
||||
//stmt.setReadBigInts(true);
|
||||
return stmt;
|
||||
};
|
||||
const getStmt = (sql: string) => db.prepare(sql);
|
||||
|
||||
return {
|
||||
db,
|
||||
@@ -49,6 +55,7 @@ export function nodeSqlite(config?: NodeSqliteConnectionConfig | { url: string }
|
||||
};
|
||||
},
|
||||
{
|
||||
...omitKeys(config ?? ({} as any), ["database", "url", "name", "supports"]),
|
||||
supports: {
|
||||
batching: false,
|
||||
},
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { nodeSqlite } from "./NodeSqliteConnection";
|
||||
import { DatabaseSync } from "node:sqlite";
|
||||
import { connectionTestSuite } from "data/connection/connection-test-suite";
|
||||
import { describe, beforeAll, afterAll } from "vitest";
|
||||
import { describe, beforeAll, afterAll, test, expect, vi } from "vitest";
|
||||
import { viTestRunner } from "../vitest";
|
||||
import { disableConsoleLog, enableConsoleLog } from "core/utils/test";
|
||||
import { GenericSqliteConnection } from "data/connection/sqlite/GenericSqliteConnection";
|
||||
|
||||
beforeAll(() => disableConsoleLog());
|
||||
afterAll(() => enableConsoleLog());
|
||||
@@ -16,4 +17,20 @@ describe("NodeSqliteConnection", () => {
|
||||
}),
|
||||
rawDialectDetails: [],
|
||||
});
|
||||
|
||||
test("onCreateConnection", async () => {
|
||||
const called = vi.fn(() => null);
|
||||
|
||||
const conn = nodeSqlite({
|
||||
onCreateConnection: (db) => {
|
||||
expect(db).toBeInstanceOf(DatabaseSync);
|
||||
called();
|
||||
},
|
||||
});
|
||||
await conn.ping();
|
||||
|
||||
expect(conn).toBeInstanceOf(GenericSqliteConnection);
|
||||
expect(conn.db).toBeInstanceOf(DatabaseSync);
|
||||
expect(called).toHaveBeenCalledOnce();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { KyselyPlugin, QueryResult } from "kysely";
|
||||
import {
|
||||
type IGenericSqlite,
|
||||
type OnCreateConnection,
|
||||
type Promisable,
|
||||
parseBigInt,
|
||||
buildQueryFn,
|
||||
@@ -9,6 +8,7 @@ import {
|
||||
} from "kysely-generic-sqlite";
|
||||
import { SqliteConnection } from "./SqliteConnection";
|
||||
import type { ConnQuery, ConnQueryResults, Features } from "../Connection";
|
||||
import type { MaybePromise } from "bknd";
|
||||
|
||||
export type { IGenericSqlite };
|
||||
export type TStatement = { sql: string; parameters?: any[] | readonly any[] };
|
||||
@@ -16,11 +16,11 @@ export interface IGenericCustomSqlite<DB = unknown> extends IGenericSqlite<DB> {
|
||||
batch?: (stmts: TStatement[]) => Promisable<QueryResult<any>[]>;
|
||||
}
|
||||
|
||||
export type GenericSqliteConnectionConfig = {
|
||||
export type GenericSqliteConnectionConfig<Database = unknown> = {
|
||||
name?: string;
|
||||
additionalPlugins?: KyselyPlugin[];
|
||||
excludeTables?: string[];
|
||||
onCreateConnection?: OnCreateConnection;
|
||||
onCreateConnection?: (db: Database) => MaybePromise<void>;
|
||||
supports?: Partial<Features>;
|
||||
};
|
||||
|
||||
@@ -35,7 +35,12 @@ export class GenericSqliteConnection<DB = unknown> extends SqliteConnection<DB>
|
||||
) {
|
||||
super({
|
||||
dialect: GenericSqliteDialect,
|
||||
dialectArgs: [executor, config?.onCreateConnection],
|
||||
dialectArgs: [
|
||||
executor,
|
||||
config?.onCreateConnection && typeof config.onCreateConnection === "function"
|
||||
? (c: any) => config.onCreateConnection?.(c.db.db as any)
|
||||
: undefined,
|
||||
],
|
||||
additionalPlugins: config?.additionalPlugins,
|
||||
excludeTables: config?.excludeTables,
|
||||
});
|
||||
@@ -61,7 +66,6 @@ export class GenericSqliteConnection<DB = unknown> extends SqliteConnection<DB>
|
||||
override async executeQueries<O extends ConnQuery[]>(...qbs: O): Promise<ConnQueryResults<O>> {
|
||||
const executor = await this.getExecutor();
|
||||
if (!executor.batch) {
|
||||
//$console.debug("Batching is not supported by this database");
|
||||
return super.executeQueries(...qbs);
|
||||
}
|
||||
|
||||
|
||||
@@ -116,6 +116,7 @@ export {
|
||||
genericSqlite,
|
||||
genericSqliteUtils,
|
||||
type GenericSqliteConnection,
|
||||
type GenericSqliteConnectionConfig,
|
||||
} from "data/connection/sqlite/GenericSqliteConnection";
|
||||
export {
|
||||
EntityTypescript,
|
||||
|
||||
@@ -63,7 +63,7 @@ import type { BkndConfig } from "bknd";
|
||||
|
||||
export default {
|
||||
connection: { url: "file:data.db" },
|
||||
} as const satisfies BkndConfig;
|
||||
} satisfies BkndConfig;
|
||||
```
|
||||
|
||||
Throughout the documentation, it is assumed you use `bknd.config.ts` to define your connection.
|
||||
@@ -93,17 +93,51 @@ import type { BkndConfig } from "bknd";
|
||||
|
||||
// no connection is required, bknd will use a SQLite database in-memory
|
||||
// this does not work on edge environments!
|
||||
export default {} as const satisfies BkndConfig;
|
||||
export default {} satisfies BkndConfig;
|
||||
|
||||
// or explicitly in-memory
|
||||
export default {
|
||||
connection: { url: ":memory:" },
|
||||
} as const satisfies BkndConfig;
|
||||
} satisfies BkndConfig;
|
||||
|
||||
// or explicitly as a file
|
||||
export default {
|
||||
connection: { url: "file:<path/to/your/database.db>" },
|
||||
} as const satisfies BkndConfig;
|
||||
} satisfies BkndConfig;
|
||||
```
|
||||
|
||||
### Bun SQLite
|
||||
|
||||
You can explicitly use the Bun SQLite adapter by passing the `bunSqlite` function to the `connection` property. This allows further configuration of the database, e.g. enabling WAL mode.
|
||||
|
||||
```typescript title="bknd.config.ts"
|
||||
import { bunSqlite, type BunBkndConfig } from "bknd/adapter/bun";
|
||||
|
||||
export default {
|
||||
connection: bunSqlite({
|
||||
url: "file:<path/to/your/database.db>",
|
||||
onCreateConnection: (db) => {
|
||||
db.run("PRAGMA journal_mode = WAL;");
|
||||
},
|
||||
}),
|
||||
} satisfies BunBkndConfig;
|
||||
```
|
||||
|
||||
### Node.js SQLite
|
||||
|
||||
To use the Node.js SQLite adapter directly, set the `connection` property to the result of the `nodeSqlite` function. This lets you customize the database connection, such as enabling WAL mode.
|
||||
|
||||
```typescript title="bknd.config.ts"
|
||||
import { nodeSqlite, type NodeBkndConfig } from "bknd/adapter/node";
|
||||
|
||||
export default {
|
||||
connection: nodeSqlite({
|
||||
url: "file:<path/to/your/database.db>",
|
||||
onCreateConnection: (db) => {
|
||||
db.exec("PRAGMA journal_mode = WAL;");
|
||||
},
|
||||
}),
|
||||
} satisfies NodeBkndConfig;
|
||||
```
|
||||
|
||||
### LibSQL
|
||||
@@ -118,7 +152,7 @@ export default {
|
||||
url: "libsql://<database>.turso.io",
|
||||
authToken: "<auth-token>",
|
||||
}),
|
||||
} as const satisfies BkndConfig;
|
||||
} satisfies BkndConfig;
|
||||
```
|
||||
|
||||
If you wish to use LibSQL as file, in-memory or make use of [Embedded Replicas](https://docs.turso.tech/features/embedded-replicas/introduction), you have to pass in the `Client` from `@libsql/client`:
|
||||
@@ -134,7 +168,7 @@ const client = createClient({
|
||||
|
||||
export default {
|
||||
connection: libsql(client),
|
||||
} as const satisfies BkndConfig;
|
||||
} satisfies BkndConfig;
|
||||
```
|
||||
|
||||
### Cloudflare D1
|
||||
|
||||
Reference in New Issue
Block a user