feat: añadir soporte dual de sqlite: bun:sqlite y better-sqlite3

Co-authored-by: aider (openrouter/openai/gpt-5) <aider@aider.chat>
webui
brobert 2 weeks ago
parent 38fd4c4cdb
commit 14223de56f

@ -18,6 +18,7 @@
"@sveltejs/kit": "^2.43.2",
"@sveltejs/vite-plugin-svelte": "^6.2.0",
"@types/bun": "^1.3.0",
"better-sqlite3": "^9.4.5",
"svelte": "^5.39.5",
"svelte-check": "^4.3.2",
"typescript": "^5.9.2",

@ -6,7 +6,13 @@ function applyDefaultPragmas(instance: any): void {
try {
instance.exec(`PRAGMA busy_timeout = 5000;`);
// Intentar activar WAL (si no es soportado, SQLite devolverá 'memory' u otro modo)
instance.query(`PRAGMA journal_mode = WAL`).get();
try {
if (typeof instance.query === 'function') {
instance.query(`PRAGMA journal_mode = WAL`)?.get?.();
} else {
instance.prepare?.(`PRAGMA journal_mode = WAL`)?.get?.();
}
} catch {}
instance.exec(`PRAGMA synchronous = NORMAL;`);
instance.exec(`PRAGMA wal_autocheckpoint = 1000;`);
// Asegurar claves foráneas siempre activas
@ -16,10 +22,27 @@ function applyDefaultPragmas(instance: any): void {
}
}
/**
* Intenta cargar un constructor de Database compatible:
* - En Bun (SSR nativo): bun:sqlite
* - En Node (Vite dev SSR): better-sqlite3
*/
async function importSqliteDatabase(): Promise<any> {
try {
// @ts-ignore
if (typeof Bun !== 'undefined') {
const mod = await import('bun:sqlite');
return (mod as any).Database;
}
} catch {}
const mod = await import('better-sqlite3');
return (mod as any).default || (mod as any).Database || mod;
}
/**
* Abre la BD compartida. En desarrollo, si el archivo no existe y DEV_AUTOSEED_DB=true,
* inicializa el esquema (migraciones) y siembra datos de demo.
* Nota: uso de import dinámico de 'bun:sqlite' para que el build con Node no falle.
* Nota: usa bun:sqlite si está disponible; en SSR Node usa better-sqlite3.
*/
async function openDb(filename: string = 'tasks.db'): Promise<any> {
const absolutePath = resolveDbAbsolutePath(filename);
@ -32,20 +55,51 @@ async function openDb(filename: string = 'tasks.db'): Promise<any> {
if (err?.code !== 'EEXIST') throw err;
}
const { Database } = await import('bun:sqlite');
const instance = new Database(absolutePath);
const DatabaseCtor = await importSqliteDatabase();
const instance = new DatabaseCtor(absolutePath);
applyDefaultPragmas(instance);
// Auto-inicialización y seed sólo en desarrollo y primer arranque
if (firstCreate && isDev() && DEV_AUTOSEED_DB) {
try {
const dbModule = await import('../../../../../src/db');
if (typeof (dbModule as any).initializeDatabase === 'function') {
(dbModule as any).initializeDatabase(instance);
// Detectar entorno Bun vs Node (Vite dev)
const isBun = typeof (globalThis as any).Bun !== 'undefined';
if (isBun) {
// En Bun podemos reutilizar initializeDatabase del repo principal
try {
const dbModule = await import('../../../../../src/db');
if (typeof (dbModule as any).initializeDatabase === 'function') {
(dbModule as any).initializeDatabase(instance);
}
} catch (e) {
console.warn('[web/db] No se pudo ejecutar initializeDatabase en dev (Bun):', e);
}
} else {
// En SSR Node: aplicar migraciones directamente con compat para .query
try {
const mod = await import('../../../../../src/db/migrations/index.ts');
const list = (mod as any).migrations as any[];
const compat: any = instance;
if (typeof compat.query !== 'function') {
compat.query = (sql: string) => ({
all: () => compat.prepare(sql).all(),
get: () => compat.prepare(sql).get()
});
}
try { compat.exec?.(`PRAGMA foreign_keys = ON;`); } catch {}
for (const m of list) {
try {
await (m.up as any)(compat);
} catch (e) {
console.warn('[web/db] Error aplicando migración en dev (Node):', (m as any)?.name ?? '(sin nombre)', e);
}
}
} catch (e) {
console.warn('[web/db] No se pudieron aplicar migraciones en dev (Node):', e);
}
} catch (e) {
console.warn('[web/db] No se pudo ejecutar initializeDatabase en dev:', e);
}
// Seed de datos de demo
try {
const seed = await import('./dev-seed');
if (typeof (seed as any).seedDev === 'function') {

@ -16,7 +16,7 @@ export async function seedDev(db: any, defaultUser: string): Promise<void> {
try { db.exec(`PRAGMA foreign_keys = ON;`); } catch {}
// Si ya hay tareas, asumimos BD poblada
try {
const row = db.query(`SELECT COUNT(*) AS c FROM tasks`).get() as any;
const row = db.prepare(`SELECT COUNT(*) AS c FROM tasks`).get() as any;
if (row && Number(row.c || 0) > 0) return;
} catch {}

Loading…
Cancel
Save