1. Stack & Architecture
- Framework: SvelteKit (
@sveltejs/kit) - Adapter:
@sveltejs/adapter-node(Required for native OS file access to~/.omp/stats.db). - Database:
better-sqlite3(Fast, synchronous, native SQLite bindings). - Architecture Pattern: Repository Pattern (SoC) -> Server-only Loaders -> Client UI.
2. Directory Structure
src/
βββ lib/
β βββ server/
β βββ db.ts # Singleton better-sqlite3 connection
β βββ statsDao.ts # Data Access Object for SQLite queries
βββ routes/
β βββ stats/
β β βββ +page.server.ts # Server-side loader (Initial Hydration)
β β βββ +page.svelte # Dashboard UI
β βββ api/
β βββ stats/
β βββ +server.ts # API Endpoint for Live Polling / SSE3. Database Connection (lib/server/db.ts)
Must open SQLite in readonly mode to prevent locking conflicts with the active OMP logger. WAL mode should be assumed active.
import Database from "better-sqlite3"
import os from "os"
import path from "path"
const dbPath = path.join(os.homedir(), ".omp", "stats.db")
const db = new Database(dbPath, { readonly: true })
db.pragma("journal_mode = WAL")
export default db4. Data Access Layer (lib/server/statsDao.ts)
Adheres to Single Responsibility Principle (SRP). Exposes pure aggregate queries.
import db from "./db"
export function getGlobalAggregates() {
const stmt = db.prepare(`
SELECT
SUM(total_tokens) as total_tokens,
SUM(cost_total) as total_cost,
COUNT(id) as total_requests
FROM messages
`)
return stmt.get()
}
export function getUserSentimentAggregates() {
const stmt = db.prepare(`
SELECT
AVG(anguish) as avg_anguish,
AVG(yelling) as avg_yelling,
AVG(profanity) as avg_profanity
FROM user_messages
`)
return stmt.get()
}
export function getLatestFileOffset() {
const stmt = db.prepare(`SELECT MAX(last_modified) as last_mod FROM file_offsets`)
return stmt.get().last_mod
}5. Server Loaders (routes/stats/+page.server.ts)
Fetches initial SSR data so the page load is instantaneous.
import type { PageServerLoad } from "./$types"
import { getGlobalAggregates, getUserSentimentAggregates } from "$lib/server/statsDao"
export const load: PageServerLoad = async () => {
return {
aggregates: getGlobalAggregates(),
sentiment: getUserSentimentAggregates(),
}
}6. Live Data Strategy
Since better-sqlite3 is synchronous and SQLite doesnβt natively push events to Node on external writes, we implement HTTP Polling combined with offset checking.
- Client Polling (SWR): Use SvelteKitβs
invalidate('omp:stats')in+page.svelteinside asetInterval. Fast, simple, leverages existing loaders. - Optimization: Check the
file_offsetstable first. Only re-run expensiveSUM()andAVG()queries iflast_modifiedoroffsethas increased since the last tick.
7. SOLID / ACID Validation
- SRP & SoC: DB connection, raw SQL queries, and HTTP routing are strictly separated.
- OCP:
statsDao.tscan be extended with new granular queries without mutating base exports. - ACID: Enforced by SQLite natively.
readonly: trueensures the node process cannot violate OMPβs write atomicity.