import type { Db } from '../types/db'; // Kysely as a TYPE-SAFE QUERY BUILDER over the existing synchronous better-sqlite3 // connection. The whole app is sync, and Kysely's `.execute()` is async — so we do // NOT execute through Kysely. Instead we build the query (fully type-checked against // the schema below), `.compile()` it to `{ sql, parameters }`, and run it // synchronously via the shared connection. This gives compile-time safety // (columns, user_id / deleted_at scoping, types) with zero change to the sync model. // // Grow the `Database` schema as queries are migrated. See docs/CODE_STANDARDS.md. const { Kysely, SqliteDialect } = require('kysely') as typeof import('kysely'); const BetterSqlite3 = require('better-sqlite3'); const { getDb } = require('./database.cts'); // ── Schema types (subset; extend as adopted) ───────────────────────────────── export interface CategoriesTable { id: number; user_id: number; name: string; sort_order: number | null; spending_enabled: number; group_id: number | null; deleted_at: string | null; is_seeded: number; created_at: string; updated_at: string; } export interface CategoryGroupsTable { id: number; user_id: number; name: string; sort_order: number; is_seeded: number; created_at: string; updated_at: string; } export interface Database { categories: CategoriesTable; category_groups: CategoryGroupsTable; } // Build-only Kysely instance. The dialect supplies the SQLite compiler; its driver // factory is never invoked because we never `.execute()` — only `.compile()`. let _kysely: import('kysely').Kysely | null = null; function kysely(): import('kysely').Kysely { if (!_kysely) { _kysely = new Kysely({ dialect: new SqliteDialect({ database: () => new BetterSqlite3(':memory:') }), }); } return _kysely; } type Compilable = { compile(): { sql: string; parameters: readonly unknown[] } }; /** Compile a Kysely query and run it synchronously (returns all rows). */ function all(query: Compilable): R[] { const c = query.compile(); return (getDb() as Db).prepare(c.sql).all(...(c.parameters as any[])) as R[]; } /** Compile a Kysely query and run it synchronously (returns the first row). */ function get(query: Compilable): R | undefined { const c = query.compile(); return (getDb() as Db).prepare(c.sql).get(...(c.parameters as any[])) as R | undefined; } /** Compile a Kysely mutation and run it synchronously (INSERT/UPDATE/DELETE). */ function run(query: Compilable) { const c = query.compile(); return (getDb() as Db).prepare(c.sql).run(...(c.parameters as any[])); } module.exports = { kysely, all, get, run };