UNPKG

alepha

Version:

Easy-to-use modern TypeScript framework for building many kind of applications.

185 lines (158 loc) 5.06 kB
import type { Database } from "bun:sqlite"; import { mkdir } from "node:fs/promises"; import { dirname } from "node:path"; import { $atom, $env, $hook, $inject, $state, AlephaError, type Static, z, } from "alepha"; import type { BunSQLiteDatabase } from "drizzle-orm/bun-sqlite"; import type { PgDatabase } from "drizzle-orm/pg-core"; import { databaseEnvSchema } from "../../schemas/databaseEnvSchema.ts"; import { SqliteModelBuilder } from "../../services/SqliteModelBuilder.ts"; import { DatabaseProvider, type SQLLike } from "./DatabaseProvider.ts"; // --------------------------------------------------------------------------------------------------------------------- const envSchema = databaseEnvSchema; /** * Configuration options for the Bun SQLite database provider. */ export const bunSqliteOptions = $atom({ name: "alepha.postgres.bun-sqlite.options", schema: z.object({ path: z .string() .describe( "Filepath or :memory:. If empty, provider will use DATABASE_URL from env.", ) .optional(), }), default: {}, }); export type BunSqliteProviderOptions = Static<typeof bunSqliteOptions.schema>; declare module "alepha" { interface State { [bunSqliteOptions.key]: BunSqliteProviderOptions; } } // --------------------------------------------------------------------------------------------------------------------- /** * Bun SQLite provider using Drizzle ORM with Bun's native SQLite client. * * This provider uses Bun's built-in `bun:sqlite` for SQLite connections, * which provides excellent performance on the Bun runtime. * * @example * ```ts * // Set DATABASE_URL environment variable * // DATABASE_URL=sqlite://./my-database.db * * // Or configure programmatically * alepha.with({ * provide: DatabaseProvider, * use: BunSqliteProvider, * }); * * // Or use options atom * alepha.store.mut(bunSqliteOptions, (old) => ({ * ...old, * path: ":memory:", * })); * ``` */ export class BunSqliteProvider extends DatabaseProvider { protected readonly env = $env(envSchema); protected readonly builder = $inject(SqliteModelBuilder); protected readonly options = $state(bunSqliteOptions); protected sqlite?: Database; protected bunDb?: BunSQLiteDatabase; public get name() { return "sqlite"; } public override readonly dialect = "sqlite"; public override get url(): string { const path = this.options.path ?? this.env.DATABASE_URL; if (path) { if (path.startsWith("postgres://")) { throw new AlephaError( "Postgres URL is not supported for SQLite provider.", ); } return path; } if (this.alepha.isTest() || this.alepha.isServerless()) { return ":memory:"; } else { return "node_modules/.alepha/bun-sqlite.db"; } } public override get db(): PgDatabase<any> { if (!this.bunDb) { throw new AlephaError("Database not initialized"); } return this.bunDb as unknown as PgDatabase<any>; } public override get nativeConnection(): unknown { return this.sqlite; } public override async execute( query: SQLLike, ): Promise<Array<Record<string, unknown>>> { return (this.bunDb as BunSQLiteDatabase).all(query); } protected readonly onStart = $hook({ on: "start", handler: async () => { // Check if we're running in Bun if (typeof Bun === "undefined") { throw new AlephaError( "BunSqliteProvider requires the Bun runtime. Use NodeSqliteProvider for Node.js.", ); } const { Database } = await import("bun:sqlite"); const { drizzle } = await import("drizzle-orm/bun-sqlite"); const filepath = this.url.replace("sqlite://", "").replace("sqlite:", ""); if (filepath !== ":memory:" && filepath !== "") { const dir = dirname(filepath); if (dir) { await mkdir(dir, { recursive: true }).catch(() => null); } } this.sqlite = new Database(filepath); this.bunDb = drizzle({ client: this.sqlite, logger: { logQuery: (query: string, params: unknown[]) => { this.log.trace(query, { params }); }, }, }); // Never migrate in serverless mode - migrations should be applied during deployment if (!this.alepha.isServerless()) { await this.migrate(); } this.log.info(`Using Bun SQLite database at ${filepath}`); }, }); protected readonly onStop = $hook({ on: "stop", priority: "last", handler: async () => { if (this.sqlite) { this.log.debug("Closing Bun SQLite connection..."); this.sqlite.close(); this.sqlite = undefined; this.bunDb = undefined; this.log.info("Bun SQLite connection closed"); } }, }); protected async executeMigrations(migrationsFolder: string): Promise<void> { const { migrate } = await import("drizzle-orm/bun-sqlite/migrator"); await migrate(this.bunDb!, { migrationsFolder }); } }