an atproto based link aggregator

Fix: lazy initialize DB connections for build

Use Proxy to defer database client creation until first access,
avoiding connection errors during SvelteKit's build-time analysis.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

+47 -15
+47 -15
src/lib/server/db/index.ts
··· 1 1 /** 2 - * Database connections 2 + * Database connections (lazy initialized to avoid build-time errors) 3 3 * 4 4 * In production: 5 5 * - contentDb: LiteFS replica (read-only) at /litefs/content.db 6 - * - localDb: Local SQLite (read-write) at /data/local.db 6 + * - localDb: Local SQLite (read-write) at /var/lib/litefs/local.db 7 7 * 8 8 * In development: 9 9 * - Both use local files in ./data/ 10 10 */ 11 11 12 - import { createClient } from '@libsql/client'; 13 - import { drizzle } from 'drizzle-orm/libsql'; 12 + import { createClient, type Client } from '@libsql/client'; 13 + import { drizzle, type LibSQLDatabase } from 'drizzle-orm/libsql'; 14 14 import { instrumentDrizzle } from '@kubiks/otel-drizzle'; 15 15 import * as contentSchema from './content-schema'; 16 16 import * as localSchema from './local-schema'; ··· 19 19 const CONTENT_DB_PATH = process.env.CONTENT_DB_PATH || './data/content.db'; 20 20 const LOCAL_DB_PATH = process.env.LOCAL_DB_PATH || './data/local.db'; 21 21 22 + // Lazy initialization to avoid connecting during SvelteKit build 23 + let _contentClient: Client | null = null; 24 + let _contentDb: LibSQLDatabase<typeof contentSchema> | null = null; 25 + let _localClient: Client | null = null; 26 + let _localDb: LibSQLDatabase<typeof localSchema> | null = null; 27 + 28 + function getContentClient(): Client { 29 + if (!_contentClient) { 30 + _contentClient = createClient({ url: `file:${CONTENT_DB_PATH}` }); 31 + instrumentDrizzle(_contentClient, { dbSystem: 'sqlite', dbName: 'content' }); 32 + } 33 + return _contentClient; 34 + } 35 + 36 + function getLocalClient(): Client { 37 + if (!_localClient) { 38 + _localClient = createClient({ url: `file:${LOCAL_DB_PATH}` }); 39 + instrumentDrizzle(_localClient, { dbSystem: 'sqlite', dbName: 'local' }); 40 + } 41 + return _localClient; 42 + } 43 + 22 44 // Content DB - posts, comments, accounts (read-only in webapp, write in ingester) 23 - // Instrument the client BEFORE passing to drizzle 24 - export const contentClient = createClient({ 25 - url: `file:${CONTENT_DB_PATH}` 45 + export const contentClient = new Proxy({} as Client, { 46 + get(_, prop) { 47 + return Reflect.get(getContentClient(), prop); 48 + } 26 49 }); 27 - instrumentDrizzle(contentClient, { dbSystem: 'sqlite', dbName: 'content' }); 28 - export const contentDb = drizzle(contentClient, { schema: contentSchema }); 50 + export const contentDb = new Proxy({} as LibSQLDatabase<typeof contentSchema>, { 51 + get(_, prop) { 52 + if (!_contentDb) { 53 + _contentDb = drizzle(getContentClient(), { schema: contentSchema }); 54 + } 55 + return Reflect.get(_contentDb, prop); 56 + } 57 + }); 29 58 30 59 // Local DB - auth, votes (webapp only) 31 - const localClient = createClient({ 32 - url: `file:${LOCAL_DB_PATH}` 60 + export const localDb = new Proxy({} as LibSQLDatabase<typeof localSchema>, { 61 + get(_, prop) { 62 + if (!_localDb) { 63 + _localDb = drizzle(getLocalClient(), { schema: localSchema }); 64 + } 65 + return Reflect.get(_localDb, prop); 66 + } 33 67 }); 34 - instrumentDrizzle(localClient, { dbSystem: 'sqlite', dbName: 'local' }); 35 - export const localDb = drizzle(localClient, { schema: localSchema }); 36 68 37 69 // Type exports 38 - export type ContentDatabase = typeof contentDb; 39 - export type LocalDatabase = typeof localDb; 70 + export type ContentDatabase = LibSQLDatabase<typeof contentSchema>; 71 + export type LocalDatabase = LibSQLDatabase<typeof localSchema>; 40 72 41 73 // Legacy export - auth code uses this type for localDb 42 74 export type Database = typeof localDb;