76 lines
2.7 KiB
TypeScript
76 lines
2.7 KiB
TypeScript
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<Database> | null = null;
|
|
function kysely(): import('kysely').Kysely<Database> {
|
|
if (!_kysely) {
|
|
_kysely = new Kysely<Database>({
|
|
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<R = any>(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<R = any>(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 };
|