@indiekitai/pg-dash 0.4.2 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/server/advisor.ts","../src/server/queries/schema.ts","../src/server/schema-diff.ts","../src/server/schema-tracker.ts","../src/server/snapshot.ts","../src/server/migration-checker.ts","../src/server/env-differ.ts","../src/cli.ts","../src/server/index.ts","../src/server/queries/overview.ts","../src/server/queries/databases.ts","../src/server/queries/tables.ts","../src/server/queries/activity.ts","../src/server/timeseries.ts","../src/server/collector.ts","../src/server/notifiers.ts","../src/server/alerts.ts","../src/server/routes/overview.ts","../src/server/routes/metrics.ts","../src/server/queries/slow-queries.ts","../src/server/routes/activity.ts","../src/server/routes/advisor.ts","../src/server/routes/schema.ts","../src/server/routes/alerts.ts","../src/server/query-analyzer.ts","../src/server/routes/explain.ts","../src/server/disk-prediction.ts","../src/server/routes/disk.ts","../src/server/query-stats.ts","../src/server/routes/query-stats.ts","../src/server/routes/export.ts"],"sourcesContent":["import type { Pool } from \"pg\";\n\nexport interface AdvisorIssue {\n id: string;\n severity: \"critical\" | \"warning\" | \"info\";\n category: \"performance\" | \"maintenance\" | \"schema\" | \"security\";\n title: string;\n description: string;\n fix: string;\n impact: string;\n effort: \"quick\" | \"moderate\" | \"involved\";\n}\n\nexport interface BatchFix {\n type: string;\n title: string;\n count: number;\n sql: string;\n}\n\nexport interface AdvisorResult {\n score: number;\n grade: string;\n issues: AdvisorIssue[];\n breakdown: Record<string, { score: number; grade: string; count: number }>;\n skipped: string[];\n ignoredCount: number;\n batchFixes: BatchFix[];\n}\n\nconst SEVERITY_WEIGHT = { critical: 15, warning: 5, info: 1 } as const;\nconst MAX_DEDUCTION = { critical: 60, warning: 30, info: 10 } as const;\n\nexport function computeAdvisorScore(issues: AdvisorIssue[]): number {\n let score = 100;\n const deductions = { critical: 0, warning: 0, info: 0 };\n const counts = { critical: 0, warning: 0, info: 0 };\n for (const issue of issues) {\n counts[issue.severity]++;\n const n = counts[issue.severity];\n const weight = SEVERITY_WEIGHT[issue.severity];\n // Diminishing penalty: full for first 3, half for 4-10, quarter for 11+\n let penalty: number;\n if (n <= 3) penalty = weight;\n else if (n <= 10) penalty = weight * 0.5;\n else penalty = weight * 0.25;\n deductions[issue.severity] += penalty;\n }\n // Cap deductions per severity\n for (const sev of [\"critical\", \"warning\", \"info\"] as const) {\n score -= Math.min(deductions[sev], MAX_DEDUCTION[sev]);\n }\n return Math.max(0, Math.min(100, Math.round(score)));\n}\n\nexport function gradeFromScore(score: number): string {\n if (score >= 90) return \"A\";\n if (score >= 80) return \"B\";\n if (score >= 70) return \"C\";\n if (score >= 50) return \"D\";\n return \"F\";\n}\n\nfunction computeBreakdown(issues: AdvisorIssue[]): Record<string, { score: number; grade: string; count: number }> {\n const categories = [\"performance\", \"maintenance\", \"schema\", \"security\"] as const;\n const result: Record<string, { score: number; grade: string; count: number }> = {};\n for (const cat of categories) {\n const catIssues = issues.filter((i) => i.category === cat);\n const score = computeAdvisorScore(catIssues);\n result[cat] = { score, grade: gradeFromScore(score), count: catIssues.length };\n }\n return result;\n}\n\nexport async function getAdvisorReport(pool: Pool, longQueryThreshold = 5): Promise<AdvisorResult> {\n const client = await pool.connect();\n const issues: AdvisorIssue[] = [];\n const skipped: string[] = [];\n\n try {\n // Detect PG version for compatibility\n const versionResult = await client.query(\"SHOW server_version_num\");\n const pgVersion = parseInt(versionResult.rows[0].server_version_num);\n\n // ── Performance Advisors ───────────────────────────────────────\n\n // Missing indexes (high seq scans on large tables)\n try {\n const r = await client.query(`\n SELECT schemaname, relname, seq_scan, seq_tup_read, n_live_tup,\n pg_size_pretty(pg_total_relation_size(relid)) AS size\n FROM pg_stat_user_tables\n WHERE n_live_tup > 10000 AND seq_scan > 100\n ORDER BY seq_tup_read DESC LIMIT 10\n `);\n for (const row of r.rows) {\n issues.push({\n id: `perf-seq-scan-${row.schemaname}-${row.relname}`,\n severity: row.seq_scan > 1000 ? \"warning\" : \"info\",\n category: \"performance\",\n title: `High sequential scans on ${row.relname}`,\n description: `Table ${row.schemaname}.${row.relname} (${row.n_live_tup} rows, ${row.size}) has ${row.seq_scan} sequential scans reading ${Number(row.seq_tup_read).toLocaleString()} tuples. Consider adding indexes on frequently filtered columns.`,\n fix: `-- Identify commonly filtered columns and add indexes:\\n-- EXPLAIN ANALYZE SELECT * FROM ${row.schemaname}.${row.relname} WHERE <your_condition>;\\nCREATE INDEX CONCURRENTLY idx_${row.relname}_<column> ON ${row.schemaname}.${row.relname} (<column>);`,\n impact: \"Queries will continue to do full table scans, degrading performance as the table grows.\",\n effort: \"moderate\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking seq scans:\", (err as Error).message); skipped.push(\"seq scans: \" + (err as Error).message);\n }\n\n // Bloated indexes (index size > 3x table size)\n try {\n const r = await client.query(`\n SELECT\n schemaname, relname, indexrelname,\n pg_relation_size(indexrelid) AS idx_size,\n pg_relation_size(relid) AS tbl_size,\n pg_size_pretty(pg_relation_size(indexrelid)) AS idx_size_pretty,\n pg_size_pretty(pg_relation_size(relid)) AS tbl_size_pretty\n FROM pg_stat_user_indexes\n WHERE pg_relation_size(indexrelid) > 1048576\n AND pg_relation_size(indexrelid) > pg_relation_size(relid) * 3\n ORDER BY pg_relation_size(indexrelid) DESC LIMIT 10\n `);\n for (const row of r.rows) {\n issues.push({\n id: `perf-bloated-idx-${row.indexrelname}`,\n severity: \"warning\",\n category: \"performance\",\n title: `Bloated index ${row.indexrelname}`,\n description: `Index ${row.indexrelname} on ${row.relname} is ${row.idx_size_pretty} but the table is only ${row.tbl_size_pretty}. The index may need rebuilding.`,\n fix: `REINDEX INDEX CONCURRENTLY ${row.schemaname}.${row.indexrelname};`,\n impact: \"Bloated indexes waste disk space and slow down queries that use them.\",\n effort: \"quick\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking bloated indexes:\", (err as Error).message); skipped.push(\"bloated indexes: \" + (err as Error).message);\n }\n\n // Table bloat (dead tuples > 10%)\n try {\n const r = await client.query(`\n SELECT schemaname, relname, n_dead_tup, n_live_tup,\n CASE WHEN n_live_tup > 0 THEN round(n_dead_tup::numeric / n_live_tup * 100, 1) ELSE 0 END AS dead_pct,\n pg_size_pretty(pg_total_relation_size(relid)) AS size\n FROM pg_stat_user_tables\n WHERE n_live_tup > 1000 AND n_dead_tup::float / GREATEST(n_live_tup, 1) > 0.1\n ORDER BY n_dead_tup DESC LIMIT 10\n `);\n for (const row of r.rows) {\n const pct = parseFloat(row.dead_pct);\n issues.push({\n id: `perf-bloat-${row.schemaname}-${row.relname}`,\n severity: pct > 30 ? \"critical\" : \"warning\",\n category: \"performance\",\n title: `Table bloat on ${row.relname} (${row.dead_pct}% dead)`,\n description: `${row.schemaname}.${row.relname} has ${Number(row.n_dead_tup).toLocaleString()} dead tuples (${row.dead_pct}% of ${Number(row.n_live_tup).toLocaleString()} live rows). Size: ${row.size}.`,\n fix: `VACUUM FULL ${row.schemaname}.${row.relname};`,\n impact: \"Dead tuples waste storage and degrade scan performance.\",\n effort: pct > 30 ? \"moderate\" : \"quick\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking table bloat:\", (err as Error).message); skipped.push(\"table bloat: \" + (err as Error).message);\n }\n\n // Cache efficiency per table\n try {\n const r = await client.query(`\n SELECT schemaname, relname,\n heap_blks_hit, heap_blks_read,\n CASE WHEN (heap_blks_hit + heap_blks_read) = 0 THEN 1\n ELSE heap_blks_hit::float / (heap_blks_hit + heap_blks_read) END AS ratio\n FROM pg_statio_user_tables\n WHERE (heap_blks_hit + heap_blks_read) > 100\n ORDER BY ratio ASC LIMIT 5\n `);\n for (const row of r.rows) {\n const ratio = parseFloat(row.ratio);\n if (ratio < 0.9) {\n issues.push({\n id: `perf-cache-${row.schemaname}-${row.relname}`,\n severity: ratio < 0.5 ? \"critical\" : \"warning\",\n category: \"performance\",\n title: `Poor cache hit ratio on ${row.relname}`,\n description: `Table ${row.schemaname}.${row.relname} has a cache hit ratio of ${(ratio * 100).toFixed(1)}%. Most reads are going to disk.`,\n fix: `-- Consider increasing shared_buffers or reducing working set:\\nSHOW shared_buffers;`,\n impact: \"Disk reads are orders of magnitude slower than memory reads.\",\n effort: \"involved\",\n });\n }\n }\n } catch (err) {\n console.error(\"[advisor] Error checking cache efficiency:\", (err as Error).message); skipped.push(\"cache efficiency: \" + (err as Error).message);\n }\n\n // Slow queries from pg_stat_statements\n try {\n const extCheck = await client.query(\"SELECT 1 FROM pg_extension WHERE extname = 'pg_stat_statements'\");\n if (extCheck.rows.length > 0) {\n const r = await client.query(`\n SELECT query, calls, mean_exec_time, total_exec_time,\n round(mean_exec_time::numeric, 2) AS mean_ms,\n round(total_exec_time::numeric / 1000, 2) AS total_sec\n FROM pg_stat_statements\n WHERE query NOT LIKE '%pg_stat%' AND query NOT LIKE '%pg_catalog%'\n AND mean_exec_time > 100\n ORDER BY mean_exec_time DESC LIMIT 5\n `);\n for (const row of r.rows) {\n issues.push({\n id: `perf-slow-${row.query.slice(0, 30).replace(/\\W/g, \"_\")}`,\n severity: parseFloat(row.mean_ms) > 1000 ? \"warning\" : \"info\",\n category: \"performance\",\n title: `Slow query (avg ${row.mean_ms}ms)`,\n description: `Query averaging ${row.mean_ms}ms over ${row.calls} calls (total: ${row.total_sec}s): ${row.query.slice(0, 200)}`,\n fix: `EXPLAIN ANALYZE ${row.query.slice(0, 500)};`,\n impact: \"Slow queries degrade overall database responsiveness.\",\n effort: \"moderate\",\n });\n }\n }\n } catch (err) {\n console.error(\"[advisor] Error checking slow queries:\", (err as Error).message); skipped.push(\"slow queries: \" + (err as Error).message);\n }\n\n // ── Maintenance Advisors ───────────────────────────────────────\n\n // VACUUM overdue\n try {\n const r = await client.query(`\n SELECT schemaname, relname, last_vacuum, last_autovacuum, n_dead_tup\n FROM pg_stat_user_tables\n WHERE n_live_tup > 100\n AND (last_vacuum IS NULL AND last_autovacuum IS NULL\n OR GREATEST(last_vacuum, last_autovacuum) < now() - interval '7 days')\n ORDER BY n_dead_tup DESC LIMIT 15\n `);\n for (const row of r.rows) {\n const never = !row.last_vacuum && !row.last_autovacuum;\n issues.push({\n id: `maint-vacuum-${row.schemaname}-${row.relname}`,\n severity: never ? \"warning\" : \"info\",\n category: \"maintenance\",\n title: `VACUUM ${never ? \"never run\" : \"overdue\"} on ${row.relname}`,\n description: `${row.schemaname}.${row.relname} ${never ? \"has never been vacuumed\" : \"was last vacuumed over 7 days ago\"}. Dead tuples: ${Number(row.n_dead_tup).toLocaleString()}.`,\n fix: `VACUUM ANALYZE ${row.schemaname}.${row.relname};`,\n impact: \"Dead tuples accumulate, increasing table size and degrading query performance.\",\n effort: \"quick\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking vacuum overdue:\", (err as Error).message); skipped.push(\"vacuum overdue: \" + (err as Error).message);\n }\n\n // ANALYZE overdue\n try {\n const r = await client.query(`\n SELECT schemaname, relname\n FROM pg_stat_user_tables\n WHERE n_live_tup > 100\n AND last_analyze IS NULL AND last_autoanalyze IS NULL\n AND NOT EXISTS (\n SELECT 1 FROM pg_stat_user_tables t2\n WHERE t2.relname = pg_stat_user_tables.relname\n AND (t2.last_vacuum IS NULL AND t2.last_autovacuum IS NULL)\n )\n LIMIT 10\n `);\n for (const row of r.rows) {\n issues.push({\n id: `maint-analyze-${row.schemaname}-${row.relname}`,\n severity: \"info\",\n category: \"maintenance\",\n title: `ANALYZE never run on ${row.relname}`,\n description: `${row.schemaname}.${row.relname} has never been analyzed. The query planner may choose suboptimal plans.`,\n fix: `ANALYZE ${row.schemaname}.${row.relname};`,\n impact: \"Without statistics, the query planner makes poor estimates leading to slow queries.\",\n effort: \"quick\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking analyze overdue:\", (err as Error).message); skipped.push(\"analyze overdue: \" + (err as Error).message);\n }\n\n // Transaction ID wraparound risk\n try {\n const r = await client.query(`\n SELECT datname, age(datfrozenxid) AS xid_age\n FROM pg_database\n WHERE datname = current_database()\n `);\n for (const row of r.rows) {\n const age = parseInt(row.xid_age);\n if (age > 1_000_000_000) {\n issues.push({\n id: `maint-xid-wraparound`,\n severity: \"critical\",\n category: \"maintenance\",\n title: `Transaction ID wraparound risk`,\n description: `Database ${row.datname} has datfrozenxid age of ${age.toLocaleString()}. Wraparound occurs at ~2 billion.`,\n fix: `VACUUM FREEZE;`,\n impact: \"If wraparound occurs, PostgreSQL will shut down to prevent data loss.\",\n effort: \"involved\",\n });\n } else if (age > 500_000_000) {\n issues.push({\n id: `maint-xid-warning`,\n severity: \"warning\",\n category: \"maintenance\",\n title: `Transaction ID age is high`,\n description: `Database ${row.datname} has datfrozenxid age of ${age.toLocaleString()}.`,\n fix: `VACUUM FREEZE;`,\n impact: \"Approaching transaction ID wraparound threshold.\",\n effort: \"moderate\",\n });\n }\n }\n } catch (err) {\n console.error(\"[advisor] Error checking xid wraparound:\", (err as Error).message); skipped.push(\"xid wraparound: \" + (err as Error).message);\n }\n\n // Idle connections > 10 min\n try {\n const r = await client.query(`\n SELECT pid, state, now() - state_change AS idle_duration,\n client_addr::text, application_name,\n extract(epoch from now() - state_change)::int AS idle_seconds\n FROM pg_stat_activity\n WHERE state IN ('idle', 'idle in transaction')\n AND now() - state_change > $1 * interval '1 minute'\n AND pid != pg_backend_pid()\n `, [longQueryThreshold]);\n for (const row of r.rows) {\n const isIdleTx = row.state === \"idle in transaction\";\n issues.push({\n id: `maint-idle-${row.pid}`,\n severity: isIdleTx ? \"warning\" : \"info\",\n category: \"maintenance\",\n title: `${isIdleTx ? \"Idle in transaction\" : \"Idle connection\"} (PID ${row.pid})`,\n description: `PID ${row.pid} from ${row.client_addr || \"local\"} (${row.application_name || \"unknown\"}) has been ${row.state} for ${Math.round(row.idle_seconds / 60)} minutes.`,\n fix: `SELECT pg_terminate_backend(${row.pid});`,\n impact: isIdleTx ? \"Idle-in-transaction connections hold locks and prevent VACUUM.\" : \"Idle connections consume connection slots.\",\n effort: \"quick\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking idle connections:\", (err as Error).message); skipped.push(\"idle connections: \" + (err as Error).message);\n }\n\n // ── Schema Advisors ────────────────────────────────────────────\n\n // Missing primary keys\n try {\n const r = await client.query(`\n SELECT c.relname AS table_name, n.nspname AS schema\n FROM pg_class c\n JOIN pg_namespace n ON c.relnamespace = n.oid\n WHERE c.relkind = 'r' AND n.nspname = 'public'\n AND NOT EXISTS (\n SELECT 1 FROM pg_constraint con WHERE con.conrelid = c.oid AND con.contype = 'p'\n )\n `);\n for (const row of r.rows) {\n issues.push({\n id: `schema-no-pk-${row.schema}-${row.table_name}`,\n severity: \"warning\",\n category: \"schema\",\n title: `Missing primary key on ${row.table_name}`,\n description: `Table ${row.schema}.${row.table_name} has no primary key. This can cause replication issues and makes row identification unreliable.`,\n fix: `ALTER TABLE ${row.schema}.${row.table_name} ADD PRIMARY KEY (<column>);`,\n impact: \"No primary key means no unique row identity, problematic for replication and ORMs.\",\n effort: \"moderate\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking missing primary keys:\", (err as Error).message); skipped.push(\"missing primary keys: \" + (err as Error).message);\n }\n\n // Unused indexes (idx_scan = 0, size > 1MB)\n try {\n const r = await client.query(`\n SELECT schemaname, relname, indexrelname, idx_scan,\n pg_size_pretty(pg_relation_size(indexrelid)) AS idx_size,\n pg_relation_size(indexrelid) AS idx_bytes\n FROM pg_stat_user_indexes\n WHERE idx_scan = 0\n AND indexrelname NOT LIKE '%_pkey'\n AND pg_relation_size(indexrelid) > 1048576\n ORDER BY pg_relation_size(indexrelid) DESC LIMIT 10\n `);\n for (const row of r.rows) {\n issues.push({\n id: `schema-unused-idx-${row.indexrelname}`,\n severity: \"warning\",\n category: \"schema\",\n title: `Unused index ${row.indexrelname} (${row.idx_size})`,\n description: `Index ${row.indexrelname} on ${row.relname} has never been used (0 scans) and takes ${row.idx_size}.`,\n fix: `DROP INDEX CONCURRENTLY ${row.schemaname}.${row.indexrelname};`,\n impact: \"Unused indexes waste disk space and slow down writes.\",\n effort: \"quick\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking unused indexes:\", (err as Error).message); skipped.push(\"unused indexes: \" + (err as Error).message);\n }\n\n // Duplicate indexes\n try {\n const r = await client.query(`\n SELECT array_agg(idx.indexrelid::regclass::text) AS indexes,\n idx.indrelid::regclass::text AS table_name,\n pg_size_pretty(sum(pg_relation_size(idx.indexrelid))) AS total_size\n FROM pg_index idx\n GROUP BY idx.indrelid, idx.indkey\n HAVING count(*) > 1\n `);\n for (const row of r.rows) {\n issues.push({\n id: `schema-dup-idx-${row.table_name}-${row.indexes[0]}`,\n severity: \"warning\",\n category: \"schema\",\n title: `Duplicate indexes on ${row.table_name}`,\n description: `These indexes cover the same columns on ${row.table_name}: ${row.indexes.join(\", \")}. Total wasted space: ${row.total_size}.`,\n fix: `-- Keep one, drop the rest:\\nDROP INDEX CONCURRENTLY ${row.indexes.slice(1).join(\";\\nDROP INDEX CONCURRENTLY \")};`,\n impact: \"Duplicate indexes double the write overhead and waste disk space.\",\n effort: \"quick\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking duplicate indexes:\", (err as Error).message); skipped.push(\"duplicate indexes: \" + (err as Error).message);\n }\n\n // Missing foreign key indexes\n try {\n const r = await client.query(`\n SELECT\n conrelid::regclass::text AS table_name,\n a.attname AS column_name,\n confrelid::regclass::text AS referenced_table\n FROM pg_constraint c\n JOIN pg_attribute a ON a.attrelid = c.conrelid AND a.attnum = ANY(c.conkey)\n WHERE c.contype = 'f'\n AND NOT EXISTS (\n SELECT 1 FROM pg_index i\n WHERE i.indrelid = c.conrelid\n AND a.attnum = ANY(i.indkey)\n )\n `);\n for (const row of r.rows) {\n issues.push({\n id: `schema-fk-no-idx-${row.table_name}-${row.column_name}`,\n severity: \"warning\",\n category: \"schema\",\n title: `Missing index on FK column ${row.table_name}.${row.column_name}`,\n description: `Foreign key column ${row.column_name} on ${row.table_name} (references ${row.referenced_table}) has no index. This causes slow JOINs and cascading deletes.`,\n fix: `CREATE INDEX CONCURRENTLY idx_${row.table_name.replace(/\\./g, \"_\")}_${row.column_name} ON ${row.table_name} (${row.column_name});`,\n impact: \"JOINs and cascading deletes on this FK will require full table scans.\",\n effort: \"quick\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking missing FK indexes:\", (err as Error).message); skipped.push(\"missing FK indexes: \" + (err as Error).message);\n }\n\n // ── Infrastructure Advisors ──────────────────────────────────────\n\n // Lock detection\n try {\n const r = await client.query(`\n SELECT blocked_locks.pid AS blocked_pid,\n blocking_locks.pid AS blocking_pid,\n blocked_activity.query AS blocked_query\n FROM pg_catalog.pg_locks blocked_locks\n JOIN pg_catalog.pg_locks blocking_locks ON blocking_locks.locktype = blocked_locks.locktype\n AND blocking_locks.database IS NOT DISTINCT FROM blocked_locks.database\n AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation\n AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page\n AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple\n AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid\n AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid\n AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid\n AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid\n AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid\n AND blocking_locks.pid != blocked_locks.pid\n JOIN pg_catalog.pg_stat_activity blocked_activity ON blocked_activity.pid = blocked_locks.pid\n WHERE NOT blocked_locks.granted\n `);\n for (const row of r.rows) {\n issues.push({\n id: `perf-lock-blocked-${row.blocked_pid}`,\n severity: \"warning\",\n category: \"performance\",\n title: `Blocked query (PID ${row.blocked_pid} blocked by PID ${row.blocking_pid})`,\n description: `PID ${row.blocked_pid} is waiting for a lock held by PID ${row.blocking_pid}. Query: ${(row.blocked_query || \"\").slice(0, 200)}`,\n fix: `SELECT pg_cancel_backend(${row.blocking_pid});`,\n impact: \"Blocked queries cause cascading delays and potential timeouts.\",\n effort: \"quick\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking locks:\", (err as Error).message); skipped.push(\"locks: \" + (err as Error).message);\n }\n\n // WAL/replication lag\n try {\n const r = await client.query(`\n SELECT CASE WHEN pg_is_in_recovery()\n THEN pg_wal_lsn_diff(pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn())\n ELSE 0 END AS lag_bytes\n `);\n const lagBytes = parseInt(r.rows[0]?.lag_bytes ?? \"0\");\n if (lagBytes > 1048576) { // > 1MB\n issues.push({\n id: `perf-replication-lag`,\n severity: lagBytes > 104857600 ? \"critical\" : \"warning\",\n category: \"performance\",\n title: `Replication lag: ${(lagBytes / 1048576).toFixed(1)} MB`,\n description: `WAL replay is lagging by ${(lagBytes / 1048576).toFixed(1)} MB. This indicates the replica is falling behind.`,\n fix: `-- Check replication status:\\nSELECT * FROM pg_stat_replication;`,\n impact: \"High replication lag means the replica has stale data and failover may lose transactions.\",\n effort: \"involved\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking replication lag:\", (err as Error).message); skipped.push(\"replication lag: \" + (err as Error).message);\n }\n\n // Checkpoint frequency\n try {\n const checkpointView = pgVersion >= 170000 ? 'pg_stat_checkpointer' : 'pg_stat_bgwriter';\n const r = await client.query(`\n SELECT checkpoints_req, checkpoints_timed,\n CASE WHEN (checkpoints_req + checkpoints_timed) = 0 THEN 0\n ELSE round(checkpoints_req::numeric / (checkpoints_req + checkpoints_timed) * 100, 1) END AS req_pct\n FROM ${checkpointView}\n `);\n const reqPct = parseFloat(r.rows[0]?.req_pct ?? \"0\");\n if (reqPct > 50) {\n issues.push({\n id: `maint-checkpoint-frequency`,\n severity: reqPct > 80 ? \"warning\" : \"info\",\n category: \"maintenance\",\n title: `${reqPct}% of checkpoints are requested (not timed)`,\n description: `${r.rows[0]?.checkpoints_req} requested vs ${r.rows[0]?.checkpoints_timed} timed checkpoints. High requested checkpoints indicate checkpoint_completion_target or max_wal_size may need tuning.`,\n fix: `-- Increase max_wal_size:\\nALTER SYSTEM SET max_wal_size = '2GB';\\nSELECT pg_reload_conf();`,\n impact: \"Frequent requested checkpoints cause I/O spikes and degrade performance.\",\n effort: \"moderate\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking checkpoint frequency:\", (err as Error).message); skipped.push(\"checkpoint frequency: \" + (err as Error).message);\n }\n\n // AutoVACUUM config check\n try {\n const r = await client.query(`SELECT setting FROM pg_settings WHERE name = 'autovacuum'`);\n if (r.rows[0]?.setting === \"off\") {\n issues.push({\n id: `maint-autovacuum-disabled`,\n severity: \"critical\",\n category: \"maintenance\",\n title: `Autovacuum is disabled`,\n description: `Autovacuum is turned off. Dead tuples will accumulate and transaction ID wraparound becomes a risk.`,\n fix: `ALTER SYSTEM SET autovacuum = on;\\nSELECT pg_reload_conf();`,\n impact: \"Without autovacuum, tables bloat indefinitely and risk transaction ID wraparound shutdown.\",\n effort: \"quick\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking autovacuum:\", (err as Error).message); skipped.push(\"autovacuum: \" + (err as Error).message);\n }\n\n // shared_buffers / work_mem check\n try {\n const sbRes = await client.query(`SELECT setting, unit FROM pg_settings WHERE name = 'shared_buffers'`);\n const memRes = await client.query(`\n SELECT (SELECT setting::bigint FROM pg_settings WHERE name = 'shared_buffers') *\n (SELECT setting::bigint FROM pg_settings WHERE name = 'block_size') AS shared_bytes\n `);\n const sharedBytes = parseInt(memRes.rows[0]?.shared_bytes ?? \"0\");\n // Get total RAM from OS via a simple query (pg doesn't expose this directly, but we can estimate)\n // We'll compare against a reasonable minimum: if shared_buffers < 128MB, warn\n if (sharedBytes > 0 && sharedBytes < 128 * 1024 * 1024) {\n issues.push({\n id: `perf-shared-buffers-low`,\n severity: \"warning\",\n category: \"performance\",\n title: `shared_buffers is only ${(sharedBytes / 1048576).toFixed(0)} MB`,\n description: `shared_buffers is set to ${sbRes.rows[0]?.setting}${sbRes.rows[0]?.unit || \"\"}. Recommended: ~25% of system RAM, typically at least 256MB for production.`,\n fix: `ALTER SYSTEM SET shared_buffers = '256MB';\\n-- Requires restart`,\n impact: \"Low shared_buffers means more disk I/O and poor cache hit ratios.\",\n effort: \"involved\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking shared_buffers:\", (err as Error).message); skipped.push(\"shared_buffers: \" + (err as Error).message);\n }\n\n try {\n const r = await client.query(`SELECT setting, unit FROM pg_settings WHERE name = 'work_mem'`);\n const workMemKB = parseInt(r.rows[0]?.setting ?? \"0\");\n if (workMemKB > 0 && workMemKB < 4096) { // < 4MB\n issues.push({\n id: `perf-work-mem-low`,\n severity: \"info\",\n category: \"performance\",\n title: `work_mem is only ${workMemKB < 1024 ? workMemKB + \"kB\" : (workMemKB / 1024).toFixed(0) + \"MB\"}`,\n description: `work_mem is ${r.rows[0]?.setting}${r.rows[0]?.unit || \"\"}. Low work_mem causes sorts and hash operations to spill to disk.`,\n fix: `ALTER SYSTEM SET work_mem = '16MB';\\nSELECT pg_reload_conf();`,\n impact: \"Operations that exceed work_mem use temporary disk files, which is much slower.\",\n effort: \"quick\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking work_mem:\", (err as Error).message); skipped.push(\"work_mem: \" + (err as Error).message);\n }\n\n // ── Security Advisors ──────────────────────────────────────────\n\n // Superuser connections from non-localhost\n try {\n const r = await client.query(`\n SELECT pid, usename, client_addr::text\n FROM pg_stat_activity\n WHERE usename IN (SELECT rolname FROM pg_roles WHERE rolsuper)\n AND client_addr IS NOT NULL\n AND client_addr::text NOT IN ('127.0.0.1', '::1')\n AND pid != pg_backend_pid()\n `);\n for (const row of r.rows) {\n issues.push({\n id: `sec-superuser-remote-${row.pid}`,\n severity: \"critical\",\n category: \"security\",\n title: `Superuser ${row.usename} connected from ${row.client_addr}`,\n description: `Superuser ${row.usename} has an active connection from non-localhost address ${row.client_addr}. This is a security risk.`,\n fix: `-- Restrict superuser access in pg_hba.conf to localhost only.\\n-- Then: SELECT pg_reload_conf();`,\n impact: \"Remote superuser access is a significant security vulnerability.\",\n effort: \"moderate\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking superuser connections:\", (err as Error).message); skipped.push(\"superuser connections: \" + (err as Error).message);\n }\n\n // SSL disabled\n try {\n const r = await client.query(`SELECT setting FROM pg_settings WHERE name = 'ssl'`);\n if (r.rows[0]?.setting === \"off\") {\n issues.push({\n id: `sec-ssl-off`,\n severity: \"warning\",\n category: \"security\",\n title: `SSL is disabled`,\n description: `SSL is turned off. Database connections are not encrypted.`,\n fix: `-- Enable SSL in postgresql.conf:\\n-- ssl = on\\n-- ssl_cert_file = 'server.crt'\\n-- ssl_key_file = 'server.key'\\nSELECT pg_reload_conf();`,\n impact: \"Database traffic can be intercepted and read in transit.\",\n effort: \"involved\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking SSL check:\", (err as Error).message); skipped.push(\"SSL check: \" + (err as Error).message);\n }\n\n // Password authentication check (PG 15+)\n try {\n const r = await client.query(`\n SELECT type, database, user_name, auth_method\n FROM pg_hba_file_rules\n WHERE auth_method = 'trust' AND type != 'local'\n LIMIT 5\n `);\n for (const row of r.rows) {\n issues.push({\n id: `sec-trust-auth-${row.database}-${row.user_name}`,\n severity: \"critical\",\n category: \"security\",\n title: `Trust authentication for ${row.user_name}@${row.database}`,\n description: `HBA rule allows trust (no password) authentication for ${row.type} connections to ${row.database} as ${row.user_name}.`,\n fix: `-- Change auth_method from 'trust' to 'scram-sha-256' in pg_hba.conf\\n-- Then: SELECT pg_reload_conf();`,\n impact: \"Anyone can connect without a password.\",\n effort: \"moderate\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking trust auth:\", (err as Error).message); skipped.push(\"trust auth: \" + (err as Error).message);\n } // pg_hba_file_rules not available pre-PG15\n\n // Filter out ignored issues\n const ignoredIds = getIgnoredIssues();\n const ignoredSet = new Set(ignoredIds);\n const activeIssues = issues.filter(i => !ignoredSet.has(i.id));\n const ignoredCount = issues.length - activeIssues.length;\n\n // Generate batch fixes for groups of same-type issues\n const batchFixes: BatchFix[] = [];\n const groups = new Map<string, AdvisorIssue[]>();\n for (const issue of activeIssues) {\n // Group by id prefix (everything before the last dash-separated segment with variable data)\n const prefix = issue.id.replace(/-[^-]+$/, \"\");\n if (!groups.has(prefix)) groups.set(prefix, []);\n groups.get(prefix)!.push(issue);\n }\n const BATCH_TITLES: Record<string, string> = {\n \"schema-fk-no-idx\": \"Create all missing FK indexes\",\n \"schema-unused-idx\": \"Drop all unused indexes\",\n \"schema-no-pk\": \"Fix all tables missing primary keys\",\n \"maint-vacuum\": \"VACUUM all overdue tables\",\n \"maint-analyze\": \"ANALYZE all tables missing statistics\",\n \"perf-bloated-idx\": \"REINDEX all bloated indexes\",\n \"perf-bloat\": \"VACUUM FULL all bloated tables\",\n };\n for (const [prefix, group] of groups) {\n if (group.length <= 1) continue;\n const title = BATCH_TITLES[prefix] || `Fix all ${group.length} ${prefix} issues`;\n const sql = group.map(i => i.fix.split(\"\\n\").filter(l => !l.trim().startsWith(\"--\")).join(\"\\n\").trim()).filter(Boolean).join(\";\\n\") + \";\";\n batchFixes.push({ type: prefix, title: `${title} (${group.length})`, count: group.length, sql });\n }\n\n const score = computeAdvisorScore(activeIssues);\n return {\n score,\n grade: gradeFromScore(score),\n issues: activeIssues,\n breakdown: computeBreakdown(activeIssues),\n skipped,\n ignoredCount,\n batchFixes,\n };\n } finally {\n client.release();\n }\n}\n\n// ── Ignored Issues Management ──────────────────────────────────\n\nimport Database from \"better-sqlite3\";\nimport path from \"node:path\";\nimport os from \"node:os\";\nimport fs from \"node:fs\";\n\nlet _ignoredDb: ReturnType<typeof Database> | null = null;\n\nfunction getIgnoredDb(): ReturnType<typeof Database> {\n if (_ignoredDb) return _ignoredDb;\n const dataDir = process.env.PG_DASH_DATA_DIR || path.join(os.homedir(), \".pg-dash\");\n fs.mkdirSync(dataDir, { recursive: true });\n const dbPath = path.join(dataDir, \"alerts.db\");\n _ignoredDb = new Database(dbPath);\n _ignoredDb.pragma(\"journal_mode = WAL\");\n _ignoredDb.exec(\"CREATE TABLE IF NOT EXISTS ignored_issues (issue_id TEXT PRIMARY KEY, ignored_at INTEGER)\");\n return _ignoredDb;\n}\n\nexport function getIgnoredIssues(): string[] {\n try {\n const db = getIgnoredDb();\n return db.prepare(\"SELECT issue_id FROM ignored_issues\").all().map((r: any) => r.issue_id);\n } catch {\n return [];\n }\n}\n\nexport function ignoreIssue(issueId: string): void {\n const db = getIgnoredDb();\n db.prepare(\"INSERT OR REPLACE INTO ignored_issues (issue_id, ignored_at) VALUES (?, ?)\").run(issueId, Date.now());\n}\n\nexport function unignoreIssue(issueId: string): void {\n const db = getIgnoredDb();\n db.prepare(\"DELETE FROM ignored_issues WHERE issue_id = ?\").run(issueId);\n}\n\n// Allowed SQL operations for the fix endpoint\n\nexport function isSafeFix(sql: string): boolean {\n const trimmed = sql.trim();\n if (!trimmed) return false;\n\n // Reject multi-statement SQL (split on semicolons, ignore trailing)\n const statements = trimmed.replace(/;\\s*$/, \"\").split(\";\").map(s => s.trim()).filter(Boolean);\n if (statements.length !== 1) return false;\n\n const upper = statements[0].toUpperCase();\n\n // EXPLAIN ANALYZE — only allow if followed by SELECT\n if (upper.startsWith(\"EXPLAIN ANALYZE\")) {\n const afterExplain = upper.replace(/^EXPLAIN\\s+ANALYZE\\s+/, \"\").trimStart();\n return afterExplain.startsWith(\"SELECT\");\n }\n\n // Simple prefix allowlist for single statements\n const ALLOWED_PREFIXES = [\n \"VACUUM\",\n \"ANALYZE\",\n \"REINDEX\",\n \"CREATE INDEX CONCURRENTLY\",\n \"DROP INDEX CONCURRENTLY\",\n \"SELECT PG_TERMINATE_BACKEND(\",\n \"SELECT PG_CANCEL_BACKEND(\",\n ];\n\n return ALLOWED_PREFIXES.some((p) => upper.startsWith(p));\n}\n","import type { Pool } from \"pg\";\n\nexport async function getSchemaTables(pool: Pool) {\n const client = await pool.connect();\n try {\n const r = await client.query(`\n SELECT\n c.relname AS name,\n n.nspname AS schema,\n pg_size_pretty(pg_total_relation_size(c.oid)) AS total_size,\n pg_total_relation_size(c.oid) AS total_size_bytes,\n pg_size_pretty(pg_relation_size(c.oid)) AS table_size,\n pg_size_pretty(pg_total_relation_size(c.oid) - pg_relation_size(c.oid)) AS index_size,\n s.n_live_tup AS row_count,\n obj_description(c.oid) AS description\n FROM pg_class c\n JOIN pg_namespace n ON c.relnamespace = n.oid\n LEFT JOIN pg_stat_user_tables s ON s.relid = c.oid\n WHERE c.relkind = 'r' AND n.nspname NOT IN ('pg_catalog', 'information_schema')\n ORDER BY pg_total_relation_size(c.oid) DESC\n `);\n return r.rows;\n } finally {\n client.release();\n }\n}\n\nexport async function getSchemaTableDetail(pool: Pool, tableName: string) {\n const client = await pool.connect();\n try {\n // Parse schema.table or default to public\n const parts = tableName.split(\".\");\n const schema = parts.length > 1 ? parts[0] : \"public\";\n const name = parts.length > 1 ? parts[1] : parts[0];\n\n // Table info\n const tableInfo = await client.query(`\n SELECT\n c.relname AS name, n.nspname AS schema,\n pg_size_pretty(pg_total_relation_size(c.oid)) AS total_size,\n pg_size_pretty(pg_relation_size(c.oid)) AS table_size,\n pg_size_pretty(pg_total_relation_size(c.oid) - pg_relation_size(c.oid)) AS index_size,\n pg_size_pretty(pg_relation_size(c.reltoastrelid)) AS toast_size,\n s.n_live_tup AS row_count, s.n_dead_tup AS dead_tuples,\n s.last_vacuum, s.last_autovacuum, s.last_analyze, s.last_autoanalyze,\n s.seq_scan, s.idx_scan\n FROM pg_class c\n JOIN pg_namespace n ON c.relnamespace = n.oid\n LEFT JOIN pg_stat_user_tables s ON s.relid = c.oid\n WHERE c.relname = $1 AND n.nspname = $2 AND c.relkind = 'r'\n `, [name, schema]);\n\n if (tableInfo.rows.length === 0) return null;\n\n // Columns\n const columns = await client.query(`\n SELECT\n a.attname AS name,\n pg_catalog.format_type(a.atttypid, a.atttypmod) AS type,\n NOT a.attnotnull AS nullable,\n pg_get_expr(d.adbin, d.adrelid) AS default_value,\n col_description(a.attrelid, a.attnum) AS description\n FROM pg_attribute a\n LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum\n WHERE a.attrelid = (SELECT c.oid FROM pg_class c JOIN pg_namespace n ON c.relnamespace = n.oid WHERE c.relname = $1 AND n.nspname = $2)\n AND a.attnum > 0 AND NOT a.attisdropped\n ORDER BY a.attnum\n `, [name, schema]);\n\n // Indexes\n const indexes = await client.query(`\n SELECT\n i.relname AS name,\n am.amname AS type,\n pg_size_pretty(pg_relation_size(i.oid)) AS size,\n pg_get_indexdef(idx.indexrelid) AS definition,\n idx.indisunique AS is_unique,\n idx.indisprimary AS is_primary,\n s.idx_scan, s.idx_tup_read, s.idx_tup_fetch\n FROM pg_index idx\n JOIN pg_class i ON idx.indexrelid = i.oid\n JOIN pg_class t ON idx.indrelid = t.oid\n JOIN pg_namespace n ON t.relnamespace = n.oid\n JOIN pg_am am ON i.relam = am.oid\n LEFT JOIN pg_stat_user_indexes s ON s.indexrelid = i.oid\n WHERE t.relname = $1 AND n.nspname = $2\n ORDER BY i.relname\n `, [name, schema]);\n\n // Constraints\n const constraints = await client.query(`\n SELECT\n conname AS name,\n CASE contype WHEN 'p' THEN 'PRIMARY KEY' WHEN 'f' THEN 'FOREIGN KEY'\n WHEN 'u' THEN 'UNIQUE' WHEN 'c' THEN 'CHECK' WHEN 'x' THEN 'EXCLUDE' END AS type,\n pg_get_constraintdef(oid) AS definition\n FROM pg_constraint\n WHERE conrelid = (SELECT c.oid FROM pg_class c JOIN pg_namespace n ON c.relnamespace = n.oid WHERE c.relname = $1 AND n.nspname = $2)\n ORDER BY\n CASE contype WHEN 'p' THEN 1 WHEN 'u' THEN 2 WHEN 'f' THEN 3 WHEN 'c' THEN 4 ELSE 5 END\n `, [name, schema]);\n\n // Foreign keys (outgoing)\n const foreignKeys = await client.query(`\n SELECT\n conname AS name,\n a.attname AS column_name,\n confrelid::regclass::text AS referenced_table,\n af.attname AS referenced_column\n FROM pg_constraint c\n JOIN pg_attribute a ON a.attrelid = c.conrelid AND a.attnum = ANY(c.conkey)\n JOIN pg_attribute af ON af.attrelid = c.confrelid AND af.attnum = ANY(c.confkey)\n WHERE c.contype = 'f'\n AND c.conrelid = (SELECT cl.oid FROM pg_class cl JOIN pg_namespace n ON cl.relnamespace = n.oid WHERE cl.relname = $1 AND n.nspname = $2)\n `, [name, schema]);\n\n // Sample data (first 10 rows)\n let sampleData: any[] = [];\n try {\n const sample = await client.query(\n `SELECT * FROM ${client.escapeIdentifier(schema)}.${client.escapeIdentifier(name)} LIMIT 10`\n );\n sampleData = sample.rows;\n } catch (err) { console.error(\"[schema] Error:\", (err as Error).message); }\n\n return {\n ...tableInfo.rows[0],\n columns: columns.rows,\n indexes: indexes.rows,\n constraints: constraints.rows,\n foreignKeys: foreignKeys.rows,\n sampleData,\n };\n } finally {\n client.release();\n }\n}\n\nexport async function getSchemaIndexes(pool: Pool) {\n const client = await pool.connect();\n try {\n const r = await client.query(`\n SELECT\n n.nspname AS schema,\n t.relname AS table_name,\n i.relname AS name,\n am.amname AS type,\n pg_size_pretty(pg_relation_size(i.oid)) AS size,\n pg_relation_size(i.oid) AS size_bytes,\n pg_get_indexdef(idx.indexrelid) AS definition,\n idx.indisunique AS is_unique,\n idx.indisprimary AS is_primary,\n s.idx_scan, s.idx_tup_read, s.idx_tup_fetch\n FROM pg_index idx\n JOIN pg_class i ON idx.indexrelid = i.oid\n JOIN pg_class t ON idx.indrelid = t.oid\n JOIN pg_namespace n ON t.relnamespace = n.oid\n JOIN pg_am am ON i.relam = am.oid\n LEFT JOIN pg_stat_user_indexes s ON s.indexrelid = i.oid\n WHERE n.nspname NOT IN ('pg_catalog', 'information_schema')\n ORDER BY pg_relation_size(i.oid) DESC\n `);\n return r.rows;\n } finally {\n client.release();\n }\n}\n\nexport async function getSchemaFunctions(pool: Pool) {\n const client = await pool.connect();\n try {\n const r = await client.query(`\n SELECT\n n.nspname AS schema,\n p.proname AS name,\n pg_get_function_result(p.oid) AS return_type,\n pg_get_function_arguments(p.oid) AS arguments,\n l.lanname AS language,\n p.prosrc AS source,\n CASE p.prokind WHEN 'f' THEN 'function' WHEN 'p' THEN 'procedure' WHEN 'a' THEN 'aggregate' WHEN 'w' THEN 'window' END AS kind\n FROM pg_proc p\n JOIN pg_namespace n ON p.pronamespace = n.oid\n JOIN pg_language l ON p.prolang = l.oid\n WHERE n.nspname NOT IN ('pg_catalog', 'information_schema')\n ORDER BY n.nspname, p.proname\n `);\n return r.rows;\n } finally {\n client.release();\n }\n}\n\nexport async function getSchemaExtensions(pool: Pool) {\n const client = await pool.connect();\n try {\n const r = await client.query(`\n SELECT extname AS name, extversion AS installed_version,\n n.nspname AS schema, obj_description(e.oid) AS description\n FROM pg_extension e\n JOIN pg_namespace n ON e.extnamespace = n.oid\n ORDER BY extname\n `);\n return r.rows;\n } finally {\n client.release();\n }\n}\n\nexport async function getSchemaEnums(pool: Pool) {\n const client = await pool.connect();\n try {\n const r = await client.query(`\n SELECT\n t.typname AS name,\n n.nspname AS schema,\n array_agg(e.enumlabel ORDER BY e.enumsortorder) AS values\n FROM pg_type t\n JOIN pg_namespace n ON t.typnamespace = n.oid\n JOIN pg_enum e ON t.oid = e.enumtypid\n WHERE n.nspname NOT IN ('pg_catalog', 'information_schema')\n GROUP BY t.typname, n.nspname\n ORDER BY t.typname\n `);\n return r.rows;\n } finally {\n client.release();\n }\n}\n","// Schema Diff — compares two schema snapshots and produces a change list\n\nexport interface SchemaSnapshot {\n tables: SnapshotTable[];\n enums: SnapshotEnum[];\n}\n\nexport interface SnapshotTable {\n name: string;\n schema: string;\n columns: SnapshotColumn[];\n indexes: SnapshotIndex[];\n constraints: SnapshotConstraint[];\n}\n\nexport interface SnapshotColumn {\n name: string;\n type: string;\n nullable: boolean;\n default_value: string | null;\n}\n\nexport interface SnapshotIndex {\n name: string;\n definition: string;\n is_unique: boolean;\n is_primary: boolean;\n}\n\nexport interface SnapshotConstraint {\n name: string;\n type: string;\n definition: string;\n}\n\nexport interface SnapshotEnum {\n name: string;\n schema: string;\n values: string[];\n}\n\nexport interface SchemaChange {\n change_type: \"added\" | \"removed\" | \"modified\";\n object_type: \"table\" | \"column\" | \"index\" | \"constraint\" | \"enum\";\n table_name: string | null;\n detail: string;\n}\n\nexport function diffSchemaSnapshots(oldSnap: SchemaSnapshot, newSnap: SchemaSnapshot): SchemaChange[] {\n const changes: SchemaChange[] = [];\n\n const oldTableMap = new Map(oldSnap.tables.map((t) => [`${t.schema}.${t.name}`, t]));\n const newTableMap = new Map(newSnap.tables.map((t) => [`${t.schema}.${t.name}`, t]));\n\n // Tables added/removed\n for (const [key, t] of newTableMap) {\n if (!oldTableMap.has(key)) {\n changes.push({ change_type: \"added\", object_type: \"table\", table_name: key, detail: `Table ${key} added` });\n }\n }\n for (const [key] of oldTableMap) {\n if (!newTableMap.has(key)) {\n changes.push({ change_type: \"removed\", object_type: \"table\", table_name: key, detail: `Table ${key} removed` });\n }\n }\n\n // Compare matching tables\n for (const [key, newTable] of newTableMap) {\n const oldTable = oldTableMap.get(key);\n if (!oldTable) continue;\n\n // Columns\n const oldCols = new Map(oldTable.columns.map((c) => [c.name, c]));\n const newCols = new Map(newTable.columns.map((c) => [c.name, c]));\n\n for (const [name, col] of newCols) {\n const oldCol = oldCols.get(name);\n if (!oldCol) {\n changes.push({ change_type: \"added\", object_type: \"column\", table_name: key, detail: `Column ${name} added (${col.type})` });\n } else {\n if (oldCol.type !== col.type) {\n changes.push({ change_type: \"modified\", object_type: \"column\", table_name: key, detail: `Column ${name} type changed: ${oldCol.type} → ${col.type}` });\n }\n if (oldCol.nullable !== col.nullable) {\n changes.push({ change_type: \"modified\", object_type: \"column\", table_name: key, detail: `Column ${name} nullable changed: ${oldCol.nullable} → ${col.nullable}` });\n }\n if (oldCol.default_value !== col.default_value) {\n changes.push({ change_type: \"modified\", object_type: \"column\", table_name: key, detail: `Column ${name} default changed: ${oldCol.default_value ?? \"NULL\"} → ${col.default_value ?? \"NULL\"}` });\n }\n }\n }\n for (const name of oldCols.keys()) {\n if (!newCols.has(name)) {\n changes.push({ change_type: \"removed\", object_type: \"column\", table_name: key, detail: `Column ${name} removed` });\n }\n }\n\n // Indexes\n const oldIdx = new Map(oldTable.indexes.map((i) => [i.name, i]));\n const newIdx = new Map(newTable.indexes.map((i) => [i.name, i]));\n for (const [name, idx] of newIdx) {\n if (!oldIdx.has(name)) {\n changes.push({ change_type: \"added\", object_type: \"index\", table_name: key, detail: `Index ${name} added` });\n } else if (oldIdx.get(name)!.definition !== idx.definition) {\n changes.push({ change_type: \"modified\", object_type: \"index\", table_name: key, detail: `Index ${name} definition changed` });\n }\n }\n for (const name of oldIdx.keys()) {\n if (!newIdx.has(name)) {\n changes.push({ change_type: \"removed\", object_type: \"index\", table_name: key, detail: `Index ${name} removed` });\n }\n }\n\n // Constraints\n const oldCon = new Map(oldTable.constraints.map((c) => [c.name, c]));\n const newCon = new Map(newTable.constraints.map((c) => [c.name, c]));\n for (const [name, con] of newCon) {\n if (!oldCon.has(name)) {\n changes.push({ change_type: \"added\", object_type: \"constraint\", table_name: key, detail: `Constraint ${name} added (${con.type})` });\n } else if (oldCon.get(name)!.definition !== con.definition) {\n changes.push({ change_type: \"modified\", object_type: \"constraint\", table_name: key, detail: `Constraint ${name} definition changed` });\n }\n }\n for (const name of oldCon.keys()) {\n if (!newCon.has(name)) {\n changes.push({ change_type: \"removed\", object_type: \"constraint\", table_name: key, detail: `Constraint ${name} removed` });\n }\n }\n }\n\n // Enums\n const oldEnums = new Map((oldSnap.enums || []).map((e) => [`${e.schema}.${e.name}`, e]));\n const newEnums = new Map((newSnap.enums || []).map((e) => [`${e.schema}.${e.name}`, e]));\n for (const [key, en] of newEnums) {\n const oldEn = oldEnums.get(key);\n if (!oldEn) {\n changes.push({ change_type: \"added\", object_type: \"enum\", table_name: null, detail: `Enum ${key} added (${en.values.join(\", \")})` });\n } else {\n const added = en.values.filter((v) => !oldEn.values.includes(v));\n const removed = oldEn.values.filter((v) => !en.values.includes(v));\n for (const v of added) {\n changes.push({ change_type: \"modified\", object_type: \"enum\", table_name: null, detail: `Enum ${key}: value '${v}' added` });\n }\n for (const v of removed) {\n changes.push({ change_type: \"modified\", object_type: \"enum\", table_name: null, detail: `Enum ${key}: value '${v}' removed` });\n }\n }\n }\n for (const key of oldEnums.keys()) {\n if (!newEnums.has(key)) {\n changes.push({ change_type: \"removed\", object_type: \"enum\", table_name: null, detail: `Enum ${key} removed` });\n }\n }\n\n return changes;\n}\n","// Schema Tracker — takes schema snapshots, stores in SQLite, detects changes\n\nconst SNAPSHOT_RETENTION = 50;\n\nimport type { Pool } from \"pg\";\nimport type Database from \"better-sqlite3\";\nimport { getSchemaTables, getSchemaTableDetail, getSchemaEnums } from \"./queries/schema.js\";\nimport { diffSchemaSnapshots, type SchemaSnapshot, type SchemaChange } from \"./schema-diff.js\";\n\n/** Build a full schema snapshot from a live pool — reusable for env comparison */\nexport async function buildLiveSnapshot(pool: Pool): Promise<SchemaSnapshot> {\n const tables = await getSchemaTables(pool);\n const enums = await getSchemaEnums(pool);\n\n const detailedTables = await Promise.all(\n tables.map(async (t: any) => {\n const detail = await getSchemaTableDetail(pool, `${t.schema}.${t.name}`);\n if (!detail) return null;\n return {\n name: detail.name,\n schema: detail.schema,\n columns: detail.columns.map((c: any) => ({\n name: c.name,\n type: c.type,\n nullable: c.nullable,\n default_value: c.default_value,\n })),\n indexes: detail.indexes.map((i: any) => ({\n name: i.name,\n definition: i.definition,\n is_unique: i.is_unique,\n is_primary: i.is_primary,\n })),\n constraints: detail.constraints.map((c: any) => ({\n name: c.name,\n type: c.type,\n definition: c.definition,\n })),\n };\n })\n );\n\n return {\n tables: detailedTables.filter(Boolean) as SchemaSnapshot[\"tables\"],\n enums: enums.map((e: any) => ({ name: e.name, schema: e.schema, values: e.values })),\n };\n}\n\nexport class SchemaTracker {\n private db: Database.Database;\n private pool: Pool;\n private intervalMs: number;\n private timer: ReturnType<typeof setInterval> | null = null;\n\n constructor(db: Database.Database, pool: Pool, intervalMs = 6 * 60 * 60 * 1000) {\n this.db = db;\n this.pool = pool;\n this.intervalMs = intervalMs;\n this.initTables();\n }\n\n private initTables() {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS schema_snapshots (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n timestamp INTEGER NOT NULL,\n snapshot TEXT NOT NULL\n );\n CREATE TABLE IF NOT EXISTS schema_changes (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n snapshot_id INTEGER NOT NULL,\n timestamp INTEGER NOT NULL,\n change_type TEXT NOT NULL,\n object_type TEXT NOT NULL,\n table_name TEXT,\n detail TEXT NOT NULL,\n FOREIGN KEY (snapshot_id) REFERENCES schema_snapshots(id)\n );\n `);\n }\n\n async takeSnapshot(): Promise<{ snapshotId: number; changes: SchemaChange[] }> {\n const snapshot = await this.buildSnapshot();\n const now = Date.now();\n const json = JSON.stringify(snapshot);\n\n const info = this.db.prepare(\"INSERT INTO schema_snapshots (timestamp, snapshot) VALUES (?, ?)\").run(now, json);\n const snapshotId = Number(info.lastInsertRowid);\n\n // Prune old snapshots, keeping only the most recent SNAPSHOT_RETENTION\n this.db.prepare(`\n DELETE FROM schema_snapshots\n WHERE id NOT IN (\n SELECT id FROM schema_snapshots\n ORDER BY timestamp DESC\n LIMIT ?\n )\n `).run(SNAPSHOT_RETENTION);\n\n // Diff against previous\n const prev = this.db.prepare(\"SELECT snapshot FROM schema_snapshots WHERE id < ? ORDER BY id DESC LIMIT 1\").get(snapshotId) as { snapshot: string } | undefined;\n let changes: SchemaChange[] = [];\n if (prev) {\n const oldSnap: SchemaSnapshot = JSON.parse(prev.snapshot);\n changes = diffSchemaSnapshots(oldSnap, snapshot);\n if (changes.length > 0) {\n const insert = this.db.prepare(\"INSERT INTO schema_changes (snapshot_id, timestamp, change_type, object_type, table_name, detail) VALUES (?, ?, ?, ?, ?, ?)\");\n const tx = this.db.transaction((chs: SchemaChange[]) => {\n for (const c of chs) {\n insert.run(snapshotId, now, c.change_type, c.object_type, c.table_name, c.detail);\n }\n });\n tx(changes);\n }\n }\n\n return { snapshotId, changes };\n }\n\n private async buildSnapshot(): Promise<SchemaSnapshot> {\n return buildLiveSnapshot(this.pool);\n }\n\n start() {\n // Take initial snapshot\n this.takeSnapshot().catch((err) => console.error(\"Schema snapshot error:\", err.message));\n this.timer = setInterval(() => {\n this.takeSnapshot().catch((err) => console.error(\"Schema snapshot error:\", err.message));\n }, this.intervalMs);\n }\n\n stop() {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n }\n\n // API helpers\n getHistory(limit = 30) {\n return this.db.prepare(\"SELECT id, timestamp FROM schema_snapshots ORDER BY id DESC LIMIT ?\").all(limit);\n }\n\n getChanges(since?: number) {\n if (since) {\n return this.db.prepare(\"SELECT * FROM schema_changes WHERE timestamp >= ? ORDER BY timestamp DESC\").all(since);\n }\n return this.db.prepare(\"SELECT * FROM schema_changes ORDER BY timestamp DESC LIMIT 100\").all();\n }\n\n getLatestChanges() {\n const latest = this.db.prepare(\"SELECT id FROM schema_snapshots ORDER BY id DESC LIMIT 1\").get() as { id: number } | undefined;\n if (!latest) return [];\n return this.db.prepare(\"SELECT * FROM schema_changes WHERE snapshot_id = ? ORDER BY id\").all(latest.id);\n }\n\n getDiff(fromId: number, toId: number) {\n const from = this.db.prepare(\"SELECT snapshot FROM schema_snapshots WHERE id = ?\").get(fromId) as { snapshot: string } | undefined;\n const to = this.db.prepare(\"SELECT snapshot FROM schema_snapshots WHERE id = ?\").get(toId) as { snapshot: string } | undefined;\n if (!from || !to) return null;\n return diffSchemaSnapshots(JSON.parse(from.snapshot), JSON.parse(to.snapshot));\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { AdvisorResult, AdvisorIssue } from \"./advisor.js\";\n\nexport interface Snapshot {\n timestamp: string;\n result: AdvisorResult;\n}\n\nexport interface SnapshotDiff {\n scoreDelta: number;\n previousScore: number;\n currentScore: number;\n previousGrade: string;\n currentGrade: string;\n newIssues: AdvisorIssue[];\n resolvedIssues: AdvisorIssue[];\n unchanged: AdvisorIssue[];\n}\n\n/**\n * Normalize a dynamic issue ID for stable comparison.\n * Strips trailing -<number> suffixes so IDs like `maint-idle-12345`\n * (where 12345 is a PID that changes every run) don't produce false noise.\n */\nfunction normalizeIssueId(id: string): string {\n return id.replace(/-\\d+$/, \"\");\n}\n\n/**\n * Save a health-check snapshot to a specific file path.\n * The parent directory is created automatically.\n *\n * @param snapshotPath Full path to the JSON file (e.g. ~/.pg-dash/last-check.json)\n */\nexport function saveSnapshot(snapshotPath: string, result: AdvisorResult): void {\n fs.mkdirSync(path.dirname(snapshotPath), { recursive: true });\n const snapshot: Snapshot = { timestamp: new Date().toISOString(), result };\n fs.writeFileSync(snapshotPath, JSON.stringify(snapshot, null, 2));\n}\n\n/**\n * Load a previously saved snapshot from a specific file path.\n * Returns null if the file doesn't exist or cannot be parsed.\n *\n * @param snapshotPath Full path to the JSON file\n */\nexport function loadSnapshot(snapshotPath: string): Snapshot | null {\n if (!fs.existsSync(snapshotPath)) return null;\n try {\n return JSON.parse(fs.readFileSync(snapshotPath, \"utf-8\"));\n } catch {\n return null;\n }\n}\n\nexport function diffSnapshots(prev: AdvisorResult, current: AdvisorResult): SnapshotDiff {\n // Use normalized IDs for comparison to avoid noise from dynamic suffixes\n // (e.g. maint-idle-12345 where 12345 is a PID that changes every run).\n const prevNormIds = new Set(prev.issues.map((i) => normalizeIssueId(i.id)));\n const currNormIds = new Set(current.issues.map((i) => normalizeIssueId(i.id)));\n\n const newIssues = current.issues.filter((i) => !prevNormIds.has(normalizeIssueId(i.id)));\n const resolvedIssues = prev.issues.filter((i) => !currNormIds.has(normalizeIssueId(i.id)));\n const unchanged = current.issues.filter((i) => prevNormIds.has(normalizeIssueId(i.id)));\n\n return {\n scoreDelta: current.score - prev.score,\n previousScore: prev.score,\n currentScore: current.score,\n previousGrade: prev.grade,\n currentGrade: current.grade,\n newIssues,\n resolvedIssues,\n unchanged,\n };\n}\n","// Migration safety checker — static + dynamic analysis of SQL migration files\n\nimport type { Pool } from \"pg\";\n\nexport interface MigrationIssue {\n severity: \"error\" | \"warning\" | \"info\";\n code: string;\n message: string;\n suggestion?: string;\n lineNumber?: number;\n tableName?: string;\n estimatedRows?: number;\n estimatedLockSeconds?: number;\n}\n\nexport interface MigrationCheckResult {\n safe: boolean;\n issues: MigrationIssue[];\n summary: {\n errors: number;\n warnings: number;\n infos: number;\n };\n checkedAt: string;\n}\n\n// Strip SQL comments while preserving line numbers (replace with spaces)\nfunction stripComments(sql: string): string {\n // Replace /* ... */ block comments (preserve newlines for line number tracking)\n let stripped = sql.replace(/\\/\\*[\\s\\S]*?\\*\\//g, (match) =>\n match.replace(/[^\\n]/g, \" \")\n );\n // Replace -- single-line comments (preserve the newline)\n stripped = stripped.replace(/--[^\\n]*/g, (match) => \" \".repeat(match.length));\n return stripped;\n}\n\n// Helper: find line number of a match in the original SQL\nfunction findLineNumber(sql: string, matchIndex: number): number {\n const before = sql.slice(0, matchIndex);\n return before.split(\"\\n\").length;\n}\n\n// Extract bare table name from possibly-quoted or schema-qualified identifier\nfunction bareTable(name: string): string {\n return name\n .replace(/^public\\./i, \"\")\n .replace(/\"/g, \"\")\n .toLowerCase()\n .trim();\n}\n\n// Parse all table names operated on by this migration\nfunction extractOperatedTables(sql: string): {\n indexTables: string[]; // CREATE INDEX ON <table>\n alterTables: string[]; // ALTER TABLE <table>\n dropTables: string[]; // DROP TABLE <table>\n refTables: string[]; // REFERENCES <table>\n} {\n sql = stripComments(sql);\n const indexTables: string[] = [];\n const alterTables: string[] = [];\n const dropTables: string[] = [];\n const refTables: string[] = [];\n\n // CREATE INDEX ... ON table\n const idxRe = /\\bCREATE\\s+(?:UNIQUE\\s+)?INDEX\\s+(?:CONCURRENTLY\\s+)?(?:IF\\s+NOT\\s+EXISTS\\s+)?(?:\\w+\\s+)?ON\\s+([\\w.\"]+)/gi;\n let m: RegExpExecArray | null;\n while ((m = idxRe.exec(sql)) !== null) indexTables.push(bareTable(m[1]));\n\n // ALTER TABLE table\n const altRe = /\\bALTER\\s+TABLE\\s+(?:IF\\s+EXISTS\\s+)?([\\w.\"]+)/gi;\n while ((m = altRe.exec(sql)) !== null) alterTables.push(bareTable(m[1]));\n\n // DROP TABLE\n const dropRe = /\\bDROP\\s+TABLE\\s+(?:IF\\s+EXISTS\\s+)?([\\w.\"]+)/gi;\n while ((m = dropRe.exec(sql)) !== null) dropTables.push(bareTable(m[1]));\n\n // REFERENCES table\n const refRe = /\\bREFERENCES\\s+([\\w.\"]+)/gi;\n while ((m = refRe.exec(sql)) !== null) refTables.push(bareTable(m[1]));\n\n return { indexTables, alterTables, dropTables, refTables };\n}\n\n// Static analysis — no DB needed\nfunction staticCheck(sql: string): MigrationIssue[] {\n const issues: MigrationIssue[] = [];\n // Strip comments before analysis to avoid false positives from commented-out SQL\n sql = stripComments(sql);\n\n // Determine tables created IN THIS MIGRATION (so we know they're brand-new)\n const createdTablesRe = /\\bCREATE\\s+TABLE\\s+(?:IF\\s+NOT\\s+EXISTS\\s+)?([\\w.\"]+)/gi;\n const createdTables = new Set<string>();\n let m: RegExpExecArray | null;\n while ((m = createdTablesRe.exec(sql)) !== null) createdTables.add(bareTable(m[1]));\n\n // 1. CREATE INDEX without CONCURRENTLY (on tables NOT created in this migration)\n const idxRe = /\\bCREATE\\s+(?:UNIQUE\\s+)?INDEX\\s+(?!CONCURRENTLY)((?:IF\\s+NOT\\s+EXISTS\\s+)?(?:\\w+\\s+)?ON\\s+([\\w.\"]+))/gi;\n while ((m = idxRe.exec(sql)) !== null) {\n const table = bareTable(m[2]);\n const lineNumber = findLineNumber(sql, m.index);\n if (!createdTables.has(table)) {\n issues.push({\n severity: \"warning\",\n code: \"INDEX_WITHOUT_CONCURRENTLY\",\n message: `CREATE INDEX on existing table will lock writes. Use CREATE INDEX CONCURRENTLY to avoid downtime.`,\n suggestion: \"Replace CREATE INDEX with CREATE INDEX CONCURRENTLY\",\n lineNumber,\n tableName: table,\n });\n }\n }\n\n // 2. CREATE INDEX CONCURRENTLY → info\n const idxConcRe = /\\bCREATE\\s+(?:UNIQUE\\s+)?INDEX\\s+CONCURRENTLY\\b/gi;\n while ((m = idxConcRe.exec(sql)) !== null) {\n issues.push({\n severity: \"info\",\n code: \"INDEX_CONCURRENTLY_OK\",\n message: \"CREATE INDEX CONCURRENTLY — safe, no write lock\",\n lineNumber: findLineNumber(sql, m.index),\n });\n }\n\n // 3 & 4. ALTER TABLE ... ADD COLUMN ... NOT NULL (with/without DEFAULT)\n // Match: ALTER TABLE <t> ADD COLUMN <col> <type> [DEFAULT <val>] [NOT NULL | NULL]\n const addColRe =\n /\\bALTER\\s+TABLE\\s+(?:IF\\s+EXISTS\\s+)?([\\w.\"]+)\\s+ADD\\s+(?:COLUMN\\s+)?(?:IF\\s+NOT\\s+EXISTS\\s+)?[\\w\"]+\\s+[\\w\\s()\"',.[\\]]+?(?=;|$)/gi;\n while ((m = addColRe.exec(sql)) !== null) {\n const fragment = m[0];\n const table = bareTable(m[1]);\n const lineNumber = findLineNumber(sql, m.index);\n const fragUpper = fragment.toUpperCase();\n\n const hasNotNull = /\\bNOT\\s+NULL\\b/.test(fragUpper);\n const hasDefault = /\\bDEFAULT\\b/.test(fragUpper);\n\n if (hasNotNull && !hasDefault) {\n issues.push({\n severity: \"error\",\n code: \"ADD_COLUMN_NOT_NULL_NO_DEFAULT\",\n message: \"ADD COLUMN NOT NULL without DEFAULT will fail if table has existing rows\",\n suggestion: \"Add a DEFAULT value, then remove it after migration\",\n lineNumber,\n tableName: table,\n });\n } else if (hasNotNull && hasDefault) {\n issues.push({\n severity: \"warning\",\n code: \"ADD_COLUMN_REWRITES_TABLE\",\n message: \"ADD COLUMN with NOT NULL DEFAULT may rewrite table on PostgreSQL < 11\",\n suggestion: \"On PostgreSQL 11+ with a constant default this is safe. For older versions, add column nullable first.\",\n lineNumber,\n tableName: table,\n });\n }\n }\n\n // 5. DROP TABLE\n const dropRe = /\\bDROP\\s+TABLE\\b/gi;\n while ((m = dropRe.exec(sql)) !== null) {\n issues.push({\n severity: \"warning\",\n code: \"DROP_TABLE\",\n message: \"DROP TABLE is destructive. Ensure this is intentional and data is backed up.\",\n lineNumber: findLineNumber(sql, m.index),\n });\n }\n\n // 5b. ALTER COLUMN TYPE — rewrites the entire table and locks it\n const alterTypeRe = /\\bALTER\\s+TABLE\\s+(?:IF\\s+EXISTS\\s+)?([\\w.\"]+)\\s+ALTER\\s+(?:COLUMN\\s+)?[\\w\"]+\\s+TYPE\\b/gi;\n while ((m = alterTypeRe.exec(sql)) !== null) {\n const table = bareTable(m[1]);\n issues.push({\n severity: \"warning\",\n code: \"ALTER_COLUMN_TYPE\",\n message: \"ALTER COLUMN TYPE rewrites the entire table and acquires an exclusive lock.\",\n suggestion: \"Consider using a new column + backfill + rename strategy to avoid downtime.\",\n lineNumber: findLineNumber(sql, m.index),\n tableName: table,\n });\n }\n\n // 5c. DROP COLUMN — safe in PostgreSQL 9.0+ (marks invisible, no rewrite), but breaks app code\n const dropColRe = /\\bALTER\\s+TABLE\\s+(?:IF\\s+EXISTS\\s+)?([\\w.\"]+)\\s+DROP\\s+(?:COLUMN\\s+)(?:IF\\s+EXISTS\\s+)?[\\w\"]+\\b/gi;\n while ((m = dropColRe.exec(sql)) !== null) {\n const table = bareTable(m[1]);\n issues.push({\n severity: \"info\",\n code: \"DROP_COLUMN\",\n message: \"DROP COLUMN is safe in PostgreSQL (no table rewrite), but may break application code referencing that column.\",\n suggestion: \"Ensure no application code references this column before dropping it.\",\n lineNumber: findLineNumber(sql, m.index),\n tableName: table,\n });\n }\n\n // 5d-i. RENAME TABLE\n const renameTableRe = /ALTER\\s+TABLE\\s+(?:IF\\s+EXISTS\\s+)?(\\w+)\\s+RENAME\\s+TO\\s+(\\w+)/gi;\n while ((m = renameTableRe.exec(sql)) !== null) {\n const oldName = m[1];\n const newName = m[2];\n issues.push({\n severity: \"warning\",\n code: \"RENAME_TABLE\",\n message: `Renaming table \"${oldName}\" to \"${newName}\" breaks application code referencing the old name`,\n suggestion: \"Deploy application code that handles both names before renaming, or use a view with the old name after renaming.\",\n lineNumber: findLineNumber(sql, m.index),\n tableName: oldName,\n });\n }\n\n // 5d-ii. RENAME COLUMN\n const renameColumnRe = /ALTER\\s+TABLE\\s+(?:IF\\s+EXISTS\\s+)?(\\w+)\\s+RENAME\\s+COLUMN\\s+(\\w+)\\s+TO\\s+(\\w+)/gi;\n while ((m = renameColumnRe.exec(sql)) !== null) {\n const table = m[1];\n const oldCol = m[2];\n const newCol = m[3];\n issues.push({\n severity: \"warning\",\n code: \"RENAME_COLUMN\",\n message: `Renaming column \"${oldCol}\" to \"${newCol}\" on table \"${table}\" breaks application code referencing the old column name`,\n suggestion: \"Add new column, backfill data, update application to use new column, then drop old column (expand/contract pattern).\",\n lineNumber: findLineNumber(sql, m.index),\n tableName: table,\n });\n }\n\n // 5e. ADD CONSTRAINT without NOT VALID — performs a full table scan to validate\n const addConRe = /\\bALTER\\s+TABLE\\s+(?:IF\\s+EXISTS\\s+)?([\\w.\"]+)\\s+ADD\\s+CONSTRAINT\\b[^;]*(;|$)/gi;\n while ((m = addConRe.exec(sql)) !== null) {\n const fragment = m[0];\n const table = bareTable(m[1]);\n const fragUpper = fragment.toUpperCase();\n // Skip if NOT VALID is already present\n if (!/\\bNOT\\s+VALID\\b/.test(fragUpper)) {\n issues.push({\n severity: \"warning\",\n code: \"ADD_CONSTRAINT_SCANS_TABLE\",\n message: \"ADD CONSTRAINT validates all existing rows and holds an exclusive lock during the scan.\",\n suggestion: \"Use ADD CONSTRAINT ... NOT VALID to skip validation, then VALIDATE CONSTRAINT in a separate transaction.\",\n lineNumber: findLineNumber(sql, m.index),\n tableName: table,\n });\n }\n }\n\n // 5e. CREATE INDEX CONCURRENTLY inside transaction (BEGIN/COMMIT)\n const hasTransaction = /\\bBEGIN\\b/i.test(sql) || /\\bSTART\\s+TRANSACTION\\b/i.test(sql);\n const hasConcurrently = /\\bCREATE\\s+(?:UNIQUE\\s+)?INDEX\\s+CONCURRENTLY\\b/i.test(sql);\n if (hasTransaction && hasConcurrently) {\n issues.push({\n severity: \"error\",\n code: \"CONCURRENTLY_IN_TRANSACTION\",\n message: \"CREATE INDEX CONCURRENTLY cannot run inside a transaction block. It will fail at runtime.\",\n suggestion: \"Remove the BEGIN/COMMIT wrapper, or use a migration tool that runs CONCURRENTLY outside transactions.\",\n });\n }\n\n // 6. TRUNCATE\n const truncRe = /\\bTRUNCATE\\b/gi;\n while ((m = truncRe.exec(sql)) !== null) {\n issues.push({\n severity: \"warning\",\n code: \"TRUNCATE_TABLE\",\n message: \"TRUNCATE will delete all rows. Ensure this is intentional.\",\n lineNumber: findLineNumber(sql, m.index),\n });\n }\n\n // 7. DELETE FROM without WHERE\n const delRe = /\\bDELETE\\s+FROM\\s+[\\w.\"]+\\s*(?:;|$)/gi;\n while ((m = delRe.exec(sql)) !== null) {\n // If there's no WHERE clause in this statement\n const stmt = m[0];\n if (!/\\bWHERE\\b/i.test(stmt)) {\n issues.push({\n severity: \"warning\",\n code: \"DELETE_WITHOUT_WHERE\",\n message: \"DELETE without WHERE clause will remove all rows.\",\n lineNumber: findLineNumber(sql, m.index),\n });\n }\n }\n\n // 8. UPDATE ... SET without WHERE\n const updRe = /\\bUPDATE\\s+[\\w.\"]+\\s+SET\\b[^;]*(;|$)/gi;\n while ((m = updRe.exec(sql)) !== null) {\n const stmt = m[0];\n if (!/\\bWHERE\\b/i.test(stmt)) {\n issues.push({\n severity: \"warning\",\n code: \"UPDATE_WITHOUT_WHERE\",\n message: \"UPDATE without WHERE clause will modify all rows.\",\n lineNumber: findLineNumber(sql, m.index),\n });\n }\n }\n\n return issues;\n}\n\n// Dynamic analysis — requires a running PG pool\nasync function dynamicCheck(sql: string, pool: Pool, staticIssues: MigrationIssue[]): Promise<MigrationIssue[]> {\n const issues: MigrationIssue[] = [];\n const { indexTables, alterTables, dropTables, refTables } = extractOperatedTables(sql);\n\n // All tables we need to look up\n const allTables = [...new Set([...indexTables, ...alterTables, ...dropTables])];\n\n // Query row counts for all tables at once\n const tableStats = new Map<string, { rowCount: number; totalSize: number }>();\n if (allTables.length > 0) {\n try {\n const res = await pool.query<{ tablename: string; n_live_tup: string; total_size: string }>(\n `SELECT tablename,\n n_live_tup,\n pg_total_relation_size(schemaname||'.'||tablename) AS total_size\n FROM pg_stat_user_tables\n WHERE tablename = ANY($1)`,\n [allTables]\n );\n for (const row of res.rows) {\n tableStats.set(row.tablename, {\n rowCount: parseInt(row.n_live_tup ?? \"0\", 10),\n totalSize: parseInt(row.total_size ?? \"0\", 10),\n });\n }\n } catch (_) {\n // Ignore DB errors in dynamic check\n }\n }\n\n // Upgrade CREATE INDEX (non-CONCURRENTLY) issues based on actual row counts\n for (const issue of staticIssues) {\n if (issue.code === \"INDEX_WITHOUT_CONCURRENTLY\" && issue.tableName) {\n const stats = tableStats.get(issue.tableName);\n if (stats) {\n const { rowCount } = stats;\n const lockSecs = Math.round(rowCount / 50000);\n issue.estimatedRows = rowCount;\n issue.estimatedLockSeconds = lockSecs;\n\n if (rowCount > 1_000_000) {\n issue.severity = \"error\";\n issue.message = `CREATE INDEX on '${issue.tableName}' will lock writes for ~${lockSecs}s (${(rowCount / 1e6).toFixed(1)}M rows). CRITICAL — use CREATE INDEX CONCURRENTLY.`;\n } else if (rowCount > 100_000) {\n issue.message = `CREATE INDEX on '${issue.tableName}' will lock writes for ~${lockSecs}s (${(rowCount / 1000).toFixed(0)}k rows).`;\n }\n }\n }\n }\n\n // Validate REFERENCES tables exist\n const uniqueRefTables = [...new Set(refTables)];\n for (const table of uniqueRefTables) {\n try {\n const res = await pool.query<{ tablename: string }>(\n `SELECT tablename FROM pg_tables WHERE schemaname = 'public' AND tablename = $1`,\n [table]\n );\n if (res.rows.length === 0) {\n issues.push({\n severity: \"error\",\n code: \"MISSING_TABLE\",\n message: `Table '${table}' referenced in migration does not exist`,\n tableName: table,\n });\n }\n } catch (_) {\n // Ignore\n }\n }\n\n return issues;\n}\n\nexport async function analyzeMigration(sql: string, pool?: Pool): Promise<MigrationCheckResult> {\n const trimmed = sql.trim();\n\n if (!trimmed) {\n return {\n safe: true,\n issues: [],\n summary: { errors: 0, warnings: 0, infos: 0 },\n checkedAt: new Date().toISOString(),\n };\n }\n\n // Static checks first (mutates issue severity if dynamic info is available)\n const issues = staticCheck(trimmed);\n\n // Dynamic checks (augments existing issues + adds new ones like MISSING_TABLE)\n if (pool) {\n const dynamicIssues = await dynamicCheck(trimmed, pool, issues);\n issues.push(...dynamicIssues);\n }\n\n const errors = issues.filter((i) => i.severity === \"error\").length;\n const warnings = issues.filter((i) => i.severity === \"warning\").length;\n const infos = issues.filter((i) => i.severity === \"info\").length;\n\n return {\n safe: errors === 0,\n issues,\n summary: { errors, warnings, infos },\n checkedAt: new Date().toISOString(),\n };\n}\n","import { Pool } from \"pg\";\nimport { getAdvisorReport } from \"./advisor.js\";\nimport { buildLiveSnapshot } from \"./schema-tracker.js\";\nimport { diffSchemaSnapshots } from \"./schema-diff.js\";\n\nexport interface ColumnInfo {\n name: string;\n type: string;\n nullable: boolean;\n default?: string;\n}\n\nexport interface ColumnTypeDiff {\n column: string;\n sourceType: string;\n targetType: string;\n}\n\nexport interface ColumnNullableDiff {\n column: string;\n sourceNullable: boolean;\n targetNullable: boolean;\n}\n\nexport interface ColumnDefaultDiff {\n column: string;\n sourceDefault: string | null;\n targetDefault: string | null;\n}\n\nexport interface ColumnDiff {\n table: string;\n missingColumns: ColumnInfo[]; // source has, target doesn't\n extraColumns: ColumnInfo[]; // target has, source doesn't\n typeDiffs: ColumnTypeDiff[]; // same name, different type\n nullableDiffs: ColumnNullableDiff[]; // same name, different nullable\n defaultDiffs: ColumnDefaultDiff[]; // same name, different default\n}\n\nexport interface IndexDefDiff {\n name: string;\n sourceDef: string;\n targetDef: string;\n}\n\nexport interface IndexDiff {\n table: string;\n missingIndexes: string[]; // source has, target doesn't\n extraIndexes: string[]; // target has, source doesn't\n modifiedIndexes: IndexDefDiff[]; // same name, different definition\n}\n\nexport interface ConstraintDiff {\n table: string | null;\n type: \"missing\" | \"extra\" | \"modified\";\n name: string;\n detail: string;\n}\n\nexport interface EnumDiff {\n type: \"missing\" | \"extra\" | \"modified\";\n name: string;\n detail: string;\n}\n\nexport interface SchemaDiff {\n missingTables: string[];\n extraTables: string[];\n columnDiffs: ColumnDiff[];\n indexDiffs: IndexDiff[];\n constraintDiffs: ConstraintDiff[];\n enumDiffs: EnumDiff[];\n}\n\nexport interface HealthDiff {\n source: { score: number; grade: string; url: string };\n target: { score: number; grade: string; url: string };\n sourceOnlyIssues: string[];\n targetOnlyIssues: string[];\n}\n\nexport interface EnvDiffResult {\n schema: SchemaDiff;\n health?: HealthDiff;\n checkedAt: string;\n summary: {\n schemaDrifts: number;\n identical: boolean;\n };\n}\n\n// ----- internal types -----\n\ninterface RawColumn {\n table_name: string;\n column_name: string;\n data_type: string;\n is_nullable: string;\n column_default: string | null;\n}\n\ninterface RawIndex {\n tablename: string;\n indexname: string;\n indexdef: string;\n}\n\n// ----- query helpers -----\n\nasync function fetchTables(pool: Pool): Promise<string[]> {\n const res = await pool.query<{ table_name: string }>(`\n SELECT table_name\n FROM information_schema.tables\n WHERE table_schema = 'public' AND table_type = 'BASE TABLE'\n ORDER BY table_name\n `);\n return res.rows.map((r) => r.table_name);\n}\n\nasync function fetchColumns(pool: Pool): Promise<RawColumn[]> {\n const res = await pool.query<RawColumn>(`\n SELECT table_name, column_name, data_type, is_nullable, column_default\n FROM information_schema.columns\n WHERE table_schema = 'public'\n ORDER BY table_name, ordinal_position\n `);\n return res.rows;\n}\n\nasync function fetchIndexes(pool: Pool): Promise<RawIndex[]> {\n const res = await pool.query<RawIndex>(`\n SELECT tablename, indexname, indexdef\n FROM pg_indexes\n WHERE schemaname = 'public' AND indexname NOT LIKE '%_pkey'\n ORDER BY tablename, indexname\n `);\n return res.rows;\n}\n\n// ----- diff logic -----\n\nfunction diffTables(sourceTables: string[], targetTables: string[]): { missingTables: string[]; extraTables: string[] } {\n const sourceSet = new Set(sourceTables);\n const targetSet = new Set(targetTables);\n return {\n missingTables: sourceTables.filter((t) => !targetSet.has(t)),\n extraTables: targetTables.filter((t) => !sourceSet.has(t)),\n };\n}\n\nfunction groupColumnsByTable(columns: RawColumn[]): Map<string, Map<string, ColumnInfo>> {\n const map = new Map<string, Map<string, ColumnInfo>>();\n for (const col of columns) {\n if (!map.has(col.table_name)) map.set(col.table_name, new Map());\n const info: ColumnInfo = {\n name: col.column_name,\n type: col.data_type,\n nullable: col.is_nullable === \"YES\",\n };\n if (col.column_default !== null && col.column_default !== undefined) {\n info.default = col.column_default;\n }\n map.get(col.table_name)!.set(col.column_name, info);\n }\n return map;\n}\n\nfunction diffColumns(\n sourceCols: RawColumn[],\n targetCols: RawColumn[],\n commonTables: string[]\n): ColumnDiff[] {\n const sourceByTable = groupColumnsByTable(sourceCols);\n const targetByTable = groupColumnsByTable(targetCols);\n const diffs: ColumnDiff[] = [];\n\n for (const table of commonTables) {\n const srcMap = sourceByTable.get(table) ?? new Map<string, ColumnInfo>();\n const tgtMap = targetByTable.get(table) ?? new Map<string, ColumnInfo>();\n\n const missingColumns: ColumnInfo[] = [];\n const extraColumns: ColumnInfo[] = [];\n const typeDiffs: ColumnTypeDiff[] = [];\n const nullableDiffs: ColumnNullableDiff[] = [];\n const defaultDiffs: ColumnDefaultDiff[] = [];\n\n for (const [colName, srcInfo] of srcMap) {\n if (!tgtMap.has(colName)) {\n missingColumns.push(srcInfo);\n } else {\n const tgtInfo = tgtMap.get(colName)!;\n if (srcInfo.type !== tgtInfo.type) {\n typeDiffs.push({ column: colName, sourceType: srcInfo.type, targetType: tgtInfo.type });\n }\n if (srcInfo.nullable !== tgtInfo.nullable) {\n nullableDiffs.push({ column: colName, sourceNullable: srcInfo.nullable, targetNullable: tgtInfo.nullable });\n }\n if ((srcInfo.default ?? null) !== (tgtInfo.default ?? null)) {\n defaultDiffs.push({ column: colName, sourceDefault: srcInfo.default ?? null, targetDefault: tgtInfo.default ?? null });\n }\n }\n }\n\n for (const [colName, tgtInfo] of tgtMap) {\n if (!srcMap.has(colName)) {\n extraColumns.push(tgtInfo);\n }\n }\n\n if (missingColumns.length > 0 || extraColumns.length > 0 || typeDiffs.length > 0 ||\n nullableDiffs.length > 0 || defaultDiffs.length > 0) {\n diffs.push({ table, missingColumns, extraColumns, typeDiffs, nullableDiffs, defaultDiffs });\n }\n }\n\n return diffs;\n}\n\nfunction groupIndexesByTable(indexes: RawIndex[]): Map<string, Map<string, string>> {\n const map = new Map<string, Map<string, string>>();\n for (const idx of indexes) {\n if (!map.has(idx.tablename)) map.set(idx.tablename, new Map());\n map.get(idx.tablename)!.set(idx.indexname, idx.indexdef);\n }\n return map;\n}\n\nfunction diffIndexes(\n sourceIdxs: RawIndex[],\n targetIdxs: RawIndex[],\n commonTables: string[]\n): IndexDiff[] {\n const srcByTable = groupIndexesByTable(sourceIdxs);\n const tgtByTable = groupIndexesByTable(targetIdxs);\n const diffs: IndexDiff[] = [];\n\n // All tables that have any indexes in source or target\n const allTables = new Set([\n ...sourceIdxs.map((i) => i.tablename),\n ...targetIdxs.map((i) => i.tablename),\n ]);\n\n for (const table of allTables) {\n // Only diff tables that exist in both environments (common tables + tables not in either missingTables/extraTables)\n if (!commonTables.includes(table)) continue;\n\n const srcMap = srcByTable.get(table) ?? new Map<string, string>();\n const tgtMap = tgtByTable.get(table) ?? new Map<string, string>();\n\n const missingIndexes = [...srcMap.keys()].filter((i) => !tgtMap.has(i));\n const extraIndexes = [...tgtMap.keys()].filter((i) => !srcMap.has(i));\n const modifiedIndexes: IndexDefDiff[] = [];\n\n for (const [name, srcDef] of srcMap) {\n if (tgtMap.has(name)) {\n const tgtDef = tgtMap.get(name)!;\n if (srcDef !== tgtDef) {\n modifiedIndexes.push({ name, sourceDef: srcDef, targetDef: tgtDef });\n }\n }\n }\n\n if (missingIndexes.length > 0 || extraIndexes.length > 0 || modifiedIndexes.length > 0) {\n diffs.push({ table, missingIndexes, extraIndexes, modifiedIndexes });\n }\n }\n\n return diffs;\n}\n\nfunction countSchemaDrifts(schema: SchemaDiff): number {\n let n = schema.missingTables.length + schema.extraTables.length;\n for (const cd of schema.columnDiffs) {\n n += cd.missingColumns.length + cd.extraColumns.length + cd.typeDiffs.length +\n cd.nullableDiffs.length + cd.defaultDiffs.length;\n }\n for (const id of schema.indexDiffs) {\n n += id.missingIndexes.length + id.extraIndexes.length + id.modifiedIndexes.length;\n }\n n += (schema.constraintDiffs ?? []).length;\n n += (schema.enumDiffs ?? []).length;\n return n;\n}\n\n// ----- public API -----\n\nexport async function diffEnvironments(\n sourceConn: string,\n targetConn: string,\n options?: { includeHealth?: boolean }\n): Promise<EnvDiffResult> {\n const sourcePool = new Pool({ connectionString: sourceConn, connectionTimeoutMillis: 10000 });\n const targetPool = new Pool({ connectionString: targetConn, connectionTimeoutMillis: 10000 });\n\n try {\n // Run all schema queries in parallel (basic + deep snapshots for constraints/enums)\n const [\n sourceTables,\n targetTables,\n sourceCols,\n targetCols,\n sourceIdxs,\n targetIdxs,\n sourceSnap,\n targetSnap,\n ] = await Promise.all([\n fetchTables(sourcePool),\n fetchTables(targetPool),\n fetchColumns(sourcePool),\n fetchColumns(targetPool),\n fetchIndexes(sourcePool),\n fetchIndexes(targetPool),\n buildLiveSnapshot(sourcePool).catch(() => null),\n buildLiveSnapshot(targetPool).catch(() => null),\n ]);\n\n const { missingTables, extraTables } = diffTables(sourceTables, targetTables);\n const targetSet = new Set(targetTables);\n const commonTables = sourceTables.filter((t) => targetSet.has(t));\n\n const columnDiffs = diffColumns(sourceCols, targetCols, commonTables);\n const indexDiffs = diffIndexes(sourceIdxs, targetIdxs, commonTables);\n\n // Constraint + enum diffs via snapshot comparison\n const constraintDiffs: ConstraintDiff[] = [];\n const enumDiffs: EnumDiff[] = [];\n\n if (sourceSnap && targetSnap) {\n // diffSnapshots treats source as \"old\" and target as \"new\":\n // added = target has, source doesn't (extra in target)\n // removed = source has, target doesn't (missing in target)\n const snapChanges = diffSchemaSnapshots(sourceSnap, targetSnap);\n\n for (const c of snapChanges) {\n if (c.object_type === \"constraint\") {\n constraintDiffs.push({\n table: c.table_name ?? null,\n type: c.change_type === \"added\" ? \"extra\" : c.change_type === \"removed\" ? \"missing\" : \"modified\",\n name: c.detail.split(\" \")[1] ?? c.detail,\n detail: c.detail,\n });\n } else if (c.object_type === \"enum\") {\n enumDiffs.push({\n type: c.change_type === \"added\" ? \"extra\" : c.change_type === \"removed\" ? \"missing\" : \"modified\",\n name: c.detail.split(\" \")[1] ?? c.detail,\n detail: c.detail,\n });\n }\n }\n }\n\n const schema: SchemaDiff = { missingTables, extraTables, columnDiffs, indexDiffs, constraintDiffs, enumDiffs };\n const schemaDrifts = countSchemaDrifts(schema);\n\n let health: HealthDiff | undefined;\n\n if (options?.includeHealth) {\n const longQueryThreshold = 5;\n const [srcReport, tgtReport] = await Promise.all([\n getAdvisorReport(sourcePool, longQueryThreshold),\n getAdvisorReport(targetPool, longQueryThreshold),\n ]);\n\n const srcIssueKeys = new Set(srcReport.issues.map((i) => i.title));\n const tgtIssueKeys = new Set(tgtReport.issues.map((i) => i.title));\n\n const sourceOnlyIssues = srcReport.issues\n .filter((i) => !tgtIssueKeys.has(i.title))\n .map((i) => `${i.severity}: ${i.title}`);\n\n const targetOnlyIssues = tgtReport.issues\n .filter((i) => !srcIssueKeys.has(i.title))\n .map((i) => `${i.severity}: ${i.title}`);\n\n health = {\n source: { score: srcReport.score, grade: srcReport.grade, url: maskConnectionString(sourceConn) },\n target: { score: tgtReport.score, grade: tgtReport.grade, url: maskConnectionString(targetConn) },\n sourceOnlyIssues,\n targetOnlyIssues,\n };\n }\n\n return {\n schema,\n health,\n checkedAt: new Date().toISOString(),\n summary: {\n schemaDrifts,\n identical: schemaDrifts === 0,\n },\n };\n } finally {\n await Promise.allSettled([sourcePool.end(), targetPool.end()]);\n }\n}\n\n/** Mask password in a connection string to avoid leaking credentials */\nfunction maskConnectionString(connStr: string): string {\n try {\n const url = new URL(connStr);\n if (url.password) url.password = \"***\";\n return url.toString();\n } catch {\n return \"<redacted>\";\n }\n}\n\n// ----- formatters -----\n\nexport function formatTextDiff(result: EnvDiffResult): string {\n const lines: string[] = [];\n const sep = \"══════════════════════════════════════\";\n\n lines.push(`Environment Diff`);\n lines.push(sep);\n lines.push(``);\n lines.push(`Schema Drift:`);\n\n const { schema } = result;\n\n if (schema.missingTables.length > 0) {\n lines.push(` ✗ target missing tables: ${schema.missingTables.join(\", \")}`);\n }\n if (schema.extraTables.length > 0) {\n lines.push(` ⚠ target has extra tables: ${schema.extraTables.join(\", \")}`);\n }\n\n const missingCols: string[] = [];\n const extraCols: string[] = [];\n const typeChanges: string[] = [];\n\n for (const cd of schema.columnDiffs) {\n for (const col of cd.missingColumns) {\n missingCols.push(` ${cd.table}: ${col.name} (${col.type})`);\n }\n for (const col of cd.extraColumns) {\n extraCols.push(` ${cd.table}: ${col.name} (${col.type})`);\n }\n for (const td of cd.typeDiffs) {\n typeChanges.push(` ${cd.table}.${td.column}: ${td.sourceType} → ${td.targetType}`);\n }\n }\n\n if (missingCols.length > 0) {\n lines.push(` ✗ target missing columns:`);\n lines.push(...missingCols);\n }\n if (extraCols.length > 0) {\n lines.push(` ⚠ target has extra columns:`);\n lines.push(...extraCols);\n }\n if (typeChanges.length > 0) {\n lines.push(` ~ column type differences:`);\n lines.push(...typeChanges);\n }\n\n const nullableChanges: string[] = [];\n const defaultChanges: string[] = [];\n\n for (const cd of schema.columnDiffs) {\n for (const nd of cd.nullableDiffs) {\n const src = nd.sourceNullable ? \"nullable\" : \"NOT NULL\";\n const tgt = nd.targetNullable ? \"nullable\" : \"NOT NULL\";\n nullableChanges.push(` ${cd.table}.${nd.column}: source=${src} → target=${tgt}`);\n }\n for (const dd of cd.defaultDiffs) {\n const src = dd.sourceDefault ?? \"(none)\";\n const tgt = dd.targetDefault ?? \"(none)\";\n defaultChanges.push(` ${cd.table}.${dd.column}: source=${src} → target=${tgt}`);\n }\n }\n\n if (nullableChanges.length > 0) {\n lines.push(` ~ nullable differences:`);\n lines.push(...nullableChanges);\n }\n if (defaultChanges.length > 0) {\n lines.push(` ~ default differences:`);\n lines.push(...defaultChanges);\n }\n\n const missingIdxs: string[] = [];\n const extraIdxs: string[] = [];\n const modifiedIdxs: string[] = [];\n\n for (const id of schema.indexDiffs) {\n for (const idx of id.missingIndexes) {\n missingIdxs.push(` ${id.table}: ${idx}`);\n }\n for (const idx of id.extraIndexes) {\n extraIdxs.push(` ${id.table}: ${idx}`);\n }\n for (const mi of id.modifiedIndexes) {\n modifiedIdxs.push(` ${id.table}: ${mi.name} source=\"${mi.sourceDef}\" → target=\"${mi.targetDef}\"`);\n }\n }\n\n if (missingIdxs.length > 0) {\n lines.push(` ✗ target missing indexes:`);\n lines.push(...missingIdxs);\n }\n if (extraIdxs.length > 0) {\n lines.push(` ⚠ target has extra indexes:`);\n lines.push(...extraIdxs);\n }\n if (modifiedIdxs.length > 0) {\n lines.push(` ~ index definition differences:`);\n lines.push(...modifiedIdxs);\n }\n\n // Constraint diffs\n const missingConstraints = (schema.constraintDiffs ?? []).filter((c) => c.type === \"missing\");\n const extraConstraints = (schema.constraintDiffs ?? []).filter((c) => c.type === \"extra\");\n const modifiedConstraints = (schema.constraintDiffs ?? []).filter((c) => c.type === \"modified\");\n\n if (missingConstraints.length > 0) {\n lines.push(` ✗ target missing constraints:`);\n for (const c of missingConstraints) {\n lines.push(` ${c.table ? c.table + \": \" : \"\"}${c.detail}`);\n }\n }\n if (extraConstraints.length > 0) {\n lines.push(` ⚠ target has extra constraints:`);\n for (const c of extraConstraints) {\n lines.push(` ${c.table ? c.table + \": \" : \"\"}${c.detail}`);\n }\n }\n if (modifiedConstraints.length > 0) {\n lines.push(` ~ constraint differences:`);\n for (const c of modifiedConstraints) {\n lines.push(` ${c.table ? c.table + \": \" : \"\"}${c.detail}`);\n }\n }\n\n // Enum diffs\n const missingEnums = (schema.enumDiffs ?? []).filter((e) => e.type === \"missing\");\n const extraEnums = (schema.enumDiffs ?? []).filter((e) => e.type === \"extra\");\n const modifiedEnums = (schema.enumDiffs ?? []).filter((e) => e.type === \"modified\");\n\n if (missingEnums.length > 0) {\n lines.push(` ✗ target missing enums:`);\n for (const e of missingEnums) lines.push(` ${e.detail}`);\n }\n if (extraEnums.length > 0) {\n lines.push(` ⚠ target has extra enums:`);\n for (const e of extraEnums) lines.push(` ${e.detail}`);\n }\n if (modifiedEnums.length > 0) {\n lines.push(` ~ enum differences:`);\n for (const e of modifiedEnums) lines.push(` ${e.detail}`);\n }\n\n const noSchemaChanges = schema.missingTables.length === 0 && schema.extraTables.length === 0 &&\n schema.columnDiffs.length === 0 && schema.indexDiffs.length === 0 &&\n (schema.constraintDiffs ?? []).length === 0 && (schema.enumDiffs ?? []).length === 0 &&\n nullableChanges.length === 0 && defaultChanges.length === 0 && modifiedIdxs.length === 0;\n if (noSchemaChanges) {\n lines.push(` ✓ Schemas are identical`);\n }\n\n if (result.health) {\n const h = result.health;\n lines.push(``);\n lines.push(`Health Comparison:`);\n lines.push(` Source: ${h.source.score}/100 (${h.source.grade}) | Target: ${h.target.score}/100 (${h.target.grade})`);\n lines.push(` Source-only issues: ${h.sourceOnlyIssues.length === 0 ? \"(none)\" : \"\"}`);\n for (const iss of h.sourceOnlyIssues) lines.push(` - ${iss}`);\n lines.push(` Target-only issues: ${h.targetOnlyIssues.length === 0 ? \"(none)\" : \"\"}`);\n for (const iss of h.targetOnlyIssues) lines.push(` - ${iss}`);\n }\n\n lines.push(``);\n lines.push(sep);\n const { schemaDrifts, identical } = result.summary;\n lines.push(`Total: ${schemaDrifts} schema drift${schemaDrifts !== 1 ? \"s\" : \"\"} | Environments are ${identical ? \"in sync ✓\" : \"NOT in sync ✗\"}`);\n\n return lines.join(\"\\n\");\n}\n\nexport function formatMdDiff(result: EnvDiffResult): string {\n const lines: string[] = [];\n lines.push(`## 🔄 Environment Diff`);\n lines.push(``);\n lines.push(`### Schema Drift`);\n lines.push(``);\n\n const { schema } = result;\n const rows: Array<[string, string]> = [];\n\n if (schema.missingTables.length > 0) {\n rows.push([`❌ Missing tables`, schema.missingTables.map((t) => `\\`${t}\\``).join(\", \")]);\n }\n if (schema.extraTables.length > 0) {\n rows.push([`⚠️ Extra tables`, schema.extraTables.map((t) => `\\`${t}\\``).join(\", \")]);\n }\n\n const missingColItems: string[] = [];\n const extraColItems: string[] = [];\n const typeItems: string[] = [];\n\n for (const cd of schema.columnDiffs) {\n for (const col of cd.missingColumns) {\n missingColItems.push(`\\`${cd.table}.${col.name}\\``);\n }\n for (const col of cd.extraColumns) {\n extraColItems.push(`\\`${cd.table}.${col.name}\\``);\n }\n for (const td of cd.typeDiffs) {\n typeItems.push(`\\`${cd.table}.${td.column}\\` (${td.sourceType}→${td.targetType})`);\n }\n }\n\n if (missingColItems.length > 0) rows.push([`❌ Missing columns`, missingColItems.join(\", \")]);\n if (extraColItems.length > 0) rows.push([`⚠️ Extra columns`, extraColItems.join(\", \")]);\n if (typeItems.length > 0) rows.push([`~ Type differences`, typeItems.join(\", \")]);\n\n const nullableItems: string[] = [];\n const defaultItems: string[] = [];\n\n for (const cd of schema.columnDiffs) {\n for (const nd of cd.nullableDiffs) {\n const src = nd.sourceNullable ? \"nullable\" : \"NOT NULL\";\n const tgt = nd.targetNullable ? \"nullable\" : \"NOT NULL\";\n nullableItems.push(`\\`${cd.table}.${nd.column}\\` (${src}→${tgt})`);\n }\n for (const dd of cd.defaultDiffs) {\n const src = dd.sourceDefault ?? \"(none)\";\n const tgt = dd.targetDefault ?? \"(none)\";\n defaultItems.push(`\\`${cd.table}.${dd.column}\\` (${src}→${tgt})`);\n }\n }\n\n if (nullableItems.length > 0) rows.push([`~ Nullable differences`, nullableItems.join(\", \")]);\n if (defaultItems.length > 0) rows.push([`~ Default differences`, defaultItems.join(\", \")]);\n\n const missingIdxItems: string[] = [];\n const extraIdxItems: string[] = [];\n const modifiedIdxItems: string[] = [];\n\n for (const id of schema.indexDiffs) {\n for (const idx of id.missingIndexes) missingIdxItems.push(`\\`${id.table}.${idx}\\``);\n for (const idx of id.extraIndexes) extraIdxItems.push(`\\`${id.table}.${idx}\\``);\n for (const mi of id.modifiedIndexes) modifiedIdxItems.push(`\\`${id.table}.${mi.name}\\``);\n }\n\n if (missingIdxItems.length > 0) rows.push([`❌ Missing indexes`, missingIdxItems.join(\", \")]);\n if (extraIdxItems.length > 0) rows.push([`⚠️ Extra indexes`, extraIdxItems.join(\", \")]);\n if (modifiedIdxItems.length > 0) rows.push([`~ Modified indexes`, modifiedIdxItems.join(\", \")]);\n\n // Constraints\n const missingConItems = (schema.constraintDiffs ?? []).filter((c) => c.type === \"missing\").map((c) => c.detail);\n const extraConItems = (schema.constraintDiffs ?? []).filter((c) => c.type === \"extra\").map((c) => c.detail);\n const modConItems = (schema.constraintDiffs ?? []).filter((c) => c.type === \"modified\").map((c) => c.detail);\n if (missingConItems.length > 0) rows.push([`❌ Missing constraints`, missingConItems.join(\"; \")]);\n if (extraConItems.length > 0) rows.push([`⚠️ Extra constraints`, extraConItems.join(\"; \")]);\n if (modConItems.length > 0) rows.push([`~ Modified constraints`, modConItems.join(\"; \")]);\n\n // Enums\n const missingEnumItems = (schema.enumDiffs ?? []).filter((e) => e.type === \"missing\").map((e) => e.detail);\n const extraEnumItems = (schema.enumDiffs ?? []).filter((e) => e.type === \"extra\").map((e) => e.detail);\n const modEnumItems = (schema.enumDiffs ?? []).filter((e) => e.type === \"modified\").map((e) => e.detail);\n if (missingEnumItems.length > 0) rows.push([`❌ Missing enums`, missingEnumItems.join(\"; \")]);\n if (extraEnumItems.length > 0) rows.push([`⚠️ Extra enums`, extraEnumItems.join(\"; \")]);\n if (modEnumItems.length > 0) rows.push([`~ Modified enums`, modEnumItems.join(\"; \")]);\n\n if (rows.length > 0) {\n lines.push(`| Type | Details |`);\n lines.push(`|------|---------|`);\n for (const [type, details] of rows) {\n lines.push(`| ${type} | ${details} |`);\n }\n } else {\n lines.push(`✅ Schemas are identical`);\n }\n\n if (result.health) {\n const h = result.health;\n lines.push(``);\n lines.push(`### Health Comparison`);\n lines.push(``);\n lines.push(`| | Score | Grade |`);\n lines.push(`|--|-------|-------|`);\n lines.push(`| Source | ${h.source.score}/100 | ${h.source.grade} |`);\n lines.push(`| Target | ${h.target.score}/100 | ${h.target.grade} |`);\n\n if (h.targetOnlyIssues.length > 0) {\n lines.push(``);\n lines.push(`**Target-only issues:**`);\n for (const iss of h.targetOnlyIssues) lines.push(`- ${iss}`);\n }\n if (h.sourceOnlyIssues.length > 0) {\n lines.push(``);\n lines.push(`**Source-only issues:**`);\n for (const iss of h.sourceOnlyIssues) lines.push(`- ${iss}`);\n }\n }\n\n lines.push(``);\n const { schemaDrifts, identical } = result.summary;\n lines.push(`**Result: ${schemaDrifts} drift${schemaDrifts !== 1 ? \"s\" : \"\"} — environments are ${identical ? \"in sync ✓\" : \"NOT in sync\"}**`);\n\n return lines.join(\"\\n\");\n}\n","import { parseArgs } from \"node:util\";\nimport { startServer } from \"./server/index.js\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nprocess.on(\"uncaughtException\", (err) => {\n console.error(\"Uncaught exception:\", err);\n});\nprocess.on(\"unhandledRejection\", (err) => {\n console.error(\"Unhandled rejection:\", err);\n});\n\nconst { values, positionals } = parseArgs({\n allowPositionals: true,\n options: {\n port: { type: \"string\", short: \"p\", default: \"3480\" },\n bind: { type: \"string\", default: \"127.0.0.1\" },\n auth: { type: \"string\" },\n token: { type: \"string\" },\n webhook: { type: \"string\" },\n \"slack-webhook\": { type: \"string\" },\n \"discord-webhook\": { type: \"string\" },\n \"no-open\": { type: \"boolean\", default: false },\n json: { type: \"boolean\", default: false },\n host: { type: \"string\" },\n user: { type: \"string\", short: \"u\" },\n password: { type: \"string\" },\n db: { type: \"string\", short: \"d\" },\n \"pg-port\": { type: \"string\" },\n \"data-dir\": { type: \"string\" },\n interval: { type: \"string\", short: \"i\" },\n \"retention-days\": { type: \"string\" },\n \"snapshot-interval\": { type: \"string\" },\n \"query-stats-interval\": { type: \"string\" },\n \"long-query-threshold\": { type: \"string\" },\n help: { type: \"boolean\", short: \"h\" },\n version: { type: \"boolean\", short: \"v\" },\n threshold: { type: \"string\" },\n format: { type: \"string\", short: \"f\" },\n ci: { type: \"boolean\", default: false },\n diff: { type: \"boolean\", default: false },\n \"snapshot-path\": { type: \"string\" },\n source: { type: \"string\" },\n target: { type: \"string\" },\n health: { type: \"boolean\", default: false },\n },\n});\n\nif (values.version) {\n try {\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n const pkg = JSON.parse(fs.readFileSync(path.resolve(__dirname, \"../package.json\"), \"utf-8\"));\n console.log(`pg-dash v${pkg.version}`);\n } catch {\n console.log(\"pg-dash v0.1.0\");\n }\n process.exit(0);\n}\n\nif (values.help) {\n console.log(`\npg-dash — Lightweight PostgreSQL Monitoring Dashboard\n\nUsage:\n pg-dash <connection-string> Start dashboard\n pg-dash check <connection-string> Run health check and exit\n pg-dash check-migration <file> [connection] Analyze migration SQL for risks\n pg-dash diff-env --source <url> --target <url> Compare two environments\n pg-dash schema-diff <connection-string> Show latest schema changes\n pg-dash --host localhost --user postgres --db mydb\n\nOptions:\n -p, --port <port> Dashboard port (default: 3480)\n --bind <addr> Bind address (default: 127.0.0.1)\n --auth <user:pass> Basic auth credentials (user:password)\n --token <token> Bearer token for authentication\n --webhook <url> Webhook URL for alert notifications\n --slack-webhook <url> Slack webhook URL (convenience alias)\n --discord-webhook <url> Discord webhook URL (convenience alias)\n --no-open Don't auto-open browser (default: opens)\n --json Dump health check as JSON and exit\n --host <host> PostgreSQL host\n -u, --user <user> PostgreSQL user\n --password <pass> PostgreSQL password\n --db, -d <database> PostgreSQL database\n --pg-port <port> PostgreSQL port (default: 5432)\n --data-dir <dir> Data directory for metrics (default: ~/.pg-dash)\n -i, --interval <sec> Collection interval in seconds (default: 30)\n --retention-days <N> Metrics retention in days (default: 7)\n --snapshot-interval <h> Schema snapshot interval in hours (default: 6)\n --query-stats-interval <min> Query stats snapshot interval in minutes (default: 5)\n --long-query-threshold <min> Long query threshold in minutes (default: 5)\n --threshold <score> Health score threshold for check command (default: 70)\n -f, --format <fmt> Output format: text|json|md (default: text)\n --ci Output GitHub Actions compatible annotations\n --diff Compare with previous run (saves snapshot for next run)\n --snapshot-path <path> Path to snapshot file for --diff (default: ~/.pg-dash/last-check.json)\n --source <url> Source database connection string (diff-env)\n --target <url> Target database connection string (diff-env)\n --health Also compare health scores and issues (diff-env)\n -v, --version Show version\n -h, --help Show this help\n\nEnvironment variables:\n PG_DASH_RETENTION_DAYS, PG_DASH_SNAPSHOT_INTERVAL, PG_DASH_LONG_QUERY_THRESHOLD\n`);\n process.exit(0);\n}\n\nconst subcommand = positionals[0];\n\nfunction resolveConnectionString(startIdx = 0): string {\n let connStr = positionals[startIdx];\n if (!connStr) {\n if (values.host) {\n const user = values.user || \"postgres\";\n const pass = values.password ? `:${values.password}` : \"\";\n const host = values.host;\n const pgPort = values[\"pg-port\"] || \"5432\";\n const db = values.db || \"postgres\";\n connStr = `postgresql://${user}${pass}@${host}:${pgPort}/${db}`;\n } else {\n console.error(\"Error: provide a connection string or --host\\n\\nRun pg-dash --help for usage.\");\n process.exit(1);\n }\n }\n return connStr;\n}\n\nif (subcommand === \"check\") {\n // Health check mode\n const connectionString = resolveConnectionString(1);\n const threshold = parseInt(values.threshold || \"70\", 10);\n const format = values.format || \"text\";\n const ci = values.ci || false;\n const useDiff = values.diff || false;\n\n const { Pool } = await import(\"pg\");\n const { getAdvisorReport } = await import(\"./server/advisor.js\");\n const { saveSnapshot, loadSnapshot, diffSnapshots } = await import(\"./server/snapshot.js\");\n const os = await import(\"node:os\");\n\n const pool = new Pool({ connectionString, connectionTimeoutMillis: 10000 });\n const checkDataDir = values[\"data-dir\"] || path.join(os.homedir(), \".pg-dash\");\n // --snapshot-path lets CI persist the snapshot across ephemeral runners via cache\n const snapshotPath = values[\"snapshot-path\"] || path.join(checkDataDir, \"last-check.json\");\n\n try {\n const lqt = parseInt(values[\"long-query-threshold\"] || process.env.PG_DASH_LONG_QUERY_THRESHOLD || \"5\", 10);\n const report = await getAdvisorReport(pool, lqt);\n\n // Diff logic\n let diff: import(\"./server/snapshot.js\").SnapshotDiff | null = null;\n if (useDiff) {\n const prev = loadSnapshot(snapshotPath);\n if (prev) {\n diff = diffSnapshots(prev.result, report);\n }\n saveSnapshot(snapshotPath, report);\n }\n\n if (format === \"json\") {\n const output: any = { ...report };\n if (diff) output.diff = diff;\n console.log(JSON.stringify(output, null, 2));\n } else if (format === \"md\" || (ci && format !== \"text\")) {\n // Markdown report (for CI PR comments)\n console.log(`## 🏥 pg-dash Health Report\\n`);\n if (diff) {\n const sign = diff.scoreDelta >= 0 ? \"+\" : \"\";\n console.log(`**Score: ${diff.previousScore} → ${report.score} (${sign}${diff.scoreDelta})**\\n`);\n } else {\n console.log(`**Score: ${report.score}/100 (${report.grade})**\\n`);\n }\n console.log(`| Category | Score | Grade | Issues |`);\n console.log(`|----------|-------|-------|--------|`);\n for (const [cat, b] of Object.entries(report.breakdown)) {\n console.log(`| ${cat} | ${b.score} | ${b.grade} | ${b.count} |`);\n }\n if (diff) {\n if (diff.resolvedIssues.length > 0) {\n console.log(`\\n### ✅ Resolved (${diff.resolvedIssues.length})`);\n for (const i of diff.resolvedIssues) console.log(`- ~~${i.title}~~`);\n }\n if (diff.newIssues.length > 0) {\n console.log(`\\n### 🆕 New Issues (${diff.newIssues.length})`);\n for (const i of diff.newIssues) {\n const icon = i.severity === \"critical\" ? \"🔴\" : i.severity === \"warning\" ? \"🟡\" : \"🔵\";\n console.log(`- ${icon} [${i.severity}] ${i.title}`);\n }\n }\n }\n if (report.issues.length > 0) {\n console.log(`\\n### ⚠️ Issues (${report.issues.length})\\n`);\n for (const issue of report.issues) {\n const sev = issue.severity === \"critical\" ? \"error\" : issue.severity === \"warning\" ? \"warning\" : \"notice\";\n console.log(`- [${sev}] ${issue.title}`);\n }\n } else {\n console.log(`\\n✅ No issues found!`);\n }\n if (report.batchFixes.length > 0) {\n console.log(`\\n### 🔧 Batch Fixes\\n`);\n console.log(\"```sql\");\n for (const fix of report.batchFixes) {\n console.log(`-- ${fix.title}`);\n console.log(fix.sql);\n }\n console.log(\"```\");\n }\n } else if (ci) {\n // GitHub Actions annotations\n for (const issue of report.issues) {\n const level = issue.severity === \"critical\" ? \"error\" : issue.severity === \"warning\" ? \"warning\" : \"notice\";\n console.log(`::${level}::${issue.title}: ${issue.description}`);\n }\n // Summary table\n console.log(`\\nHealth Score: ${report.score}/100 (${report.grade})`);\n for (const [cat, b] of Object.entries(report.breakdown)) {\n console.log(` ${cat.padEnd(14)} ${b.grade} (${b.score}/100) — ${b.count} issue${b.count !== 1 ? \"s\" : \"\"}`);\n }\n if (diff) {\n const sign = diff.scoreDelta >= 0 ? \"+\" : \"\";\n console.log(`\\nScore: ${diff.previousScore} → ${report.score} (${sign}${diff.scoreDelta})`);\n console.log(`Resolved: ${diff.resolvedIssues.length} issues`);\n console.log(`New: ${diff.newIssues.length} issues`);\n }\n } else {\n // Plain text\n if (diff) {\n const sign = diff.scoreDelta >= 0 ? \"+\" : \"\";\n console.log(`\\n Score: ${diff.previousScore} → ${report.score} (${sign}${diff.scoreDelta})\\n`);\n if (diff.resolvedIssues.length > 0) {\n console.log(` ✅ Resolved: ${diff.resolvedIssues.length} issues`);\n for (const i of diff.resolvedIssues) console.log(` - ${i.title}`);\n }\n if (diff.newIssues.length > 0) {\n console.log(` 🆕 New: ${diff.newIssues.length} issues`);\n for (const i of diff.newIssues) console.log(` - ${i.title}`);\n }\n console.log();\n } else {\n console.log(`\\n Health Score: ${report.score}/100 (Grade: ${report.grade})\\n`);\n }\n for (const [cat, b] of Object.entries(report.breakdown)) {\n console.log(` ${cat.padEnd(14)} ${b.grade} (${b.score}/100) — ${b.count} issue${b.count !== 1 ? \"s\" : \"\"}`);\n }\n if (report.issues.length > 0) {\n console.log(`\\n Issues (${report.issues.length}):\\n`);\n for (const issue of report.issues) {\n const icon = issue.severity === \"critical\" ? \"🔴\" : issue.severity === \"warning\" ? \"🟡\" : \"🔵\";\n console.log(` ${icon} [${issue.severity}] ${issue.title}`);\n }\n }\n console.log();\n }\n await pool.end();\n process.exit(report.score < threshold ? 1 : 0);\n } catch (err: any) {\n console.error(`Error: ${err.message}`);\n await pool.end();\n process.exit(1);\n }\n} else if (subcommand === \"check-migration\") {\n // Migration safety check mode\n // Usage: pg-dash check-migration <file> [connection] [--ci] [-f json|text|md]\n const filePath = positionals[1];\n if (!filePath) {\n console.error(\"Error: provide a migration SQL file path.\\n\\nUsage: pg-dash check-migration <file> [connection]\");\n process.exit(1);\n }\n\n if (!fs.existsSync(filePath)) {\n console.error(`Error: File not found: ${filePath}`);\n process.exit(1);\n }\n\n const sql = fs.readFileSync(filePath, \"utf-8\");\n\n // Optional connection string (third positional arg)\n const migrationConn = positionals[2];\n const format = values.format || \"text\";\n const ci = values.ci || false;\n\n const { analyzeMigration } = await import(\"./server/migration-checker.js\");\n\n let pool: import(\"pg\").Pool | undefined;\n if (migrationConn) {\n const { Pool } = await import(\"pg\");\n pool = new Pool({ connectionString: migrationConn, connectionTimeoutMillis: 10000 });\n }\n\n try {\n const result = await analyzeMigration(sql, pool);\n if (pool) await pool.end();\n\n const sep = \"─\".repeat(48);\n\n if (format === \"json\") {\n console.log(JSON.stringify(result, null, 2));\n } else if (format === \"md\") {\n console.log(\"## 🔍 Migration Safety Check\\n\");\n console.log(\"| Severity | Code | Message |\");\n console.log(\"|----------|------|---------|\");\n for (const issue of result.issues) {\n const sev =\n issue.severity === \"error\"\n ? \"🔴 ERROR\"\n : issue.severity === \"warning\"\n ? \"⚠️ WARNING\"\n : \"ℹ️ INFO\";\n console.log(`| ${sev} | ${issue.code} | ${issue.message} |`);\n }\n const { errors, warnings, infos } = result.summary;\n const safeLabel = result.safe ? \"✅ SAFE\" : \"❌ UNSAFE\";\n console.log(`\\n**Result: ${safeLabel} — ${errors} error${errors !== 1 ? \"s\" : \"\"}, ${warnings} warning${warnings !== 1 ? \"s\" : \"\"}, ${infos} info${infos !== 1 ? \"s\" : \"\"}**`);\n } else {\n // Text format\n console.log(`\\nMigration check: ${filePath}`);\n console.log(sep);\n if (result.issues.length === 0) {\n console.log(\"\\n ✅ No issues found!\\n\");\n } else {\n for (const issue of result.issues) {\n const icon =\n issue.severity === \"error\" ? \"✗\" : issue.severity === \"warning\" ? \"⚠\" : \"✓\";\n const indent = \" \";\n const parts = [`${indent}${icon} ${issue.message}`];\n if (issue.suggestion) parts.push(`${indent} Suggestion: ${issue.suggestion}`);\n if (issue.estimatedRows !== undefined) {\n parts.push(\n `${indent} Est. rows: ${issue.estimatedRows.toLocaleString()}` +\n (issue.estimatedLockSeconds !== undefined\n ? `, lock ~${issue.estimatedLockSeconds}s`\n : \"\")\n );\n }\n if (issue.lineNumber !== undefined) parts.push(`${indent} Line ${issue.lineNumber}`);\n console.log(parts.join(\"\\n\") + \"\\n\");\n }\n }\n console.log(sep);\n const { errors, warnings, infos } = result.summary;\n const safeLabel = result.safe ? \"SAFE\" : \"UNSAFE\";\n console.log(\n `Result: ${safeLabel} — ${errors} error${errors !== 1 ? \"s\" : \"\"}, ${warnings} warning${warnings !== 1 ? \"s\" : \"\"}, ${infos} info${infos !== 1 ? \"s\" : \"\"}\\n`\n );\n if (!migrationConn) {\n console.log(\"Run with a connection string for more accurate row count estimates.\\n\");\n }\n }\n\n // --ci annotations\n if (ci) {\n for (const issue of result.issues) {\n const level = issue.severity === \"error\" ? \"error\" : issue.severity === \"warning\" ? \"warning\" : \"notice\";\n const loc = issue.lineNumber ? `,line=${issue.lineNumber}` : \"\";\n const file = `file=${filePath}${loc}`;\n console.log(`::${level} ${file}::${issue.message}`);\n }\n }\n\n process.exit(result.safe ? 0 : 1);\n } catch (err: any) {\n if (pool) await pool.end().catch(() => {});\n console.error(`Error: ${err.message}`);\n process.exit(1);\n }\n} else if (subcommand === \"schema-diff\") {\n // Schema diff mode\n const connectionString = resolveConnectionString(1);\n const format = values.format || \"text\";\n const dataDir = values[\"data-dir\"] || path.join((await import(\"node:os\")).homedir(), \".pg-dash\");\n const schemaDbPath = path.join(dataDir, \"schema.db\");\n\n if (!fs.existsSync(schemaDbPath)) {\n console.error(\"No schema tracking data found. Run pg-dash server first to collect schema snapshots.\");\n process.exit(1);\n }\n\n const Database = (await import(\"better-sqlite3\")).default;\n const db = new Database(schemaDbPath, { readonly: true });\n const changes = db.prepare(\"SELECT * FROM schema_changes ORDER BY timestamp DESC LIMIT 50\").all() as any[];\n db.close();\n\n if (format === \"json\") {\n console.log(JSON.stringify(changes.map((c) => ({\n type: c.change_type,\n objectType: c.object_type,\n objectName: c.object_name,\n tableName: c.table_name,\n detail: c.detail,\n timestamp: c.timestamp,\n })), null, 2));\n } else {\n if (changes.length === 0) {\n console.log(\"No schema changes detected.\");\n } else {\n console.log(`\\n Schema Changes (${changes.length}):\\n`);\n for (const c of changes) {\n const icon = c.change_type === \"added\" ? \"+\" : c.change_type === \"removed\" ? \"−\" : \"~\";\n const color = c.change_type === \"added\" ? \"\\x1b[32m\" : c.change_type === \"removed\" ? \"\\x1b[31m\" : \"\\x1b[33m\";\n console.log(` ${color}${icon}\\x1b[0m ${c.detail}${c.table_name ? ` (${c.table_name})` : \"\"} — ${new Date(c.timestamp).toLocaleString()}`);\n }\n console.log();\n }\n }\n process.exit(0);\n} else if (subcommand === \"diff-env\") {\n // Multi-environment schema + health diff\n const sourceUrl = values.source;\n const targetUrl = values.target;\n if (!sourceUrl || !targetUrl) {\n console.error(\"Error: diff-env requires --source <url> and --target <url>\");\n process.exit(1);\n }\n const format = values.format || \"text\";\n const includeHealth = values.health || false;\n const ci = values.ci || false;\n\n const { diffEnvironments, formatTextDiff, formatMdDiff } = await import(\"./server/env-differ.js\");\n\n try {\n const result = await diffEnvironments(sourceUrl, targetUrl, { includeHealth });\n\n if (format === \"json\") {\n console.log(JSON.stringify(result, null, 2));\n } else if (format === \"md\") {\n console.log(formatMdDiff(result));\n } else {\n // text (default)\n const text = formatTextDiff(result);\n console.log(text);\n if (ci) {\n // GitHub Actions annotations — severity matches impact\n for (const t of result.schema.missingTables) {\n console.log(`::error::diff-env: target missing table: ${t}`);\n }\n for (const t of result.schema.extraTables) {\n console.log(`::notice::diff-env: target has extra table: ${t}`);\n }\n for (const cd of result.schema.columnDiffs) {\n for (const col of cd.missingColumns) {\n console.log(`::error::diff-env: target missing column: ${cd.table}.${col.name} (${col.type})`);\n }\n for (const col of cd.extraColumns) {\n console.log(`::notice::diff-env: target has extra column: ${cd.table}.${col.name} (${col.type})`);\n }\n for (const td of cd.typeDiffs) {\n console.log(`::error::diff-env: type mismatch: ${cd.table}.${td.column} ${td.sourceType}→${td.targetType}`);\n }\n }\n for (const id of result.schema.indexDiffs) {\n for (const idx of id.missingIndexes) {\n console.log(`::warning::diff-env: target missing index: ${id.table}.${idx}`);\n }\n for (const idx of id.extraIndexes) {\n console.log(`::notice::diff-env: target has extra index: ${id.table}.${idx}`);\n }\n }\n for (const c of result.schema.constraintDiffs ?? []) {\n const level = c.type === \"missing\" ? \"error\" : c.type === \"extra\" ? \"notice\" : \"warning\";\n console.log(`::${level}::diff-env: constraint ${c.type}: ${c.detail}`);\n }\n for (const e of result.schema.enumDiffs ?? []) {\n const level = e.type === \"missing\" ? \"error\" : e.type === \"extra\" ? \"notice\" : \"warning\";\n console.log(`::${level}::diff-env: enum ${e.type}: ${e.detail}`);\n }\n }\n }\n\n process.exit(result.summary.identical ? 0 : 1);\n } catch (err: any) {\n console.error(`Error: ${err.message}`);\n process.exit(1);\n }\n} else {\n // Default: start server\n const connectionString = resolveConnectionString(0);\n const port = parseInt(values.port!, 10);\n const bind = values.bind || process.env.PG_DASH_BIND || \"127.0.0.1\";\n const interval = values.interval ? parseInt(values.interval, 10) : undefined;\n const retentionDays = parseInt(values[\"retention-days\"] || process.env.PG_DASH_RETENTION_DAYS || \"7\", 10);\n const snapshotInterval = parseInt(values[\"snapshot-interval\"] || process.env.PG_DASH_SNAPSHOT_INTERVAL || \"6\", 10);\n const queryStatsInterval = parseInt(values[\"query-stats-interval\"] || process.env.PG_DASH_QUERY_STATS_INTERVAL || \"5\", 10);\n const longQueryThreshold = parseInt(values[\"long-query-threshold\"] || process.env.PG_DASH_LONG_QUERY_THRESHOLD || \"5\", 10);\n const auth = values.auth || undefined;\n const token = values.token || undefined;\n const webhook = values[\"slack-webhook\"] || values[\"discord-webhook\"] || values.webhook || undefined;\n\n // Security warning\n if (bind === \"0.0.0.0\" && !auth && !token) {\n console.warn(\"\\n ⚠️ WARNING: Dashboard is exposed without authentication. Use --auth or --token.\\n\");\n }\n\n await startServer({\n connectionString,\n port,\n bind,\n open: !values[\"no-open\"],\n json: values.json!,\n dataDir: values[\"data-dir\"],\n interval,\n retentionDays,\n snapshotInterval,\n queryStatsInterval,\n longQueryThreshold,\n auth,\n token,\n webhook,\n });\n}\n","import { Hono } from \"hono\";\nimport path from \"node:path\";\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport { fileURLToPath } from \"node:url\";\nimport { Pool } from \"pg\";\nimport { getOverview } from \"./queries/overview.js\";\nimport { getDatabases } from \"./queries/databases.js\";\nimport { getTables } from \"./queries/tables.js\";\nimport { getActivity } from \"./queries/activity.js\";\nimport { getAdvisorReport } from \"./advisor.js\";\nimport { TimeseriesStore } from \"./timeseries.js\";\nimport { Collector } from \"./collector.js\";\nimport { SchemaTracker } from \"./schema-tracker.js\";\nimport { AlertManager } from \"./alerts.js\";\nimport { registerOverviewRoutes } from \"./routes/overview.js\";\nimport { registerMetricsRoutes } from \"./routes/metrics.js\";\nimport { registerActivityRoutes } from \"./routes/activity.js\";\nimport { registerAdvisorRoutes } from \"./routes/advisor.js\";\nimport { registerSchemaRoutes } from \"./routes/schema.js\";\nimport { registerAlertsRoutes } from \"./routes/alerts.js\";\nimport { registerExplainRoutes } from \"./routes/explain.js\";\nimport { registerDiskRoutes } from \"./routes/disk.js\";\nimport { QueryStatsStore } from \"./query-stats.js\";\nimport { registerQueryStatsRoutes } from \"./routes/query-stats.js\";\nimport { registerExportRoutes } from \"./routes/export.js\";\nimport { DiskPredictor } from \"./disk-prediction.js\";\nimport Database from \"better-sqlite3\";\nimport { WebSocketServer, WebSocket } from \"ws\";\nimport http from \"node:http\";\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\ninterface ServerOptions {\n connectionString: string;\n port: number;\n bind?: string;\n open: boolean;\n json: boolean;\n dataDir?: string;\n interval?: number;\n retentionDays?: number;\n snapshotInterval?: number;\n queryStatsInterval?: number;\n longQueryThreshold?: number;\n auth?: string;\n token?: string;\n webhook?: string;\n}\n\nexport async function startServer(opts: ServerOptions) {\n const pool = new Pool({ connectionString: opts.connectionString, connectionTimeoutMillis: 10000 });\n\n // Test connection\n try {\n const client = await pool.connect();\n client.release();\n } catch (err: any) {\n console.error(`Failed to connect to PostgreSQL: ${err.message}`);\n process.exit(1);\n }\n\n const longQueryThreshold = opts.longQueryThreshold || 5;\n const diskPredictor = new DiskPredictor();\n\n // JSON mode: dump health and exit\n if (opts.json) {\n try {\n const [overview, advisor, databases, tables] = await Promise.all([\n getOverview(pool),\n getAdvisorReport(pool, longQueryThreshold),\n getDatabases(pool),\n getTables(pool),\n ]);\n console.log(JSON.stringify({ overview, advisor, databases, tables }, null, 2));\n } catch (err: any) {\n console.error(JSON.stringify({ error: err.message }));\n process.exit(1);\n }\n await pool.end();\n process.exit(0);\n }\n\n const dataDir = opts.dataDir || path.join(os.homedir(), \".pg-dash\");\n fs.mkdirSync(dataDir, { recursive: true });\n\n // Initialize shared metrics database\n const metricsDbPath = path.join(dataDir, \"metrics.db\");\n const metricsDb = new Database(metricsDbPath);\n metricsDb.pragma(\"journal_mode = WAL\");\n\n // Initialize time-series store and collector\n const store = new TimeseriesStore(metricsDb, opts.retentionDays);\n const intervalMs = (opts.interval || 30) * 1000;\n const collector = new Collector(pool, store, intervalMs);\n\n console.log(` Collecting metrics every ${(intervalMs / 1000)}s...`);\n collector.start();\n\n // Initialize schema tracker\n const schemaDbPath = path.join(dataDir, \"schema.db\");\n const schemaDb = new Database(schemaDbPath);\n schemaDb.pragma(\"journal_mode = WAL\");\n const snapshotIntervalMs = (opts.snapshotInterval || 6) * 60 * 60 * 1000;\n const schemaTracker = new SchemaTracker(schemaDb, pool, snapshotIntervalMs);\n schemaTracker.start();\n console.log(\" Schema change tracking enabled\");\n\n // Initialize alerts\n const alertsDbPath = path.join(dataDir, \"alerts.db\");\n const alertsDb = new Database(alertsDbPath);\n alertsDb.pragma(\"journal_mode = WAL\");\n const alertManager = new AlertManager(alertsDb, opts.webhook);\n console.log(\" Alert monitoring enabled\");\n\n // Initialize query stats store (shares metricsDb)\n const queryStatsStore = new QueryStatsStore(metricsDb, opts.retentionDays);\n const querySnapshotIntervalMs = (opts.queryStatsInterval || 5) * 60 * 1000;\n queryStatsStore.startPeriodicSnapshot(pool, querySnapshotIntervalMs);\n console.log(` Query stats snapshots every ${querySnapshotIntervalMs / 60000}m`);\n\n const app = new Hono();\n\n // Auth endpoint for cookie-based auth (must be before auth middleware)\n if (opts.token) {\n app.post(\"/api/auth\", async (c) => {\n try {\n const body = await c.req.json();\n if (body?.token === opts.token) {\n c.header(\"Set-Cookie\", `pg-dash-token=${opts.token}; Path=/; HttpOnly; SameSite=Strict; Max-Age=86400`);\n return c.json({ ok: true });\n }\n return c.json({ error: \"Invalid token\" }, 401);\n } catch {\n return c.json({ error: \"Invalid request\" }, 400);\n }\n });\n }\n\n // Auth middleware\n if (opts.auth || opts.token) {\n app.use(\"*\", async (c, next) => {\n const authHeader = c.req.header(\"authorization\") || \"\";\n if (opts.token) {\n if (authHeader === `Bearer ${opts.token}`) return next();\n }\n if (opts.auth) {\n const [user, pass] = opts.auth.split(\":\");\n const expected = \"Basic \" + Buffer.from(`${user}:${pass}`).toString(\"base64\");\n if (authHeader === expected) return next();\n }\n // Check query param token for WebSocket upgrade compatibility\n const url = new URL(c.req.url, \"http://localhost\");\n if (opts.token && url.searchParams.get(\"token\") === opts.token) return next();\n\n // Check cookie for token auth\n if (opts.token) {\n const cookies = c.req.header(\"cookie\") || \"\";\n const match = cookies.match(/(?:^|;\\s*)pg-dash-token=([^;]*)/);\n if (match && match[1] === opts.token) return next();\n }\n\n if (opts.auth) {\n c.header(\"WWW-Authenticate\", 'Basic realm=\"pg-dash\"');\n }\n return c.text(\"Unauthorized\", 401);\n });\n }\n\n // Register route modules\n registerOverviewRoutes(app, pool);\n registerMetricsRoutes(app, store, collector);\n registerActivityRoutes(app, pool);\n registerAdvisorRoutes(app, pool, longQueryThreshold, store);\n registerSchemaRoutes(app, pool, schemaTracker);\n registerAlertsRoutes(app, alertManager);\n registerExplainRoutes(app, pool);\n registerDiskRoutes(app, pool, store);\n registerQueryStatsRoutes(app, queryStatsStore);\n registerExportRoutes(app, pool, longQueryThreshold);\n\n // Serve frontend\n const uiPath = path.resolve(__dirname, \"ui\");\n const MIME_TYPES: Record<string, string> = {\n \".html\": \"text/html\",\n \".js\": \"application/javascript\",\n \".css\": \"text/css\",\n \".json\": \"application/json\",\n \".png\": \"image/png\",\n \".jpg\": \"image/jpeg\",\n \".svg\": \"image/svg+xml\",\n \".ico\": \"image/x-icon\",\n \".woff\": \"font/woff\",\n \".woff2\": \"font/woff2\",\n };\n app.get(\"/*\", async (c) => {\n const urlPath = c.req.path === \"/\" ? \"/index.html\" : c.req.path;\n const filePath = path.join(uiPath, urlPath);\n try {\n const content = await fs.promises.readFile(filePath);\n const ext = path.extname(filePath);\n const contentType = MIME_TYPES[ext] || \"application/octet-stream\";\n return new Response(content, { headers: { \"content-type\": contentType } });\n } catch {\n // SPA fallback\n try {\n const html = await fs.promises.readFile(path.join(uiPath, \"index.html\"));\n return new Response(html, { headers: { \"content-type\": \"text/html\" } });\n } catch (err) {\n console.error(\"[static] Error reading index.html:\", (err as Error).message);\n return c.text(\"Not Found\", 404);\n }\n }\n });\n\n // Create HTTP server + WebSocket server\n const server = http.createServer(async (req, res) => {\n const chunks: Buffer[] = [];\n for await (const chunk of req) chunks.push(chunk as Buffer);\n const body = Buffer.concat(chunks);\n\n const url = new URL(req.url || \"/\", `http://localhost:${opts.port}`);\n const init: RequestInit = {\n method: req.method,\n headers: req.headers as any,\n };\n if (req.method !== \"GET\" && req.method !== \"HEAD\" && body.length > 0) {\n init.body = body;\n }\n const request = new Request(url.toString(), init);\n app.fetch(request).then((response) => {\n res.writeHead(response.status, Object.fromEntries(response.headers.entries()));\n response.arrayBuffer().then((buf) => {\n res.end(Buffer.from(buf));\n });\n }).catch(() => {\n res.writeHead(500);\n res.end(\"Internal Server Error\");\n });\n });\n\n const wss = new WebSocketServer({\n server,\n path: \"/ws\",\n verifyClient: (opts.auth || opts.token) ? (info, cb) => {\n const url = new URL(info.req.url || \"/\", `http://localhost:${opts.port}`);\n const qToken = url.searchParams.get(\"token\");\n if (opts.token && qToken === opts.token) return cb(true);\n\n const authHeader = info.req.headers[\"authorization\"] || \"\";\n if (opts.token && authHeader === `Bearer ${opts.token}`) return cb(true);\n if (opts.auth) {\n const [user, pass] = opts.auth.split(\":\");\n const expected = \"Basic \" + Buffer.from(`${user}:${pass}`).toString(\"base64\");\n if (authHeader === expected) return cb(true);\n }\n\n // Check cookie for token auth\n if (opts.token) {\n const cookies = (info.req.headers[\"cookie\"] as string) || \"\";\n const match = cookies.match(/(?:^|;\\s*)pg-dash-token=([^;]*)/);\n if (match && match[1] === opts.token) return cb(true);\n }\n\n cb(false, 401, \"Unauthorized\");\n } : undefined,\n });\n const clients = new Set<WebSocket>();\n\n wss.on(\"connection\", (ws) => {\n clients.add(ws);\n const snap = collector.getLastSnapshot();\n if (Object.keys(snap).length > 0) {\n ws.send(JSON.stringify({ type: \"metrics\", data: snap }));\n }\n ws.on(\"close\", () => clients.delete(ws));\n ws.on(\"error\", () => clients.delete(ws));\n });\n\n // Broadcast metrics, activity, and check alerts on each collection\n let collectCycleCount = 0;\n collector.on(\"collected\", async (snapshot: Record<string, number>) => {\n if (clients.size > 0 && Object.keys(snapshot).length > 0) {\n const metricsMsg = JSON.stringify({ type: \"metrics\", data: snapshot });\n let activityData: any[] = [];\n try { activityData = await getActivity(pool); } catch (err) { console.error(\"[ws] Error fetching activity:\", (err as Error).message); }\n const activityMsg = JSON.stringify({ type: \"activity\", data: activityData });\n for (const ws of clients) {\n if (ws.readyState === WebSocket.OPEN) {\n ws.send(metricsMsg);\n ws.send(activityMsg);\n }\n }\n }\n\n // Check alerts after each collection\n if (Object.keys(snapshot).length > 0) {\n try {\n const alertMetrics: Record<string, number> = {};\n\n // Acquire a single client for all alert metric queries this cycle\n const alertClient = await pool.connect();\n try {\n if (snapshot.connections_total !== undefined) {\n const r = await alertClient.query(\"SELECT setting::int AS max FROM pg_settings WHERE name = 'max_connections'\");\n const max = r.rows[0]?.max || 100;\n alertMetrics.connection_util = (snapshot.connections_total / max) * 100;\n }\n\n if (snapshot.cache_hit_ratio !== undefined) {\n alertMetrics.cache_hit_pct = snapshot.cache_hit_ratio * 100;\n }\n\n const [longQueriesResult, idleInTxResult] = await Promise.all([\n alertClient.query(\n `SELECT count(*)::int AS c FROM pg_stat_activity WHERE state = 'active' AND now() - query_start > $1 * interval '1 minute' AND pid != pg_backend_pid()`,\n [longQueryThreshold]\n ),\n alertClient.query(\n `SELECT count(*)::int AS c FROM pg_stat_activity WHERE state = 'idle in transaction' AND now() - state_change > $1 * interval '1 minute'`,\n [longQueryThreshold]\n ),\n ]);\n alertMetrics.long_query_count = longQueriesResult.rows[0]?.c || 0;\n alertMetrics.idle_in_tx_count = idleInTxResult.rows[0]?.c || 0;\n } catch (err) { console.error(\"[alerts] Error collecting alert metrics:\", (err as Error).message); }\n finally { alertClient.release(); }\n\n collectCycleCount++;\n if (collectCycleCount % 10 === 0) {\n try {\n const report = await getAdvisorReport(pool, longQueryThreshold);\n alertMetrics.health_score = report.score;\n store.insert(\"health_score\", report.score);\n } catch (err) { console.error(\"[alerts] Error checking health score:\", (err as Error).message); }\n\n // db_growth_pct_24h\n try {\n if (snapshot.db_size_bytes !== undefined) {\n const dayAgo = Date.now() - 24 * 60 * 60 * 1000;\n const oldData = store.query(\"db_size_bytes\", dayAgo, dayAgo + 5 * 60 * 1000);\n if (oldData.length > 0) {\n const oldVal = oldData[0].value;\n if (oldVal > 0) {\n alertMetrics.db_growth_pct_24h = ((snapshot.db_size_bytes - oldVal) / oldVal) * 100;\n }\n }\n }\n } catch (err) { console.error(\"[alerts] Error computing db_growth_pct_24h:\", (err as Error).message); }\n\n // days_until_full\n try {\n const pred = diskPredictor.predict(store, \"db_size_bytes\", 30);\n if (pred?.daysUntilFull !== null && pred?.daysUntilFull !== undefined) {\n alertMetrics.days_until_full = pred.daysUntilFull;\n }\n } catch (err) { console.error(\"[alerts] Error computing days_until_full:\", (err as Error).message); }\n }\n\n const fired = alertManager.checkAlerts(alertMetrics);\n\n if (fired.length > 0 && clients.size > 0) {\n const alertMsg = JSON.stringify({ type: \"alerts\", data: fired });\n for (const ws of clients) {\n if (ws.readyState === WebSocket.OPEN) {\n ws.send(alertMsg);\n }\n }\n }\n } catch (err) {\n console.error(\"[alerts] Error checking alerts:\", (err as Error).message);\n }\n }\n });\n\n const bindAddr = opts.bind || \"127.0.0.1\";\n server.listen(opts.port, bindAddr, async () => {\n console.log(`\\n pg-dash running at http://${bindAddr}:${opts.port}\\n`);\n if (opts.open) {\n try {\n const openMod = await import(\"open\");\n await openMod.default(`http://localhost:${opts.port}`);\n } catch (err) { console.error(\"[open] Failed to open browser:\", (err as Error).message); }\n }\n });\n\n // Graceful shutdown\n const shutdown = async () => {\n console.log(\"\\n Shutting down gracefully...\");\n collector.stop();\n schemaTracker.stop();\n queryStatsStore.stop();\n wss.close();\n server.close();\n metricsDb.close();\n schemaDb.close();\n alertsDb.close();\n await pool.end();\n console.log(\" Goodbye!\");\n process.exit(0);\n };\n\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n\n await new Promise(() => {});\n}\n","import type { Pool } from \"pg\";\n\nexport async function getOverview(pool: Pool) {\n const client = await pool.connect();\n try {\n const version = await client.query(\"SHOW server_version\");\n const uptime = await client.query(\n \"SELECT to_char(now() - pg_postmaster_start_time(), 'DD \\\"d\\\" HH24 \\\"h\\\" MI \\\"m\\\"') AS uptime\"\n );\n const dbSize = await client.query(\n \"SELECT pg_size_pretty(pg_database_size(current_database())) AS size\"\n );\n const dbCount = await client.query(\n \"SELECT count(*)::int AS count FROM pg_database WHERE NOT datistemplate\"\n );\n const connections = await client.query(`\n SELECT\n (SELECT count(*)::int FROM pg_stat_activity WHERE state = 'active') AS active,\n (SELECT count(*)::int FROM pg_stat_activity WHERE state = 'idle') AS idle,\n (SELECT setting::int FROM pg_settings WHERE name = 'max_connections') AS max\n `);\n\n return {\n version: version.rows[0].server_version,\n uptime: uptime.rows[0].uptime,\n dbSize: dbSize.rows[0].size,\n databaseCount: dbCount.rows[0].count,\n connections: connections.rows[0],\n };\n } finally {\n client.release();\n }\n}\n","import type { Pool } from \"pg\";\n\nexport async function getDatabases(pool: Pool) {\n const client = await pool.connect();\n try {\n const r = await client.query(`\n SELECT datname AS name,\n pg_size_pretty(pg_database_size(datname)) AS size,\n pg_database_size(datname) AS size_bytes\n FROM pg_database\n WHERE NOT datistemplate\n ORDER BY pg_database_size(datname) DESC\n `);\n return r.rows;\n } finally {\n client.release();\n }\n}\n","import type { Pool } from \"pg\";\n\nexport async function getTables(pool: Pool) {\n const client = await pool.connect();\n try {\n const r = await client.query(`\n SELECT\n schemaname AS schema,\n relname AS name,\n pg_size_pretty(pg_total_relation_size(relid)) AS total_size,\n pg_total_relation_size(relid) AS size_bytes,\n n_live_tup AS rows,\n n_dead_tup AS dead_tuples,\n CASE WHEN n_live_tup > 0 \n THEN round(n_dead_tup::numeric / n_live_tup * 100, 1) \n ELSE 0 END AS dead_pct\n FROM pg_stat_user_tables\n ORDER BY pg_total_relation_size(relid) DESC\n `);\n return r.rows;\n } finally {\n client.release();\n }\n}\n","import type { Pool } from \"pg\";\n\nexport interface Activity {\n pid: number;\n query: string;\n state: string;\n wait_event: string | null;\n wait_event_type: string | null;\n duration: string | null;\n client_addr: string | null;\n application_name: string;\n backend_start: string;\n}\n\nexport async function getActivity(pool: Pool): Promise<Activity[]> {\n const client = await pool.connect();\n try {\n const r = await client.query(`\n SELECT\n pid,\n COALESCE(query, '') AS query,\n COALESCE(state, 'unknown') AS state,\n wait_event,\n wait_event_type,\n CASE WHEN state = 'active' THEN (now() - query_start)::text\n WHEN state = 'idle in transaction' THEN (now() - state_change)::text\n ELSE NULL END AS duration,\n client_addr::text,\n COALESCE(application_name, '') AS application_name,\n backend_start::text\n FROM pg_stat_activity\n WHERE pid != pg_backend_pid()\n AND state IS NOT NULL\n ORDER BY\n CASE state\n WHEN 'active' THEN 1\n WHEN 'idle in transaction' THEN 2\n ELSE 3\n END,\n query_start ASC NULLS LAST\n `);\n return r.rows;\n } finally {\n client.release();\n }\n}\n","import Database from \"better-sqlite3\";\nimport path from \"node:path\";\nimport os from \"node:os\";\nimport fs from \"node:fs\";\n\nconst DEFAULT_DIR = path.join(os.homedir(), \".pg-dash\");\nconst DEFAULT_RETENTION_DAYS = 7;\n\nexport interface DataPoint {\n timestamp: number;\n metric: string;\n value: number;\n}\n\nexport class TimeseriesStore {\n private db: Database.Database;\n private insertStmt: Database.Statement;\n private retentionMs: number;\n\n constructor(db: Database.Database, retentionDays = DEFAULT_RETENTION_DAYS);\n constructor(dataDir?: string, retentionDays?: number);\n constructor(dbOrDir?: Database.Database | string, retentionDays = DEFAULT_RETENTION_DAYS) {\n if (dbOrDir instanceof Database) {\n this.db = dbOrDir;\n } else {\n const dir = dbOrDir || DEFAULT_DIR;\n fs.mkdirSync(dir, { recursive: true });\n const dbPath = path.join(dir, \"metrics.db\");\n this.db = new Database(dbPath);\n }\n this.retentionMs = retentionDays * 24 * 60 * 60 * 1000;\n\n this.db.pragma(\"journal_mode = WAL\");\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS metrics (\n timestamp INTEGER NOT NULL,\n metric TEXT NOT NULL,\n value REAL NOT NULL\n );\n CREATE INDEX IF NOT EXISTS idx_metrics_metric_ts ON metrics(metric, timestamp);\n `);\n\n this.insertStmt = this.db.prepare(\n \"INSERT INTO metrics (timestamp, metric, value) VALUES (?, ?, ?)\"\n );\n }\n\n insert(metric: string, value: number, timestamp?: number): void {\n this.insertStmt.run(timestamp ?? Date.now(), metric, value);\n }\n\n insertMany(points: DataPoint[]): void {\n const tx = this.db.transaction((pts: DataPoint[]) => {\n for (const p of pts) {\n this.insertStmt.run(p.timestamp, p.metric, p.value);\n }\n });\n tx(points);\n }\n\n query(metric: string, startMs: number, endMs?: number): { timestamp: number; value: number }[] {\n const end = endMs ?? Date.now();\n return this.db\n .prepare(\n \"SELECT timestamp, value FROM metrics WHERE metric = ? AND timestamp >= ? AND timestamp <= ? ORDER BY timestamp\"\n )\n .all(metric, startMs, end) as { timestamp: number; value: number }[];\n }\n\n latest(metrics?: string[]): Record<string, { timestamp: number; value: number }> {\n const result: Record<string, { timestamp: number; value: number }> = {};\n if (metrics && metrics.length > 0) {\n const placeholders = metrics.map(() => \"?\").join(\",\");\n const rows = this.db\n .prepare(\n `SELECT m.metric, m.timestamp, m.value FROM metrics m INNER JOIN (SELECT metric, MAX(timestamp) as max_ts FROM metrics WHERE metric IN (${placeholders}) GROUP BY metric) g ON m.metric = g.metric AND m.timestamp = g.max_ts`\n )\n .all(...metrics) as DataPoint[];\n for (const r of rows) result[r.metric] = { timestamp: r.timestamp, value: r.value };\n } else {\n const rows = this.db\n .prepare(\n \"SELECT m.metric, m.timestamp, m.value FROM metrics m INNER JOIN (SELECT metric, MAX(timestamp) as max_ts FROM metrics GROUP BY metric) g ON m.metric = g.metric AND m.timestamp = g.max_ts\"\n )\n .all() as DataPoint[];\n for (const r of rows) result[r.metric] = { timestamp: r.timestamp, value: r.value };\n }\n return result;\n }\n\n prune(): number {\n const cutoff = Date.now() - this.retentionMs;\n const info = this.db.prepare(\"DELETE FROM metrics WHERE timestamp < ?\").run(cutoff);\n return info.changes;\n }\n\n close(): void {\n this.db.close();\n }\n}\n","import { EventEmitter } from \"node:events\";\nimport type { Pool } from \"pg\";\nimport type { TimeseriesStore } from \"./timeseries.js\";\n\nexport const ALL_METRICS = [\n \"connections_active\",\n \"connections_idle\",\n \"connections_total\",\n \"tps_commit\",\n \"tps_rollback\",\n \"cache_hit_ratio\",\n \"deadlocks\",\n \"temp_bytes\",\n \"db_size_bytes\",\n \"tuple_inserted\",\n \"tuple_updated\",\n \"tuple_deleted\",\n \"replication_lag_bytes\",\n \"disk_used_bytes\",\n] as const;\n\nexport type MetricName = (typeof ALL_METRICS)[number];\n\ninterface CumulativeState {\n timestamp: number;\n xact_commit: number;\n xact_rollback: number;\n deadlocks: number;\n temp_bytes: number;\n tup_inserted: number;\n tup_updated: number;\n tup_deleted: number;\n}\n\nexport class Collector extends EventEmitter {\n private timer: ReturnType<typeof setInterval> | null = null;\n private pruneTimer: ReturnType<typeof setInterval> | null = null;\n private prev: CumulativeState | null = null;\n private lastSnapshot: Record<string, number> = {};\n private collectCount = 0;\n\n constructor(\n private pool: Pool,\n private store: TimeseriesStore,\n private intervalMs: number = 30000\n ) {\n super();\n }\n\n start(): void {\n this.collect().catch(err => console.error(\"[collector] Initial collection failed:\", err));\n this.timer = setInterval(() => {\n this.collect().catch(err => console.error(\"[collector] Collection failed:\", err));\n }, this.intervalMs);\n // Prune once per hour\n this.pruneTimer = setInterval(() => this.store.prune(), 60 * 60 * 1000);\n }\n\n stop(): void {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n if (this.pruneTimer) {\n clearInterval(this.pruneTimer);\n this.pruneTimer = null;\n }\n }\n\n getLastSnapshot(): Record<string, number> {\n return { ...this.lastSnapshot };\n }\n\n async collect(): Promise<Record<string, number>> {\n const now = Date.now();\n const snapshot: Record<string, number> = {};\n\n try {\n const client = await this.pool.connect();\n try {\n // Connections\n const connRes = await client.query(`\n SELECT\n count(*) FILTER (WHERE state = 'active')::int AS active,\n count(*) FILTER (WHERE state = 'idle')::int AS idle,\n count(*)::int AS total\n FROM pg_stat_activity\n `);\n const conn = connRes.rows[0];\n snapshot.connections_active = conn.active;\n snapshot.connections_idle = conn.idle;\n snapshot.connections_total = conn.total;\n\n // Database stats (cumulative counters + cache ratio + size)\n const dbRes = await client.query(`\n SELECT\n xact_commit, xact_rollback, deadlocks, temp_bytes,\n tup_inserted, tup_updated, tup_deleted,\n CASE WHEN (blks_hit + blks_read) = 0 THEN 1\n ELSE blks_hit::float / (blks_hit + blks_read) END AS cache_ratio,\n pg_database_size(current_database()) AS db_size\n FROM pg_stat_database WHERE datname = current_database()\n `);\n const db = dbRes.rows[0];\n if (db) {\n snapshot.cache_hit_ratio = parseFloat(db.cache_ratio);\n snapshot.db_size_bytes = parseInt(db.db_size);\n\n const cur: CumulativeState = {\n timestamp: now,\n xact_commit: parseInt(db.xact_commit),\n xact_rollback: parseInt(db.xact_rollback),\n deadlocks: parseInt(db.deadlocks),\n temp_bytes: parseInt(db.temp_bytes),\n tup_inserted: parseInt(db.tup_inserted),\n tup_updated: parseInt(db.tup_updated),\n tup_deleted: parseInt(db.tup_deleted),\n };\n\n if (this.prev) {\n const dtSec = (now - this.prev.timestamp) / 1000;\n if (dtSec > 0) {\n snapshot.tps_commit = Math.max(0, (cur.xact_commit - this.prev.xact_commit) / dtSec);\n snapshot.tps_rollback = Math.max(0, (cur.xact_rollback - this.prev.xact_rollback) / dtSec);\n snapshot.deadlocks = Math.max(0, cur.deadlocks - this.prev.deadlocks);\n snapshot.temp_bytes = Math.max(0, cur.temp_bytes - this.prev.temp_bytes);\n snapshot.tuple_inserted = Math.max(0, (cur.tup_inserted - this.prev.tup_inserted) / dtSec);\n snapshot.tuple_updated = Math.max(0, (cur.tup_updated - this.prev.tup_updated) / dtSec);\n snapshot.tuple_deleted = Math.max(0, (cur.tup_deleted - this.prev.tup_deleted) / dtSec);\n }\n }\n this.prev = cur;\n }\n\n // Tablespace sizes (stored as individual metrics)\n try {\n const tsRes = await client.query(`SELECT spcname, pg_tablespace_size(oid) AS size FROM pg_tablespace`);\n let totalTablespaceSize = 0;\n for (const row of tsRes.rows) {\n totalTablespaceSize += parseInt(row.size);\n }\n // Use total tablespace size as a proxy for disk usage\n if (totalTablespaceSize > 0) {\n snapshot.disk_used_bytes = totalTablespaceSize;\n }\n } catch {\n // Not all users have tablespace permissions\n }\n\n // Replication lag\n try {\n const repRes = await client.query(`\n SELECT CASE WHEN pg_is_in_recovery() \n THEN pg_wal_lsn_diff(pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn())\n ELSE 0 END AS lag_bytes\n `);\n snapshot.replication_lag_bytes = parseInt(repRes.rows[0]?.lag_bytes ?? \"0\");\n } catch {\n snapshot.replication_lag_bytes = 0;\n }\n } finally {\n client.release();\n }\n } catch (err) {\n console.error(\"[collector] Error collecting metrics:\", (err as Error).message);\n return snapshot;\n }\n\n // Per-table sizes every 10th cycle\n this.collectCount++;\n if (this.collectCount % 10 === 0) {\n try {\n const client = await this.pool.connect();\n try {\n const tableRes = await client.query(`\n SELECT schemaname, relname,\n pg_total_relation_size(quote_ident(schemaname) || '.' || quote_ident(relname)) as total_size\n FROM pg_stat_user_tables\n ORDER BY pg_total_relation_size(quote_ident(schemaname) || '.' || quote_ident(relname)) DESC\n LIMIT 20\n `);\n for (const row of tableRes.rows) {\n this.store.insert(`table_size:${row.schemaname}.${row.relname}`, parseInt(row.total_size), now);\n }\n } finally { client.release(); }\n } catch (err) {\n console.error(\"[collector] Error collecting table sizes:\", (err as Error).message);\n }\n }\n\n // Store to SQLite\n const points = Object.entries(snapshot).map(([metric, value]) => ({\n timestamp: now,\n metric,\n value,\n }));\n if (points.length > 0) {\n this.store.insertMany(points);\n }\n\n this.lastSnapshot = snapshot;\n this.emit(\"collected\", snapshot);\n return snapshot;\n }\n}\n","// Notification formatters for Slack, Discord, and generic webhooks\n\nimport type { AlertRule, AlertHistoryEntry } from \"./alerts.js\";\n\nexport type WebhookType = \"slack\" | \"discord\" | \"unknown\";\n\nconst SEVERITY_COLORS: Record<string, { hex: string; decimal: number; emoji: string }> = {\n critical: { hex: \"#e74c3c\", decimal: 0xe74c3c, emoji: \"🔴\" },\n warning: { hex: \"#f39c12\", decimal: 0xf39c12, emoji: \"🟡\" },\n info: { hex: \"#3498db\", decimal: 0x3498db, emoji: \"🔵\" },\n};\n\nexport function detectWebhookType(url: string): WebhookType {\n try {\n const { hostname } = new URL(url);\n if (hostname.endsWith(\"hooks.slack.com\")) return \"slack\";\n if (hostname.endsWith(\"discord.com\") || hostname.endsWith(\"discordapp.com\")) return \"discord\";\n return \"unknown\";\n } catch {\n return \"unknown\";\n }\n}\n\nexport function formatSlackMessage(alert: AlertHistoryEntry, rule: AlertRule): object {\n const colors = SEVERITY_COLORS[rule.severity] || SEVERITY_COLORS.info;\n return {\n attachments: [\n {\n color: colors.hex,\n blocks: [\n {\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: `${colors.emoji} *pg-dash Alert: ${rule.name}*`,\n },\n },\n {\n type: \"section\",\n fields: [\n { type: \"mrkdwn\", text: `*Metric:*\\n${rule.metric}` },\n { type: \"mrkdwn\", text: `*Current Value:*\\n${alert.value}` },\n { type: \"mrkdwn\", text: `*Threshold:*\\n${rule.operator} ${rule.threshold}` },\n { type: \"mrkdwn\", text: `*Severity:*\\n${rule.severity}` },\n { type: \"mrkdwn\", text: `*Timestamp:*\\n${new Date(alert.timestamp).toISOString()}` },\n ],\n },\n ],\n },\n ],\n };\n}\n\nexport function formatDiscordMessage(alert: AlertHistoryEntry, rule: AlertRule): object {\n const colors = SEVERITY_COLORS[rule.severity] || SEVERITY_COLORS.info;\n return {\n embeds: [\n {\n title: `${colors.emoji} pg-dash Alert: ${rule.name}`,\n color: colors.decimal,\n fields: [\n { name: \"Metric\", value: rule.metric, inline: true },\n { name: \"Current Value\", value: String(alert.value), inline: true },\n { name: \"Threshold\", value: `${rule.operator} ${rule.threshold}`, inline: true },\n { name: \"Severity\", value: rule.severity, inline: true },\n { name: \"Timestamp\", value: new Date(alert.timestamp).toISOString(), inline: false },\n ],\n footer: { text: \"pg-dash · PostgreSQL Monitoring\" },\n },\n ],\n };\n}\n\nexport function formatGenericWebhook(alert: AlertHistoryEntry, rule: AlertRule): object {\n return {\n severity: rule.severity,\n rule: rule.name,\n metric: rule.metric,\n value: alert.value,\n message: alert.message,\n timestamp: alert.timestamp,\n };\n}\n\nexport function formatWebhookPayload(alert: AlertHistoryEntry, rule: AlertRule, webhookUrl: string): object {\n const type = detectWebhookType(webhookUrl);\n switch (type) {\n case \"slack\": return formatSlackMessage(alert, rule);\n case \"discord\": return formatDiscordMessage(alert, rule);\n default: return formatGenericWebhook(alert, rule);\n }\n}\n\nexport function getSeverityColor(severity: string) {\n return SEVERITY_COLORS[severity] || SEVERITY_COLORS.info;\n}\n","// Alerts system — rules stored in SQLite, threshold checking with cooldown, webhook notifications\n\nimport type Database from \"better-sqlite3\";\nimport { formatWebhookPayload, detectWebhookType } from \"./notifiers.js\";\n\nexport interface AlertRule {\n id: number;\n name: string;\n metric: string;\n operator: \"gt\" | \"lt\" | \"eq\";\n threshold: number;\n severity: \"info\" | \"warning\" | \"critical\";\n enabled: number;\n cooldown_minutes: number;\n}\n\nexport interface AlertHistoryEntry {\n id: number;\n rule_id: number;\n timestamp: number;\n value: number;\n message: string;\n notified: number;\n}\n\nconst DEFAULT_RULES: Omit<AlertRule, \"id\">[] = [\n { name: \"Connection utilization > 80%\", metric: \"connection_util\", operator: \"gt\", threshold: 80, severity: \"warning\", enabled: 1, cooldown_minutes: 60 },\n { name: \"Connection utilization > 90%\", metric: \"connection_util\", operator: \"gt\", threshold: 90, severity: \"critical\", enabled: 1, cooldown_minutes: 30 },\n { name: \"Cache hit ratio < 99%\", metric: \"cache_hit_pct\", operator: \"lt\", threshold: 99, severity: \"warning\", enabled: 1, cooldown_minutes: 60 },\n { name: \"Cache hit ratio < 95%\", metric: \"cache_hit_pct\", operator: \"lt\", threshold: 95, severity: \"critical\", enabled: 1, cooldown_minutes: 30 },\n { name: \"Long-running query > 5 min\", metric: \"long_query_count\", operator: \"gt\", threshold: 0, severity: \"warning\", enabled: 1, cooldown_minutes: 15 },\n { name: \"Idle in transaction > 10 min\", metric: \"idle_in_tx_count\", operator: \"gt\", threshold: 0, severity: \"warning\", enabled: 1, cooldown_minutes: 15 },\n { name: \"Health score below D\", metric: \"health_score\", operator: \"lt\", threshold: 50, severity: \"warning\", enabled: 1, cooldown_minutes: 120 },\n { name: \"Database size growth > 10% in 24h\", metric: \"db_growth_pct_24h\", operator: \"gt\", threshold: 10, severity: \"warning\", enabled: 1, cooldown_minutes: 60 },\n { name: \"Predicted disk full within 7 days\", metric: \"days_until_full\", operator: \"lt\", threshold: 7, severity: \"critical\", enabled: 1, cooldown_minutes: 360 },\n];\n\nexport class AlertManager {\n private db: Database.Database;\n private webhookUrl: string | null;\n\n constructor(db: Database.Database, webhookUrl?: string) {\n this.db = db;\n this.webhookUrl = webhookUrl || null;\n this.initTables();\n }\n\n private initTables() {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS alert_rules (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n name TEXT NOT NULL,\n metric TEXT NOT NULL,\n operator TEXT NOT NULL,\n threshold REAL NOT NULL,\n severity TEXT NOT NULL DEFAULT 'warning',\n enabled INTEGER DEFAULT 1,\n cooldown_minutes INTEGER DEFAULT 60\n );\n CREATE TABLE IF NOT EXISTS alert_history (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n rule_id INTEGER NOT NULL,\n timestamp INTEGER NOT NULL,\n value REAL NOT NULL,\n message TEXT NOT NULL,\n notified INTEGER DEFAULT 0,\n FOREIGN KEY (rule_id) REFERENCES alert_rules(id)\n );\n `);\n\n // Seed default rules on first run\n const count = (this.db.prepare(\"SELECT COUNT(*) as c FROM alert_rules\").get() as any).c;\n if (count === 0) {\n const insert = this.db.prepare(\"INSERT INTO alert_rules (name, metric, operator, threshold, severity, enabled, cooldown_minutes) VALUES (?, ?, ?, ?, ?, ?, ?)\");\n const tx = this.db.transaction(() => {\n for (const r of DEFAULT_RULES) {\n insert.run(r.name, r.metric, r.operator, r.threshold, r.severity, r.enabled, r.cooldown_minutes);\n }\n });\n tx();\n }\n }\n\n getRules(): AlertRule[] {\n return this.db.prepare(\"SELECT * FROM alert_rules ORDER BY id\").all() as AlertRule[];\n }\n\n addRule(rule: Omit<AlertRule, \"id\">): AlertRule {\n const info = this.db.prepare(\"INSERT INTO alert_rules (name, metric, operator, threshold, severity, enabled, cooldown_minutes) VALUES (?, ?, ?, ?, ?, ?, ?)\").run(\n rule.name, rule.metric, rule.operator, rule.threshold, rule.severity, rule.enabled ?? 1, rule.cooldown_minutes ?? 60\n );\n return { ...rule, id: Number(info.lastInsertRowid) } as AlertRule;\n }\n\n updateRule(id: number, updates: Partial<Omit<AlertRule, \"id\">>): boolean {\n const existing = this.db.prepare(\"SELECT * FROM alert_rules WHERE id = ?\").get(id) as AlertRule | undefined;\n if (!existing) return false;\n const merged = { ...existing, ...updates };\n this.db.prepare(\"UPDATE alert_rules SET name=?, metric=?, operator=?, threshold=?, severity=?, enabled=?, cooldown_minutes=? WHERE id=?\").run(\n merged.name, merged.metric, merged.operator, merged.threshold, merged.severity, merged.enabled, merged.cooldown_minutes, id\n );\n return true;\n }\n\n deleteRule(id: number): boolean {\n const info = this.db.prepare(\"DELETE FROM alert_rules WHERE id = ?\").run(id);\n return info.changes > 0;\n }\n\n getHistory(limit = 50): AlertHistoryEntry[] {\n return this.db.prepare(\"SELECT * FROM alert_history ORDER BY timestamp DESC LIMIT ?\").all(limit) as AlertHistoryEntry[];\n }\n\n /**\n * Check all enabled rules against current metric values.\n * `metrics` is a map of metric name → current value.\n */\n checkAlerts(metrics: Record<string, number>): AlertHistoryEntry[] {\n const rules = this.db.prepare(\"SELECT * FROM alert_rules WHERE enabled = 1\").all() as AlertRule[];\n const fired: AlertHistoryEntry[] = [];\n const now = Date.now();\n\n for (const rule of rules) {\n const value = metrics[rule.metric];\n if (value === undefined) continue;\n\n const triggered = this.evaluateRule(rule, value);\n if (!triggered) continue;\n\n // Check cooldown\n const lastAlert = this.db.prepare(\n \"SELECT timestamp FROM alert_history WHERE rule_id = ? ORDER BY timestamp DESC LIMIT 1\"\n ).get(rule.id) as { timestamp: number } | undefined;\n\n if (lastAlert && (now - lastAlert.timestamp) < rule.cooldown_minutes * 60 * 1000) {\n continue; // Still in cooldown\n }\n\n const message = `${rule.name}: ${rule.metric} = ${value} (threshold: ${rule.operator} ${rule.threshold})`;\n const info = this.db.prepare(\"INSERT INTO alert_history (rule_id, timestamp, value, message, notified) VALUES (?, ?, ?, ?, 0)\").run(\n rule.id, now, value, message\n );\n const entry: AlertHistoryEntry = { id: Number(info.lastInsertRowid), rule_id: rule.id, timestamp: now, value, message, notified: 0 };\n fired.push(entry);\n\n // Log to console\n const icon = rule.severity === \"critical\" ? \"🔴\" : rule.severity === \"warning\" ? \"🟡\" : \"🔵\";\n console.log(`[alert] ${icon} ${message}`);\n\n // Webhook notification\n if (this.webhookUrl) {\n this.sendWebhook(rule, entry).catch((err) => console.error(\"[alert] Webhook failed:\", err.message));\n }\n }\n\n return fired;\n }\n\n evaluateRule(rule: Pick<AlertRule, \"operator\" | \"threshold\">, value: number): boolean {\n switch (rule.operator) {\n case \"gt\": return value > rule.threshold;\n case \"lt\": return value < rule.threshold;\n case \"eq\": return value === rule.threshold;\n default: return false;\n }\n }\n\n getWebhookUrl(): string | null {\n return this.webhookUrl;\n }\n\n getWebhookType(): string | null {\n if (!this.webhookUrl) return null;\n return detectWebhookType(this.webhookUrl);\n }\n\n async sendTestWebhook(): Promise<{ ok: boolean; type: string; error?: string }> {\n if (!this.webhookUrl) return { ok: false, type: \"none\", error: \"No webhook URL configured\" };\n const type = detectWebhookType(this.webhookUrl);\n const testRule: AlertRule = {\n id: 0, name: \"Test Alert\", metric: \"test_metric\", operator: \"gt\",\n threshold: 80, severity: \"info\", enabled: 1, cooldown_minutes: 60,\n };\n const testEntry: AlertHistoryEntry = {\n id: 0, rule_id: 0, timestamp: Date.now(), value: 85,\n message: \"Test Alert: test_metric = 85 (threshold: gt 80)\", notified: 0,\n };\n try {\n const payload = formatWebhookPayload(testEntry, testRule, this.webhookUrl);\n const res = await fetch(this.webhookUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(payload),\n });\n if (!res.ok) return { ok: false, type, error: `HTTP ${res.status}` };\n return { ok: true, type };\n } catch (err) {\n return { ok: false, type, error: (err as Error).message };\n }\n }\n\n private async sendWebhook(rule: AlertRule, entry: AlertHistoryEntry) {\n if (!this.webhookUrl) return;\n try {\n const payload = formatWebhookPayload(entry, rule, this.webhookUrl);\n await fetch(this.webhookUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(payload),\n });\n this.db.prepare(\"UPDATE alert_history SET notified = 1 WHERE id = ?\").run(entry.id);\n } catch (err) {\n console.error(\"[alert] Webhook error:\", (err as Error).message);\n }\n }\n}\n","import type { Hono } from \"hono\";\nimport type { Pool } from \"pg\";\nimport { getOverview } from \"../queries/overview.js\";\nimport { getDatabases } from \"../queries/databases.js\";\nimport { getTables } from \"../queries/tables.js\";\n\nexport function registerOverviewRoutes(app: Hono, pool: Pool) {\n app.get(\"/api/overview\", async (c) => {\n try { return c.json(await getOverview(pool)); }\n catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.get(\"/api/databases\", async (c) => {\n try { return c.json(await getDatabases(pool)); }\n catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.get(\"/api/tables\", async (c) => {\n try { return c.json(await getTables(pool)); }\n catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n}\n","import type { Hono } from \"hono\";\nimport type { TimeseriesStore } from \"../timeseries.js\";\nimport type { Collector } from \"../collector.js\";\n\nconst RANGE_MAP: Record<string, number> = {\n \"5m\": 5 * 60 * 1000,\n \"15m\": 15 * 60 * 1000,\n \"1h\": 60 * 60 * 1000,\n \"6h\": 6 * 60 * 60 * 1000,\n \"24h\": 24 * 60 * 60 * 1000,\n \"7d\": 7 * 24 * 60 * 60 * 1000,\n};\n\nexport function registerMetricsRoutes(app: Hono, store: TimeseriesStore, collector: Collector) {\n app.get(\"/api/metrics\", (c) => {\n try {\n const metric = c.req.query(\"metric\");\n const range = c.req.query(\"range\") || \"1h\";\n if (!metric) return c.json({ error: \"metric param required\" }, 400);\n const rangeMs = RANGE_MAP[range] || RANGE_MAP[\"1h\"];\n const now = Date.now();\n const data = store.query(metric, now - rangeMs, now);\n return c.json(data);\n } catch (err: any) {\n return c.json({ error: err.message }, 500);\n }\n });\n\n app.get(\"/api/metrics/latest\", (_c) => {\n try {\n const snapshot = collector.getLastSnapshot();\n return _c.json(snapshot);\n } catch (err: any) {\n return _c.json({ error: err.message }, 500);\n }\n });\n}\n","import type { Pool } from \"pg\";\n\nexport interface SlowQuery {\n queryid: string;\n query: string;\n calls: number;\n total_time: number;\n mean_time: number;\n rows: number;\n total_time_pretty: string;\n mean_time_pretty: string;\n}\n\nexport async function getSlowQueries(pool: Pool): Promise<SlowQuery[]> {\n const client = await pool.connect();\n try {\n // Check if pg_stat_statements is available\n const extCheck = await client.query(\n \"SELECT 1 FROM pg_extension WHERE extname = 'pg_stat_statements'\"\n );\n if (extCheck.rows.length === 0) {\n return [];\n }\n\n const r = await client.query(`\n SELECT\n queryid::text,\n query,\n calls::int,\n total_exec_time AS total_time,\n mean_exec_time AS mean_time,\n rows::int,\n round(total_exec_time::numeric / 1000, 2)::text || 's' AS total_time_pretty,\n round(mean_exec_time::numeric, 2)::text || 'ms' AS mean_time_pretty\n FROM pg_stat_statements\n WHERE query NOT LIKE '%pg_stat%'\n AND query NOT LIKE '%pg_catalog%'\n ORDER BY total_exec_time DESC\n LIMIT 50\n `);\n return r.rows;\n } catch {\n // pg_stat_statements might not be accessible\n return [];\n } finally {\n client.release();\n }\n}\n","import type { Hono } from \"hono\";\nimport type { Pool } from \"pg\";\nimport { getActivity } from \"../queries/activity.js\";\nimport { getSlowQueries } from \"../queries/slow-queries.js\";\n\nexport function registerActivityRoutes(app: Hono, pool: Pool) {\n app.get(\"/api/activity\", async (c) => {\n try { return c.json(await getActivity(pool)); }\n catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.get(\"/api/queries\", async (c) => {\n try { return c.json(await getSlowQueries(pool)); }\n catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.post(\"/api/activity/:pid/cancel\", async (c) => {\n try {\n const pid = parseInt(c.req.param(\"pid\"));\n const client = await pool.connect();\n try {\n await client.query(\"SELECT pg_cancel_backend($1)\", [pid]);\n return c.json({ ok: true });\n } finally {\n client.release();\n }\n } catch (err: any) {\n return c.json({ error: err.message }, 500);\n }\n });\n}\n","import type { Hono } from \"hono\";\nimport type { Pool } from \"pg\";\nimport type { TimeseriesStore } from \"../timeseries.js\";\nimport { getAdvisorReport, isSafeFix, getIgnoredIssues, ignoreIssue, unignoreIssue } from \"../advisor.js\";\n\nconst RANGE_MAP: Record<string, number> = {\n \"24h\": 24 * 60 * 60 * 1000,\n \"7d\": 7 * 24 * 60 * 60 * 1000,\n \"30d\": 30 * 24 * 60 * 60 * 1000,\n};\n\nexport function registerAdvisorRoutes(app: Hono, pool: Pool, longQueryThreshold: number, store?: TimeseriesStore) {\n app.get(\"/api/advisor\", async (c) => {\n try { return c.json(await getAdvisorReport(pool, longQueryThreshold)); }\n catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.get(\"/api/advisor/ignored\", (c) => {\n try { return c.json(getIgnoredIssues()); }\n catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.post(\"/api/advisor/ignore\", async (c) => {\n try {\n const body = await c.req.json();\n const issueId = body?.issueId;\n if (!issueId) return c.json({ error: \"issueId required\" }, 400);\n ignoreIssue(issueId);\n return c.json({ ok: true });\n } catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.delete(\"/api/advisor/ignore/:issueId\", (c) => {\n try {\n const issueId = c.req.param(\"issueId\");\n unignoreIssue(issueId);\n return c.json({ ok: true });\n } catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.get(\"/api/advisor/history\", (c) => {\n if (!store) return c.json([]);\n try {\n const range = c.req.query(\"range\") || \"24h\";\n const rangeMs = RANGE_MAP[range] || RANGE_MAP[\"24h\"];\n const now = Date.now();\n const data = store.query(\"health_score\", now - rangeMs, now);\n return c.json(data);\n } catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.post(\"/api/fix\", async (c) => {\n try {\n const body = await c.req.json();\n const sql = body?.sql?.trim();\n if (!sql) return c.json({ error: \"sql field required\" }, 400);\n if (!isSafeFix(sql)) return c.json({ error: \"Operation not allowed. Only VACUUM, ANALYZE, REINDEX, CREATE/DROP INDEX CONCURRENTLY, pg_terminate_backend, pg_cancel_backend, and EXPLAIN ANALYZE are permitted.\" }, 403);\n const client = await pool.connect();\n try {\n const start = Date.now();\n const result = await client.query(sql);\n const duration = Date.now() - start;\n return c.json({ ok: true, duration, rowCount: result.rowCount, rows: result.rows || [] });\n } finally {\n client.release();\n }\n } catch (err: any) {\n return c.json({ error: err.message }, 500);\n }\n });\n}\n","import type { Hono } from \"hono\";\nimport type { Pool } from \"pg\";\nimport type { SchemaTracker } from \"../schema-tracker.js\";\nimport { getSchemaTables, getSchemaTableDetail, getSchemaIndexes, getSchemaFunctions, getSchemaExtensions, getSchemaEnums } from \"../queries/schema.js\";\n\nexport function registerSchemaRoutes(app: Hono, pool: Pool, schemaTracker: SchemaTracker) {\n app.get(\"/api/schema/tables\", async (c) => {\n try { return c.json(await getSchemaTables(pool)); }\n catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.get(\"/api/schema/tables/:name\", async (c) => {\n try {\n const name = c.req.param(\"name\");\n const detail = await getSchemaTableDetail(pool, name);\n if (!detail) return c.json({ error: \"Table not found\" }, 404);\n return c.json(detail);\n } catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.get(\"/api/schema/indexes\", async (c) => {\n try { return c.json(await getSchemaIndexes(pool)); }\n catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.get(\"/api/schema/functions\", async (c) => {\n try { return c.json(await getSchemaFunctions(pool)); }\n catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.get(\"/api/schema/extensions\", async (c) => {\n try { return c.json(await getSchemaExtensions(pool)); }\n catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.get(\"/api/schema/enums\", async (c) => {\n try { return c.json(await getSchemaEnums(pool)); }\n catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n // Schema change tracking endpoints\n app.get(\"/api/schema/history\", (c) => {\n try {\n const limit = parseInt(c.req.query(\"limit\") || \"30\");\n return c.json(schemaTracker.getHistory(limit));\n } catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.get(\"/api/schema/changes\", (c) => {\n try {\n const since = c.req.query(\"since\");\n return c.json(schemaTracker.getChanges(since ? parseInt(since) : undefined));\n } catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.get(\"/api/schema/changes/latest\", (c) => {\n try { return c.json(schemaTracker.getLatestChanges()); }\n catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.get(\"/api/schema/diff\", (c) => {\n try {\n const from = parseInt(c.req.query(\"from\") || \"0\");\n const to = parseInt(c.req.query(\"to\") || \"0\");\n if (!from || !to) return c.json({ error: \"from and to params required\" }, 400);\n const diff = schemaTracker.getDiff(from, to);\n if (!diff) return c.json({ error: \"Snapshot not found\" }, 404);\n return c.json(diff);\n } catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.post(\"/api/schema/snapshot\", async (c) => {\n try {\n const result = await schemaTracker.takeSnapshot();\n return c.json(result);\n } catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n}\n","import type { Hono } from \"hono\";\nimport type { AlertManager } from \"../alerts.js\";\n\nexport function registerAlertsRoutes(app: Hono, alertManager: AlertManager) {\n app.get(\"/api/alerts/rules\", (c) => {\n try { return c.json(alertManager.getRules()); }\n catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.post(\"/api/alerts/rules\", async (c) => {\n try {\n const body = await c.req.json();\n const rule = alertManager.addRule(body);\n return c.json(rule, 201);\n } catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.put(\"/api/alerts/rules/:id\", async (c) => {\n try {\n const id = parseInt(c.req.param(\"id\"));\n const body = await c.req.json();\n const ok = alertManager.updateRule(id, body);\n if (!ok) return c.json({ error: \"Rule not found\" }, 404);\n return c.json({ ok: true });\n } catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.delete(\"/api/alerts/rules/:id\", (c) => {\n try {\n const id = parseInt(c.req.param(\"id\"));\n const ok = alertManager.deleteRule(id);\n if (!ok) return c.json({ error: \"Rule not found\" }, 404);\n return c.json({ ok: true });\n } catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.get(\"/api/alerts/webhook-info\", (c) => {\n try {\n const url = alertManager.getWebhookUrl();\n const type = alertManager.getWebhookType();\n const masked = url ? url.replace(/\\/[^/]{8,}$/, \"/****\") : null;\n return c.json({ url: masked, type: type || \"none\", configured: !!url });\n } catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.post(\"/api/alerts/test-webhook\", async (c) => {\n try {\n const result = await alertManager.sendTestWebhook();\n return c.json(result, result.ok ? 200 : 400);\n } catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.get(\"/api/alerts/history\", (c) => {\n try {\n const limit = parseInt(c.req.query(\"limit\") || \"50\");\n return c.json(alertManager.getHistory(limit));\n } catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n}\n","// query-analyzer.ts — deep EXPLAIN plan analysis with auto index suggestions\n\nimport type { Pool } from \"pg\";\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport interface PlanNodeSummary {\n nodeType: string;\n table?: string;\n totalCost: number;\n actualRows?: number;\n actualTime?: number; // ms\n filter?: string;\n}\n\nexport interface SeqScanInfo {\n table: string;\n rowCount: number;\n filter?: string; // filter condition from explain output\n suggestion?: string;\n}\n\nexport interface IndexSuggestion {\n table: string;\n columns: string[];\n reason: string;\n sql: string; // CREATE INDEX CONCURRENTLY …\n estimatedBenefit: \"high\" | \"medium\" | \"low\";\n}\n\nexport interface ExplainAnalysis {\n planNodes: PlanNodeSummary[];\n seqScans: SeqScanInfo[];\n missingIndexes: IndexSuggestion[];\n costEstimate: {\n totalCost: number;\n actualTime?: number;\n planningTime?: number;\n };\n recommendations: string[];\n}\n\nexport interface QueryRegressionInfo {\n queryId: string; // queryid from pg_stat_statements\n currentMeanMs: number;\n previousMeanMs: number;\n changePercent: number;\n degradedAt?: string; // approximate timestamp\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\n/**\n * Recursively walk a plan tree (EXPLAIN FORMAT JSON) and collect every node.\n * Each node looks like { \"Node Type\": \"...\", \"Plans\": [...], ... }\n */\nfunction collectNodes(node: any, acc: any[] = []): any[] {\n if (!node || typeof node !== \"object\") return acc;\n acc.push(node);\n const plans = node[\"Plans\"] ?? node[\"plans\"];\n if (Array.isArray(plans)) {\n for (const child of plans) collectNodes(child, acc);\n }\n return acc;\n}\n\n/**\n * Extract simple column names from a Postgres filter expression.\n * Handles patterns like:\n * (col = $1)\n * (col > $1)\n * (col IS NULL)\n * (col IS NOT NULL)\n * (col ~~ '%foo%') -- LIKE\n */\nfunction extractColumnsFromFilter(filter: string): string[] {\n // Match identifiers that appear before comparison operators\n const colPattern = /\\(?\"?([a-z_][a-z0-9_]*)\"?\\s*(?:=|<|>|<=|>=|<>|!=|IS\\s+(?:NOT\\s+)?NULL|~~|!~~)/gi;\n const found = new Set<string>();\n let m: RegExpExecArray | null;\n while ((m = colPattern.exec(filter)) !== null) {\n const col = m[1].toLowerCase();\n // Skip Postgres internal names\n if (![\"and\", \"or\", \"not\", \"true\", \"false\", \"null\"].includes(col)) {\n found.add(col);\n }\n }\n return Array.from(found);\n}\n\n/**\n * Fetch the list of indexed column sets for a given table from pg_indexes.\n * Returns an array of column name arrays (one per index).\n */\nasync function getExistingIndexColumns(pool: Pool, tableName: string): Promise<string[][]> {\n try {\n // Query pg_indexes to get index definitions\n const r = await pool.query(\n `SELECT indexdef FROM pg_indexes WHERE tablename = $1`,\n [tableName]\n );\n return r.rows.map((row: any) => {\n // Parse column list from: ... ON table (col1, col2, ...)\n const m = /\\(([^)]+)\\)/.exec(row.indexdef);\n if (!m) return [] as string[];\n return m[1]\n .split(\",\")\n .map((c: string) => c.trim().replace(/^\"|\"$/g, \"\").toLowerCase());\n });\n } catch {\n return [];\n }\n}\n\n/**\n * Benefit rating based on estimated row count.\n */\nfunction rateBenefit(rowCount: number): \"high\" | \"medium\" | \"low\" {\n if (rowCount > 100_000) return \"high\";\n if (rowCount >= 10_000) return \"medium\";\n return \"low\";\n}\n\n/**\n * Format a large number as human-readable (1.2M, 50K, etc.)\n */\nfunction fmtRows(n: number): string {\n if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;\n if (n >= 1_000) return `${(n / 1_000).toFixed(0)}K`;\n return String(n);\n}\n\n// ─── Core analysis ────────────────────────────────────────────────────────────\n\n/**\n * Analyse a EXPLAIN (FORMAT JSON) result and return rich diagnostics.\n *\n * @param explainJson - The value of `r.rows[0][\"QUERY PLAN\"]` (an array with one plan object)\n * @param pool - Optional PG pool; without it only static analysis is performed\n */\nexport async function analyzeExplainPlan(\n explainJson: any,\n pool?: Pool | null\n): Promise<ExplainAnalysis> {\n const result: ExplainAnalysis = {\n planNodes: [],\n seqScans: [],\n missingIndexes: [],\n costEstimate: { totalCost: 0 },\n recommendations: [],\n };\n\n if (!explainJson || !Array.isArray(explainJson) || explainJson.length === 0) {\n return result;\n }\n\n const topLevel = explainJson[0];\n const planRoot = topLevel?.[\"Plan\"] ?? topLevel?.[\"plan\"];\n\n // Planning / execution times from top-level\n const planningTime: number | undefined = topLevel?.[\"Planning Time\"] ?? undefined;\n const executionTime: number | undefined = topLevel?.[\"Execution Time\"] ?? undefined;\n\n if (!planRoot) return result;\n\n // Collect all nodes\n const allNodes = collectNodes(planRoot);\n\n // Build planNodes summary\n result.planNodes = allNodes.map((n: any) => {\n const s: PlanNodeSummary = {\n nodeType: n[\"Node Type\"] ?? \"Unknown\",\n totalCost: n[\"Total Cost\"] ?? 0,\n };\n if (n[\"Relation Name\"]) s.table = n[\"Relation Name\"];\n if (n[\"Actual Rows\"] !== undefined) s.actualRows = n[\"Actual Rows\"];\n if (n[\"Actual Total Time\"] !== undefined) s.actualTime = n[\"Actual Total Time\"];\n if (n[\"Filter\"]) s.filter = n[\"Filter\"];\n return s;\n });\n\n // Cost estimate from root node\n result.costEstimate = {\n totalCost: planRoot[\"Total Cost\"] ?? 0,\n actualTime: executionTime,\n planningTime,\n };\n\n // ── Seq Scan analysis ──────────────────────────────────────────────────────\n const seqScanNodes = allNodes.filter((n: any) => n[\"Node Type\"] === \"Seq Scan\");\n\n for (const node of seqScanNodes) {\n const table: string = node[\"Relation Name\"] ?? \"unknown\";\n const rowCount: number = node[\"Plan Rows\"] ?? node[\"Actual Rows\"] ?? 0;\n const filter: string | undefined = node[\"Filter\"];\n\n const info: SeqScanInfo = { table, rowCount, filter };\n\n if (rowCount > 10_000) {\n info.suggestion = filter\n ? `Consider adding an index to support the filter on ${table}`\n : `Full table scan on large table ${table} — review query`;\n }\n\n result.seqScans.push(info);\n }\n\n // ── Missing index inference ────────────────────────────────────────────────\n for (const scan of result.seqScans) {\n if (!scan.filter) continue;\n\n const cols = extractColumnsFromFilter(scan.filter);\n if (cols.length === 0) continue;\n\n // Check existing indexes (needs DB)\n let existingIndexCols: string[][] = [];\n if (pool) {\n existingIndexCols = await getExistingIndexColumns(pool, scan.table);\n }\n\n // Filter out columns already covered as the leading column of an existing index\n const uncoveredCols = cols.filter(\n (col) => !existingIndexCols.some((idxCols) => idxCols.length > 0 && idxCols[0] === col)\n );\n\n if (uncoveredCols.length === 0) continue;\n\n const benefit = rateBenefit(scan.rowCount);\n\n if (uncoveredCols.length >= 2) {\n // Suggest a composite index\n const idxName = `idx_${scan.table}_${uncoveredCols.join(\"_\")}`;\n const sql = `CREATE INDEX CONCURRENTLY ${idxName} ON ${scan.table} (${uncoveredCols.join(\", \")})`;\n result.missingIndexes.push({\n table: scan.table,\n columns: uncoveredCols,\n reason: `Seq Scan with multi-column filter (${uncoveredCols.join(\", \")}) on ${fmtRows(scan.rowCount)} rows — composite index preferred`,\n sql,\n estimatedBenefit: benefit,\n });\n } else {\n // Single column\n const col = uncoveredCols[0];\n const idxName = `idx_${scan.table}_${col}`;\n const sql = `CREATE INDEX CONCURRENTLY ${idxName} ON ${scan.table} (${col})`;\n result.missingIndexes.push({\n table: scan.table,\n columns: [col],\n reason: `Seq Scan with Filter on ${col} (${fmtRows(scan.rowCount)} rows)`,\n sql,\n estimatedBenefit: benefit,\n });\n }\n }\n\n // ── Recommendations ────────────────────────────────────────────────────────\n for (const scan of result.seqScans) {\n if (scan.rowCount > 10_000) {\n const filterPart = scan.filter\n ? ` — consider adding index on ${extractColumnsFromFilter(scan.filter).join(\", \") || \"filter columns\"}`\n : \" — no filter; full scan may be intentional\";\n result.recommendations.push(\n `Seq Scan on ${scan.table} (${fmtRows(scan.rowCount)} rows)${filterPart}`\n );\n }\n }\n\n if (planningTime !== undefined) {\n const label = planningTime > 10 ? \"high — check statistics\" : \"normal\";\n result.recommendations.push(`Planning time ${planningTime.toFixed(1)}ms — ${label}`);\n }\n\n if (result.missingIndexes.length === 0 && result.seqScans.length === 0) {\n result.recommendations.push(\"No obvious sequential scans detected — query looks efficient\");\n }\n\n return result;\n}\n\n// ─── Regression detection ─────────────────────────────────────────────────────\n\n/**\n * Detect queries whose mean execution time has increased by more than 50%\n * compared to the earliest snapshot in the query_stats store for the given window.\n *\n * This is a best-effort function; it silently returns [] if pg_stat_statements\n * is unavailable or the query_stats store doesn't have enough history.\n *\n * @param pool - PG pool (used to read pg_stat_statements)\n * @param statsDb - Optional better-sqlite3 Database with query_stats table\n * @param windowHours - How far back to compare (default 24 h)\n */\nexport async function detectQueryRegressions(\n pool: Pool,\n statsDb?: any | null,\n windowHours = 24\n): Promise<QueryRegressionInfo[]> {\n try {\n // ── 1. Check pg_stat_statements is available ───────────────────────────\n const extCheck = await pool.query(\n \"SELECT 1 FROM pg_extension WHERE extname = 'pg_stat_statements'\"\n );\n if (extCheck.rows.length === 0) return [];\n\n // ── 2. Get current snapshot from pg_stat_statements ───────────────────\n const current = await pool.query(`\n SELECT queryid::text AS queryid, mean_exec_time\n FROM pg_stat_statements\n WHERE query NOT LIKE '%pg_stat%'\n AND queryid IS NOT NULL\n `);\n\n const currentMap = new Map<string, number>();\n for (const row of current.rows) {\n currentMap.set(row.queryid, parseFloat(row.mean_exec_time));\n }\n\n if (!statsDb) return [];\n\n // ── 3. Fetch historical baselines from SQLite query_stats ─────────────\n const windowMs = windowHours * 60 * 60 * 1000;\n const since = Date.now() - windowMs;\n\n let historical: { queryid: string; mean_exec_time: number; timestamp: number }[];\n try {\n historical = statsDb\n .prepare(\n `SELECT queryid, mean_exec_time, timestamp\n FROM query_stats\n WHERE timestamp >= ?\n ORDER BY queryid, timestamp ASC`\n )\n .all(since) as any[];\n } catch {\n return [];\n }\n\n // Keep only the *earliest* record per queryid in the window\n const baselineMap = new Map<string, { meanMs: number; timestamp: number }>();\n for (const row of historical) {\n if (!baselineMap.has(row.queryid)) {\n baselineMap.set(row.queryid, {\n meanMs: row.mean_exec_time,\n timestamp: row.timestamp,\n });\n }\n }\n\n // ── 4. Detect regressions > 50% ────────────────────────────────────────\n const regressions: QueryRegressionInfo[] = [];\n\n for (const [queryId, baseline] of baselineMap) {\n const currentMean = currentMap.get(queryId);\n if (currentMean === undefined || baseline.meanMs === 0) continue;\n\n const changePercent =\n ((currentMean - baseline.meanMs) / baseline.meanMs) * 100;\n\n if (changePercent > 50) {\n regressions.push({\n queryId,\n currentMeanMs: currentMean,\n previousMeanMs: baseline.meanMs,\n changePercent: Math.round(changePercent),\n degradedAt: new Date(baseline.timestamp).toISOString(),\n });\n }\n }\n\n return regressions.sort((a, b) => b.changePercent - a.changePercent);\n } catch {\n return [];\n }\n}\n","import type { Hono } from \"hono\";\nimport type { Pool } from \"pg\";\nimport { analyzeExplainPlan } from \"../query-analyzer.js\";\n\nconst DDL_PATTERN = /\\b(CREATE|DROP|ALTER|TRUNCATE|GRANT|REVOKE)\\b/i;\n\nexport function registerExplainRoutes(app: Hono, pool: Pool) {\n app.post(\"/api/explain\", async (c) => {\n try {\n const body = await c.req.json();\n const query = body?.query?.trim();\n if (!query) return c.json({ error: \"Missing query\" }, 400);\n if (DDL_PATTERN.test(query)) return c.json({ error: \"DDL statements are not allowed\" }, 400);\n if (!/^\\s*SELECT\\b/i.test(query)) return c.json({ error: \"Only SELECT queries can be explained for safety. DELETE/UPDATE/INSERT are blocked.\" }, 400);\n\n const client = await pool.connect();\n try {\n await client.query(\"SET statement_timeout = '30s'\");\n await client.query(\"BEGIN\");\n try {\n const r = await client.query(`EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON) ${query}`);\n await client.query(\"ROLLBACK\");\n await client.query(\"RESET statement_timeout\");\n\n const plan = r.rows[0][\"QUERY PLAN\"];\n\n // Deep analysis (best-effort; never throws)\n let analysis = null;\n try {\n analysis = await analyzeExplainPlan(plan, pool);\n } catch {\n // analysis unavailable — return plan only\n }\n\n return c.json({ plan, analysis });\n } catch (err: any) {\n await client.query(\"ROLLBACK\").catch(() => {});\n await client.query(\"RESET statement_timeout\").catch(() => {});\n return c.json({ error: err.message }, 400);\n }\n } finally {\n client.release();\n }\n } catch (err: any) {\n return c.json({ error: err.message }, 500);\n }\n });\n}\n","import type { TimeseriesStore } from \"./timeseries.js\";\n\nexport interface DiskPrediction {\n currentBytes: number;\n growthRatePerDay: number;\n predictedFullDate: Date | null;\n daysUntilFull: number | null;\n confidence: number;\n}\n\n/**\n * Simple linear regression: y = mx + b\n * Returns { slope, intercept, r2 }\n */\nexport function linearRegression(points: { x: number; y: number }[]): { slope: number; intercept: number; r2: number } {\n const n = points.length;\n if (n < 2) return { slope: 0, intercept: 0, r2: 0 };\n\n let sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0, sumY2 = 0;\n for (const p of points) {\n sumX += p.x;\n sumY += p.y;\n sumXY += p.x * p.y;\n sumX2 += p.x * p.x;\n sumY2 += p.y * p.y;\n }\n\n const denom = n * sumX2 - sumX * sumX;\n if (denom === 0) return { slope: 0, intercept: sumY / n, r2: 0 };\n\n const slope = (n * sumXY - sumX * sumY) / denom;\n const intercept = (sumY - slope * sumX) / n;\n\n // R² calculation\n const meanY = sumY / n;\n let ssTot = 0, ssRes = 0;\n for (const p of points) {\n ssTot += (p.y - meanY) ** 2;\n ssRes += (p.y - (slope * p.x + intercept)) ** 2;\n }\n const r2 = ssTot === 0 ? 1 : Math.max(0, 1 - ssRes / ssTot);\n\n return { slope, intercept, r2 };\n}\n\nexport class DiskPredictor {\n /**\n * Predict disk growth based on historical metric data.\n * @param store TimeseriesStore instance\n * @param metric Metric name (e.g. \"db_size_bytes\")\n * @param daysAhead How many days to project ahead\n * @param maxDiskBytes Optional max disk capacity for \"days until full\" calc\n */\n predict(store: TimeseriesStore, metric: string, daysAhead: number, maxDiskBytes?: number): DiskPrediction | null {\n const now = Date.now();\n // Get all available data (up to 30 days)\n const data = store.query(metric, now - 30 * 24 * 60 * 60 * 1000, now);\n\n if (data.length < 2) return null;\n\n // Need at least 24 hours of data\n const timeSpanMs = data[data.length - 1].timestamp - data[0].timestamp;\n if (timeSpanMs < 24 * 60 * 60 * 1000) return null;\n\n const currentBytes = data[data.length - 1].value;\n\n // Normalize timestamps to days from first point\n const t0 = data[0].timestamp;\n const points = data.map(d => ({\n x: (d.timestamp - t0) / (24 * 60 * 60 * 1000), // days\n y: d.value,\n }));\n\n const { slope, r2 } = linearRegression(points);\n const growthRatePerDay = slope; // bytes per day\n\n let predictedFullDate: Date | null = null;\n let daysUntilFull: number | null = null;\n\n if (maxDiskBytes && growthRatePerDay > 0) {\n const remainingBytes = maxDiskBytes - currentBytes;\n daysUntilFull = remainingBytes / growthRatePerDay;\n if (daysUntilFull > 0 && daysUntilFull < 365 * 10) {\n predictedFullDate = new Date(now + daysUntilFull * 24 * 60 * 60 * 1000);\n }\n }\n\n return {\n currentBytes,\n growthRatePerDay,\n predictedFullDate,\n daysUntilFull: daysUntilFull !== null && daysUntilFull > 0 ? daysUntilFull : null,\n confidence: r2,\n };\n }\n}\n","import type { Hono } from \"hono\";\nimport type { Pool } from \"pg\";\nimport type { TimeseriesStore } from \"../timeseries.js\";\nimport { DiskPredictor } from \"../disk-prediction.js\";\n\nconst RANGE_MAP: Record<string, number> = {\n \"24h\": 24 * 60 * 60 * 1000,\n \"7d\": 7 * 24 * 60 * 60 * 1000,\n \"30d\": 30 * 24 * 60 * 60 * 1000,\n};\n\nexport function registerDiskRoutes(app: Hono, pool: Pool, store: TimeseriesStore) {\n const predictor = new DiskPredictor();\n\n app.get(\"/api/disk/usage\", async (c) => {\n try {\n const client = await pool.connect();\n try {\n // Database size + data directory\n const dbRes = await client.query(`\n SELECT pg_database_size(current_database()) AS db_size,\n (SELECT setting FROM pg_settings WHERE name = 'data_directory') AS data_dir\n `);\n const { db_size, data_dir } = dbRes.rows[0];\n\n // Tablespace sizes\n const tsRes = await client.query(`SELECT spcname, pg_tablespace_size(oid) AS size FROM pg_tablespace`);\n const tablespaces = tsRes.rows.map((r: any) => ({\n name: r.spcname,\n size: parseInt(r.size),\n }));\n\n // Top 20 largest tables\n const tableRes = await client.query(`\n SELECT schemaname, relname,\n pg_total_relation_size(quote_ident(schemaname) || '.' || quote_ident(relname)) as total_size,\n pg_relation_size(quote_ident(schemaname) || '.' || quote_ident(relname)) as table_size,\n pg_indexes_size(quote_ident(schemaname) || '.' || quote_ident(relname)) as index_size\n FROM pg_stat_user_tables\n ORDER BY pg_total_relation_size(quote_ident(schemaname) || '.' || quote_ident(relname)) DESC\n LIMIT 20\n `);\n const tables = tableRes.rows.map((r: any) => ({\n schema: r.schemaname,\n name: r.relname,\n totalSize: parseInt(r.total_size),\n tableSize: parseInt(r.table_size),\n indexSize: parseInt(r.index_size),\n }));\n\n return c.json({\n dbSize: parseInt(db_size),\n dataDir: data_dir,\n tablespaces,\n tables,\n });\n } finally {\n client.release();\n }\n } catch (err: any) {\n return c.json({ error: err.message }, 500);\n }\n });\n\n app.get(\"/api/disk/prediction\", (c) => {\n try {\n const days = parseInt(c.req.query(\"days\") || \"30\");\n const maxDisk = c.req.query(\"maxDisk\") ? parseInt(c.req.query(\"maxDisk\")!) : undefined;\n const prediction = predictor.predict(store, \"db_size_bytes\", days, maxDisk);\n return c.json({ prediction });\n } catch (err: any) {\n return c.json({ error: err.message }, 500);\n }\n });\n\n app.get(\"/api/disk/table-history/:table\", (c) => {\n try {\n const table = c.req.param(\"table\");\n const range = c.req.query(\"range\") || \"24h\";\n const rangeMs = RANGE_MAP[range] || RANGE_MAP[\"24h\"];\n const now = Date.now();\n const data = store.query(`table_size:${table}`, now - rangeMs, now);\n return c.json(data);\n } catch (err: any) {\n return c.json({ error: err.message }, 500);\n }\n });\n\n app.get(\"/api/disk/history\", (c) => {\n try {\n const range = c.req.query(\"range\") || \"24h\";\n const rangeMs = RANGE_MAP[range] || RANGE_MAP[\"24h\"];\n const now = Date.now();\n const data = store.query(\"db_size_bytes\", now - rangeMs, now);\n return c.json(data);\n } catch (err: any) {\n return c.json({ error: err.message }, 500);\n }\n });\n}\n","import type Database from \"better-sqlite3\";\nimport type { Pool } from \"pg\";\n\nconst DEFAULT_RETENTION_DAYS = 7;\n\nexport interface QueryStatRow {\n timestamp: number;\n queryid: string;\n query: string;\n calls: number;\n total_exec_time: number;\n mean_exec_time: number;\n min_exec_time: number;\n max_exec_time: number;\n rows: number;\n shared_blks_hit: number;\n shared_blks_read: number;\n}\n\nexport interface TopQuery {\n queryid: string;\n query: string;\n total_calls: number;\n total_exec_time: number;\n mean_exec_time: number;\n total_rows: number;\n}\n\ninterface CumulativeRow {\n queryid: string;\n query: string;\n calls: number;\n total_exec_time: number;\n mean_exec_time: number;\n min_exec_time: number;\n max_exec_time: number;\n rows: number;\n shared_blks_hit: number;\n shared_blks_read: number;\n}\n\nexport class QueryStatsStore {\n private db: Database.Database;\n private insertStmt: Database.Statement;\n private retentionMs: number;\n private prev: Map<string, CumulativeRow> = new Map();\n private timer: ReturnType<typeof setInterval> | null = null;\n private pruneTimer: ReturnType<typeof setInterval> | null = null;\n\n constructor(db: Database.Database, retentionDays = DEFAULT_RETENTION_DAYS) {\n this.db = db;\n this.retentionMs = retentionDays * 24 * 60 * 60 * 1000;\n\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS query_stats (\n timestamp INTEGER NOT NULL,\n queryid TEXT NOT NULL,\n query TEXT,\n calls INTEGER,\n total_exec_time REAL,\n mean_exec_time REAL,\n min_exec_time REAL,\n max_exec_time REAL,\n rows INTEGER,\n shared_blks_hit INTEGER,\n shared_blks_read INTEGER\n );\n CREATE INDEX IF NOT EXISTS idx_qs_queryid_ts ON query_stats(queryid, timestamp);\n `);\n\n this.insertStmt = this.db.prepare(\n `INSERT INTO query_stats (timestamp, queryid, query, calls, total_exec_time, mean_exec_time, min_exec_time, max_exec_time, rows, shared_blks_hit, shared_blks_read)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`\n );\n }\n\n startPeriodicSnapshot(pool: Pool, intervalMs = 5 * 60 * 1000): void {\n this.snapshot(pool).catch((err) =>\n console.error(\"[query-stats] Initial snapshot failed:\", err.message)\n );\n this.timer = setInterval(() => {\n this.snapshot(pool).catch((err) =>\n console.error(\"[query-stats] Snapshot failed:\", err.message)\n );\n }, intervalMs);\n // Prune once per hour\n this.pruneTimer = setInterval(() => this.prune(), 60 * 60 * 1000);\n }\n\n stop(): void {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n if (this.pruneTimer) {\n clearInterval(this.pruneTimer);\n this.pruneTimer = null;\n }\n }\n\n async snapshot(pool: Pool): Promise<number> {\n const client = await pool.connect();\n try {\n // Check if pg_stat_statements is available\n const extCheck = await client.query(\n \"SELECT 1 FROM pg_extension WHERE extname = 'pg_stat_statements'\"\n );\n if (extCheck.rows.length === 0) return 0;\n\n const r = await client.query(`\n SELECT\n queryid::text,\n query,\n calls::int,\n total_exec_time,\n mean_exec_time,\n min_exec_time,\n max_exec_time,\n rows::int,\n shared_blks_hit::int,\n shared_blks_read::int\n FROM pg_stat_statements\n WHERE query NOT LIKE '%pg_stat%'\n AND query NOT LIKE '%pg_catalog%'\n AND queryid IS NOT NULL\n `);\n\n const now = Date.now();\n const hasPrev = this.prev.size > 0;\n let count = 0;\n\n const tx = this.db.transaction((rows: CumulativeRow[]) => {\n for (const row of rows) {\n const prev = this.prev.get(row.queryid);\n if (hasPrev && prev) {\n const deltaCalls = Math.max(0, row.calls - prev.calls);\n if (deltaCalls === 0) continue; // no new activity\n const deltaTime = Math.max(0, row.total_exec_time - prev.total_exec_time);\n const deltaRows = Math.max(0, row.rows - prev.rows);\n const deltaHit = Math.max(0, row.shared_blks_hit - prev.shared_blks_hit);\n const deltaRead = Math.max(0, row.shared_blks_read - prev.shared_blks_read);\n const meanTime = deltaCalls > 0 ? deltaTime / deltaCalls : 0;\n\n this.insertStmt.run(\n now, row.queryid, row.query,\n deltaCalls, deltaTime, meanTime,\n row.min_exec_time, row.max_exec_time,\n deltaRows, deltaHit, deltaRead\n );\n count++;\n } else if (!hasPrev) {\n // First snapshot: store cumulative values\n this.insertStmt.run(\n now, row.queryid, row.query,\n row.calls, row.total_exec_time, row.mean_exec_time,\n row.min_exec_time, row.max_exec_time,\n row.rows, row.shared_blks_hit, row.shared_blks_read\n );\n count++;\n }\n }\n });\n\n tx(r.rows);\n\n // Update prev state\n this.prev.clear();\n for (const row of r.rows) {\n this.prev.set(row.queryid, row);\n }\n\n return count;\n } catch (err) {\n console.error(\"[query-stats] Error snapshotting:\", (err as Error).message);\n return 0;\n } finally {\n client.release();\n }\n }\n\n /** Insert a row directly (for testing) */\n insertRow(row: QueryStatRow): void {\n this.insertStmt.run(\n row.timestamp, row.queryid, row.query,\n row.calls, row.total_exec_time, row.mean_exec_time,\n row.min_exec_time, row.max_exec_time,\n row.rows, row.shared_blks_hit, row.shared_blks_read\n );\n }\n\n getTrend(queryid: string, startMs: number, endMs?: number): QueryStatRow[] {\n const end = endMs ?? Date.now();\n return this.db\n .prepare(\n `SELECT timestamp, queryid, query, calls, total_exec_time, mean_exec_time,\n min_exec_time, max_exec_time, rows, shared_blks_hit, shared_blks_read\n FROM query_stats\n WHERE queryid = ? AND timestamp >= ? AND timestamp <= ?\n ORDER BY timestamp`\n )\n .all(queryid, startMs, end) as QueryStatRow[];\n }\n\n getTopQueries(\n startMs: number,\n endMs: number,\n orderBy: \"total_time\" | \"mean_time\" | \"calls\" = \"total_time\",\n limit = 20\n ): TopQuery[] {\n const orderCol =\n orderBy === \"total_time\" ? \"SUM(total_exec_time)\"\n : orderBy === \"calls\" ? \"SUM(calls)\"\n : \"AVG(mean_exec_time)\";\n\n return this.db\n .prepare(\n `SELECT queryid, \n MAX(query) as query,\n SUM(calls) as total_calls,\n SUM(total_exec_time) as total_exec_time,\n AVG(mean_exec_time) as mean_exec_time,\n SUM(rows) as total_rows\n FROM query_stats\n WHERE timestamp >= ? AND timestamp <= ?\n GROUP BY queryid\n ORDER BY ${orderCol} DESC\n LIMIT ?`\n )\n .all(startMs, endMs, limit) as TopQuery[];\n }\n\n prune(retentionMs?: number): number {\n const cutoff = Date.now() - (retentionMs ?? this.retentionMs);\n const info = this.db.prepare(\"DELETE FROM query_stats WHERE timestamp < ?\").run(cutoff);\n return info.changes;\n }\n\n close(): void {\n // No-op: DB lifecycle managed externally\n }\n}\n","import type { Hono } from \"hono\";\nimport type { QueryStatsStore } from \"../query-stats.js\";\n\nconst RANGE_MAP: Record<string, number> = {\n \"1h\": 60 * 60 * 1000,\n \"6h\": 6 * 60 * 60 * 1000,\n \"24h\": 24 * 60 * 60 * 1000,\n \"7d\": 7 * 24 * 60 * 60 * 1000,\n};\n\nexport function registerQueryStatsRoutes(app: Hono, store: QueryStatsStore) {\n app.get(\"/api/query-stats/top\", (c) => {\n try {\n const range = c.req.query(\"range\") || \"1h\";\n const orderBy = (c.req.query(\"orderBy\") || \"total_time\") as \"total_time\" | \"mean_time\" | \"calls\";\n const limit = parseInt(c.req.query(\"limit\") || \"20\", 10);\n const rangeMs = RANGE_MAP[range] || RANGE_MAP[\"1h\"];\n const now = Date.now();\n const data = store.getTopQueries(now - rangeMs, now, orderBy, limit);\n return c.json(data);\n } catch (err: any) {\n return c.json({ error: err.message }, 500);\n }\n });\n\n app.get(\"/api/query-stats/trend/:queryid\", (c) => {\n try {\n const queryid = c.req.param(\"queryid\");\n const range = c.req.query(\"range\") || \"1h\";\n const rangeMs = RANGE_MAP[range] || RANGE_MAP[\"1h\"];\n const now = Date.now();\n const data = store.getTrend(queryid, now - rangeMs, now);\n return c.json(data);\n } catch (err: any) {\n return c.json({ error: err.message }, 500);\n }\n });\n}\n","import type { Hono } from \"hono\";\nimport type { Pool } from \"pg\";\nimport { getOverview } from \"../queries/overview.js\";\nimport { getAdvisorReport, gradeFromScore } from \"../advisor.js\";\n\nexport function registerExportRoutes(app: Hono, pool: Pool, longQueryThreshold: number) {\n app.get(\"/api/export\", async (c) => {\n const format = c.req.query(\"format\") || \"json\";\n\n try {\n const [overview, advisor] = await Promise.all([\n getOverview(pool),\n getAdvisorReport(pool, longQueryThreshold),\n ]);\n\n if (format === \"md\") {\n const lines: string[] = [];\n lines.push(`# pg-dash Health Report`);\n lines.push(`\\nGenerated: ${new Date().toISOString()}\\n`);\n lines.push(`## Overview\\n`);\n lines.push(`- **PostgreSQL**: ${overview.version}`);\n lines.push(`- **Database Size**: ${overview.dbSize}`);\n lines.push(`- **Connections**: ${overview.connections.active} active / ${overview.connections.idle} idle / ${overview.connections.max} max`);\n lines.push(`\\n## Health Score: ${advisor.score}/100 (Grade: ${advisor.grade})\\n`);\n lines.push(`### Category Breakdown\\n`);\n lines.push(`| Category | Grade | Score | Issues |`);\n lines.push(`|----------|-------|-------|--------|`);\n for (const [cat, b] of Object.entries(advisor.breakdown)) {\n lines.push(`| ${cat} | ${b.grade} | ${b.score}/100 | ${b.count} |`);\n }\n if (advisor.issues.length > 0) {\n lines.push(`\\n### Issues (${advisor.issues.length})\\n`);\n for (const issue of advisor.issues) {\n const icon = issue.severity === \"critical\" ? \"🔴\" : issue.severity === \"warning\" ? \"🟡\" : \"🔵\";\n lines.push(`#### ${icon} [${issue.severity}] ${issue.title}\\n`);\n lines.push(`${issue.description}\\n`);\n lines.push(`**Impact**: ${issue.impact}\\n`);\n lines.push(`**Fix**:\\n\\`\\`\\`sql\\n${issue.fix}\\n\\`\\`\\`\\n`);\n }\n } else {\n lines.push(`\\n✅ No issues found!\\n`);\n }\n const md = lines.join(\"\\n\");\n c.header(\"Content-Type\", \"text/markdown; charset=utf-8\");\n c.header(\"Content-Disposition\", `attachment; filename=\"pg-dash-report-${new Date().toISOString().slice(0, 10)}.md\"`);\n return c.body(md);\n }\n\n // Default: JSON\n const data = { overview, advisor, exportedAt: new Date().toISOString() };\n c.header(\"Content-Disposition\", `attachment; filename=\"pg-dash-report-${new Date().toISOString().slice(0, 10)}.json\"`);\n return c.json(data);\n } catch (err: any) {\n return c.json({ error: err.message }, 500);\n }\n });\n}\n"],"mappings":"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","names":["fs","path","Pool","path","fs","os","Database","path","os","fs","RANGE_MAP","RANGE_MAP","DEFAULT_RETENTION_DAYS","RANGE_MAP","Database","path","os","fs","fs","path","fileURLToPath","__dirname","Pool","getAdvisorReport","saveSnapshot","loadSnapshot","diffSnapshots","os","analyzeMigration","Database","diffEnvironments","formatTextDiff","formatMdDiff"]}
1
+ {"version":3,"sources":["../src/server/advisor.ts","../src/server/queries/schema.ts","../src/server/schema-diff.ts","../src/server/schema-tracker.ts","../src/server/snapshot.ts","../src/server/migration-checker.ts","../src/server/env-differ.ts","../src/cli.ts","../src/server/index.ts","../src/server/queries/overview.ts","../src/server/queries/databases.ts","../src/server/queries/tables.ts","../src/server/queries/activity.ts","../src/server/timeseries.ts","../src/server/collector.ts","../src/server/notifiers.ts","../src/server/alerts.ts","../src/server/routes/overview.ts","../src/server/routes/metrics.ts","../src/server/queries/slow-queries.ts","../src/server/routes/activity.ts","../src/server/routes/advisor.ts","../src/server/routes/schema.ts","../src/server/routes/alerts.ts","../src/server/query-analyzer.ts","../src/server/routes/explain.ts","../src/server/disk-prediction.ts","../src/server/routes/disk.ts","../src/server/query-stats.ts","../src/server/routes/query-stats.ts","../src/server/routes/export.ts"],"sourcesContent":["import type { Pool } from \"pg\";\n\nexport interface AdvisorIssue {\n id: string;\n severity: \"critical\" | \"warning\" | \"info\";\n category: \"performance\" | \"maintenance\" | \"schema\" | \"security\";\n title: string;\n description: string;\n fix: string;\n impact: string;\n effort: \"quick\" | \"moderate\" | \"involved\";\n}\n\nexport interface BatchFix {\n type: string;\n title: string;\n count: number;\n sql: string;\n}\n\nexport interface AdvisorResult {\n score: number;\n grade: string;\n issues: AdvisorIssue[];\n breakdown: Record<string, { score: number; grade: string; count: number }>;\n skipped: string[];\n ignoredCount: number;\n batchFixes: BatchFix[];\n}\n\nconst SEVERITY_WEIGHT = { critical: 15, warning: 5, info: 1 } as const;\nconst MAX_DEDUCTION = { critical: 60, warning: 30, info: 10 } as const;\n\nexport function computeAdvisorScore(issues: AdvisorIssue[]): number {\n let score = 100;\n const deductions = { critical: 0, warning: 0, info: 0 };\n const counts = { critical: 0, warning: 0, info: 0 };\n for (const issue of issues) {\n counts[issue.severity]++;\n const n = counts[issue.severity];\n const weight = SEVERITY_WEIGHT[issue.severity];\n // Diminishing penalty: full for first 3, half for 4-10, quarter for 11+\n let penalty: number;\n if (n <= 3) penalty = weight;\n else if (n <= 10) penalty = weight * 0.5;\n else penalty = weight * 0.25;\n deductions[issue.severity] += penalty;\n }\n // Cap deductions per severity\n for (const sev of [\"critical\", \"warning\", \"info\"] as const) {\n score -= Math.min(deductions[sev], MAX_DEDUCTION[sev]);\n }\n return Math.max(0, Math.min(100, Math.round(score)));\n}\n\nexport function gradeFromScore(score: number): string {\n if (score >= 90) return \"A\";\n if (score >= 80) return \"B\";\n if (score >= 70) return \"C\";\n if (score >= 50) return \"D\";\n return \"F\";\n}\n\nfunction computeBreakdown(issues: AdvisorIssue[]): Record<string, { score: number; grade: string; count: number }> {\n const categories = [\"performance\", \"maintenance\", \"schema\", \"security\"] as const;\n const result: Record<string, { score: number; grade: string; count: number }> = {};\n for (const cat of categories) {\n const catIssues = issues.filter((i) => i.category === cat);\n const score = computeAdvisorScore(catIssues);\n result[cat] = { score, grade: gradeFromScore(score), count: catIssues.length };\n }\n return result;\n}\n\nexport async function getAdvisorReport(pool: Pool, longQueryThreshold = 5): Promise<AdvisorResult> {\n const client = await pool.connect();\n const issues: AdvisorIssue[] = [];\n const skipped: string[] = [];\n\n try {\n // Detect PG version for compatibility\n const versionResult = await client.query(\"SHOW server_version_num\");\n const pgVersion = parseInt(versionResult.rows[0].server_version_num);\n\n // ── Performance Advisors ───────────────────────────────────────\n\n // Missing indexes (high seq scans on large tables)\n try {\n const r = await client.query(`\n SELECT schemaname, relname, seq_scan, seq_tup_read, n_live_tup,\n pg_size_pretty(pg_total_relation_size(relid)) AS size\n FROM pg_stat_user_tables\n WHERE n_live_tup > 10000 AND seq_scan > 100\n ORDER BY seq_tup_read DESC LIMIT 10\n `);\n for (const row of r.rows) {\n issues.push({\n id: `perf-seq-scan-${row.schemaname}-${row.relname}`,\n severity: row.seq_scan > 1000 ? \"warning\" : \"info\",\n category: \"performance\",\n title: `High sequential scans on ${row.relname}`,\n description: `Table ${row.schemaname}.${row.relname} (${row.n_live_tup} rows, ${row.size}) has ${row.seq_scan} sequential scans reading ${Number(row.seq_tup_read).toLocaleString()} tuples. Consider adding indexes on frequently filtered columns.`,\n fix: `-- Identify commonly filtered columns and add indexes:\\n-- EXPLAIN ANALYZE SELECT * FROM ${row.schemaname}.${row.relname} WHERE <your_condition>;\\nCREATE INDEX CONCURRENTLY idx_${row.relname}_<column> ON ${row.schemaname}.${row.relname} (<column>);`,\n impact: \"Queries will continue to do full table scans, degrading performance as the table grows.\",\n effort: \"moderate\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking seq scans:\", (err as Error).message); skipped.push(\"seq scans: \" + (err as Error).message);\n }\n\n // Bloated indexes (index size > 3x table size)\n try {\n const r = await client.query(`\n SELECT\n schemaname, relname, indexrelname,\n pg_relation_size(indexrelid) AS idx_size,\n pg_relation_size(relid) AS tbl_size,\n pg_size_pretty(pg_relation_size(indexrelid)) AS idx_size_pretty,\n pg_size_pretty(pg_relation_size(relid)) AS tbl_size_pretty\n FROM pg_stat_user_indexes\n WHERE pg_relation_size(indexrelid) > 1048576\n AND pg_relation_size(indexrelid) > pg_relation_size(relid) * 3\n ORDER BY pg_relation_size(indexrelid) DESC LIMIT 10\n `);\n for (const row of r.rows) {\n issues.push({\n id: `perf-bloated-idx-${row.indexrelname}`,\n severity: \"warning\",\n category: \"performance\",\n title: `Bloated index ${row.indexrelname}`,\n description: `Index ${row.indexrelname} on ${row.relname} is ${row.idx_size_pretty} but the table is only ${row.tbl_size_pretty}. The index may need rebuilding.`,\n fix: `REINDEX INDEX CONCURRENTLY ${row.schemaname}.${row.indexrelname};`,\n impact: \"Bloated indexes waste disk space and slow down queries that use them.\",\n effort: \"quick\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking bloated indexes:\", (err as Error).message); skipped.push(\"bloated indexes: \" + (err as Error).message);\n }\n\n // Table bloat (dead tuples > 10%)\n try {\n const r = await client.query(`\n SELECT schemaname, relname, n_dead_tup, n_live_tup,\n CASE WHEN n_live_tup > 0 THEN round(n_dead_tup::numeric / n_live_tup * 100, 1) ELSE 0 END AS dead_pct,\n pg_size_pretty(pg_total_relation_size(relid)) AS size\n FROM pg_stat_user_tables\n WHERE n_live_tup > 1000 AND n_dead_tup::float / GREATEST(n_live_tup, 1) > 0.1\n ORDER BY n_dead_tup DESC LIMIT 10\n `);\n for (const row of r.rows) {\n const pct = parseFloat(row.dead_pct);\n issues.push({\n id: `perf-bloat-${row.schemaname}-${row.relname}`,\n severity: pct > 30 ? \"critical\" : \"warning\",\n category: \"performance\",\n title: `Table bloat on ${row.relname} (${row.dead_pct}% dead)`,\n description: `${row.schemaname}.${row.relname} has ${Number(row.n_dead_tup).toLocaleString()} dead tuples (${row.dead_pct}% of ${Number(row.n_live_tup).toLocaleString()} live rows). Size: ${row.size}.`,\n fix: `VACUUM FULL ${row.schemaname}.${row.relname};`,\n impact: \"Dead tuples waste storage and degrade scan performance.\",\n effort: pct > 30 ? \"moderate\" : \"quick\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking table bloat:\", (err as Error).message); skipped.push(\"table bloat: \" + (err as Error).message);\n }\n\n // Cache efficiency per table\n try {\n const r = await client.query(`\n SELECT schemaname, relname,\n heap_blks_hit, heap_blks_read,\n CASE WHEN (heap_blks_hit + heap_blks_read) = 0 THEN 1\n ELSE heap_blks_hit::float / (heap_blks_hit + heap_blks_read) END AS ratio\n FROM pg_statio_user_tables\n WHERE (heap_blks_hit + heap_blks_read) > 100\n ORDER BY ratio ASC LIMIT 5\n `);\n for (const row of r.rows) {\n const ratio = parseFloat(row.ratio);\n if (ratio < 0.9) {\n issues.push({\n id: `perf-cache-${row.schemaname}-${row.relname}`,\n severity: ratio < 0.5 ? \"critical\" : \"warning\",\n category: \"performance\",\n title: `Poor cache hit ratio on ${row.relname}`,\n description: `Table ${row.schemaname}.${row.relname} has a cache hit ratio of ${(ratio * 100).toFixed(1)}%. Most reads are going to disk.`,\n fix: `-- Consider increasing shared_buffers or reducing working set:\\nSHOW shared_buffers;`,\n impact: \"Disk reads are orders of magnitude slower than memory reads.\",\n effort: \"involved\",\n });\n }\n }\n } catch (err) {\n console.error(\"[advisor] Error checking cache efficiency:\", (err as Error).message); skipped.push(\"cache efficiency: \" + (err as Error).message);\n }\n\n // Slow queries from pg_stat_statements\n try {\n const extCheck = await client.query(\"SELECT 1 FROM pg_extension WHERE extname = 'pg_stat_statements'\");\n if (extCheck.rows.length > 0) {\n const r = await client.query(`\n SELECT query, calls, mean_exec_time, total_exec_time,\n round(mean_exec_time::numeric, 2) AS mean_ms,\n round(total_exec_time::numeric / 1000, 2) AS total_sec\n FROM pg_stat_statements\n WHERE query NOT LIKE '%pg_stat%' AND query NOT LIKE '%pg_catalog%'\n AND mean_exec_time > 100\n ORDER BY mean_exec_time DESC LIMIT 5\n `);\n for (const row of r.rows) {\n issues.push({\n id: `perf-slow-${row.query.slice(0, 30).replace(/\\W/g, \"_\")}`,\n severity: parseFloat(row.mean_ms) > 1000 ? \"warning\" : \"info\",\n category: \"performance\",\n title: `Slow query (avg ${row.mean_ms}ms)`,\n description: `Query averaging ${row.mean_ms}ms over ${row.calls} calls (total: ${row.total_sec}s): ${row.query.slice(0, 200)}`,\n fix: `EXPLAIN ANALYZE ${row.query.slice(0, 500)};`,\n impact: \"Slow queries degrade overall database responsiveness.\",\n effort: \"moderate\",\n });\n }\n }\n } catch (err) {\n console.error(\"[advisor] Error checking slow queries:\", (err as Error).message); skipped.push(\"slow queries: \" + (err as Error).message);\n }\n\n // ── Maintenance Advisors ───────────────────────────────────────\n\n // VACUUM overdue\n try {\n const r = await client.query(`\n SELECT schemaname, relname, last_vacuum, last_autovacuum, n_dead_tup\n FROM pg_stat_user_tables\n WHERE n_live_tup > 100\n AND (last_vacuum IS NULL AND last_autovacuum IS NULL\n OR GREATEST(last_vacuum, last_autovacuum) < now() - interval '7 days')\n ORDER BY n_dead_tup DESC LIMIT 15\n `);\n for (const row of r.rows) {\n const never = !row.last_vacuum && !row.last_autovacuum;\n issues.push({\n id: `maint-vacuum-${row.schemaname}-${row.relname}`,\n severity: never ? \"warning\" : \"info\",\n category: \"maintenance\",\n title: `VACUUM ${never ? \"never run\" : \"overdue\"} on ${row.relname}`,\n description: `${row.schemaname}.${row.relname} ${never ? \"has never been vacuumed\" : \"was last vacuumed over 7 days ago\"}. Dead tuples: ${Number(row.n_dead_tup).toLocaleString()}.`,\n fix: `VACUUM ANALYZE ${row.schemaname}.${row.relname};`,\n impact: \"Dead tuples accumulate, increasing table size and degrading query performance.\",\n effort: \"quick\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking vacuum overdue:\", (err as Error).message); skipped.push(\"vacuum overdue: \" + (err as Error).message);\n }\n\n // ANALYZE overdue\n try {\n const r = await client.query(`\n SELECT schemaname, relname\n FROM pg_stat_user_tables\n WHERE n_live_tup > 100\n AND last_analyze IS NULL AND last_autoanalyze IS NULL\n AND NOT EXISTS (\n SELECT 1 FROM pg_stat_user_tables t2\n WHERE t2.relname = pg_stat_user_tables.relname\n AND (t2.last_vacuum IS NULL AND t2.last_autovacuum IS NULL)\n )\n LIMIT 10\n `);\n for (const row of r.rows) {\n issues.push({\n id: `maint-analyze-${row.schemaname}-${row.relname}`,\n severity: \"info\",\n category: \"maintenance\",\n title: `ANALYZE never run on ${row.relname}`,\n description: `${row.schemaname}.${row.relname} has never been analyzed. The query planner may choose suboptimal plans.`,\n fix: `ANALYZE ${row.schemaname}.${row.relname};`,\n impact: \"Without statistics, the query planner makes poor estimates leading to slow queries.\",\n effort: \"quick\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking analyze overdue:\", (err as Error).message); skipped.push(\"analyze overdue: \" + (err as Error).message);\n }\n\n // Transaction ID wraparound risk\n try {\n const r = await client.query(`\n SELECT datname, age(datfrozenxid) AS xid_age\n FROM pg_database\n WHERE datname = current_database()\n `);\n for (const row of r.rows) {\n const age = parseInt(row.xid_age);\n if (age > 1_000_000_000) {\n issues.push({\n id: `maint-xid-wraparound`,\n severity: \"critical\",\n category: \"maintenance\",\n title: `Transaction ID wraparound risk`,\n description: `Database ${row.datname} has datfrozenxid age of ${age.toLocaleString()}. Wraparound occurs at ~2 billion.`,\n fix: `VACUUM FREEZE;`,\n impact: \"If wraparound occurs, PostgreSQL will shut down to prevent data loss.\",\n effort: \"involved\",\n });\n } else if (age > 500_000_000) {\n issues.push({\n id: `maint-xid-warning`,\n severity: \"warning\",\n category: \"maintenance\",\n title: `Transaction ID age is high`,\n description: `Database ${row.datname} has datfrozenxid age of ${age.toLocaleString()}.`,\n fix: `VACUUM FREEZE;`,\n impact: \"Approaching transaction ID wraparound threshold.\",\n effort: \"moderate\",\n });\n }\n }\n } catch (err) {\n console.error(\"[advisor] Error checking xid wraparound:\", (err as Error).message); skipped.push(\"xid wraparound: \" + (err as Error).message);\n }\n\n // Idle connections > 10 min\n try {\n const r = await client.query(`\n SELECT pid, state, now() - state_change AS idle_duration,\n client_addr::text, application_name,\n extract(epoch from now() - state_change)::int AS idle_seconds\n FROM pg_stat_activity\n WHERE state IN ('idle', 'idle in transaction')\n AND now() - state_change > $1 * interval '1 minute'\n AND pid != pg_backend_pid()\n `, [longQueryThreshold]);\n for (const row of r.rows) {\n const isIdleTx = row.state === \"idle in transaction\";\n issues.push({\n id: `maint-idle-${row.pid}`,\n severity: isIdleTx ? \"warning\" : \"info\",\n category: \"maintenance\",\n title: `${isIdleTx ? \"Idle in transaction\" : \"Idle connection\"} (PID ${row.pid})`,\n description: `PID ${row.pid} from ${row.client_addr || \"local\"} (${row.application_name || \"unknown\"}) has been ${row.state} for ${Math.round(row.idle_seconds / 60)} minutes.`,\n fix: `SELECT pg_terminate_backend(${row.pid});`,\n impact: isIdleTx ? \"Idle-in-transaction connections hold locks and prevent VACUUM.\" : \"Idle connections consume connection slots.\",\n effort: \"quick\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking idle connections:\", (err as Error).message); skipped.push(\"idle connections: \" + (err as Error).message);\n }\n\n // ── Schema Advisors ────────────────────────────────────────────\n\n // Missing primary keys\n try {\n const r = await client.query(`\n SELECT c.relname AS table_name, n.nspname AS schema\n FROM pg_class c\n JOIN pg_namespace n ON c.relnamespace = n.oid\n WHERE c.relkind = 'r' AND n.nspname = 'public'\n AND NOT EXISTS (\n SELECT 1 FROM pg_constraint con WHERE con.conrelid = c.oid AND con.contype = 'p'\n )\n `);\n for (const row of r.rows) {\n issues.push({\n id: `schema-no-pk-${row.schema}-${row.table_name}`,\n severity: \"warning\",\n category: \"schema\",\n title: `Missing primary key on ${row.table_name}`,\n description: `Table ${row.schema}.${row.table_name} has no primary key. This can cause replication issues and makes row identification unreliable.`,\n fix: `ALTER TABLE ${row.schema}.${row.table_name} ADD PRIMARY KEY (<column>);`,\n impact: \"No primary key means no unique row identity, problematic for replication and ORMs.\",\n effort: \"moderate\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking missing primary keys:\", (err as Error).message); skipped.push(\"missing primary keys: \" + (err as Error).message);\n }\n\n // Unused indexes (idx_scan = 0, size > 1MB)\n try {\n const r = await client.query(`\n SELECT schemaname, relname, indexrelname, idx_scan,\n pg_size_pretty(pg_relation_size(indexrelid)) AS idx_size,\n pg_relation_size(indexrelid) AS idx_bytes\n FROM pg_stat_user_indexes\n WHERE idx_scan = 0\n AND indexrelname NOT LIKE '%_pkey'\n AND pg_relation_size(indexrelid) > 1048576\n ORDER BY pg_relation_size(indexrelid) DESC LIMIT 10\n `);\n for (const row of r.rows) {\n issues.push({\n id: `schema-unused-idx-${row.indexrelname}`,\n severity: \"warning\",\n category: \"schema\",\n title: `Unused index ${row.indexrelname} (${row.idx_size})`,\n description: `Index ${row.indexrelname} on ${row.relname} has never been used (0 scans) and takes ${row.idx_size}.`,\n fix: `DROP INDEX CONCURRENTLY ${row.schemaname}.${row.indexrelname};`,\n impact: \"Unused indexes waste disk space and slow down writes.\",\n effort: \"quick\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking unused indexes:\", (err as Error).message); skipped.push(\"unused indexes: \" + (err as Error).message);\n }\n\n // Duplicate indexes\n try {\n const r = await client.query(`\n SELECT array_agg(idx.indexrelid::regclass::text) AS indexes,\n idx.indrelid::regclass::text AS table_name,\n pg_size_pretty(sum(pg_relation_size(idx.indexrelid))) AS total_size\n FROM pg_index idx\n GROUP BY idx.indrelid, idx.indkey\n HAVING count(*) > 1\n `);\n for (const row of r.rows) {\n issues.push({\n id: `schema-dup-idx-${row.table_name}-${row.indexes[0]}`,\n severity: \"warning\",\n category: \"schema\",\n title: `Duplicate indexes on ${row.table_name}`,\n description: `These indexes cover the same columns on ${row.table_name}: ${row.indexes.join(\", \")}. Total wasted space: ${row.total_size}.`,\n fix: `-- Keep one, drop the rest:\\nDROP INDEX CONCURRENTLY ${row.indexes.slice(1).join(\";\\nDROP INDEX CONCURRENTLY \")};`,\n impact: \"Duplicate indexes double the write overhead and waste disk space.\",\n effort: \"quick\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking duplicate indexes:\", (err as Error).message); skipped.push(\"duplicate indexes: \" + (err as Error).message);\n }\n\n // Missing foreign key indexes\n try {\n const r = await client.query(`\n SELECT\n conrelid::regclass::text AS table_name,\n a.attname AS column_name,\n confrelid::regclass::text AS referenced_table\n FROM pg_constraint c\n JOIN pg_attribute a ON a.attrelid = c.conrelid AND a.attnum = ANY(c.conkey)\n WHERE c.contype = 'f'\n AND NOT EXISTS (\n SELECT 1 FROM pg_index i\n WHERE i.indrelid = c.conrelid\n AND a.attnum = ANY(i.indkey)\n )\n `);\n for (const row of r.rows) {\n issues.push({\n id: `schema-fk-no-idx-${row.table_name}-${row.column_name}`,\n severity: \"warning\",\n category: \"schema\",\n title: `Missing index on FK column ${row.table_name}.${row.column_name}`,\n description: `Foreign key column ${row.column_name} on ${row.table_name} (references ${row.referenced_table}) has no index. This causes slow JOINs and cascading deletes.`,\n fix: `CREATE INDEX CONCURRENTLY idx_${row.table_name.replace(/\\./g, \"_\")}_${row.column_name} ON ${row.table_name} (${row.column_name});`,\n impact: \"JOINs and cascading deletes on this FK will require full table scans.\",\n effort: \"quick\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking missing FK indexes:\", (err as Error).message); skipped.push(\"missing FK indexes: \" + (err as Error).message);\n }\n\n // ── Infrastructure Advisors ──────────────────────────────────────\n\n // Lock detection\n try {\n const r = await client.query(`\n SELECT blocked_locks.pid AS blocked_pid,\n blocking_locks.pid AS blocking_pid,\n blocked_activity.query AS blocked_query\n FROM pg_catalog.pg_locks blocked_locks\n JOIN pg_catalog.pg_locks blocking_locks ON blocking_locks.locktype = blocked_locks.locktype\n AND blocking_locks.database IS NOT DISTINCT FROM blocked_locks.database\n AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation\n AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page\n AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple\n AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid\n AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid\n AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid\n AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid\n AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid\n AND blocking_locks.pid != blocked_locks.pid\n JOIN pg_catalog.pg_stat_activity blocked_activity ON blocked_activity.pid = blocked_locks.pid\n WHERE NOT blocked_locks.granted\n `);\n for (const row of r.rows) {\n issues.push({\n id: `perf-lock-blocked-${row.blocked_pid}`,\n severity: \"warning\",\n category: \"performance\",\n title: `Blocked query (PID ${row.blocked_pid} blocked by PID ${row.blocking_pid})`,\n description: `PID ${row.blocked_pid} is waiting for a lock held by PID ${row.blocking_pid}. Query: ${(row.blocked_query || \"\").slice(0, 200)}`,\n fix: `SELECT pg_cancel_backend(${row.blocking_pid});`,\n impact: \"Blocked queries cause cascading delays and potential timeouts.\",\n effort: \"quick\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking locks:\", (err as Error).message); skipped.push(\"locks: \" + (err as Error).message);\n }\n\n // WAL/replication lag\n try {\n const r = await client.query(`\n SELECT CASE WHEN pg_is_in_recovery()\n THEN pg_wal_lsn_diff(pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn())\n ELSE 0 END AS lag_bytes\n `);\n const lagBytes = parseInt(r.rows[0]?.lag_bytes ?? \"0\");\n if (lagBytes > 1048576) { // > 1MB\n issues.push({\n id: `perf-replication-lag`,\n severity: lagBytes > 104857600 ? \"critical\" : \"warning\",\n category: \"performance\",\n title: `Replication lag: ${(lagBytes / 1048576).toFixed(1)} MB`,\n description: `WAL replay is lagging by ${(lagBytes / 1048576).toFixed(1)} MB. This indicates the replica is falling behind.`,\n fix: `-- Check replication status:\\nSELECT * FROM pg_stat_replication;`,\n impact: \"High replication lag means the replica has stale data and failover may lose transactions.\",\n effort: \"involved\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking replication lag:\", (err as Error).message); skipped.push(\"replication lag: \" + (err as Error).message);\n }\n\n // Checkpoint frequency\n try {\n const checkpointView = pgVersion >= 170000 ? 'pg_stat_checkpointer' : 'pg_stat_bgwriter';\n const r = await client.query(`\n SELECT checkpoints_req, checkpoints_timed,\n CASE WHEN (checkpoints_req + checkpoints_timed) = 0 THEN 0\n ELSE round(checkpoints_req::numeric / (checkpoints_req + checkpoints_timed) * 100, 1) END AS req_pct\n FROM ${checkpointView}\n `);\n const reqPct = parseFloat(r.rows[0]?.req_pct ?? \"0\");\n if (reqPct > 50) {\n issues.push({\n id: `maint-checkpoint-frequency`,\n severity: reqPct > 80 ? \"warning\" : \"info\",\n category: \"maintenance\",\n title: `${reqPct}% of checkpoints are requested (not timed)`,\n description: `${r.rows[0]?.checkpoints_req} requested vs ${r.rows[0]?.checkpoints_timed} timed checkpoints. High requested checkpoints indicate checkpoint_completion_target or max_wal_size may need tuning.`,\n fix: `-- Increase max_wal_size:\\nALTER SYSTEM SET max_wal_size = '2GB';\\nSELECT pg_reload_conf();`,\n impact: \"Frequent requested checkpoints cause I/O spikes and degrade performance.\",\n effort: \"moderate\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking checkpoint frequency:\", (err as Error).message); skipped.push(\"checkpoint frequency: \" + (err as Error).message);\n }\n\n // AutoVACUUM config check\n try {\n const r = await client.query(`SELECT setting FROM pg_settings WHERE name = 'autovacuum'`);\n if (r.rows[0]?.setting === \"off\") {\n issues.push({\n id: `maint-autovacuum-disabled`,\n severity: \"critical\",\n category: \"maintenance\",\n title: `Autovacuum is disabled`,\n description: `Autovacuum is turned off. Dead tuples will accumulate and transaction ID wraparound becomes a risk.`,\n fix: `ALTER SYSTEM SET autovacuum = on;\\nSELECT pg_reload_conf();`,\n impact: \"Without autovacuum, tables bloat indefinitely and risk transaction ID wraparound shutdown.\",\n effort: \"quick\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking autovacuum:\", (err as Error).message); skipped.push(\"autovacuum: \" + (err as Error).message);\n }\n\n // shared_buffers / work_mem check\n try {\n const sbRes = await client.query(`SELECT setting, unit FROM pg_settings WHERE name = 'shared_buffers'`);\n const memRes = await client.query(`\n SELECT (SELECT setting::bigint FROM pg_settings WHERE name = 'shared_buffers') *\n (SELECT setting::bigint FROM pg_settings WHERE name = 'block_size') AS shared_bytes\n `);\n const sharedBytes = parseInt(memRes.rows[0]?.shared_bytes ?? \"0\");\n // Get total RAM from OS via a simple query (pg doesn't expose this directly, but we can estimate)\n // We'll compare against a reasonable minimum: if shared_buffers < 128MB, warn\n if (sharedBytes > 0 && sharedBytes < 128 * 1024 * 1024) {\n issues.push({\n id: `perf-shared-buffers-low`,\n severity: \"warning\",\n category: \"performance\",\n title: `shared_buffers is only ${(sharedBytes / 1048576).toFixed(0)} MB`,\n description: `shared_buffers is set to ${sbRes.rows[0]?.setting}${sbRes.rows[0]?.unit || \"\"}. Recommended: ~25% of system RAM, typically at least 256MB for production.`,\n fix: `ALTER SYSTEM SET shared_buffers = '256MB';\\n-- Requires restart`,\n impact: \"Low shared_buffers means more disk I/O and poor cache hit ratios.\",\n effort: \"involved\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking shared_buffers:\", (err as Error).message); skipped.push(\"shared_buffers: \" + (err as Error).message);\n }\n\n try {\n const r = await client.query(`SELECT setting, unit FROM pg_settings WHERE name = 'work_mem'`);\n const workMemKB = parseInt(r.rows[0]?.setting ?? \"0\");\n if (workMemKB > 0 && workMemKB < 4096) { // < 4MB\n issues.push({\n id: `perf-work-mem-low`,\n severity: \"info\",\n category: \"performance\",\n title: `work_mem is only ${workMemKB < 1024 ? workMemKB + \"kB\" : (workMemKB / 1024).toFixed(0) + \"MB\"}`,\n description: `work_mem is ${r.rows[0]?.setting}${r.rows[0]?.unit || \"\"}. Low work_mem causes sorts and hash operations to spill to disk.`,\n fix: `ALTER SYSTEM SET work_mem = '16MB';\\nSELECT pg_reload_conf();`,\n impact: \"Operations that exceed work_mem use temporary disk files, which is much slower.\",\n effort: \"quick\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking work_mem:\", (err as Error).message); skipped.push(\"work_mem: \" + (err as Error).message);\n }\n\n // ── Security Advisors ──────────────────────────────────────────\n\n // Superuser connections from non-localhost\n try {\n const r = await client.query(`\n SELECT pid, usename, client_addr::text\n FROM pg_stat_activity\n WHERE usename IN (SELECT rolname FROM pg_roles WHERE rolsuper)\n AND client_addr IS NOT NULL\n AND client_addr::text NOT IN ('127.0.0.1', '::1')\n AND pid != pg_backend_pid()\n `);\n for (const row of r.rows) {\n issues.push({\n id: `sec-superuser-remote-${row.pid}`,\n severity: \"critical\",\n category: \"security\",\n title: `Superuser ${row.usename} connected from ${row.client_addr}`,\n description: `Superuser ${row.usename} has an active connection from non-localhost address ${row.client_addr}. This is a security risk.`,\n fix: `-- Restrict superuser access in pg_hba.conf to localhost only.\\n-- Then: SELECT pg_reload_conf();`,\n impact: \"Remote superuser access is a significant security vulnerability.\",\n effort: \"moderate\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking superuser connections:\", (err as Error).message); skipped.push(\"superuser connections: \" + (err as Error).message);\n }\n\n // SSL disabled\n try {\n const r = await client.query(`SELECT setting FROM pg_settings WHERE name = 'ssl'`);\n if (r.rows[0]?.setting === \"off\") {\n issues.push({\n id: `sec-ssl-off`,\n severity: \"warning\",\n category: \"security\",\n title: `SSL is disabled`,\n description: `SSL is turned off. Database connections are not encrypted.`,\n fix: `-- Enable SSL in postgresql.conf:\\n-- ssl = on\\n-- ssl_cert_file = 'server.crt'\\n-- ssl_key_file = 'server.key'\\nSELECT pg_reload_conf();`,\n impact: \"Database traffic can be intercepted and read in transit.\",\n effort: \"involved\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking SSL check:\", (err as Error).message); skipped.push(\"SSL check: \" + (err as Error).message);\n }\n\n // Password authentication check (PG 15+)\n try {\n const r = await client.query(`\n SELECT type, database, user_name, auth_method\n FROM pg_hba_file_rules\n WHERE auth_method = 'trust' AND type != 'local'\n LIMIT 5\n `);\n for (const row of r.rows) {\n issues.push({\n id: `sec-trust-auth-${row.database}-${row.user_name}`,\n severity: \"critical\",\n category: \"security\",\n title: `Trust authentication for ${row.user_name}@${row.database}`,\n description: `HBA rule allows trust (no password) authentication for ${row.type} connections to ${row.database} as ${row.user_name}.`,\n fix: `-- Change auth_method from 'trust' to 'scram-sha-256' in pg_hba.conf\\n-- Then: SELECT pg_reload_conf();`,\n impact: \"Anyone can connect without a password.\",\n effort: \"moderate\",\n });\n }\n } catch (err) {\n console.error(\"[advisor] Error checking trust auth:\", (err as Error).message); skipped.push(\"trust auth: \" + (err as Error).message);\n } // pg_hba_file_rules not available pre-PG15\n\n // Filter out ignored issues\n const ignoredIds = getIgnoredIssues();\n const ignoredSet = new Set(ignoredIds);\n const activeIssues = issues.filter(i => !ignoredSet.has(i.id));\n const ignoredCount = issues.length - activeIssues.length;\n\n // Generate batch fixes for groups of same-type issues\n const batchFixes: BatchFix[] = [];\n const groups = new Map<string, AdvisorIssue[]>();\n for (const issue of activeIssues) {\n // Group by id prefix (everything before the last dash-separated segment with variable data)\n const prefix = issue.id.replace(/-[^-]+$/, \"\");\n if (!groups.has(prefix)) groups.set(prefix, []);\n groups.get(prefix)!.push(issue);\n }\n const BATCH_TITLES: Record<string, string> = {\n \"schema-fk-no-idx\": \"Create all missing FK indexes\",\n \"schema-unused-idx\": \"Drop all unused indexes\",\n \"schema-no-pk\": \"Fix all tables missing primary keys\",\n \"maint-vacuum\": \"VACUUM all overdue tables\",\n \"maint-analyze\": \"ANALYZE all tables missing statistics\",\n \"perf-bloated-idx\": \"REINDEX all bloated indexes\",\n \"perf-bloat\": \"VACUUM FULL all bloated tables\",\n };\n for (const [prefix, group] of groups) {\n if (group.length <= 1) continue;\n const title = BATCH_TITLES[prefix] || `Fix all ${group.length} ${prefix} issues`;\n const sql = group.map(i => i.fix.split(\"\\n\").filter(l => !l.trim().startsWith(\"--\")).join(\"\\n\").trim()).filter(Boolean).join(\";\\n\") + \";\";\n batchFixes.push({ type: prefix, title: `${title} (${group.length})`, count: group.length, sql });\n }\n\n const score = computeAdvisorScore(activeIssues);\n return {\n score,\n grade: gradeFromScore(score),\n issues: activeIssues,\n breakdown: computeBreakdown(activeIssues),\n skipped,\n ignoredCount,\n batchFixes,\n };\n } finally {\n client.release();\n }\n}\n\n// ── Ignored Issues Management ──────────────────────────────────\n\nimport Database from \"better-sqlite3\";\nimport path from \"node:path\";\nimport os from \"node:os\";\nimport fs from \"node:fs\";\n\nlet _ignoredDb: ReturnType<typeof Database> | null = null;\n\nfunction getIgnoredDb(): ReturnType<typeof Database> {\n if (_ignoredDb) return _ignoredDb;\n const dataDir = process.env.PG_DASH_DATA_DIR || path.join(os.homedir(), \".pg-dash\");\n fs.mkdirSync(dataDir, { recursive: true });\n const dbPath = path.join(dataDir, \"alerts.db\");\n _ignoredDb = new Database(dbPath);\n _ignoredDb.pragma(\"journal_mode = WAL\");\n _ignoredDb.exec(\"CREATE TABLE IF NOT EXISTS ignored_issues (issue_id TEXT PRIMARY KEY, ignored_at INTEGER)\");\n return _ignoredDb;\n}\n\nexport function getIgnoredIssues(): string[] {\n try {\n const db = getIgnoredDb();\n return db.prepare(\"SELECT issue_id FROM ignored_issues\").all().map((r: any) => r.issue_id);\n } catch {\n return [];\n }\n}\n\nexport function ignoreIssue(issueId: string): void {\n const db = getIgnoredDb();\n db.prepare(\"INSERT OR REPLACE INTO ignored_issues (issue_id, ignored_at) VALUES (?, ?)\").run(issueId, Date.now());\n}\n\nexport function unignoreIssue(issueId: string): void {\n const db = getIgnoredDb();\n db.prepare(\"DELETE FROM ignored_issues WHERE issue_id = ?\").run(issueId);\n}\n\n// Allowed SQL operations for the fix endpoint\n\nexport function isSafeFix(sql: string): boolean {\n const trimmed = sql.trim();\n if (!trimmed) return false;\n\n // Reject multi-statement SQL (split on semicolons, ignore trailing)\n const statements = trimmed.replace(/;\\s*$/, \"\").split(\";\").map(s => s.trim()).filter(Boolean);\n if (statements.length !== 1) return false;\n\n const upper = statements[0].toUpperCase();\n\n // EXPLAIN ANALYZE — only allow if followed by SELECT\n if (upper.startsWith(\"EXPLAIN ANALYZE\")) {\n const afterExplain = upper.replace(/^EXPLAIN\\s+ANALYZE\\s+/, \"\").trimStart();\n return afterExplain.startsWith(\"SELECT\");\n }\n\n // Simple prefix allowlist for single statements\n const ALLOWED_PREFIXES = [\n \"VACUUM\",\n \"ANALYZE\",\n \"REINDEX\",\n \"CREATE INDEX CONCURRENTLY\",\n \"DROP INDEX CONCURRENTLY\",\n \"SELECT PG_TERMINATE_BACKEND(\",\n \"SELECT PG_CANCEL_BACKEND(\",\n ];\n\n return ALLOWED_PREFIXES.some((p) => upper.startsWith(p));\n}\n","import type { Pool } from \"pg\";\n\nexport async function getSchemaTables(pool: Pool) {\n const client = await pool.connect();\n try {\n const r = await client.query(`\n SELECT\n c.relname AS name,\n n.nspname AS schema,\n pg_size_pretty(pg_total_relation_size(c.oid)) AS total_size,\n pg_total_relation_size(c.oid) AS total_size_bytes,\n pg_size_pretty(pg_relation_size(c.oid)) AS table_size,\n pg_size_pretty(pg_total_relation_size(c.oid) - pg_relation_size(c.oid)) AS index_size,\n s.n_live_tup AS row_count,\n obj_description(c.oid) AS description\n FROM pg_class c\n JOIN pg_namespace n ON c.relnamespace = n.oid\n LEFT JOIN pg_stat_user_tables s ON s.relid = c.oid\n WHERE c.relkind = 'r' AND n.nspname NOT IN ('pg_catalog', 'information_schema')\n ORDER BY pg_total_relation_size(c.oid) DESC\n `);\n return r.rows;\n } finally {\n client.release();\n }\n}\n\nexport async function getSchemaTableDetail(pool: Pool, tableName: string) {\n const client = await pool.connect();\n try {\n // Parse schema.table or default to public\n const parts = tableName.split(\".\");\n const schema = parts.length > 1 ? parts[0] : \"public\";\n const name = parts.length > 1 ? parts[1] : parts[0];\n\n // Table info\n const tableInfo = await client.query(`\n SELECT\n c.relname AS name, n.nspname AS schema,\n pg_size_pretty(pg_total_relation_size(c.oid)) AS total_size,\n pg_size_pretty(pg_relation_size(c.oid)) AS table_size,\n pg_size_pretty(pg_total_relation_size(c.oid) - pg_relation_size(c.oid)) AS index_size,\n pg_size_pretty(pg_relation_size(c.reltoastrelid)) AS toast_size,\n s.n_live_tup AS row_count, s.n_dead_tup AS dead_tuples,\n s.last_vacuum, s.last_autovacuum, s.last_analyze, s.last_autoanalyze,\n s.seq_scan, s.idx_scan\n FROM pg_class c\n JOIN pg_namespace n ON c.relnamespace = n.oid\n LEFT JOIN pg_stat_user_tables s ON s.relid = c.oid\n WHERE c.relname = $1 AND n.nspname = $2 AND c.relkind = 'r'\n `, [name, schema]);\n\n if (tableInfo.rows.length === 0) return null;\n\n // Columns\n const columns = await client.query(`\n SELECT\n a.attname AS name,\n pg_catalog.format_type(a.atttypid, a.atttypmod) AS type,\n NOT a.attnotnull AS nullable,\n pg_get_expr(d.adbin, d.adrelid) AS default_value,\n col_description(a.attrelid, a.attnum) AS description\n FROM pg_attribute a\n LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum\n WHERE a.attrelid = (SELECT c.oid FROM pg_class c JOIN pg_namespace n ON c.relnamespace = n.oid WHERE c.relname = $1 AND n.nspname = $2)\n AND a.attnum > 0 AND NOT a.attisdropped\n ORDER BY a.attnum\n `, [name, schema]);\n\n // Indexes\n const indexes = await client.query(`\n SELECT\n i.relname AS name,\n am.amname AS type,\n pg_size_pretty(pg_relation_size(i.oid)) AS size,\n pg_get_indexdef(idx.indexrelid) AS definition,\n idx.indisunique AS is_unique,\n idx.indisprimary AS is_primary,\n s.idx_scan, s.idx_tup_read, s.idx_tup_fetch\n FROM pg_index idx\n JOIN pg_class i ON idx.indexrelid = i.oid\n JOIN pg_class t ON idx.indrelid = t.oid\n JOIN pg_namespace n ON t.relnamespace = n.oid\n JOIN pg_am am ON i.relam = am.oid\n LEFT JOIN pg_stat_user_indexes s ON s.indexrelid = i.oid\n WHERE t.relname = $1 AND n.nspname = $2\n ORDER BY i.relname\n `, [name, schema]);\n\n // Constraints\n const constraints = await client.query(`\n SELECT\n conname AS name,\n CASE contype WHEN 'p' THEN 'PRIMARY KEY' WHEN 'f' THEN 'FOREIGN KEY'\n WHEN 'u' THEN 'UNIQUE' WHEN 'c' THEN 'CHECK' WHEN 'x' THEN 'EXCLUDE' END AS type,\n pg_get_constraintdef(oid) AS definition\n FROM pg_constraint\n WHERE conrelid = (SELECT c.oid FROM pg_class c JOIN pg_namespace n ON c.relnamespace = n.oid WHERE c.relname = $1 AND n.nspname = $2)\n ORDER BY\n CASE contype WHEN 'p' THEN 1 WHEN 'u' THEN 2 WHEN 'f' THEN 3 WHEN 'c' THEN 4 ELSE 5 END\n `, [name, schema]);\n\n // Foreign keys (outgoing)\n const foreignKeys = await client.query(`\n SELECT\n conname AS name,\n a.attname AS column_name,\n confrelid::regclass::text AS referenced_table,\n af.attname AS referenced_column\n FROM pg_constraint c\n JOIN pg_attribute a ON a.attrelid = c.conrelid AND a.attnum = ANY(c.conkey)\n JOIN pg_attribute af ON af.attrelid = c.confrelid AND af.attnum = ANY(c.confkey)\n WHERE c.contype = 'f'\n AND c.conrelid = (SELECT cl.oid FROM pg_class cl JOIN pg_namespace n ON cl.relnamespace = n.oid WHERE cl.relname = $1 AND n.nspname = $2)\n `, [name, schema]);\n\n // Sample data (first 10 rows)\n let sampleData: any[] = [];\n try {\n const sample = await client.query(\n `SELECT * FROM ${client.escapeIdentifier(schema)}.${client.escapeIdentifier(name)} LIMIT 10`\n );\n sampleData = sample.rows;\n } catch (err) { console.error(\"[schema] Error:\", (err as Error).message); }\n\n return {\n ...tableInfo.rows[0],\n columns: columns.rows,\n indexes: indexes.rows,\n constraints: constraints.rows,\n foreignKeys: foreignKeys.rows,\n sampleData,\n };\n } finally {\n client.release();\n }\n}\n\nexport async function getSchemaIndexes(pool: Pool) {\n const client = await pool.connect();\n try {\n const r = await client.query(`\n SELECT\n n.nspname AS schema,\n t.relname AS table_name,\n i.relname AS name,\n am.amname AS type,\n pg_size_pretty(pg_relation_size(i.oid)) AS size,\n pg_relation_size(i.oid) AS size_bytes,\n pg_get_indexdef(idx.indexrelid) AS definition,\n idx.indisunique AS is_unique,\n idx.indisprimary AS is_primary,\n s.idx_scan, s.idx_tup_read, s.idx_tup_fetch\n FROM pg_index idx\n JOIN pg_class i ON idx.indexrelid = i.oid\n JOIN pg_class t ON idx.indrelid = t.oid\n JOIN pg_namespace n ON t.relnamespace = n.oid\n JOIN pg_am am ON i.relam = am.oid\n LEFT JOIN pg_stat_user_indexes s ON s.indexrelid = i.oid\n WHERE n.nspname NOT IN ('pg_catalog', 'information_schema')\n ORDER BY pg_relation_size(i.oid) DESC\n `);\n return r.rows;\n } finally {\n client.release();\n }\n}\n\nexport async function getSchemaFunctions(pool: Pool) {\n const client = await pool.connect();\n try {\n const r = await client.query(`\n SELECT\n n.nspname AS schema,\n p.proname AS name,\n pg_get_function_result(p.oid) AS return_type,\n pg_get_function_arguments(p.oid) AS arguments,\n l.lanname AS language,\n p.prosrc AS source,\n CASE p.prokind WHEN 'f' THEN 'function' WHEN 'p' THEN 'procedure' WHEN 'a' THEN 'aggregate' WHEN 'w' THEN 'window' END AS kind\n FROM pg_proc p\n JOIN pg_namespace n ON p.pronamespace = n.oid\n JOIN pg_language l ON p.prolang = l.oid\n WHERE n.nspname NOT IN ('pg_catalog', 'information_schema')\n ORDER BY n.nspname, p.proname\n `);\n return r.rows;\n } finally {\n client.release();\n }\n}\n\nexport async function getSchemaExtensions(pool: Pool) {\n const client = await pool.connect();\n try {\n const r = await client.query(`\n SELECT extname AS name, extversion AS installed_version,\n n.nspname AS schema, obj_description(e.oid) AS description\n FROM pg_extension e\n JOIN pg_namespace n ON e.extnamespace = n.oid\n ORDER BY extname\n `);\n return r.rows;\n } finally {\n client.release();\n }\n}\n\nexport async function getSchemaEnums(pool: Pool) {\n const client = await pool.connect();\n try {\n const r = await client.query(`\n SELECT\n t.typname AS name,\n n.nspname AS schema,\n array_agg(e.enumlabel ORDER BY e.enumsortorder) AS values\n FROM pg_type t\n JOIN pg_namespace n ON t.typnamespace = n.oid\n JOIN pg_enum e ON t.oid = e.enumtypid\n WHERE n.nspname NOT IN ('pg_catalog', 'information_schema')\n GROUP BY t.typname, n.nspname\n ORDER BY t.typname\n `);\n return r.rows;\n } finally {\n client.release();\n }\n}\n","// Schema Diff — compares two schema snapshots and produces a change list\n\nexport interface SchemaSnapshot {\n tables: SnapshotTable[];\n enums: SnapshotEnum[];\n}\n\nexport interface SnapshotTable {\n name: string;\n schema: string;\n columns: SnapshotColumn[];\n indexes: SnapshotIndex[];\n constraints: SnapshotConstraint[];\n}\n\nexport interface SnapshotColumn {\n name: string;\n type: string;\n nullable: boolean;\n default_value: string | null;\n}\n\nexport interface SnapshotIndex {\n name: string;\n definition: string;\n is_unique: boolean;\n is_primary: boolean;\n}\n\nexport interface SnapshotConstraint {\n name: string;\n type: string;\n definition: string;\n}\n\nexport interface SnapshotEnum {\n name: string;\n schema: string;\n values: string[];\n}\n\nexport interface SchemaChange {\n change_type: \"added\" | \"removed\" | \"modified\";\n object_type: \"table\" | \"column\" | \"index\" | \"constraint\" | \"enum\";\n table_name: string | null;\n detail: string;\n}\n\nexport function diffSchemaSnapshots(oldSnap: SchemaSnapshot, newSnap: SchemaSnapshot): SchemaChange[] {\n const changes: SchemaChange[] = [];\n\n const oldTableMap = new Map(oldSnap.tables.map((t) => [`${t.schema}.${t.name}`, t]));\n const newTableMap = new Map(newSnap.tables.map((t) => [`${t.schema}.${t.name}`, t]));\n\n // Tables added/removed\n for (const [key, t] of newTableMap) {\n if (!oldTableMap.has(key)) {\n changes.push({ change_type: \"added\", object_type: \"table\", table_name: key, detail: `Table ${key} added` });\n }\n }\n for (const [key] of oldTableMap) {\n if (!newTableMap.has(key)) {\n changes.push({ change_type: \"removed\", object_type: \"table\", table_name: key, detail: `Table ${key} removed` });\n }\n }\n\n // Compare matching tables\n for (const [key, newTable] of newTableMap) {\n const oldTable = oldTableMap.get(key);\n if (!oldTable) continue;\n\n // Columns\n const oldCols = new Map(oldTable.columns.map((c) => [c.name, c]));\n const newCols = new Map(newTable.columns.map((c) => [c.name, c]));\n\n for (const [name, col] of newCols) {\n const oldCol = oldCols.get(name);\n if (!oldCol) {\n changes.push({ change_type: \"added\", object_type: \"column\", table_name: key, detail: `Column ${name} added (${col.type})` });\n } else {\n if (oldCol.type !== col.type) {\n changes.push({ change_type: \"modified\", object_type: \"column\", table_name: key, detail: `Column ${name} type changed: ${oldCol.type} → ${col.type}` });\n }\n if (oldCol.nullable !== col.nullable) {\n changes.push({ change_type: \"modified\", object_type: \"column\", table_name: key, detail: `Column ${name} nullable changed: ${oldCol.nullable} → ${col.nullable}` });\n }\n if (oldCol.default_value !== col.default_value) {\n changes.push({ change_type: \"modified\", object_type: \"column\", table_name: key, detail: `Column ${name} default changed: ${oldCol.default_value ?? \"NULL\"} → ${col.default_value ?? \"NULL\"}` });\n }\n }\n }\n for (const name of oldCols.keys()) {\n if (!newCols.has(name)) {\n changes.push({ change_type: \"removed\", object_type: \"column\", table_name: key, detail: `Column ${name} removed` });\n }\n }\n\n // Indexes\n const oldIdx = new Map(oldTable.indexes.map((i) => [i.name, i]));\n const newIdx = new Map(newTable.indexes.map((i) => [i.name, i]));\n for (const [name, idx] of newIdx) {\n if (!oldIdx.has(name)) {\n changes.push({ change_type: \"added\", object_type: \"index\", table_name: key, detail: `Index ${name} added` });\n } else if (oldIdx.get(name)!.definition !== idx.definition) {\n changes.push({ change_type: \"modified\", object_type: \"index\", table_name: key, detail: `Index ${name} definition changed` });\n }\n }\n for (const name of oldIdx.keys()) {\n if (!newIdx.has(name)) {\n changes.push({ change_type: \"removed\", object_type: \"index\", table_name: key, detail: `Index ${name} removed` });\n }\n }\n\n // Constraints\n const oldCon = new Map(oldTable.constraints.map((c) => [c.name, c]));\n const newCon = new Map(newTable.constraints.map((c) => [c.name, c]));\n for (const [name, con] of newCon) {\n if (!oldCon.has(name)) {\n changes.push({ change_type: \"added\", object_type: \"constraint\", table_name: key, detail: `Constraint ${name} added (${con.type})` });\n } else if (oldCon.get(name)!.definition !== con.definition) {\n changes.push({ change_type: \"modified\", object_type: \"constraint\", table_name: key, detail: `Constraint ${name} definition changed` });\n }\n }\n for (const name of oldCon.keys()) {\n if (!newCon.has(name)) {\n changes.push({ change_type: \"removed\", object_type: \"constraint\", table_name: key, detail: `Constraint ${name} removed` });\n }\n }\n }\n\n // Enums\n const oldEnums = new Map((oldSnap.enums || []).map((e) => [`${e.schema}.${e.name}`, e]));\n const newEnums = new Map((newSnap.enums || []).map((e) => [`${e.schema}.${e.name}`, e]));\n for (const [key, en] of newEnums) {\n const oldEn = oldEnums.get(key);\n if (!oldEn) {\n changes.push({ change_type: \"added\", object_type: \"enum\", table_name: null, detail: `Enum ${key} added (${en.values.join(\", \")})` });\n } else {\n const added = en.values.filter((v) => !oldEn.values.includes(v));\n const removed = oldEn.values.filter((v) => !en.values.includes(v));\n for (const v of added) {\n changes.push({ change_type: \"modified\", object_type: \"enum\", table_name: null, detail: `Enum ${key}: value '${v}' added` });\n }\n for (const v of removed) {\n changes.push({ change_type: \"modified\", object_type: \"enum\", table_name: null, detail: `Enum ${key}: value '${v}' removed` });\n }\n }\n }\n for (const key of oldEnums.keys()) {\n if (!newEnums.has(key)) {\n changes.push({ change_type: \"removed\", object_type: \"enum\", table_name: null, detail: `Enum ${key} removed` });\n }\n }\n\n return changes;\n}\n","// Schema Tracker — takes schema snapshots, stores in SQLite, detects changes\n\nconst SNAPSHOT_RETENTION = 50;\n\nimport type { Pool } from \"pg\";\nimport type Database from \"better-sqlite3\";\nimport { getSchemaTables, getSchemaTableDetail, getSchemaEnums } from \"./queries/schema.js\";\nimport { diffSchemaSnapshots, type SchemaSnapshot, type SchemaChange } from \"./schema-diff.js\";\n\n/** Build a full schema snapshot from a live pool — reusable for env comparison */\nexport async function buildLiveSnapshot(pool: Pool): Promise<SchemaSnapshot> {\n const tables = await getSchemaTables(pool);\n const enums = await getSchemaEnums(pool);\n\n const detailedTables = await Promise.all(\n tables.map(async (t: any) => {\n const detail = await getSchemaTableDetail(pool, `${t.schema}.${t.name}`);\n if (!detail) return null;\n return {\n name: detail.name,\n schema: detail.schema,\n columns: detail.columns.map((c: any) => ({\n name: c.name,\n type: c.type,\n nullable: c.nullable,\n default_value: c.default_value,\n })),\n indexes: detail.indexes.map((i: any) => ({\n name: i.name,\n definition: i.definition,\n is_unique: i.is_unique,\n is_primary: i.is_primary,\n })),\n constraints: detail.constraints.map((c: any) => ({\n name: c.name,\n type: c.type,\n definition: c.definition,\n })),\n };\n })\n );\n\n return {\n tables: detailedTables.filter(Boolean) as SchemaSnapshot[\"tables\"],\n enums: enums.map((e: any) => ({ name: e.name, schema: e.schema, values: e.values })),\n };\n}\n\nexport class SchemaTracker {\n private db: Database.Database;\n private pool: Pool;\n private intervalMs: number;\n private timer: ReturnType<typeof setInterval> | null = null;\n\n constructor(db: Database.Database, pool: Pool, intervalMs = 6 * 60 * 60 * 1000) {\n this.db = db;\n this.pool = pool;\n this.intervalMs = intervalMs;\n this.initTables();\n }\n\n private initTables() {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS schema_snapshots (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n timestamp INTEGER NOT NULL,\n snapshot TEXT NOT NULL\n );\n CREATE TABLE IF NOT EXISTS schema_changes (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n snapshot_id INTEGER NOT NULL,\n timestamp INTEGER NOT NULL,\n change_type TEXT NOT NULL,\n object_type TEXT NOT NULL,\n table_name TEXT,\n detail TEXT NOT NULL,\n FOREIGN KEY (snapshot_id) REFERENCES schema_snapshots(id)\n );\n `);\n }\n\n async takeSnapshot(): Promise<{ snapshotId: number; changes: SchemaChange[] }> {\n const snapshot = await this.buildSnapshot();\n const now = Date.now();\n const json = JSON.stringify(snapshot);\n\n const info = this.db.prepare(\"INSERT INTO schema_snapshots (timestamp, snapshot) VALUES (?, ?)\").run(now, json);\n const snapshotId = Number(info.lastInsertRowid);\n\n // Prune old snapshots, keeping only the most recent SNAPSHOT_RETENTION\n this.db.prepare(`\n DELETE FROM schema_snapshots\n WHERE id NOT IN (\n SELECT id FROM schema_snapshots\n ORDER BY timestamp DESC\n LIMIT ?\n )\n `).run(SNAPSHOT_RETENTION);\n\n // Diff against previous\n const prev = this.db.prepare(\"SELECT snapshot FROM schema_snapshots WHERE id < ? ORDER BY id DESC LIMIT 1\").get(snapshotId) as { snapshot: string } | undefined;\n let changes: SchemaChange[] = [];\n if (prev) {\n const oldSnap: SchemaSnapshot = JSON.parse(prev.snapshot);\n changes = diffSchemaSnapshots(oldSnap, snapshot);\n if (changes.length > 0) {\n const insert = this.db.prepare(\"INSERT INTO schema_changes (snapshot_id, timestamp, change_type, object_type, table_name, detail) VALUES (?, ?, ?, ?, ?, ?)\");\n const tx = this.db.transaction((chs: SchemaChange[]) => {\n for (const c of chs) {\n insert.run(snapshotId, now, c.change_type, c.object_type, c.table_name, c.detail);\n }\n });\n tx(changes);\n }\n }\n\n return { snapshotId, changes };\n }\n\n private async buildSnapshot(): Promise<SchemaSnapshot> {\n return buildLiveSnapshot(this.pool);\n }\n\n start() {\n // Take initial snapshot\n this.takeSnapshot().catch((err) => console.error(\"Schema snapshot error:\", err.message));\n this.timer = setInterval(() => {\n this.takeSnapshot().catch((err) => console.error(\"Schema snapshot error:\", err.message));\n }, this.intervalMs);\n }\n\n stop() {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n }\n\n // API helpers\n getHistory(limit = 30) {\n return this.db.prepare(\"SELECT id, timestamp FROM schema_snapshots ORDER BY id DESC LIMIT ?\").all(limit);\n }\n\n getChanges(since?: number) {\n if (since) {\n return this.db.prepare(\"SELECT * FROM schema_changes WHERE timestamp >= ? ORDER BY timestamp DESC\").all(since);\n }\n return this.db.prepare(\"SELECT * FROM schema_changes ORDER BY timestamp DESC LIMIT 100\").all();\n }\n\n getLatestChanges() {\n const latest = this.db.prepare(\"SELECT id FROM schema_snapshots ORDER BY id DESC LIMIT 1\").get() as { id: number } | undefined;\n if (!latest) return [];\n return this.db.prepare(\"SELECT * FROM schema_changes WHERE snapshot_id = ? ORDER BY id\").all(latest.id);\n }\n\n getDiff(fromId: number, toId: number) {\n const from = this.db.prepare(\"SELECT snapshot FROM schema_snapshots WHERE id = ?\").get(fromId) as { snapshot: string } | undefined;\n const to = this.db.prepare(\"SELECT snapshot FROM schema_snapshots WHERE id = ?\").get(toId) as { snapshot: string } | undefined;\n if (!from || !to) return null;\n return diffSchemaSnapshots(JSON.parse(from.snapshot), JSON.parse(to.snapshot));\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { AdvisorResult, AdvisorIssue } from \"./advisor.js\";\n\nexport interface Snapshot {\n timestamp: string;\n result: AdvisorResult;\n}\n\nexport interface SnapshotDiff {\n scoreDelta: number;\n previousScore: number;\n currentScore: number;\n previousGrade: string;\n currentGrade: string;\n newIssues: AdvisorIssue[];\n resolvedIssues: AdvisorIssue[];\n unchanged: AdvisorIssue[];\n}\n\n/**\n * Normalize a dynamic issue ID for stable comparison.\n * Strips trailing -<number> suffixes so IDs like `maint-idle-12345`\n * (where 12345 is a PID that changes every run) don't produce false noise.\n */\nfunction normalizeIssueId(id: string): string {\n return id.replace(/-\\d+$/, \"\");\n}\n\n/**\n * Save a health-check snapshot to a specific file path.\n * The parent directory is created automatically.\n *\n * @param snapshotPath Full path to the JSON file (e.g. ~/.pg-dash/last-check.json)\n */\nexport function saveSnapshot(snapshotPath: string, result: AdvisorResult): void {\n fs.mkdirSync(path.dirname(snapshotPath), { recursive: true });\n const snapshot: Snapshot = { timestamp: new Date().toISOString(), result };\n fs.writeFileSync(snapshotPath, JSON.stringify(snapshot, null, 2));\n}\n\n/**\n * Load a previously saved snapshot from a specific file path.\n * Returns null if the file doesn't exist or cannot be parsed.\n *\n * @param snapshotPath Full path to the JSON file\n */\nexport function loadSnapshot(snapshotPath: string): Snapshot | null {\n if (!fs.existsSync(snapshotPath)) return null;\n try {\n return JSON.parse(fs.readFileSync(snapshotPath, \"utf-8\"));\n } catch {\n return null;\n }\n}\n\nexport function diffSnapshots(prev: AdvisorResult, current: AdvisorResult): SnapshotDiff {\n // Use normalized IDs for comparison to avoid noise from dynamic suffixes\n // (e.g. maint-idle-12345 where 12345 is a PID that changes every run).\n const prevNormIds = new Set(prev.issues.map((i) => normalizeIssueId(i.id)));\n const currNormIds = new Set(current.issues.map((i) => normalizeIssueId(i.id)));\n\n const newIssues = current.issues.filter((i) => !prevNormIds.has(normalizeIssueId(i.id)));\n const resolvedIssues = prev.issues.filter((i) => !currNormIds.has(normalizeIssueId(i.id)));\n const unchanged = current.issues.filter((i) => prevNormIds.has(normalizeIssueId(i.id)));\n\n return {\n scoreDelta: current.score - prev.score,\n previousScore: prev.score,\n currentScore: current.score,\n previousGrade: prev.grade,\n currentGrade: current.grade,\n newIssues,\n resolvedIssues,\n unchanged,\n };\n}\n","// Migration safety checker — static + dynamic analysis of SQL migration files\n\nimport type { Pool } from \"pg\";\n\nexport interface MigrationIssue {\n severity: \"error\" | \"warning\" | \"info\";\n code: string;\n message: string;\n suggestion?: string;\n lineNumber?: number;\n tableName?: string;\n estimatedRows?: number;\n estimatedLockSeconds?: number;\n}\n\nexport interface MigrationCheckResult {\n safe: boolean;\n issues: MigrationIssue[];\n summary: {\n errors: number;\n warnings: number;\n infos: number;\n };\n checkedAt: string;\n}\n\n// Strip SQL comments while preserving line numbers (replace with spaces)\nfunction stripComments(sql: string): string {\n // Replace /* ... */ block comments (preserve newlines for line number tracking)\n let stripped = sql.replace(/\\/\\*[\\s\\S]*?\\*\\//g, (match) =>\n match.replace(/[^\\n]/g, \" \")\n );\n // Replace -- single-line comments (preserve the newline)\n stripped = stripped.replace(/--[^\\n]*/g, (match) => \" \".repeat(match.length));\n return stripped;\n}\n\n// Helper: find line number of a match in the original SQL\nfunction findLineNumber(sql: string, matchIndex: number): number {\n const before = sql.slice(0, matchIndex);\n return before.split(\"\\n\").length;\n}\n\n// Extract bare table name from possibly-quoted or schema-qualified identifier\nfunction bareTable(name: string): string {\n return name\n .replace(/^public\\./i, \"\")\n .replace(/\"/g, \"\")\n .toLowerCase()\n .trim();\n}\n\n// Parse all table names operated on by this migration\nfunction extractOperatedTables(sql: string): {\n indexTables: string[]; // CREATE INDEX ON <table>\n alterTables: string[]; // ALTER TABLE <table>\n dropTables: string[]; // DROP TABLE <table>\n refTables: string[]; // REFERENCES <table>\n} {\n sql = stripComments(sql);\n const indexTables: string[] = [];\n const alterTables: string[] = [];\n const dropTables: string[] = [];\n const refTables: string[] = [];\n\n // CREATE INDEX ... ON table\n const idxRe = /\\bCREATE\\s+(?:UNIQUE\\s+)?INDEX\\s+(?:CONCURRENTLY\\s+)?(?:IF\\s+NOT\\s+EXISTS\\s+)?(?:\\w+\\s+)?ON\\s+([\\w.\"]+)/gi;\n let m: RegExpExecArray | null;\n while ((m = idxRe.exec(sql)) !== null) indexTables.push(bareTable(m[1]));\n\n // ALTER TABLE table\n const altRe = /\\bALTER\\s+TABLE\\s+(?:IF\\s+EXISTS\\s+)?([\\w.\"]+)/gi;\n while ((m = altRe.exec(sql)) !== null) alterTables.push(bareTable(m[1]));\n\n // DROP TABLE\n const dropRe = /\\bDROP\\s+TABLE\\s+(?:IF\\s+EXISTS\\s+)?([\\w.\"]+)/gi;\n while ((m = dropRe.exec(sql)) !== null) dropTables.push(bareTable(m[1]));\n\n // REFERENCES table\n const refRe = /\\bREFERENCES\\s+([\\w.\"]+)/gi;\n while ((m = refRe.exec(sql)) !== null) refTables.push(bareTable(m[1]));\n\n return { indexTables, alterTables, dropTables, refTables };\n}\n\n// Static analysis — no DB needed\nfunction staticCheck(sql: string): MigrationIssue[] {\n const issues: MigrationIssue[] = [];\n // Strip comments before analysis to avoid false positives from commented-out SQL\n sql = stripComments(sql);\n\n // Determine tables created IN THIS MIGRATION (so we know they're brand-new)\n const createdTablesRe = /\\bCREATE\\s+TABLE\\s+(?:IF\\s+NOT\\s+EXISTS\\s+)?([\\w.\"]+)/gi;\n const createdTables = new Set<string>();\n let m: RegExpExecArray | null;\n while ((m = createdTablesRe.exec(sql)) !== null) createdTables.add(bareTable(m[1]));\n\n // 1. CREATE INDEX without CONCURRENTLY (on tables NOT created in this migration)\n const idxRe = /\\bCREATE\\s+(?:UNIQUE\\s+)?INDEX\\s+(?!CONCURRENTLY)((?:IF\\s+NOT\\s+EXISTS\\s+)?(?:\\w+\\s+)?ON\\s+([\\w.\"]+))/gi;\n while ((m = idxRe.exec(sql)) !== null) {\n const table = bareTable(m[2]);\n const lineNumber = findLineNumber(sql, m.index);\n if (!createdTables.has(table)) {\n issues.push({\n severity: \"warning\",\n code: \"INDEX_WITHOUT_CONCURRENTLY\",\n message: `CREATE INDEX on existing table will lock writes. Use CREATE INDEX CONCURRENTLY to avoid downtime.`,\n suggestion: \"Replace CREATE INDEX with CREATE INDEX CONCURRENTLY\",\n lineNumber,\n tableName: table,\n });\n }\n }\n\n // 2. CREATE INDEX CONCURRENTLY → info\n const idxConcRe = /\\bCREATE\\s+(?:UNIQUE\\s+)?INDEX\\s+CONCURRENTLY\\b/gi;\n while ((m = idxConcRe.exec(sql)) !== null) {\n issues.push({\n severity: \"info\",\n code: \"INDEX_CONCURRENTLY_OK\",\n message: \"CREATE INDEX CONCURRENTLY — safe, no write lock\",\n lineNumber: findLineNumber(sql, m.index),\n });\n }\n\n // 3 & 4. ALTER TABLE ... ADD COLUMN ... NOT NULL (with/without DEFAULT)\n // Match: ALTER TABLE <t> ADD COLUMN <col> <type> [DEFAULT <val>] [NOT NULL | NULL]\n const addColRe =\n /\\bALTER\\s+TABLE\\s+(?:IF\\s+EXISTS\\s+)?([\\w.\"]+)\\s+ADD\\s+(?:COLUMN\\s+)?(?:IF\\s+NOT\\s+EXISTS\\s+)?[\\w\"]+\\s+[\\w\\s()\"',.[\\]]+?(?=;|$)/gi;\n while ((m = addColRe.exec(sql)) !== null) {\n const fragment = m[0];\n const table = bareTable(m[1]);\n const lineNumber = findLineNumber(sql, m.index);\n const fragUpper = fragment.toUpperCase();\n\n const hasNotNull = /\\bNOT\\s+NULL\\b/.test(fragUpper);\n const hasDefault = /\\bDEFAULT\\b/.test(fragUpper);\n\n if (hasNotNull && !hasDefault) {\n issues.push({\n severity: \"error\",\n code: \"ADD_COLUMN_NOT_NULL_NO_DEFAULT\",\n message: \"ADD COLUMN NOT NULL without DEFAULT will fail if table has existing rows\",\n suggestion: \"Add a DEFAULT value, then remove it after migration\",\n lineNumber,\n tableName: table,\n });\n } else if (hasNotNull && hasDefault) {\n issues.push({\n severity: \"warning\",\n code: \"ADD_COLUMN_REWRITES_TABLE\",\n message: \"ADD COLUMN with NOT NULL DEFAULT may rewrite table on PostgreSQL < 11\",\n suggestion: \"On PostgreSQL 11+ with a constant default this is safe. For older versions, add column nullable first.\",\n lineNumber,\n tableName: table,\n });\n }\n }\n\n // 5. DROP TABLE\n const dropRe = /\\bDROP\\s+TABLE\\b/gi;\n while ((m = dropRe.exec(sql)) !== null) {\n issues.push({\n severity: \"warning\",\n code: \"DROP_TABLE\",\n message: \"DROP TABLE is destructive. Ensure this is intentional and data is backed up.\",\n lineNumber: findLineNumber(sql, m.index),\n });\n }\n\n // 5b. ALTER COLUMN TYPE — rewrites the entire table and locks it\n const alterTypeRe = /\\bALTER\\s+TABLE\\s+(?:IF\\s+EXISTS\\s+)?([\\w.\"]+)\\s+ALTER\\s+(?:COLUMN\\s+)?[\\w\"]+\\s+TYPE\\b/gi;\n while ((m = alterTypeRe.exec(sql)) !== null) {\n const table = bareTable(m[1]);\n issues.push({\n severity: \"warning\",\n code: \"ALTER_COLUMN_TYPE\",\n message: \"ALTER COLUMN TYPE rewrites the entire table and acquires an exclusive lock.\",\n suggestion: \"Consider using a new column + backfill + rename strategy to avoid downtime.\",\n lineNumber: findLineNumber(sql, m.index),\n tableName: table,\n });\n }\n\n // 5c. DROP COLUMN — safe in PostgreSQL 9.0+ (marks invisible, no rewrite), but breaks app code\n const dropColRe = /\\bALTER\\s+TABLE\\s+(?:IF\\s+EXISTS\\s+)?([\\w.\"]+)\\s+DROP\\s+(?:COLUMN\\s+)(?:IF\\s+EXISTS\\s+)?[\\w\"]+\\b/gi;\n while ((m = dropColRe.exec(sql)) !== null) {\n const table = bareTable(m[1]);\n issues.push({\n severity: \"info\",\n code: \"DROP_COLUMN\",\n message: \"DROP COLUMN is safe in PostgreSQL (no table rewrite), but may break application code referencing that column.\",\n suggestion: \"Ensure no application code references this column before dropping it.\",\n lineNumber: findLineNumber(sql, m.index),\n tableName: table,\n });\n }\n\n // 5d-i. RENAME TABLE\n const renameTableRe = /ALTER\\s+TABLE\\s+(?:IF\\s+EXISTS\\s+)?(\\w+)\\s+RENAME\\s+TO\\s+(\\w+)/gi;\n while ((m = renameTableRe.exec(sql)) !== null) {\n const oldName = m[1];\n const newName = m[2];\n issues.push({\n severity: \"warning\",\n code: \"RENAME_TABLE\",\n message: `Renaming table \"${oldName}\" to \"${newName}\" breaks application code referencing the old name`,\n suggestion: \"Deploy application code that handles both names before renaming, or use a view with the old name after renaming.\",\n lineNumber: findLineNumber(sql, m.index),\n tableName: oldName,\n });\n }\n\n // 5d-ii. RENAME COLUMN\n const renameColumnRe = /ALTER\\s+TABLE\\s+(?:IF\\s+EXISTS\\s+)?(\\w+)\\s+RENAME\\s+COLUMN\\s+(\\w+)\\s+TO\\s+(\\w+)/gi;\n while ((m = renameColumnRe.exec(sql)) !== null) {\n const table = m[1];\n const oldCol = m[2];\n const newCol = m[3];\n issues.push({\n severity: \"warning\",\n code: \"RENAME_COLUMN\",\n message: `Renaming column \"${oldCol}\" to \"${newCol}\" on table \"${table}\" breaks application code referencing the old column name`,\n suggestion: \"Add new column, backfill data, update application to use new column, then drop old column (expand/contract pattern).\",\n lineNumber: findLineNumber(sql, m.index),\n tableName: table,\n });\n }\n\n // 5e. ADD CONSTRAINT without NOT VALID — performs a full table scan to validate\n const addConRe = /\\bALTER\\s+TABLE\\s+(?:IF\\s+EXISTS\\s+)?([\\w.\"]+)\\s+ADD\\s+CONSTRAINT\\b[^;]*(;|$)/gi;\n while ((m = addConRe.exec(sql)) !== null) {\n const fragment = m[0];\n const table = bareTable(m[1]);\n const fragUpper = fragment.toUpperCase();\n // Skip if NOT VALID is already present\n if (!/\\bNOT\\s+VALID\\b/.test(fragUpper)) {\n issues.push({\n severity: \"warning\",\n code: \"ADD_CONSTRAINT_SCANS_TABLE\",\n message: \"ADD CONSTRAINT validates all existing rows and holds an exclusive lock during the scan.\",\n suggestion: \"Use ADD CONSTRAINT ... NOT VALID to skip validation, then VALIDATE CONSTRAINT in a separate transaction.\",\n lineNumber: findLineNumber(sql, m.index),\n tableName: table,\n });\n }\n }\n\n // 5e. CREATE INDEX CONCURRENTLY inside transaction (BEGIN/COMMIT)\n const hasTransaction = /\\bBEGIN\\b/i.test(sql) || /\\bSTART\\s+TRANSACTION\\b/i.test(sql);\n const hasConcurrently = /\\bCREATE\\s+(?:UNIQUE\\s+)?INDEX\\s+CONCURRENTLY\\b/i.test(sql);\n if (hasTransaction && hasConcurrently) {\n issues.push({\n severity: \"error\",\n code: \"CONCURRENTLY_IN_TRANSACTION\",\n message: \"CREATE INDEX CONCURRENTLY cannot run inside a transaction block. It will fail at runtime.\",\n suggestion: \"Remove the BEGIN/COMMIT wrapper, or use a migration tool that runs CONCURRENTLY outside transactions.\",\n });\n }\n\n // 6. TRUNCATE\n const truncRe = /\\bTRUNCATE\\b/gi;\n while ((m = truncRe.exec(sql)) !== null) {\n issues.push({\n severity: \"warning\",\n code: \"TRUNCATE_TABLE\",\n message: \"TRUNCATE will delete all rows. Ensure this is intentional.\",\n lineNumber: findLineNumber(sql, m.index),\n });\n }\n\n // 7. DELETE FROM without WHERE\n const delRe = /\\bDELETE\\s+FROM\\s+[\\w.\"]+\\s*(?:;|$)/gi;\n while ((m = delRe.exec(sql)) !== null) {\n // If there's no WHERE clause in this statement\n const stmt = m[0];\n if (!/\\bWHERE\\b/i.test(stmt)) {\n issues.push({\n severity: \"warning\",\n code: \"DELETE_WITHOUT_WHERE\",\n message: \"DELETE without WHERE clause will remove all rows.\",\n lineNumber: findLineNumber(sql, m.index),\n });\n }\n }\n\n // 8. UPDATE ... SET without WHERE\n const updRe = /\\bUPDATE\\s+[\\w.\"]+\\s+SET\\b[^;]*(;|$)/gi;\n while ((m = updRe.exec(sql)) !== null) {\n const stmt = m[0];\n if (!/\\bWHERE\\b/i.test(stmt)) {\n issues.push({\n severity: \"warning\",\n code: \"UPDATE_WITHOUT_WHERE\",\n message: \"UPDATE without WHERE clause will modify all rows.\",\n lineNumber: findLineNumber(sql, m.index),\n });\n }\n }\n\n return issues;\n}\n\n// Dynamic analysis — requires a running PG pool\nasync function dynamicCheck(sql: string, pool: Pool, staticIssues: MigrationIssue[]): Promise<MigrationIssue[]> {\n const issues: MigrationIssue[] = [];\n const { indexTables, alterTables, dropTables, refTables } = extractOperatedTables(sql);\n\n // All tables we need to look up\n const allTables = [...new Set([...indexTables, ...alterTables, ...dropTables])];\n\n // Query row counts for all tables at once\n const tableStats = new Map<string, { rowCount: number; totalSize: number }>();\n if (allTables.length > 0) {\n try {\n const res = await pool.query<{ tablename: string; n_live_tup: string; total_size: string }>(\n `SELECT tablename,\n n_live_tup,\n pg_total_relation_size(schemaname||'.'||tablename) AS total_size\n FROM pg_stat_user_tables\n WHERE tablename = ANY($1)`,\n [allTables]\n );\n for (const row of res.rows) {\n tableStats.set(row.tablename, {\n rowCount: parseInt(row.n_live_tup ?? \"0\", 10),\n totalSize: parseInt(row.total_size ?? \"0\", 10),\n });\n }\n } catch (_) {\n // Ignore DB errors in dynamic check\n }\n }\n\n // Upgrade CREATE INDEX (non-CONCURRENTLY) issues based on actual row counts\n for (const issue of staticIssues) {\n if (issue.code === \"INDEX_WITHOUT_CONCURRENTLY\" && issue.tableName) {\n const stats = tableStats.get(issue.tableName);\n if (stats) {\n const { rowCount } = stats;\n const lockSecs = Math.round(rowCount / 50000);\n issue.estimatedRows = rowCount;\n issue.estimatedLockSeconds = lockSecs;\n\n if (rowCount > 1_000_000) {\n issue.severity = \"error\";\n issue.message = `CREATE INDEX on '${issue.tableName}' will lock writes for ~${lockSecs}s (${(rowCount / 1e6).toFixed(1)}M rows). CRITICAL — use CREATE INDEX CONCURRENTLY.`;\n } else if (rowCount > 100_000) {\n issue.message = `CREATE INDEX on '${issue.tableName}' will lock writes for ~${lockSecs}s (${(rowCount / 1000).toFixed(0)}k rows).`;\n }\n }\n }\n }\n\n // Validate REFERENCES tables exist\n const uniqueRefTables = [...new Set(refTables)];\n for (const table of uniqueRefTables) {\n try {\n const res = await pool.query<{ tablename: string }>(\n `SELECT tablename FROM pg_tables WHERE schemaname = 'public' AND tablename = $1`,\n [table]\n );\n if (res.rows.length === 0) {\n issues.push({\n severity: \"error\",\n code: \"MISSING_TABLE\",\n message: `Table '${table}' referenced in migration does not exist`,\n tableName: table,\n });\n }\n } catch (_) {\n // Ignore\n }\n }\n\n return issues;\n}\n\nexport async function analyzeMigration(sql: string, pool?: Pool): Promise<MigrationCheckResult> {\n const trimmed = sql.trim();\n\n if (!trimmed) {\n return {\n safe: true,\n issues: [],\n summary: { errors: 0, warnings: 0, infos: 0 },\n checkedAt: new Date().toISOString(),\n };\n }\n\n // Static checks first (mutates issue severity if dynamic info is available)\n const issues = staticCheck(trimmed);\n\n // Dynamic checks (augments existing issues + adds new ones like MISSING_TABLE)\n if (pool) {\n const dynamicIssues = await dynamicCheck(trimmed, pool, issues);\n issues.push(...dynamicIssues);\n }\n\n const errors = issues.filter((i) => i.severity === \"error\").length;\n const warnings = issues.filter((i) => i.severity === \"warning\").length;\n const infos = issues.filter((i) => i.severity === \"info\").length;\n\n return {\n safe: errors === 0,\n issues,\n summary: { errors, warnings, infos },\n checkedAt: new Date().toISOString(),\n };\n}\n","import { Pool } from \"pg\";\nimport { getAdvisorReport } from \"./advisor.js\";\nimport { buildLiveSnapshot } from \"./schema-tracker.js\";\nimport { diffSchemaSnapshots } from \"./schema-diff.js\";\n\nexport interface ColumnInfo {\n name: string;\n type: string;\n nullable: boolean;\n default?: string;\n}\n\nexport interface ColumnTypeDiff {\n column: string;\n sourceType: string;\n targetType: string;\n}\n\nexport interface ColumnNullableDiff {\n column: string;\n sourceNullable: boolean;\n targetNullable: boolean;\n}\n\nexport interface ColumnDefaultDiff {\n column: string;\n sourceDefault: string | null;\n targetDefault: string | null;\n}\n\nexport interface ColumnDiff {\n table: string;\n missingColumns: ColumnInfo[]; // source has, target doesn't\n extraColumns: ColumnInfo[]; // target has, source doesn't\n typeDiffs: ColumnTypeDiff[]; // same name, different type\n nullableDiffs: ColumnNullableDiff[]; // same name, different nullable\n defaultDiffs: ColumnDefaultDiff[]; // same name, different default\n}\n\nexport interface IndexDefDiff {\n name: string;\n sourceDef: string;\n targetDef: string;\n}\n\nexport interface IndexDiff {\n table: string;\n missingIndexes: string[]; // source has, target doesn't\n extraIndexes: string[]; // target has, source doesn't\n modifiedIndexes: IndexDefDiff[]; // same name, different definition\n}\n\nexport interface ConstraintDiff {\n table: string | null;\n type: \"missing\" | \"extra\" | \"modified\";\n name: string;\n detail: string;\n}\n\nexport interface EnumDiff {\n type: \"missing\" | \"extra\" | \"modified\";\n name: string;\n detail: string;\n}\n\nexport interface SchemaDiff {\n missingTables: string[];\n extraTables: string[];\n columnDiffs: ColumnDiff[];\n indexDiffs: IndexDiff[];\n constraintDiffs: ConstraintDiff[];\n enumDiffs: EnumDiff[];\n}\n\nexport interface HealthDiff {\n source: { score: number; grade: string; url: string };\n target: { score: number; grade: string; url: string };\n sourceOnlyIssues: string[];\n targetOnlyIssues: string[];\n}\n\nexport interface EnvDiffResult {\n schema: SchemaDiff;\n health?: HealthDiff;\n checkedAt: string;\n summary: {\n schemaDrifts: number;\n identical: boolean;\n };\n}\n\n// ----- internal types -----\n\ninterface RawColumn {\n table_name: string;\n column_name: string;\n data_type: string;\n is_nullable: string;\n column_default: string | null;\n}\n\ninterface RawIndex {\n tablename: string;\n indexname: string;\n indexdef: string;\n}\n\n// ----- query helpers -----\n\nasync function fetchTables(pool: Pool): Promise<string[]> {\n const res = await pool.query<{ table_name: string }>(`\n SELECT table_name\n FROM information_schema.tables\n WHERE table_schema = 'public' AND table_type = 'BASE TABLE'\n ORDER BY table_name\n `);\n return res.rows.map((r) => r.table_name);\n}\n\nasync function fetchColumns(pool: Pool): Promise<RawColumn[]> {\n const res = await pool.query<RawColumn>(`\n SELECT table_name, column_name, data_type, is_nullable, column_default\n FROM information_schema.columns\n WHERE table_schema = 'public'\n ORDER BY table_name, ordinal_position\n `);\n return res.rows;\n}\n\nasync function fetchIndexes(pool: Pool): Promise<RawIndex[]> {\n const res = await pool.query<RawIndex>(`\n SELECT tablename, indexname, indexdef\n FROM pg_indexes\n WHERE schemaname = 'public' AND indexname NOT LIKE '%_pkey'\n ORDER BY tablename, indexname\n `);\n return res.rows;\n}\n\n// ----- diff logic -----\n\nfunction diffTables(sourceTables: string[], targetTables: string[]): { missingTables: string[]; extraTables: string[] } {\n const sourceSet = new Set(sourceTables);\n const targetSet = new Set(targetTables);\n return {\n missingTables: sourceTables.filter((t) => !targetSet.has(t)),\n extraTables: targetTables.filter((t) => !sourceSet.has(t)),\n };\n}\n\nfunction groupColumnsByTable(columns: RawColumn[]): Map<string, Map<string, ColumnInfo>> {\n const map = new Map<string, Map<string, ColumnInfo>>();\n for (const col of columns) {\n if (!map.has(col.table_name)) map.set(col.table_name, new Map());\n const info: ColumnInfo = {\n name: col.column_name,\n type: col.data_type,\n nullable: col.is_nullable === \"YES\",\n };\n if (col.column_default !== null && col.column_default !== undefined) {\n info.default = col.column_default;\n }\n map.get(col.table_name)!.set(col.column_name, info);\n }\n return map;\n}\n\nfunction diffColumns(\n sourceCols: RawColumn[],\n targetCols: RawColumn[],\n commonTables: string[]\n): ColumnDiff[] {\n const sourceByTable = groupColumnsByTable(sourceCols);\n const targetByTable = groupColumnsByTable(targetCols);\n const diffs: ColumnDiff[] = [];\n\n for (const table of commonTables) {\n const srcMap = sourceByTable.get(table) ?? new Map<string, ColumnInfo>();\n const tgtMap = targetByTable.get(table) ?? new Map<string, ColumnInfo>();\n\n const missingColumns: ColumnInfo[] = [];\n const extraColumns: ColumnInfo[] = [];\n const typeDiffs: ColumnTypeDiff[] = [];\n const nullableDiffs: ColumnNullableDiff[] = [];\n const defaultDiffs: ColumnDefaultDiff[] = [];\n\n for (const [colName, srcInfo] of srcMap) {\n if (!tgtMap.has(colName)) {\n missingColumns.push(srcInfo);\n } else {\n const tgtInfo = tgtMap.get(colName)!;\n if (srcInfo.type !== tgtInfo.type) {\n typeDiffs.push({ column: colName, sourceType: srcInfo.type, targetType: tgtInfo.type });\n }\n if (srcInfo.nullable !== tgtInfo.nullable) {\n nullableDiffs.push({ column: colName, sourceNullable: srcInfo.nullable, targetNullable: tgtInfo.nullable });\n }\n if ((srcInfo.default ?? null) !== (tgtInfo.default ?? null)) {\n defaultDiffs.push({ column: colName, sourceDefault: srcInfo.default ?? null, targetDefault: tgtInfo.default ?? null });\n }\n }\n }\n\n for (const [colName, tgtInfo] of tgtMap) {\n if (!srcMap.has(colName)) {\n extraColumns.push(tgtInfo);\n }\n }\n\n if (missingColumns.length > 0 || extraColumns.length > 0 || typeDiffs.length > 0 ||\n nullableDiffs.length > 0 || defaultDiffs.length > 0) {\n diffs.push({ table, missingColumns, extraColumns, typeDiffs, nullableDiffs, defaultDiffs });\n }\n }\n\n return diffs;\n}\n\nfunction groupIndexesByTable(indexes: RawIndex[]): Map<string, Map<string, string>> {\n const map = new Map<string, Map<string, string>>();\n for (const idx of indexes) {\n if (!map.has(idx.tablename)) map.set(idx.tablename, new Map());\n map.get(idx.tablename)!.set(idx.indexname, idx.indexdef);\n }\n return map;\n}\n\nfunction diffIndexes(\n sourceIdxs: RawIndex[],\n targetIdxs: RawIndex[],\n commonTables: string[]\n): IndexDiff[] {\n const srcByTable = groupIndexesByTable(sourceIdxs);\n const tgtByTable = groupIndexesByTable(targetIdxs);\n const diffs: IndexDiff[] = [];\n\n // All tables that have any indexes in source or target\n const allTables = new Set([\n ...sourceIdxs.map((i) => i.tablename),\n ...targetIdxs.map((i) => i.tablename),\n ]);\n\n for (const table of allTables) {\n // Only diff tables that exist in both environments (common tables + tables not in either missingTables/extraTables)\n if (!commonTables.includes(table)) continue;\n\n const srcMap = srcByTable.get(table) ?? new Map<string, string>();\n const tgtMap = tgtByTable.get(table) ?? new Map<string, string>();\n\n const missingIndexes = [...srcMap.keys()].filter((i) => !tgtMap.has(i));\n const extraIndexes = [...tgtMap.keys()].filter((i) => !srcMap.has(i));\n const modifiedIndexes: IndexDefDiff[] = [];\n\n for (const [name, srcDef] of srcMap) {\n if (tgtMap.has(name)) {\n const tgtDef = tgtMap.get(name)!;\n if (srcDef !== tgtDef) {\n modifiedIndexes.push({ name, sourceDef: srcDef, targetDef: tgtDef });\n }\n }\n }\n\n if (missingIndexes.length > 0 || extraIndexes.length > 0 || modifiedIndexes.length > 0) {\n diffs.push({ table, missingIndexes, extraIndexes, modifiedIndexes });\n }\n }\n\n return diffs;\n}\n\nfunction countSchemaDrifts(schema: SchemaDiff): number {\n let n = schema.missingTables.length + schema.extraTables.length;\n for (const cd of schema.columnDiffs) {\n n += cd.missingColumns.length + cd.extraColumns.length + cd.typeDiffs.length +\n cd.nullableDiffs.length + cd.defaultDiffs.length;\n }\n for (const id of schema.indexDiffs) {\n n += id.missingIndexes.length + id.extraIndexes.length + id.modifiedIndexes.length;\n }\n n += (schema.constraintDiffs ?? []).length;\n n += (schema.enumDiffs ?? []).length;\n return n;\n}\n\n// ----- public API -----\n\nexport async function diffEnvironments(\n sourceConn: string,\n targetConn: string,\n options?: { includeHealth?: boolean }\n): Promise<EnvDiffResult> {\n const sourcePool = new Pool({ connectionString: sourceConn, connectionTimeoutMillis: 10000 });\n const targetPool = new Pool({ connectionString: targetConn, connectionTimeoutMillis: 10000 });\n\n try {\n // Run all schema queries in parallel (basic + deep snapshots for constraints/enums)\n const [\n sourceTables,\n targetTables,\n sourceCols,\n targetCols,\n sourceIdxs,\n targetIdxs,\n sourceSnap,\n targetSnap,\n ] = await Promise.all([\n fetchTables(sourcePool),\n fetchTables(targetPool),\n fetchColumns(sourcePool),\n fetchColumns(targetPool),\n fetchIndexes(sourcePool),\n fetchIndexes(targetPool),\n buildLiveSnapshot(sourcePool).catch(() => null),\n buildLiveSnapshot(targetPool).catch(() => null),\n ]);\n\n const { missingTables, extraTables } = diffTables(sourceTables, targetTables);\n const targetSet = new Set(targetTables);\n const commonTables = sourceTables.filter((t) => targetSet.has(t));\n\n const columnDiffs = diffColumns(sourceCols, targetCols, commonTables);\n const indexDiffs = diffIndexes(sourceIdxs, targetIdxs, commonTables);\n\n // Constraint + enum diffs via snapshot comparison\n const constraintDiffs: ConstraintDiff[] = [];\n const enumDiffs: EnumDiff[] = [];\n\n if (sourceSnap && targetSnap) {\n // diffSnapshots treats source as \"old\" and target as \"new\":\n // added = target has, source doesn't (extra in target)\n // removed = source has, target doesn't (missing in target)\n const snapChanges = diffSchemaSnapshots(sourceSnap, targetSnap);\n\n for (const c of snapChanges) {\n if (c.object_type === \"constraint\") {\n constraintDiffs.push({\n table: c.table_name ?? null,\n type: c.change_type === \"added\" ? \"extra\" : c.change_type === \"removed\" ? \"missing\" : \"modified\",\n name: c.detail.split(\" \")[1] ?? c.detail,\n detail: c.detail,\n });\n } else if (c.object_type === \"enum\") {\n enumDiffs.push({\n type: c.change_type === \"added\" ? \"extra\" : c.change_type === \"removed\" ? \"missing\" : \"modified\",\n name: c.detail.split(\" \")[1] ?? c.detail,\n detail: c.detail,\n });\n }\n }\n }\n\n const schema: SchemaDiff = { missingTables, extraTables, columnDiffs, indexDiffs, constraintDiffs, enumDiffs };\n const schemaDrifts = countSchemaDrifts(schema);\n\n let health: HealthDiff | undefined;\n\n if (options?.includeHealth) {\n const longQueryThreshold = 5;\n const [srcReport, tgtReport] = await Promise.all([\n getAdvisorReport(sourcePool, longQueryThreshold),\n getAdvisorReport(targetPool, longQueryThreshold),\n ]);\n\n const srcIssueKeys = new Set(srcReport.issues.map((i) => i.title));\n const tgtIssueKeys = new Set(tgtReport.issues.map((i) => i.title));\n\n const sourceOnlyIssues = srcReport.issues\n .filter((i) => !tgtIssueKeys.has(i.title))\n .map((i) => `${i.severity}: ${i.title}`);\n\n const targetOnlyIssues = tgtReport.issues\n .filter((i) => !srcIssueKeys.has(i.title))\n .map((i) => `${i.severity}: ${i.title}`);\n\n health = {\n source: { score: srcReport.score, grade: srcReport.grade, url: maskConnectionString(sourceConn) },\n target: { score: tgtReport.score, grade: tgtReport.grade, url: maskConnectionString(targetConn) },\n sourceOnlyIssues,\n targetOnlyIssues,\n };\n }\n\n return {\n schema,\n health,\n checkedAt: new Date().toISOString(),\n summary: {\n schemaDrifts,\n identical: schemaDrifts === 0,\n },\n };\n } finally {\n await Promise.allSettled([sourcePool.end(), targetPool.end()]);\n }\n}\n\n/** Mask password in a connection string to avoid leaking credentials */\nfunction maskConnectionString(connStr: string): string {\n try {\n const url = new URL(connStr);\n if (url.password) url.password = \"***\";\n return url.toString();\n } catch {\n return \"<redacted>\";\n }\n}\n\n// ----- formatters -----\n\nexport function formatTextDiff(result: EnvDiffResult): string {\n const lines: string[] = [];\n const sep = \"══════════════════════════════════════\";\n\n lines.push(`Environment Diff`);\n lines.push(sep);\n lines.push(``);\n lines.push(`Schema Drift:`);\n\n const { schema } = result;\n\n if (schema.missingTables.length > 0) {\n lines.push(` ✗ target missing tables: ${schema.missingTables.join(\", \")}`);\n }\n if (schema.extraTables.length > 0) {\n lines.push(` ⚠ target has extra tables: ${schema.extraTables.join(\", \")}`);\n }\n\n const missingCols: string[] = [];\n const extraCols: string[] = [];\n const typeChanges: string[] = [];\n\n for (const cd of schema.columnDiffs) {\n for (const col of cd.missingColumns) {\n missingCols.push(` ${cd.table}: ${col.name} (${col.type})`);\n }\n for (const col of cd.extraColumns) {\n extraCols.push(` ${cd.table}: ${col.name} (${col.type})`);\n }\n for (const td of cd.typeDiffs) {\n typeChanges.push(` ${cd.table}.${td.column}: ${td.sourceType} → ${td.targetType}`);\n }\n }\n\n if (missingCols.length > 0) {\n lines.push(` ✗ target missing columns:`);\n lines.push(...missingCols);\n }\n if (extraCols.length > 0) {\n lines.push(` ⚠ target has extra columns:`);\n lines.push(...extraCols);\n }\n if (typeChanges.length > 0) {\n lines.push(` ~ column type differences:`);\n lines.push(...typeChanges);\n }\n\n const nullableChanges: string[] = [];\n const defaultChanges: string[] = [];\n\n for (const cd of schema.columnDiffs) {\n for (const nd of cd.nullableDiffs) {\n const src = nd.sourceNullable ? \"nullable\" : \"NOT NULL\";\n const tgt = nd.targetNullable ? \"nullable\" : \"NOT NULL\";\n nullableChanges.push(` ${cd.table}.${nd.column}: source=${src} → target=${tgt}`);\n }\n for (const dd of cd.defaultDiffs) {\n const src = dd.sourceDefault ?? \"(none)\";\n const tgt = dd.targetDefault ?? \"(none)\";\n defaultChanges.push(` ${cd.table}.${dd.column}: source=${src} → target=${tgt}`);\n }\n }\n\n if (nullableChanges.length > 0) {\n lines.push(` ~ nullable differences:`);\n lines.push(...nullableChanges);\n }\n if (defaultChanges.length > 0) {\n lines.push(` ~ default differences:`);\n lines.push(...defaultChanges);\n }\n\n const missingIdxs: string[] = [];\n const extraIdxs: string[] = [];\n const modifiedIdxs: string[] = [];\n\n for (const id of schema.indexDiffs) {\n for (const idx of id.missingIndexes) {\n missingIdxs.push(` ${id.table}: ${idx}`);\n }\n for (const idx of id.extraIndexes) {\n extraIdxs.push(` ${id.table}: ${idx}`);\n }\n for (const mi of id.modifiedIndexes) {\n modifiedIdxs.push(` ${id.table}: ${mi.name} source=\"${mi.sourceDef}\" → target=\"${mi.targetDef}\"`);\n }\n }\n\n if (missingIdxs.length > 0) {\n lines.push(` ✗ target missing indexes:`);\n lines.push(...missingIdxs);\n }\n if (extraIdxs.length > 0) {\n lines.push(` ⚠ target has extra indexes:`);\n lines.push(...extraIdxs);\n }\n if (modifiedIdxs.length > 0) {\n lines.push(` ~ index definition differences:`);\n lines.push(...modifiedIdxs);\n }\n\n // Constraint diffs\n const missingConstraints = (schema.constraintDiffs ?? []).filter((c) => c.type === \"missing\");\n const extraConstraints = (schema.constraintDiffs ?? []).filter((c) => c.type === \"extra\");\n const modifiedConstraints = (schema.constraintDiffs ?? []).filter((c) => c.type === \"modified\");\n\n if (missingConstraints.length > 0) {\n lines.push(` ✗ target missing constraints:`);\n for (const c of missingConstraints) {\n lines.push(` ${c.table ? c.table + \": \" : \"\"}${c.detail}`);\n }\n }\n if (extraConstraints.length > 0) {\n lines.push(` ⚠ target has extra constraints:`);\n for (const c of extraConstraints) {\n lines.push(` ${c.table ? c.table + \": \" : \"\"}${c.detail}`);\n }\n }\n if (modifiedConstraints.length > 0) {\n lines.push(` ~ constraint differences:`);\n for (const c of modifiedConstraints) {\n lines.push(` ${c.table ? c.table + \": \" : \"\"}${c.detail}`);\n }\n }\n\n // Enum diffs\n const missingEnums = (schema.enumDiffs ?? []).filter((e) => e.type === \"missing\");\n const extraEnums = (schema.enumDiffs ?? []).filter((e) => e.type === \"extra\");\n const modifiedEnums = (schema.enumDiffs ?? []).filter((e) => e.type === \"modified\");\n\n if (missingEnums.length > 0) {\n lines.push(` ✗ target missing enums:`);\n for (const e of missingEnums) lines.push(` ${e.detail}`);\n }\n if (extraEnums.length > 0) {\n lines.push(` ⚠ target has extra enums:`);\n for (const e of extraEnums) lines.push(` ${e.detail}`);\n }\n if (modifiedEnums.length > 0) {\n lines.push(` ~ enum differences:`);\n for (const e of modifiedEnums) lines.push(` ${e.detail}`);\n }\n\n const noSchemaChanges = schema.missingTables.length === 0 && schema.extraTables.length === 0 &&\n schema.columnDiffs.length === 0 && schema.indexDiffs.length === 0 &&\n (schema.constraintDiffs ?? []).length === 0 && (schema.enumDiffs ?? []).length === 0 &&\n nullableChanges.length === 0 && defaultChanges.length === 0 && modifiedIdxs.length === 0;\n if (noSchemaChanges) {\n lines.push(` ✓ Schemas are identical`);\n }\n\n if (result.health) {\n const h = result.health;\n lines.push(``);\n lines.push(`Health Comparison:`);\n lines.push(` Source: ${h.source.score}/100 (${h.source.grade}) | Target: ${h.target.score}/100 (${h.target.grade})`);\n lines.push(` Source-only issues: ${h.sourceOnlyIssues.length === 0 ? \"(none)\" : \"\"}`);\n for (const iss of h.sourceOnlyIssues) lines.push(` - ${iss}`);\n lines.push(` Target-only issues: ${h.targetOnlyIssues.length === 0 ? \"(none)\" : \"\"}`);\n for (const iss of h.targetOnlyIssues) lines.push(` - ${iss}`);\n }\n\n lines.push(``);\n lines.push(sep);\n const { schemaDrifts, identical } = result.summary;\n lines.push(`Total: ${schemaDrifts} schema drift${schemaDrifts !== 1 ? \"s\" : \"\"} | Environments are ${identical ? \"in sync ✓\" : \"NOT in sync ✗\"}`);\n\n return lines.join(\"\\n\");\n}\n\nexport function formatMdDiff(result: EnvDiffResult): string {\n const lines: string[] = [];\n lines.push(`## 🔄 Environment Diff`);\n lines.push(``);\n lines.push(`### Schema Drift`);\n lines.push(``);\n\n const { schema } = result;\n const rows: Array<[string, string]> = [];\n\n if (schema.missingTables.length > 0) {\n rows.push([`❌ Missing tables`, schema.missingTables.map((t) => `\\`${t}\\``).join(\", \")]);\n }\n if (schema.extraTables.length > 0) {\n rows.push([`⚠️ Extra tables`, schema.extraTables.map((t) => `\\`${t}\\``).join(\", \")]);\n }\n\n const missingColItems: string[] = [];\n const extraColItems: string[] = [];\n const typeItems: string[] = [];\n\n for (const cd of schema.columnDiffs) {\n for (const col of cd.missingColumns) {\n missingColItems.push(`\\`${cd.table}.${col.name}\\``);\n }\n for (const col of cd.extraColumns) {\n extraColItems.push(`\\`${cd.table}.${col.name}\\``);\n }\n for (const td of cd.typeDiffs) {\n typeItems.push(`\\`${cd.table}.${td.column}\\` (${td.sourceType}→${td.targetType})`);\n }\n }\n\n if (missingColItems.length > 0) rows.push([`❌ Missing columns`, missingColItems.join(\", \")]);\n if (extraColItems.length > 0) rows.push([`⚠️ Extra columns`, extraColItems.join(\", \")]);\n if (typeItems.length > 0) rows.push([`~ Type differences`, typeItems.join(\", \")]);\n\n const nullableItems: string[] = [];\n const defaultItems: string[] = [];\n\n for (const cd of schema.columnDiffs) {\n for (const nd of cd.nullableDiffs) {\n const src = nd.sourceNullable ? \"nullable\" : \"NOT NULL\";\n const tgt = nd.targetNullable ? \"nullable\" : \"NOT NULL\";\n nullableItems.push(`\\`${cd.table}.${nd.column}\\` (${src}→${tgt})`);\n }\n for (const dd of cd.defaultDiffs) {\n const src = dd.sourceDefault ?? \"(none)\";\n const tgt = dd.targetDefault ?? \"(none)\";\n defaultItems.push(`\\`${cd.table}.${dd.column}\\` (${src}→${tgt})`);\n }\n }\n\n if (nullableItems.length > 0) rows.push([`~ Nullable differences`, nullableItems.join(\", \")]);\n if (defaultItems.length > 0) rows.push([`~ Default differences`, defaultItems.join(\", \")]);\n\n const missingIdxItems: string[] = [];\n const extraIdxItems: string[] = [];\n const modifiedIdxItems: string[] = [];\n\n for (const id of schema.indexDiffs) {\n for (const idx of id.missingIndexes) missingIdxItems.push(`\\`${id.table}.${idx}\\``);\n for (const idx of id.extraIndexes) extraIdxItems.push(`\\`${id.table}.${idx}\\``);\n for (const mi of id.modifiedIndexes) modifiedIdxItems.push(`\\`${id.table}.${mi.name}\\``);\n }\n\n if (missingIdxItems.length > 0) rows.push([`❌ Missing indexes`, missingIdxItems.join(\", \")]);\n if (extraIdxItems.length > 0) rows.push([`⚠️ Extra indexes`, extraIdxItems.join(\", \")]);\n if (modifiedIdxItems.length > 0) rows.push([`~ Modified indexes`, modifiedIdxItems.join(\", \")]);\n\n // Constraints\n const missingConItems = (schema.constraintDiffs ?? []).filter((c) => c.type === \"missing\").map((c) => c.detail);\n const extraConItems = (schema.constraintDiffs ?? []).filter((c) => c.type === \"extra\").map((c) => c.detail);\n const modConItems = (schema.constraintDiffs ?? []).filter((c) => c.type === \"modified\").map((c) => c.detail);\n if (missingConItems.length > 0) rows.push([`❌ Missing constraints`, missingConItems.join(\"; \")]);\n if (extraConItems.length > 0) rows.push([`⚠️ Extra constraints`, extraConItems.join(\"; \")]);\n if (modConItems.length > 0) rows.push([`~ Modified constraints`, modConItems.join(\"; \")]);\n\n // Enums\n const missingEnumItems = (schema.enumDiffs ?? []).filter((e) => e.type === \"missing\").map((e) => e.detail);\n const extraEnumItems = (schema.enumDiffs ?? []).filter((e) => e.type === \"extra\").map((e) => e.detail);\n const modEnumItems = (schema.enumDiffs ?? []).filter((e) => e.type === \"modified\").map((e) => e.detail);\n if (missingEnumItems.length > 0) rows.push([`❌ Missing enums`, missingEnumItems.join(\"; \")]);\n if (extraEnumItems.length > 0) rows.push([`⚠️ Extra enums`, extraEnumItems.join(\"; \")]);\n if (modEnumItems.length > 0) rows.push([`~ Modified enums`, modEnumItems.join(\"; \")]);\n\n if (rows.length > 0) {\n lines.push(`| Type | Details |`);\n lines.push(`|------|---------|`);\n for (const [type, details] of rows) {\n lines.push(`| ${type} | ${details} |`);\n }\n } else {\n lines.push(`✅ Schemas are identical`);\n }\n\n if (result.health) {\n const h = result.health;\n lines.push(``);\n lines.push(`### Health Comparison`);\n lines.push(``);\n lines.push(`| | Score | Grade |`);\n lines.push(`|--|-------|-------|`);\n lines.push(`| Source | ${h.source.score}/100 | ${h.source.grade} |`);\n lines.push(`| Target | ${h.target.score}/100 | ${h.target.grade} |`);\n\n if (h.targetOnlyIssues.length > 0) {\n lines.push(``);\n lines.push(`**Target-only issues:**`);\n for (const iss of h.targetOnlyIssues) lines.push(`- ${iss}`);\n }\n if (h.sourceOnlyIssues.length > 0) {\n lines.push(``);\n lines.push(`**Source-only issues:**`);\n for (const iss of h.sourceOnlyIssues) lines.push(`- ${iss}`);\n }\n }\n\n lines.push(``);\n const { schemaDrifts, identical } = result.summary;\n lines.push(`**Result: ${schemaDrifts} drift${schemaDrifts !== 1 ? \"s\" : \"\"} — environments are ${identical ? \"in sync ✓\" : \"NOT in sync\"}**`);\n\n return lines.join(\"\\n\");\n}\n","import { parseArgs } from \"node:util\";\nimport { startServer } from \"./server/index.js\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nprocess.on(\"uncaughtException\", (err) => {\n console.error(\"Uncaught exception:\", err);\n});\nprocess.on(\"unhandledRejection\", (err) => {\n console.error(\"Unhandled rejection:\", err);\n});\n\nconst { values, positionals } = parseArgs({\n allowPositionals: true,\n options: {\n port: { type: \"string\", short: \"p\", default: \"3480\" },\n bind: { type: \"string\", default: \"127.0.0.1\" },\n auth: { type: \"string\" },\n token: { type: \"string\" },\n webhook: { type: \"string\" },\n \"slack-webhook\": { type: \"string\" },\n \"discord-webhook\": { type: \"string\" },\n \"no-open\": { type: \"boolean\", default: false },\n json: { type: \"boolean\", default: false },\n host: { type: \"string\" },\n user: { type: \"string\", short: \"u\" },\n password: { type: \"string\" },\n db: { type: \"string\", short: \"d\" },\n \"pg-port\": { type: \"string\" },\n \"data-dir\": { type: \"string\" },\n interval: { type: \"string\", short: \"i\" },\n \"retention-days\": { type: \"string\" },\n \"snapshot-interval\": { type: \"string\" },\n \"query-stats-interval\": { type: \"string\" },\n \"long-query-threshold\": { type: \"string\" },\n help: { type: \"boolean\", short: \"h\" },\n version: { type: \"boolean\", short: \"v\" },\n threshold: { type: \"string\" },\n format: { type: \"string\", short: \"f\" },\n ci: { type: \"boolean\", default: false },\n diff: { type: \"boolean\", default: false },\n \"snapshot-path\": { type: \"string\" },\n source: { type: \"string\" },\n target: { type: \"string\" },\n health: { type: \"boolean\", default: false },\n },\n});\n\nif (values.version) {\n try {\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n const pkg = JSON.parse(fs.readFileSync(path.resolve(__dirname, \"../package.json\"), \"utf-8\"));\n console.log(`pg-dash v${pkg.version}`);\n } catch {\n console.log(\"pg-dash v0.1.0\");\n }\n process.exit(0);\n}\n\nif (values.help) {\n console.log(`\npg-dash — Lightweight PostgreSQL Monitoring Dashboard\n\nUsage:\n pg-dash <connection-string> Start dashboard\n pg-dash check <connection-string> Run health check and exit\n pg-dash check-migration <file> [connection] Analyze migration SQL for risks\n pg-dash diff-env --source <url> --target <url> Compare two environments\n pg-dash schema-diff <connection-string> Show latest schema changes\n pg-dash --host localhost --user postgres --db mydb\n\nOptions:\n -p, --port <port> Dashboard port (default: 3480)\n --bind <addr> Bind address (default: 127.0.0.1)\n --auth <user:pass> Basic auth credentials (user:password)\n --token <token> Bearer token for authentication\n --webhook <url> Webhook URL for alert notifications\n --slack-webhook <url> Slack webhook URL (convenience alias)\n --discord-webhook <url> Discord webhook URL (convenience alias)\n --no-open Don't auto-open browser (default: opens)\n --json Dump health check as JSON and exit\n --host <host> PostgreSQL host\n -u, --user <user> PostgreSQL user\n --password <pass> PostgreSQL password\n --db, -d <database> PostgreSQL database\n --pg-port <port> PostgreSQL port (default: 5432)\n --data-dir <dir> Data directory for metrics (default: ~/.pg-dash)\n -i, --interval <sec> Collection interval in seconds (default: 30)\n --retention-days <N> Metrics retention in days (default: 7)\n --snapshot-interval <h> Schema snapshot interval in hours (default: 6)\n --query-stats-interval <min> Query stats snapshot interval in minutes (default: 5)\n --long-query-threshold <min> Long query threshold in minutes (default: 5)\n --threshold <score> Health score threshold for check command (default: 70)\n -f, --format <fmt> Output format: text|json|md (default: text)\n --ci Output GitHub Actions compatible annotations\n --diff Compare with previous run (saves snapshot for next run)\n --snapshot-path <path> Path to snapshot file for --diff (default: ~/.pg-dash/last-check.json)\n --source <url> Source database connection string (diff-env)\n --target <url> Target database connection string (diff-env)\n --health Also compare health scores and issues (diff-env)\n -v, --version Show version\n -h, --help Show this help\n\nEnvironment variables:\n PG_DASH_RETENTION_DAYS, PG_DASH_SNAPSHOT_INTERVAL, PG_DASH_LONG_QUERY_THRESHOLD\n`);\n process.exit(0);\n}\n\nconst KNOWN_SUBCOMMANDS = [\"check\", \"check-migration\", \"schema-diff\", \"diff-env\"];\nconst subcommand = positionals[0];\n\nfunction isValidConnectionString(s: string): boolean {\n return (\n s.startsWith(\"postgresql://\") ||\n s.startsWith(\"postgres://\") ||\n s.includes(\"@\") || // user@host shorthand\n s.includes(\"=\") // key=value DSN\n );\n}\n\nfunction resolveConnectionString(startIdx = 0): string {\n let connStr = positionals[startIdx];\n if (!connStr) {\n if (values.host) {\n const user = values.user || \"postgres\";\n const pass = values.password ? `:${values.password}` : \"\";\n const host = values.host;\n const pgPort = values[\"pg-port\"] || \"5432\";\n const db = values.db || \"postgres\";\n connStr = `postgresql://${user}${pass}@${host}:${pgPort}/${db}`;\n } else {\n console.error(\"Error: provide a connection string or --host\\n\\nRun pg-dash --help for usage.\");\n process.exit(1);\n }\n }\n if (!isValidConnectionString(connStr)) {\n console.error(\n `Error: \"${connStr}\" doesn't look like a valid connection string.\\n` +\n ` Expected: postgresql://user:pass@host:5432/db\\n\\n` +\n `Known subcommands: ${KNOWN_SUBCOMMANDS.join(\", \")}\\n` +\n `Run pg-dash --help for usage.`\n );\n process.exit(1);\n }\n return connStr;\n}\n\nif (subcommand === \"check\") {\n // Health check mode\n const connectionString = resolveConnectionString(1);\n const threshold = parseInt(values.threshold || \"70\", 10);\n const format = values.format || \"text\";\n const ci = values.ci || false;\n const useDiff = values.diff || false;\n\n const { Pool } = await import(\"pg\");\n const { getAdvisorReport } = await import(\"./server/advisor.js\");\n const { saveSnapshot, loadSnapshot, diffSnapshots } = await import(\"./server/snapshot.js\");\n const os = await import(\"node:os\");\n\n const pool = new Pool({ connectionString, connectionTimeoutMillis: 10000 });\n const checkDataDir = values[\"data-dir\"] || path.join(os.homedir(), \".pg-dash\");\n // --snapshot-path lets CI persist the snapshot across ephemeral runners via cache\n const snapshotPath = values[\"snapshot-path\"] || path.join(checkDataDir, \"last-check.json\");\n\n try {\n const lqt = parseInt(values[\"long-query-threshold\"] || process.env.PG_DASH_LONG_QUERY_THRESHOLD || \"5\", 10);\n const report = await getAdvisorReport(pool, lqt);\n\n // Diff logic\n let diff: import(\"./server/snapshot.js\").SnapshotDiff | null = null;\n if (useDiff) {\n const prev = loadSnapshot(snapshotPath);\n if (prev) {\n diff = diffSnapshots(prev.result, report);\n }\n saveSnapshot(snapshotPath, report);\n }\n\n if (format === \"json\") {\n const output: any = { ...report };\n if (diff) output.diff = diff;\n console.log(JSON.stringify(output, null, 2));\n } else if (format === \"md\" || (ci && format !== \"text\")) {\n // Markdown report (for CI PR comments)\n console.log(`## 🏥 pg-dash Health Report\\n`);\n if (diff) {\n const sign = diff.scoreDelta >= 0 ? \"+\" : \"\";\n console.log(`**Score: ${diff.previousScore} → ${report.score} (${sign}${diff.scoreDelta})**\\n`);\n } else {\n console.log(`**Score: ${report.score}/100 (${report.grade})**\\n`);\n }\n console.log(`| Category | Score | Grade | Issues |`);\n console.log(`|----------|-------|-------|--------|`);\n for (const [cat, b] of Object.entries(report.breakdown)) {\n console.log(`| ${cat} | ${b.score} | ${b.grade} | ${b.count} |`);\n }\n if (diff) {\n if (diff.resolvedIssues.length > 0) {\n console.log(`\\n### ✅ Resolved (${diff.resolvedIssues.length})`);\n for (const i of diff.resolvedIssues) console.log(`- ~~${i.title}~~`);\n }\n if (diff.newIssues.length > 0) {\n console.log(`\\n### 🆕 New Issues (${diff.newIssues.length})`);\n for (const i of diff.newIssues) {\n const icon = i.severity === \"critical\" ? \"🔴\" : i.severity === \"warning\" ? \"🟡\" : \"🔵\";\n console.log(`- ${icon} [${i.severity}] ${i.title}`);\n }\n }\n }\n if (report.issues.length > 0) {\n console.log(`\\n### ⚠️ Issues (${report.issues.length})\\n`);\n for (const issue of report.issues) {\n const sev = issue.severity === \"critical\" ? \"error\" : issue.severity === \"warning\" ? \"warning\" : \"notice\";\n console.log(`- [${sev}] ${issue.title}`);\n }\n } else {\n console.log(`\\n✅ No issues found!`);\n }\n if (report.batchFixes.length > 0) {\n console.log(`\\n### 🔧 Batch Fixes\\n`);\n console.log(\"```sql\");\n for (const fix of report.batchFixes) {\n console.log(`-- ${fix.title}`);\n console.log(fix.sql);\n }\n console.log(\"```\");\n }\n } else if (ci) {\n // GitHub Actions annotations\n for (const issue of report.issues) {\n const level = issue.severity === \"critical\" ? \"error\" : issue.severity === \"warning\" ? \"warning\" : \"notice\";\n console.log(`::${level}::${issue.title}: ${issue.description}`);\n }\n // Summary table\n console.log(`\\nHealth Score: ${report.score}/100 (${report.grade})`);\n for (const [cat, b] of Object.entries(report.breakdown)) {\n console.log(` ${cat.padEnd(14)} ${b.grade} (${b.score}/100) — ${b.count} issue${b.count !== 1 ? \"s\" : \"\"}`);\n }\n if (diff) {\n const sign = diff.scoreDelta >= 0 ? \"+\" : \"\";\n console.log(`\\nScore: ${diff.previousScore} → ${report.score} (${sign}${diff.scoreDelta})`);\n console.log(`Resolved: ${diff.resolvedIssues.length} issues`);\n console.log(`New: ${diff.newIssues.length} issues`);\n }\n } else {\n // Plain text\n if (diff) {\n const sign = diff.scoreDelta >= 0 ? \"+\" : \"\";\n console.log(`\\n Score: ${diff.previousScore} → ${report.score} (${sign}${diff.scoreDelta})\\n`);\n if (diff.resolvedIssues.length > 0) {\n console.log(` ✅ Resolved: ${diff.resolvedIssues.length} issues`);\n for (const i of diff.resolvedIssues) console.log(` - ${i.title}`);\n }\n if (diff.newIssues.length > 0) {\n console.log(` 🆕 New: ${diff.newIssues.length} issues`);\n for (const i of diff.newIssues) console.log(` - ${i.title}`);\n }\n console.log();\n } else {\n console.log(`\\n Health Score: ${report.score}/100 (Grade: ${report.grade})\\n`);\n }\n for (const [cat, b] of Object.entries(report.breakdown)) {\n console.log(` ${cat.padEnd(14)} ${b.grade} (${b.score}/100) — ${b.count} issue${b.count !== 1 ? \"s\" : \"\"}`);\n }\n if (report.issues.length > 0) {\n console.log(`\\n Issues (${report.issues.length}):\\n`);\n for (const issue of report.issues) {\n const icon = issue.severity === \"critical\" ? \"🔴\" : issue.severity === \"warning\" ? \"🟡\" : \"🔵\";\n console.log(` ${icon} [${issue.severity}] ${issue.title}`);\n }\n }\n console.log();\n }\n await pool.end();\n process.exit(report.score < threshold ? 1 : 0);\n } catch (err: any) {\n console.error(`Error: ${err.message}`);\n await pool.end();\n process.exit(1);\n }\n} else if (subcommand === \"check-migration\") {\n // Migration safety check mode\n // Usage: pg-dash check-migration <file> [connection] [--ci] [-f json|text|md]\n const filePath = positionals[1];\n if (!filePath) {\n console.error(\"Error: provide a migration SQL file path.\\n\\nUsage: pg-dash check-migration <file> [connection]\");\n process.exit(1);\n }\n\n if (!fs.existsSync(filePath)) {\n console.error(`Error: File not found: ${filePath}`);\n process.exit(1);\n }\n\n const sql = fs.readFileSync(filePath, \"utf-8\");\n\n // Optional connection string (third positional arg)\n const migrationConn = positionals[2];\n const format = values.format || \"text\";\n const ci = values.ci || false;\n\n const { analyzeMigration } = await import(\"./server/migration-checker.js\");\n\n let pool: import(\"pg\").Pool | undefined;\n if (migrationConn) {\n const { Pool } = await import(\"pg\");\n pool = new Pool({ connectionString: migrationConn, connectionTimeoutMillis: 10000 });\n }\n\n try {\n const result = await analyzeMigration(sql, pool);\n if (pool) await pool.end();\n\n const sep = \"─\".repeat(48);\n\n if (format === \"json\") {\n console.log(JSON.stringify(result, null, 2));\n } else if (format === \"md\") {\n console.log(\"## 🔍 Migration Safety Check\\n\");\n console.log(\"| Severity | Code | Message |\");\n console.log(\"|----------|------|---------|\");\n for (const issue of result.issues) {\n const sev =\n issue.severity === \"error\"\n ? \"🔴 ERROR\"\n : issue.severity === \"warning\"\n ? \"⚠️ WARNING\"\n : \"ℹ️ INFO\";\n console.log(`| ${sev} | ${issue.code} | ${issue.message} |`);\n }\n const { errors, warnings, infos } = result.summary;\n const safeLabel = result.safe ? \"✅ SAFE\" : \"❌ UNSAFE\";\n console.log(`\\n**Result: ${safeLabel} — ${errors} error${errors !== 1 ? \"s\" : \"\"}, ${warnings} warning${warnings !== 1 ? \"s\" : \"\"}, ${infos} info${infos !== 1 ? \"s\" : \"\"}**`);\n } else {\n // Text format\n console.log(`\\nMigration check: ${filePath}`);\n console.log(sep);\n if (result.issues.length === 0) {\n console.log(\"\\n ✅ No issues found!\\n\");\n } else {\n for (const issue of result.issues) {\n const icon =\n issue.severity === \"error\" ? \"✗\" : issue.severity === \"warning\" ? \"⚠\" : \"✓\";\n const indent = \" \";\n const parts = [`${indent}${icon} ${issue.message}`];\n if (issue.suggestion) parts.push(`${indent} Suggestion: ${issue.suggestion}`);\n if (issue.estimatedRows !== undefined) {\n parts.push(\n `${indent} Est. rows: ${issue.estimatedRows.toLocaleString()}` +\n (issue.estimatedLockSeconds !== undefined\n ? `, lock ~${issue.estimatedLockSeconds}s`\n : \"\")\n );\n }\n if (issue.lineNumber !== undefined) parts.push(`${indent} Line ${issue.lineNumber}`);\n console.log(parts.join(\"\\n\") + \"\\n\");\n }\n }\n console.log(sep);\n const { errors, warnings, infos } = result.summary;\n const safeLabel = result.safe ? \"SAFE\" : \"UNSAFE\";\n console.log(\n `Result: ${safeLabel} — ${errors} error${errors !== 1 ? \"s\" : \"\"}, ${warnings} warning${warnings !== 1 ? \"s\" : \"\"}, ${infos} info${infos !== 1 ? \"s\" : \"\"}\\n`\n );\n if (!migrationConn) {\n console.log(\"Run with a connection string for more accurate row count estimates.\\n\");\n }\n }\n\n // --ci annotations\n if (ci) {\n for (const issue of result.issues) {\n const level = issue.severity === \"error\" ? \"error\" : issue.severity === \"warning\" ? \"warning\" : \"notice\";\n const loc = issue.lineNumber ? `,line=${issue.lineNumber}` : \"\";\n const file = `file=${filePath}${loc}`;\n console.log(`::${level} ${file}::${issue.message}`);\n }\n }\n\n process.exit(result.safe ? 0 : 1);\n } catch (err: any) {\n if (pool) await pool.end().catch(() => {});\n console.error(`Error: ${err.message}`);\n process.exit(1);\n }\n} else if (subcommand === \"schema-diff\") {\n // Schema diff mode\n const connectionString = resolveConnectionString(1);\n const format = values.format || \"text\";\n const dataDir = values[\"data-dir\"] || path.join((await import(\"node:os\")).homedir(), \".pg-dash\");\n const schemaDbPath = path.join(dataDir, \"schema.db\");\n\n if (!fs.existsSync(schemaDbPath)) {\n console.error(\"No schema tracking data found. Run pg-dash server first to collect schema snapshots.\");\n process.exit(1);\n }\n\n const Database = (await import(\"better-sqlite3\")).default;\n const db = new Database(schemaDbPath, { readonly: true });\n const changes = db.prepare(\"SELECT * FROM schema_changes ORDER BY timestamp DESC LIMIT 50\").all() as any[];\n db.close();\n\n if (format === \"json\") {\n console.log(JSON.stringify(changes.map((c) => ({\n type: c.change_type,\n objectType: c.object_type,\n objectName: c.object_name,\n tableName: c.table_name,\n detail: c.detail,\n timestamp: c.timestamp,\n })), null, 2));\n } else {\n if (changes.length === 0) {\n console.log(\"No schema changes detected.\");\n } else {\n console.log(`\\n Schema Changes (${changes.length}):\\n`);\n for (const c of changes) {\n const icon = c.change_type === \"added\" ? \"+\" : c.change_type === \"removed\" ? \"−\" : \"~\";\n const color = c.change_type === \"added\" ? \"\\x1b[32m\" : c.change_type === \"removed\" ? \"\\x1b[31m\" : \"\\x1b[33m\";\n console.log(` ${color}${icon}\\x1b[0m ${c.detail}${c.table_name ? ` (${c.table_name})` : \"\"} — ${new Date(c.timestamp).toLocaleString()}`);\n }\n console.log();\n }\n }\n process.exit(0);\n} else if (subcommand === \"diff-env\") {\n // Multi-environment schema + health diff\n const sourceUrl = values.source;\n const targetUrl = values.target;\n if (!sourceUrl || !targetUrl) {\n console.error(\"Error: diff-env requires --source <url> and --target <url>\");\n process.exit(1);\n }\n const format = values.format || \"text\";\n const includeHealth = values.health || false;\n const ci = values.ci || false;\n\n const { diffEnvironments, formatTextDiff, formatMdDiff } = await import(\"./server/env-differ.js\");\n\n try {\n const result = await diffEnvironments(sourceUrl, targetUrl, { includeHealth });\n\n if (format === \"json\") {\n console.log(JSON.stringify(result, null, 2));\n } else if (format === \"md\") {\n console.log(formatMdDiff(result));\n } else {\n // text (default)\n const text = formatTextDiff(result);\n console.log(text);\n if (ci) {\n // GitHub Actions annotations — severity matches impact\n for (const t of result.schema.missingTables) {\n console.log(`::error::diff-env: target missing table: ${t}`);\n }\n for (const t of result.schema.extraTables) {\n console.log(`::notice::diff-env: target has extra table: ${t}`);\n }\n for (const cd of result.schema.columnDiffs) {\n for (const col of cd.missingColumns) {\n console.log(`::error::diff-env: target missing column: ${cd.table}.${col.name} (${col.type})`);\n }\n for (const col of cd.extraColumns) {\n console.log(`::notice::diff-env: target has extra column: ${cd.table}.${col.name} (${col.type})`);\n }\n for (const td of cd.typeDiffs) {\n console.log(`::error::diff-env: type mismatch: ${cd.table}.${td.column} ${td.sourceType}→${td.targetType}`);\n }\n }\n for (const id of result.schema.indexDiffs) {\n for (const idx of id.missingIndexes) {\n console.log(`::warning::diff-env: target missing index: ${id.table}.${idx}`);\n }\n for (const idx of id.extraIndexes) {\n console.log(`::notice::diff-env: target has extra index: ${id.table}.${idx}`);\n }\n }\n for (const c of result.schema.constraintDiffs ?? []) {\n const level = c.type === \"missing\" ? \"error\" : c.type === \"extra\" ? \"notice\" : \"warning\";\n console.log(`::${level}::diff-env: constraint ${c.type}: ${c.detail}`);\n }\n for (const e of result.schema.enumDiffs ?? []) {\n const level = e.type === \"missing\" ? \"error\" : e.type === \"extra\" ? \"notice\" : \"warning\";\n console.log(`::${level}::diff-env: enum ${e.type}: ${e.detail}`);\n }\n }\n }\n\n process.exit(result.summary.identical ? 0 : 1);\n } catch (err: any) {\n console.error(`Error: ${err.message}`);\n process.exit(1);\n }\n} else {\n // Check for unknown subcommands before treating positional as connection string\n if (subcommand && !isValidConnectionString(subcommand) && KNOWN_SUBCOMMANDS.indexOf(subcommand) === -1) {\n console.error(\n `Error: Unknown subcommand \"${subcommand}\".\\n\\n` +\n `Known subcommands: ${KNOWN_SUBCOMMANDS.join(\", \")}\\n` +\n `Run pg-dash --help for usage.`\n );\n process.exit(1);\n }\n\n // Default: start server\n const connectionString = resolveConnectionString(0);\n const port = parseInt(values.port!, 10);\n const bind = values.bind || process.env.PG_DASH_BIND || \"127.0.0.1\";\n const interval = values.interval ? parseInt(values.interval, 10) : undefined;\n const retentionDays = parseInt(values[\"retention-days\"] || process.env.PG_DASH_RETENTION_DAYS || \"7\", 10);\n const snapshotInterval = parseInt(values[\"snapshot-interval\"] || process.env.PG_DASH_SNAPSHOT_INTERVAL || \"6\", 10);\n const queryStatsInterval = parseInt(values[\"query-stats-interval\"] || process.env.PG_DASH_QUERY_STATS_INTERVAL || \"5\", 10);\n const longQueryThreshold = parseInt(values[\"long-query-threshold\"] || process.env.PG_DASH_LONG_QUERY_THRESHOLD || \"5\", 10);\n const auth = values.auth || undefined;\n const token = values.token || undefined;\n const webhook = values[\"slack-webhook\"] || values[\"discord-webhook\"] || values.webhook || undefined;\n\n // Security warning\n if (bind === \"0.0.0.0\" && !auth && !token) {\n console.warn(\"\\n ⚠️ WARNING: Dashboard is exposed without authentication. Use --auth or --token.\\n\");\n }\n\n await startServer({\n connectionString,\n port,\n bind,\n open: !values[\"no-open\"],\n json: values.json!,\n dataDir: values[\"data-dir\"],\n interval,\n retentionDays,\n snapshotInterval,\n queryStatsInterval,\n longQueryThreshold,\n auth,\n token,\n webhook,\n });\n}\n","import { Hono } from \"hono\";\nimport path from \"node:path\";\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport { fileURLToPath } from \"node:url\";\nimport { Pool } from \"pg\";\nimport { getOverview } from \"./queries/overview.js\";\nimport { getDatabases } from \"./queries/databases.js\";\nimport { getTables } from \"./queries/tables.js\";\nimport { getActivity } from \"./queries/activity.js\";\nimport { getAdvisorReport } from \"./advisor.js\";\nimport { TimeseriesStore } from \"./timeseries.js\";\nimport { Collector } from \"./collector.js\";\nimport { SchemaTracker } from \"./schema-tracker.js\";\nimport { AlertManager } from \"./alerts.js\";\nimport { registerOverviewRoutes } from \"./routes/overview.js\";\nimport { registerMetricsRoutes } from \"./routes/metrics.js\";\nimport { registerActivityRoutes } from \"./routes/activity.js\";\nimport { registerAdvisorRoutes } from \"./routes/advisor.js\";\nimport { registerSchemaRoutes } from \"./routes/schema.js\";\nimport { registerAlertsRoutes } from \"./routes/alerts.js\";\nimport { registerExplainRoutes } from \"./routes/explain.js\";\nimport { registerDiskRoutes } from \"./routes/disk.js\";\nimport { QueryStatsStore } from \"./query-stats.js\";\nimport { registerQueryStatsRoutes } from \"./routes/query-stats.js\";\nimport { registerExportRoutes } from \"./routes/export.js\";\nimport { DiskPredictor } from \"./disk-prediction.js\";\nimport Database from \"better-sqlite3\";\nimport { WebSocketServer, WebSocket } from \"ws\";\nimport http from \"node:http\";\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\ninterface ServerOptions {\n connectionString: string;\n port: number;\n bind?: string;\n open: boolean;\n json: boolean;\n dataDir?: string;\n interval?: number;\n retentionDays?: number;\n snapshotInterval?: number;\n queryStatsInterval?: number;\n longQueryThreshold?: number;\n auth?: string;\n token?: string;\n webhook?: string;\n}\n\nexport async function startServer(opts: ServerOptions) {\n const pool = new Pool({ connectionString: opts.connectionString, connectionTimeoutMillis: 10000 });\n\n // Test connection\n try {\n const client = await pool.connect();\n client.release();\n } catch (err: any) {\n console.error(`Failed to connect to PostgreSQL: ${err.message}`);\n process.exit(1);\n }\n\n const longQueryThreshold = opts.longQueryThreshold || 5;\n const diskPredictor = new DiskPredictor();\n\n // JSON mode: dump health and exit\n if (opts.json) {\n try {\n const [overview, advisor, databases, tables] = await Promise.all([\n getOverview(pool),\n getAdvisorReport(pool, longQueryThreshold),\n getDatabases(pool),\n getTables(pool),\n ]);\n console.log(JSON.stringify({ overview, advisor, databases, tables }, null, 2));\n } catch (err: any) {\n console.error(JSON.stringify({ error: err.message }));\n process.exit(1);\n }\n await pool.end();\n process.exit(0);\n }\n\n const dataDir = opts.dataDir || path.join(os.homedir(), \".pg-dash\");\n fs.mkdirSync(dataDir, { recursive: true });\n\n // Initialize shared metrics database\n const metricsDbPath = path.join(dataDir, \"metrics.db\");\n const metricsDb = new Database(metricsDbPath);\n metricsDb.pragma(\"journal_mode = WAL\");\n\n // Initialize time-series store and collector\n const store = new TimeseriesStore(metricsDb, opts.retentionDays);\n const intervalMs = (opts.interval || 30) * 1000;\n const collector = new Collector(pool, store, intervalMs);\n\n console.log(` Collecting metrics every ${(intervalMs / 1000)}s...`);\n collector.start();\n\n // Initialize schema tracker\n const schemaDbPath = path.join(dataDir, \"schema.db\");\n const schemaDb = new Database(schemaDbPath);\n schemaDb.pragma(\"journal_mode = WAL\");\n const snapshotIntervalMs = (opts.snapshotInterval || 6) * 60 * 60 * 1000;\n const schemaTracker = new SchemaTracker(schemaDb, pool, snapshotIntervalMs);\n schemaTracker.start();\n console.log(\" Schema change tracking enabled\");\n\n // Initialize alerts\n const alertsDbPath = path.join(dataDir, \"alerts.db\");\n const alertsDb = new Database(alertsDbPath);\n alertsDb.pragma(\"journal_mode = WAL\");\n const alertManager = new AlertManager(alertsDb, opts.webhook);\n console.log(\" Alert monitoring enabled\");\n\n // Initialize query stats store (shares metricsDb)\n const queryStatsStore = new QueryStatsStore(metricsDb, opts.retentionDays);\n const querySnapshotIntervalMs = (opts.queryStatsInterval || 5) * 60 * 1000;\n queryStatsStore.startPeriodicSnapshot(pool, querySnapshotIntervalMs);\n console.log(` Query stats snapshots every ${querySnapshotIntervalMs / 60000}m`);\n\n const app = new Hono();\n\n // Auth endpoint for cookie-based auth (must be before auth middleware)\n if (opts.token) {\n app.post(\"/api/auth\", async (c) => {\n try {\n const body = await c.req.json();\n if (body?.token === opts.token) {\n c.header(\"Set-Cookie\", `pg-dash-token=${opts.token}; Path=/; HttpOnly; SameSite=Strict; Max-Age=86400`);\n return c.json({ ok: true });\n }\n return c.json({ error: \"Invalid token\" }, 401);\n } catch {\n return c.json({ error: \"Invalid request\" }, 400);\n }\n });\n }\n\n // Auth middleware\n if (opts.auth || opts.token) {\n app.use(\"*\", async (c, next) => {\n const authHeader = c.req.header(\"authorization\") || \"\";\n if (opts.token) {\n if (authHeader === `Bearer ${opts.token}`) return next();\n }\n if (opts.auth) {\n const [user, pass] = opts.auth.split(\":\");\n const expected = \"Basic \" + Buffer.from(`${user}:${pass}`).toString(\"base64\");\n if (authHeader === expected) return next();\n }\n // Check query param token for WebSocket upgrade compatibility\n const url = new URL(c.req.url, \"http://localhost\");\n if (opts.token && url.searchParams.get(\"token\") === opts.token) return next();\n\n // Check cookie for token auth\n if (opts.token) {\n const cookies = c.req.header(\"cookie\") || \"\";\n const match = cookies.match(/(?:^|;\\s*)pg-dash-token=([^;]*)/);\n if (match && match[1] === opts.token) return next();\n }\n\n if (opts.auth) {\n c.header(\"WWW-Authenticate\", 'Basic realm=\"pg-dash\"');\n }\n return c.text(\"Unauthorized\", 401);\n });\n }\n\n // Register route modules\n registerOverviewRoutes(app, pool);\n registerMetricsRoutes(app, store, collector);\n registerActivityRoutes(app, pool);\n registerAdvisorRoutes(app, pool, longQueryThreshold, store);\n registerSchemaRoutes(app, pool, schemaTracker);\n registerAlertsRoutes(app, alertManager);\n registerExplainRoutes(app, pool);\n registerDiskRoutes(app, pool, store);\n registerQueryStatsRoutes(app, queryStatsStore);\n registerExportRoutes(app, pool, longQueryThreshold);\n\n // Serve frontend\n const uiPath = path.resolve(__dirname, \"ui\");\n const MIME_TYPES: Record<string, string> = {\n \".html\": \"text/html\",\n \".js\": \"application/javascript\",\n \".css\": \"text/css\",\n \".json\": \"application/json\",\n \".png\": \"image/png\",\n \".jpg\": \"image/jpeg\",\n \".svg\": \"image/svg+xml\",\n \".ico\": \"image/x-icon\",\n \".woff\": \"font/woff\",\n \".woff2\": \"font/woff2\",\n };\n app.get(\"/*\", async (c) => {\n const urlPath = c.req.path === \"/\" ? \"/index.html\" : c.req.path;\n const filePath = path.join(uiPath, urlPath);\n try {\n const content = await fs.promises.readFile(filePath);\n const ext = path.extname(filePath);\n const contentType = MIME_TYPES[ext] || \"application/octet-stream\";\n return new Response(content, { headers: { \"content-type\": contentType } });\n } catch {\n // SPA fallback\n try {\n const html = await fs.promises.readFile(path.join(uiPath, \"index.html\"));\n return new Response(html, { headers: { \"content-type\": \"text/html\" } });\n } catch (err) {\n console.error(\"[static] Error reading index.html:\", (err as Error).message);\n return c.text(\"Not Found\", 404);\n }\n }\n });\n\n // Create HTTP server + WebSocket server\n const server = http.createServer(async (req, res) => {\n const chunks: Buffer[] = [];\n for await (const chunk of req) chunks.push(chunk as Buffer);\n const body = Buffer.concat(chunks);\n\n const url = new URL(req.url || \"/\", `http://localhost:${opts.port}`);\n const init: RequestInit = {\n method: req.method,\n headers: req.headers as any,\n };\n if (req.method !== \"GET\" && req.method !== \"HEAD\" && body.length > 0) {\n init.body = body;\n }\n const request = new Request(url.toString(), init);\n app.fetch(request).then((response) => {\n res.writeHead(response.status, Object.fromEntries(response.headers.entries()));\n response.arrayBuffer().then((buf) => {\n res.end(Buffer.from(buf));\n });\n }).catch(() => {\n res.writeHead(500);\n res.end(\"Internal Server Error\");\n });\n });\n\n const wss = new WebSocketServer({\n server,\n path: \"/ws\",\n verifyClient: (opts.auth || opts.token) ? (info, cb) => {\n const url = new URL(info.req.url || \"/\", `http://localhost:${opts.port}`);\n const qToken = url.searchParams.get(\"token\");\n if (opts.token && qToken === opts.token) return cb(true);\n\n const authHeader = info.req.headers[\"authorization\"] || \"\";\n if (opts.token && authHeader === `Bearer ${opts.token}`) return cb(true);\n if (opts.auth) {\n const [user, pass] = opts.auth.split(\":\");\n const expected = \"Basic \" + Buffer.from(`${user}:${pass}`).toString(\"base64\");\n if (authHeader === expected) return cb(true);\n }\n\n // Check cookie for token auth\n if (opts.token) {\n const cookies = (info.req.headers[\"cookie\"] as string) || \"\";\n const match = cookies.match(/(?:^|;\\s*)pg-dash-token=([^;]*)/);\n if (match && match[1] === opts.token) return cb(true);\n }\n\n cb(false, 401, \"Unauthorized\");\n } : undefined,\n });\n const clients = new Set<WebSocket>();\n\n wss.on(\"connection\", (ws) => {\n clients.add(ws);\n const snap = collector.getLastSnapshot();\n if (Object.keys(snap).length > 0) {\n ws.send(JSON.stringify({ type: \"metrics\", data: snap }));\n }\n ws.on(\"close\", () => clients.delete(ws));\n ws.on(\"error\", () => clients.delete(ws));\n });\n\n // Broadcast metrics, activity, and check alerts on each collection\n let collectCycleCount = 0;\n collector.on(\"collected\", async (snapshot: Record<string, number>) => {\n if (clients.size > 0 && Object.keys(snapshot).length > 0) {\n const metricsMsg = JSON.stringify({ type: \"metrics\", data: snapshot });\n let activityData: any[] = [];\n try { activityData = await getActivity(pool); } catch (err) { console.error(\"[ws] Error fetching activity:\", (err as Error).message); }\n const activityMsg = JSON.stringify({ type: \"activity\", data: activityData });\n for (const ws of clients) {\n if (ws.readyState === WebSocket.OPEN) {\n ws.send(metricsMsg);\n ws.send(activityMsg);\n }\n }\n }\n\n // Check alerts after each collection\n if (Object.keys(snapshot).length > 0) {\n try {\n const alertMetrics: Record<string, number> = {};\n\n // Acquire a single client for all alert metric queries this cycle\n const alertClient = await pool.connect();\n try {\n if (snapshot.connections_total !== undefined) {\n const r = await alertClient.query(\"SELECT setting::int AS max FROM pg_settings WHERE name = 'max_connections'\");\n const max = r.rows[0]?.max || 100;\n alertMetrics.connection_util = (snapshot.connections_total / max) * 100;\n }\n\n if (snapshot.cache_hit_ratio !== undefined) {\n alertMetrics.cache_hit_pct = snapshot.cache_hit_ratio * 100;\n }\n\n const [longQueriesResult, idleInTxResult] = await Promise.all([\n alertClient.query(\n `SELECT count(*)::int AS c FROM pg_stat_activity WHERE state = 'active' AND now() - query_start > $1 * interval '1 minute' AND pid != pg_backend_pid()`,\n [longQueryThreshold]\n ),\n alertClient.query(\n `SELECT count(*)::int AS c FROM pg_stat_activity WHERE state = 'idle in transaction' AND now() - state_change > $1 * interval '1 minute'`,\n [longQueryThreshold]\n ),\n ]);\n alertMetrics.long_query_count = longQueriesResult.rows[0]?.c || 0;\n alertMetrics.idle_in_tx_count = idleInTxResult.rows[0]?.c || 0;\n } catch (err) { console.error(\"[alerts] Error collecting alert metrics:\", (err as Error).message); }\n finally { alertClient.release(); }\n\n collectCycleCount++;\n if (collectCycleCount % 10 === 0) {\n try {\n const report = await getAdvisorReport(pool, longQueryThreshold);\n alertMetrics.health_score = report.score;\n store.insert(\"health_score\", report.score);\n } catch (err) { console.error(\"[alerts] Error checking health score:\", (err as Error).message); }\n\n // db_growth_pct_24h\n try {\n if (snapshot.db_size_bytes !== undefined) {\n const dayAgo = Date.now() - 24 * 60 * 60 * 1000;\n const oldData = store.query(\"db_size_bytes\", dayAgo, dayAgo + 5 * 60 * 1000);\n if (oldData.length > 0) {\n const oldVal = oldData[0].value;\n if (oldVal > 0) {\n alertMetrics.db_growth_pct_24h = ((snapshot.db_size_bytes - oldVal) / oldVal) * 100;\n }\n }\n }\n } catch (err) { console.error(\"[alerts] Error computing db_growth_pct_24h:\", (err as Error).message); }\n\n // days_until_full\n try {\n const pred = diskPredictor.predict(store, \"db_size_bytes\", 30);\n if (pred?.daysUntilFull !== null && pred?.daysUntilFull !== undefined) {\n alertMetrics.days_until_full = pred.daysUntilFull;\n }\n } catch (err) { console.error(\"[alerts] Error computing days_until_full:\", (err as Error).message); }\n }\n\n const fired = alertManager.checkAlerts(alertMetrics);\n\n if (fired.length > 0 && clients.size > 0) {\n const alertMsg = JSON.stringify({ type: \"alerts\", data: fired });\n for (const ws of clients) {\n if (ws.readyState === WebSocket.OPEN) {\n ws.send(alertMsg);\n }\n }\n }\n } catch (err) {\n console.error(\"[alerts] Error checking alerts:\", (err as Error).message);\n }\n }\n });\n\n const bindAddr = opts.bind || \"127.0.0.1\";\n server.listen(opts.port, bindAddr, async () => {\n console.log(`\\n pg-dash running at http://${bindAddr}:${opts.port}\\n`);\n if (opts.open) {\n try {\n const openMod = await import(\"open\");\n await openMod.default(`http://localhost:${opts.port}`);\n } catch (err) { console.error(\"[open] Failed to open browser:\", (err as Error).message); }\n }\n });\n\n // Graceful shutdown\n const shutdown = async () => {\n console.log(\"\\n Shutting down gracefully...\");\n collector.stop();\n schemaTracker.stop();\n queryStatsStore.stop();\n wss.close();\n server.close();\n metricsDb.close();\n schemaDb.close();\n alertsDb.close();\n await pool.end();\n console.log(\" Goodbye!\");\n process.exit(0);\n };\n\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n\n await new Promise(() => {});\n}\n","import type { Pool } from \"pg\";\n\nexport async function getOverview(pool: Pool) {\n const client = await pool.connect();\n try {\n const version = await client.query(\"SHOW server_version\");\n const uptime = await client.query(\n \"SELECT to_char(now() - pg_postmaster_start_time(), 'DD \\\"d\\\" HH24 \\\"h\\\" MI \\\"m\\\"') AS uptime\"\n );\n const dbSize = await client.query(\n \"SELECT pg_size_pretty(pg_database_size(current_database())) AS size\"\n );\n const dbCount = await client.query(\n \"SELECT count(*)::int AS count FROM pg_database WHERE NOT datistemplate\"\n );\n const connections = await client.query(`\n SELECT\n (SELECT count(*)::int FROM pg_stat_activity WHERE state = 'active') AS active,\n (SELECT count(*)::int FROM pg_stat_activity WHERE state = 'idle') AS idle,\n (SELECT setting::int FROM pg_settings WHERE name = 'max_connections') AS max\n `);\n\n return {\n version: version.rows[0].server_version,\n uptime: uptime.rows[0].uptime,\n dbSize: dbSize.rows[0].size,\n databaseCount: dbCount.rows[0].count,\n connections: connections.rows[0],\n };\n } finally {\n client.release();\n }\n}\n","import type { Pool } from \"pg\";\n\nexport async function getDatabases(pool: Pool) {\n const client = await pool.connect();\n try {\n const r = await client.query(`\n SELECT datname AS name,\n pg_size_pretty(pg_database_size(datname)) AS size,\n pg_database_size(datname) AS size_bytes\n FROM pg_database\n WHERE NOT datistemplate\n ORDER BY pg_database_size(datname) DESC\n `);\n return r.rows;\n } finally {\n client.release();\n }\n}\n","import type { Pool } from \"pg\";\n\nexport async function getTables(pool: Pool) {\n const client = await pool.connect();\n try {\n const r = await client.query(`\n SELECT\n schemaname AS schema,\n relname AS name,\n pg_size_pretty(pg_total_relation_size(relid)) AS total_size,\n pg_total_relation_size(relid) AS size_bytes,\n n_live_tup AS rows,\n n_dead_tup AS dead_tuples,\n CASE WHEN n_live_tup > 0 \n THEN round(n_dead_tup::numeric / n_live_tup * 100, 1) \n ELSE 0 END AS dead_pct\n FROM pg_stat_user_tables\n ORDER BY pg_total_relation_size(relid) DESC\n `);\n return r.rows;\n } finally {\n client.release();\n }\n}\n","import type { Pool } from \"pg\";\n\nexport interface Activity {\n pid: number;\n query: string;\n state: string;\n wait_event: string | null;\n wait_event_type: string | null;\n duration: string | null;\n client_addr: string | null;\n application_name: string;\n backend_start: string;\n}\n\nexport async function getActivity(pool: Pool): Promise<Activity[]> {\n const client = await pool.connect();\n try {\n const r = await client.query(`\n SELECT\n pid,\n COALESCE(query, '') AS query,\n COALESCE(state, 'unknown') AS state,\n wait_event,\n wait_event_type,\n CASE WHEN state = 'active' THEN (now() - query_start)::text\n WHEN state = 'idle in transaction' THEN (now() - state_change)::text\n ELSE NULL END AS duration,\n client_addr::text,\n COALESCE(application_name, '') AS application_name,\n backend_start::text\n FROM pg_stat_activity\n WHERE pid != pg_backend_pid()\n AND state IS NOT NULL\n ORDER BY\n CASE state\n WHEN 'active' THEN 1\n WHEN 'idle in transaction' THEN 2\n ELSE 3\n END,\n query_start ASC NULLS LAST\n `);\n return r.rows;\n } finally {\n client.release();\n }\n}\n","import Database from \"better-sqlite3\";\nimport path from \"node:path\";\nimport os from \"node:os\";\nimport fs from \"node:fs\";\n\nconst DEFAULT_DIR = path.join(os.homedir(), \".pg-dash\");\nconst DEFAULT_RETENTION_DAYS = 7;\n\nexport interface DataPoint {\n timestamp: number;\n metric: string;\n value: number;\n}\n\nexport class TimeseriesStore {\n private db: Database.Database;\n private insertStmt: Database.Statement;\n private retentionMs: number;\n\n constructor(db: Database.Database, retentionDays = DEFAULT_RETENTION_DAYS);\n constructor(dataDir?: string, retentionDays?: number);\n constructor(dbOrDir?: Database.Database | string, retentionDays = DEFAULT_RETENTION_DAYS) {\n if (dbOrDir instanceof Database) {\n this.db = dbOrDir;\n } else {\n const dir = dbOrDir || DEFAULT_DIR;\n fs.mkdirSync(dir, { recursive: true });\n const dbPath = path.join(dir, \"metrics.db\");\n this.db = new Database(dbPath);\n }\n this.retentionMs = retentionDays * 24 * 60 * 60 * 1000;\n\n this.db.pragma(\"journal_mode = WAL\");\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS metrics (\n timestamp INTEGER NOT NULL,\n metric TEXT NOT NULL,\n value REAL NOT NULL\n );\n CREATE INDEX IF NOT EXISTS idx_metrics_metric_ts ON metrics(metric, timestamp);\n `);\n\n this.insertStmt = this.db.prepare(\n \"INSERT INTO metrics (timestamp, metric, value) VALUES (?, ?, ?)\"\n );\n }\n\n insert(metric: string, value: number, timestamp?: number): void {\n this.insertStmt.run(timestamp ?? Date.now(), metric, value);\n }\n\n insertMany(points: DataPoint[]): void {\n const tx = this.db.transaction((pts: DataPoint[]) => {\n for (const p of pts) {\n this.insertStmt.run(p.timestamp, p.metric, p.value);\n }\n });\n tx(points);\n }\n\n query(metric: string, startMs: number, endMs?: number): { timestamp: number; value: number }[] {\n const end = endMs ?? Date.now();\n return this.db\n .prepare(\n \"SELECT timestamp, value FROM metrics WHERE metric = ? AND timestamp >= ? AND timestamp <= ? ORDER BY timestamp\"\n )\n .all(metric, startMs, end) as { timestamp: number; value: number }[];\n }\n\n latest(metrics?: string[]): Record<string, { timestamp: number; value: number }> {\n const result: Record<string, { timestamp: number; value: number }> = {};\n if (metrics && metrics.length > 0) {\n const placeholders = metrics.map(() => \"?\").join(\",\");\n const rows = this.db\n .prepare(\n `SELECT m.metric, m.timestamp, m.value FROM metrics m INNER JOIN (SELECT metric, MAX(timestamp) as max_ts FROM metrics WHERE metric IN (${placeholders}) GROUP BY metric) g ON m.metric = g.metric AND m.timestamp = g.max_ts`\n )\n .all(...metrics) as DataPoint[];\n for (const r of rows) result[r.metric] = { timestamp: r.timestamp, value: r.value };\n } else {\n const rows = this.db\n .prepare(\n \"SELECT m.metric, m.timestamp, m.value FROM metrics m INNER JOIN (SELECT metric, MAX(timestamp) as max_ts FROM metrics GROUP BY metric) g ON m.metric = g.metric AND m.timestamp = g.max_ts\"\n )\n .all() as DataPoint[];\n for (const r of rows) result[r.metric] = { timestamp: r.timestamp, value: r.value };\n }\n return result;\n }\n\n prune(): number {\n const cutoff = Date.now() - this.retentionMs;\n const info = this.db.prepare(\"DELETE FROM metrics WHERE timestamp < ?\").run(cutoff);\n return info.changes;\n }\n\n close(): void {\n this.db.close();\n }\n}\n","import { EventEmitter } from \"node:events\";\nimport type { Pool } from \"pg\";\nimport type { TimeseriesStore } from \"./timeseries.js\";\n\nexport const ALL_METRICS = [\n \"connections_active\",\n \"connections_idle\",\n \"connections_total\",\n \"tps_commit\",\n \"tps_rollback\",\n \"cache_hit_ratio\",\n \"deadlocks\",\n \"temp_bytes\",\n \"db_size_bytes\",\n \"tuple_inserted\",\n \"tuple_updated\",\n \"tuple_deleted\",\n \"replication_lag_bytes\",\n \"disk_used_bytes\",\n] as const;\n\nexport type MetricName = (typeof ALL_METRICS)[number];\n\ninterface CumulativeState {\n timestamp: number;\n xact_commit: number;\n xact_rollback: number;\n deadlocks: number;\n temp_bytes: number;\n tup_inserted: number;\n tup_updated: number;\n tup_deleted: number;\n}\n\nexport class Collector extends EventEmitter {\n private timer: ReturnType<typeof setInterval> | null = null;\n private pruneTimer: ReturnType<typeof setInterval> | null = null;\n private prev: CumulativeState | null = null;\n private lastSnapshot: Record<string, number> = {};\n private collectCount = 0;\n\n constructor(\n private pool: Pool,\n private store: TimeseriesStore,\n private intervalMs: number = 30000\n ) {\n super();\n }\n\n start(): void {\n this.collect().catch(err => console.error(\"[collector] Initial collection failed:\", err));\n this.timer = setInterval(() => {\n this.collect().catch(err => console.error(\"[collector] Collection failed:\", err));\n }, this.intervalMs);\n // Prune once per hour\n this.pruneTimer = setInterval(() => this.store.prune(), 60 * 60 * 1000);\n }\n\n stop(): void {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n if (this.pruneTimer) {\n clearInterval(this.pruneTimer);\n this.pruneTimer = null;\n }\n }\n\n getLastSnapshot(): Record<string, number> {\n return { ...this.lastSnapshot };\n }\n\n async collect(): Promise<Record<string, number>> {\n const now = Date.now();\n const snapshot: Record<string, number> = {};\n\n try {\n const client = await this.pool.connect();\n try {\n // Connections\n const connRes = await client.query(`\n SELECT\n count(*) FILTER (WHERE state = 'active')::int AS active,\n count(*) FILTER (WHERE state = 'idle')::int AS idle,\n count(*)::int AS total\n FROM pg_stat_activity\n `);\n const conn = connRes.rows[0];\n snapshot.connections_active = conn.active;\n snapshot.connections_idle = conn.idle;\n snapshot.connections_total = conn.total;\n\n // Database stats (cumulative counters + cache ratio + size)\n const dbRes = await client.query(`\n SELECT\n xact_commit, xact_rollback, deadlocks, temp_bytes,\n tup_inserted, tup_updated, tup_deleted,\n CASE WHEN (blks_hit + blks_read) = 0 THEN 1\n ELSE blks_hit::float / (blks_hit + blks_read) END AS cache_ratio,\n pg_database_size(current_database()) AS db_size\n FROM pg_stat_database WHERE datname = current_database()\n `);\n const db = dbRes.rows[0];\n if (db) {\n snapshot.cache_hit_ratio = parseFloat(db.cache_ratio);\n snapshot.db_size_bytes = parseInt(db.db_size);\n\n const cur: CumulativeState = {\n timestamp: now,\n xact_commit: parseInt(db.xact_commit),\n xact_rollback: parseInt(db.xact_rollback),\n deadlocks: parseInt(db.deadlocks),\n temp_bytes: parseInt(db.temp_bytes),\n tup_inserted: parseInt(db.tup_inserted),\n tup_updated: parseInt(db.tup_updated),\n tup_deleted: parseInt(db.tup_deleted),\n };\n\n if (this.prev) {\n const dtSec = (now - this.prev.timestamp) / 1000;\n if (dtSec > 0) {\n snapshot.tps_commit = Math.max(0, (cur.xact_commit - this.prev.xact_commit) / dtSec);\n snapshot.tps_rollback = Math.max(0, (cur.xact_rollback - this.prev.xact_rollback) / dtSec);\n snapshot.deadlocks = Math.max(0, cur.deadlocks - this.prev.deadlocks);\n snapshot.temp_bytes = Math.max(0, cur.temp_bytes - this.prev.temp_bytes);\n snapshot.tuple_inserted = Math.max(0, (cur.tup_inserted - this.prev.tup_inserted) / dtSec);\n snapshot.tuple_updated = Math.max(0, (cur.tup_updated - this.prev.tup_updated) / dtSec);\n snapshot.tuple_deleted = Math.max(0, (cur.tup_deleted - this.prev.tup_deleted) / dtSec);\n }\n }\n this.prev = cur;\n }\n\n // Tablespace sizes (stored as individual metrics)\n try {\n const tsRes = await client.query(`SELECT spcname, pg_tablespace_size(oid) AS size FROM pg_tablespace`);\n let totalTablespaceSize = 0;\n for (const row of tsRes.rows) {\n totalTablespaceSize += parseInt(row.size);\n }\n // Use total tablespace size as a proxy for disk usage\n if (totalTablespaceSize > 0) {\n snapshot.disk_used_bytes = totalTablespaceSize;\n }\n } catch {\n // Not all users have tablespace permissions\n }\n\n // Replication lag\n try {\n const repRes = await client.query(`\n SELECT CASE WHEN pg_is_in_recovery() \n THEN pg_wal_lsn_diff(pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn())\n ELSE 0 END AS lag_bytes\n `);\n snapshot.replication_lag_bytes = parseInt(repRes.rows[0]?.lag_bytes ?? \"0\");\n } catch {\n snapshot.replication_lag_bytes = 0;\n }\n } finally {\n client.release();\n }\n } catch (err) {\n console.error(\"[collector] Error collecting metrics:\", (err as Error).message);\n return snapshot;\n }\n\n // Per-table sizes every 10th cycle\n this.collectCount++;\n if (this.collectCount % 10 === 0) {\n try {\n const client = await this.pool.connect();\n try {\n const tableRes = await client.query(`\n SELECT schemaname, relname,\n pg_total_relation_size(quote_ident(schemaname) || '.' || quote_ident(relname)) as total_size\n FROM pg_stat_user_tables\n ORDER BY pg_total_relation_size(quote_ident(schemaname) || '.' || quote_ident(relname)) DESC\n LIMIT 20\n `);\n for (const row of tableRes.rows) {\n this.store.insert(`table_size:${row.schemaname}.${row.relname}`, parseInt(row.total_size), now);\n }\n } finally { client.release(); }\n } catch (err) {\n console.error(\"[collector] Error collecting table sizes:\", (err as Error).message);\n }\n }\n\n // Store to SQLite\n const points = Object.entries(snapshot).map(([metric, value]) => ({\n timestamp: now,\n metric,\n value,\n }));\n if (points.length > 0) {\n this.store.insertMany(points);\n }\n\n this.lastSnapshot = snapshot;\n this.emit(\"collected\", snapshot);\n return snapshot;\n }\n}\n","// Notification formatters for Slack, Discord, and generic webhooks\n\nimport type { AlertRule, AlertHistoryEntry } from \"./alerts.js\";\n\nexport type WebhookType = \"slack\" | \"discord\" | \"unknown\";\n\nconst SEVERITY_COLORS: Record<string, { hex: string; decimal: number; emoji: string }> = {\n critical: { hex: \"#e74c3c\", decimal: 0xe74c3c, emoji: \"🔴\" },\n warning: { hex: \"#f39c12\", decimal: 0xf39c12, emoji: \"🟡\" },\n info: { hex: \"#3498db\", decimal: 0x3498db, emoji: \"🔵\" },\n};\n\nexport function detectWebhookType(url: string): WebhookType {\n try {\n const { hostname } = new URL(url);\n if (hostname.endsWith(\"hooks.slack.com\")) return \"slack\";\n if (hostname.endsWith(\"discord.com\") || hostname.endsWith(\"discordapp.com\")) return \"discord\";\n return \"unknown\";\n } catch {\n return \"unknown\";\n }\n}\n\nexport function formatSlackMessage(alert: AlertHistoryEntry, rule: AlertRule): object {\n const colors = SEVERITY_COLORS[rule.severity] || SEVERITY_COLORS.info;\n return {\n attachments: [\n {\n color: colors.hex,\n blocks: [\n {\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: `${colors.emoji} *pg-dash Alert: ${rule.name}*`,\n },\n },\n {\n type: \"section\",\n fields: [\n { type: \"mrkdwn\", text: `*Metric:*\\n${rule.metric}` },\n { type: \"mrkdwn\", text: `*Current Value:*\\n${alert.value}` },\n { type: \"mrkdwn\", text: `*Threshold:*\\n${rule.operator} ${rule.threshold}` },\n { type: \"mrkdwn\", text: `*Severity:*\\n${rule.severity}` },\n { type: \"mrkdwn\", text: `*Timestamp:*\\n${new Date(alert.timestamp).toISOString()}` },\n ],\n },\n ],\n },\n ],\n };\n}\n\nexport function formatDiscordMessage(alert: AlertHistoryEntry, rule: AlertRule): object {\n const colors = SEVERITY_COLORS[rule.severity] || SEVERITY_COLORS.info;\n return {\n embeds: [\n {\n title: `${colors.emoji} pg-dash Alert: ${rule.name}`,\n color: colors.decimal,\n fields: [\n { name: \"Metric\", value: rule.metric, inline: true },\n { name: \"Current Value\", value: String(alert.value), inline: true },\n { name: \"Threshold\", value: `${rule.operator} ${rule.threshold}`, inline: true },\n { name: \"Severity\", value: rule.severity, inline: true },\n { name: \"Timestamp\", value: new Date(alert.timestamp).toISOString(), inline: false },\n ],\n footer: { text: \"pg-dash · PostgreSQL Monitoring\" },\n },\n ],\n };\n}\n\nexport function formatGenericWebhook(alert: AlertHistoryEntry, rule: AlertRule): object {\n return {\n severity: rule.severity,\n rule: rule.name,\n metric: rule.metric,\n value: alert.value,\n message: alert.message,\n timestamp: alert.timestamp,\n };\n}\n\nexport function formatWebhookPayload(alert: AlertHistoryEntry, rule: AlertRule, webhookUrl: string): object {\n const type = detectWebhookType(webhookUrl);\n switch (type) {\n case \"slack\": return formatSlackMessage(alert, rule);\n case \"discord\": return formatDiscordMessage(alert, rule);\n default: return formatGenericWebhook(alert, rule);\n }\n}\n\nexport function getSeverityColor(severity: string) {\n return SEVERITY_COLORS[severity] || SEVERITY_COLORS.info;\n}\n","// Alerts system — rules stored in SQLite, threshold checking with cooldown, webhook notifications\n\nimport type Database from \"better-sqlite3\";\nimport { formatWebhookPayload, detectWebhookType } from \"./notifiers.js\";\n\nexport interface AlertRule {\n id: number;\n name: string;\n metric: string;\n operator: \"gt\" | \"lt\" | \"eq\";\n threshold: number;\n severity: \"info\" | \"warning\" | \"critical\";\n enabled: number;\n cooldown_minutes: number;\n}\n\nexport interface AlertHistoryEntry {\n id: number;\n rule_id: number;\n timestamp: number;\n value: number;\n message: string;\n notified: number;\n}\n\nconst DEFAULT_RULES: Omit<AlertRule, \"id\">[] = [\n { name: \"Connection utilization > 80%\", metric: \"connection_util\", operator: \"gt\", threshold: 80, severity: \"warning\", enabled: 1, cooldown_minutes: 60 },\n { name: \"Connection utilization > 90%\", metric: \"connection_util\", operator: \"gt\", threshold: 90, severity: \"critical\", enabled: 1, cooldown_minutes: 30 },\n { name: \"Cache hit ratio < 99%\", metric: \"cache_hit_pct\", operator: \"lt\", threshold: 99, severity: \"warning\", enabled: 1, cooldown_minutes: 60 },\n { name: \"Cache hit ratio < 95%\", metric: \"cache_hit_pct\", operator: \"lt\", threshold: 95, severity: \"critical\", enabled: 1, cooldown_minutes: 30 },\n { name: \"Long-running query > 5 min\", metric: \"long_query_count\", operator: \"gt\", threshold: 0, severity: \"warning\", enabled: 1, cooldown_minutes: 15 },\n { name: \"Idle in transaction > 10 min\", metric: \"idle_in_tx_count\", operator: \"gt\", threshold: 0, severity: \"warning\", enabled: 1, cooldown_minutes: 15 },\n { name: \"Health score below D\", metric: \"health_score\", operator: \"lt\", threshold: 50, severity: \"warning\", enabled: 1, cooldown_minutes: 120 },\n { name: \"Database size growth > 10% in 24h\", metric: \"db_growth_pct_24h\", operator: \"gt\", threshold: 10, severity: \"warning\", enabled: 1, cooldown_minutes: 60 },\n { name: \"Predicted disk full within 7 days\", metric: \"days_until_full\", operator: \"lt\", threshold: 7, severity: \"critical\", enabled: 1, cooldown_minutes: 360 },\n];\n\nexport class AlertManager {\n private db: Database.Database;\n private webhookUrl: string | null;\n\n constructor(db: Database.Database, webhookUrl?: string) {\n this.db = db;\n this.webhookUrl = webhookUrl || null;\n this.initTables();\n }\n\n private initTables() {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS alert_rules (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n name TEXT NOT NULL,\n metric TEXT NOT NULL,\n operator TEXT NOT NULL,\n threshold REAL NOT NULL,\n severity TEXT NOT NULL DEFAULT 'warning',\n enabled INTEGER DEFAULT 1,\n cooldown_minutes INTEGER DEFAULT 60\n );\n CREATE TABLE IF NOT EXISTS alert_history (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n rule_id INTEGER NOT NULL,\n timestamp INTEGER NOT NULL,\n value REAL NOT NULL,\n message TEXT NOT NULL,\n notified INTEGER DEFAULT 0,\n FOREIGN KEY (rule_id) REFERENCES alert_rules(id)\n );\n `);\n\n // Seed default rules on first run\n const count = (this.db.prepare(\"SELECT COUNT(*) as c FROM alert_rules\").get() as any).c;\n if (count === 0) {\n const insert = this.db.prepare(\"INSERT INTO alert_rules (name, metric, operator, threshold, severity, enabled, cooldown_minutes) VALUES (?, ?, ?, ?, ?, ?, ?)\");\n const tx = this.db.transaction(() => {\n for (const r of DEFAULT_RULES) {\n insert.run(r.name, r.metric, r.operator, r.threshold, r.severity, r.enabled, r.cooldown_minutes);\n }\n });\n tx();\n }\n }\n\n getRules(): AlertRule[] {\n return this.db.prepare(\"SELECT * FROM alert_rules ORDER BY id\").all() as AlertRule[];\n }\n\n addRule(rule: Omit<AlertRule, \"id\">): AlertRule {\n const info = this.db.prepare(\"INSERT INTO alert_rules (name, metric, operator, threshold, severity, enabled, cooldown_minutes) VALUES (?, ?, ?, ?, ?, ?, ?)\").run(\n rule.name, rule.metric, rule.operator, rule.threshold, rule.severity, rule.enabled ?? 1, rule.cooldown_minutes ?? 60\n );\n return { ...rule, id: Number(info.lastInsertRowid) } as AlertRule;\n }\n\n updateRule(id: number, updates: Partial<Omit<AlertRule, \"id\">>): boolean {\n const existing = this.db.prepare(\"SELECT * FROM alert_rules WHERE id = ?\").get(id) as AlertRule | undefined;\n if (!existing) return false;\n const merged = { ...existing, ...updates };\n this.db.prepare(\"UPDATE alert_rules SET name=?, metric=?, operator=?, threshold=?, severity=?, enabled=?, cooldown_minutes=? WHERE id=?\").run(\n merged.name, merged.metric, merged.operator, merged.threshold, merged.severity, merged.enabled, merged.cooldown_minutes, id\n );\n return true;\n }\n\n deleteRule(id: number): boolean {\n const info = this.db.prepare(\"DELETE FROM alert_rules WHERE id = ?\").run(id);\n return info.changes > 0;\n }\n\n getHistory(limit = 50): AlertHistoryEntry[] {\n return this.db.prepare(\"SELECT * FROM alert_history ORDER BY timestamp DESC LIMIT ?\").all(limit) as AlertHistoryEntry[];\n }\n\n /**\n * Check all enabled rules against current metric values.\n * `metrics` is a map of metric name → current value.\n */\n checkAlerts(metrics: Record<string, number>): AlertHistoryEntry[] {\n const rules = this.db.prepare(\"SELECT * FROM alert_rules WHERE enabled = 1\").all() as AlertRule[];\n const fired: AlertHistoryEntry[] = [];\n const now = Date.now();\n\n for (const rule of rules) {\n const value = metrics[rule.metric];\n if (value === undefined) continue;\n\n const triggered = this.evaluateRule(rule, value);\n if (!triggered) continue;\n\n // Check cooldown\n const lastAlert = this.db.prepare(\n \"SELECT timestamp FROM alert_history WHERE rule_id = ? ORDER BY timestamp DESC LIMIT 1\"\n ).get(rule.id) as { timestamp: number } | undefined;\n\n if (lastAlert && (now - lastAlert.timestamp) < rule.cooldown_minutes * 60 * 1000) {\n continue; // Still in cooldown\n }\n\n const message = `${rule.name}: ${rule.metric} = ${value} (threshold: ${rule.operator} ${rule.threshold})`;\n const info = this.db.prepare(\"INSERT INTO alert_history (rule_id, timestamp, value, message, notified) VALUES (?, ?, ?, ?, 0)\").run(\n rule.id, now, value, message\n );\n const entry: AlertHistoryEntry = { id: Number(info.lastInsertRowid), rule_id: rule.id, timestamp: now, value, message, notified: 0 };\n fired.push(entry);\n\n // Log to console\n const icon = rule.severity === \"critical\" ? \"🔴\" : rule.severity === \"warning\" ? \"🟡\" : \"🔵\";\n console.log(`[alert] ${icon} ${message}`);\n\n // Webhook notification\n if (this.webhookUrl) {\n this.sendWebhook(rule, entry).catch((err) => console.error(\"[alert] Webhook failed:\", err.message));\n }\n }\n\n return fired;\n }\n\n evaluateRule(rule: Pick<AlertRule, \"operator\" | \"threshold\">, value: number): boolean {\n switch (rule.operator) {\n case \"gt\": return value > rule.threshold;\n case \"lt\": return value < rule.threshold;\n case \"eq\": return value === rule.threshold;\n default: return false;\n }\n }\n\n getWebhookUrl(): string | null {\n return this.webhookUrl;\n }\n\n getWebhookType(): string | null {\n if (!this.webhookUrl) return null;\n return detectWebhookType(this.webhookUrl);\n }\n\n async sendTestWebhook(): Promise<{ ok: boolean; type: string; error?: string }> {\n if (!this.webhookUrl) return { ok: false, type: \"none\", error: \"No webhook URL configured\" };\n const type = detectWebhookType(this.webhookUrl);\n const testRule: AlertRule = {\n id: 0, name: \"Test Alert\", metric: \"test_metric\", operator: \"gt\",\n threshold: 80, severity: \"info\", enabled: 1, cooldown_minutes: 60,\n };\n const testEntry: AlertHistoryEntry = {\n id: 0, rule_id: 0, timestamp: Date.now(), value: 85,\n message: \"Test Alert: test_metric = 85 (threshold: gt 80)\", notified: 0,\n };\n try {\n const payload = formatWebhookPayload(testEntry, testRule, this.webhookUrl);\n const res = await fetch(this.webhookUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(payload),\n });\n if (!res.ok) return { ok: false, type, error: `HTTP ${res.status}` };\n return { ok: true, type };\n } catch (err) {\n return { ok: false, type, error: (err as Error).message };\n }\n }\n\n private async sendWebhook(rule: AlertRule, entry: AlertHistoryEntry) {\n if (!this.webhookUrl) return;\n try {\n const payload = formatWebhookPayload(entry, rule, this.webhookUrl);\n await fetch(this.webhookUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(payload),\n });\n this.db.prepare(\"UPDATE alert_history SET notified = 1 WHERE id = ?\").run(entry.id);\n } catch (err) {\n console.error(\"[alert] Webhook error:\", (err as Error).message);\n }\n }\n}\n","import type { Hono } from \"hono\";\nimport type { Pool } from \"pg\";\nimport { getOverview } from \"../queries/overview.js\";\nimport { getDatabases } from \"../queries/databases.js\";\nimport { getTables } from \"../queries/tables.js\";\n\nexport function registerOverviewRoutes(app: Hono, pool: Pool) {\n app.get(\"/api/overview\", async (c) => {\n try { return c.json(await getOverview(pool)); }\n catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.get(\"/api/databases\", async (c) => {\n try { return c.json(await getDatabases(pool)); }\n catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.get(\"/api/tables\", async (c) => {\n try { return c.json(await getTables(pool)); }\n catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n}\n","import type { Hono } from \"hono\";\nimport type { TimeseriesStore } from \"../timeseries.js\";\nimport type { Collector } from \"../collector.js\";\n\nconst RANGE_MAP: Record<string, number> = {\n \"5m\": 5 * 60 * 1000,\n \"15m\": 15 * 60 * 1000,\n \"1h\": 60 * 60 * 1000,\n \"6h\": 6 * 60 * 60 * 1000,\n \"24h\": 24 * 60 * 60 * 1000,\n \"7d\": 7 * 24 * 60 * 60 * 1000,\n};\n\nexport function registerMetricsRoutes(app: Hono, store: TimeseriesStore, collector: Collector) {\n app.get(\"/api/metrics\", (c) => {\n try {\n const metric = c.req.query(\"metric\");\n const range = c.req.query(\"range\") || \"1h\";\n if (!metric) return c.json({ error: \"metric param required\" }, 400);\n const rangeMs = RANGE_MAP[range] || RANGE_MAP[\"1h\"];\n const now = Date.now();\n const data = store.query(metric, now - rangeMs, now);\n return c.json(data);\n } catch (err: any) {\n return c.json({ error: err.message }, 500);\n }\n });\n\n app.get(\"/api/metrics/latest\", (_c) => {\n try {\n const snapshot = collector.getLastSnapshot();\n return _c.json(snapshot);\n } catch (err: any) {\n return _c.json({ error: err.message }, 500);\n }\n });\n}\n","import type { Pool } from \"pg\";\n\nexport interface SlowQuery {\n queryid: string;\n query: string;\n calls: number;\n total_time: number;\n mean_time: number;\n rows: number;\n total_time_pretty: string;\n mean_time_pretty: string;\n}\n\nexport async function getSlowQueries(pool: Pool): Promise<SlowQuery[]> {\n const client = await pool.connect();\n try {\n // Check if pg_stat_statements is available\n const extCheck = await client.query(\n \"SELECT 1 FROM pg_extension WHERE extname = 'pg_stat_statements'\"\n );\n if (extCheck.rows.length === 0) {\n return [];\n }\n\n const r = await client.query(`\n SELECT\n queryid::text,\n query,\n calls::int,\n total_exec_time AS total_time,\n mean_exec_time AS mean_time,\n rows::int,\n round(total_exec_time::numeric / 1000, 2)::text || 's' AS total_time_pretty,\n round(mean_exec_time::numeric, 2)::text || 'ms' AS mean_time_pretty\n FROM pg_stat_statements\n WHERE query NOT LIKE '%pg_stat%'\n AND query NOT LIKE '%pg_catalog%'\n ORDER BY total_exec_time DESC\n LIMIT 50\n `);\n return r.rows;\n } catch {\n // pg_stat_statements might not be accessible\n return [];\n } finally {\n client.release();\n }\n}\n","import type { Hono } from \"hono\";\nimport type { Pool } from \"pg\";\nimport { getActivity } from \"../queries/activity.js\";\nimport { getSlowQueries } from \"../queries/slow-queries.js\";\n\nexport function registerActivityRoutes(app: Hono, pool: Pool) {\n app.get(\"/api/activity\", async (c) => {\n try { return c.json(await getActivity(pool)); }\n catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.get(\"/api/queries\", async (c) => {\n try { return c.json(await getSlowQueries(pool)); }\n catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.post(\"/api/activity/:pid/cancel\", async (c) => {\n try {\n const pid = parseInt(c.req.param(\"pid\"));\n const client = await pool.connect();\n try {\n await client.query(\"SELECT pg_cancel_backend($1)\", [pid]);\n return c.json({ ok: true });\n } finally {\n client.release();\n }\n } catch (err: any) {\n return c.json({ error: err.message }, 500);\n }\n });\n}\n","import type { Hono } from \"hono\";\nimport type { Pool } from \"pg\";\nimport type { TimeseriesStore } from \"../timeseries.js\";\nimport { getAdvisorReport, isSafeFix, getIgnoredIssues, ignoreIssue, unignoreIssue } from \"../advisor.js\";\n\nconst RANGE_MAP: Record<string, number> = {\n \"24h\": 24 * 60 * 60 * 1000,\n \"7d\": 7 * 24 * 60 * 60 * 1000,\n \"30d\": 30 * 24 * 60 * 60 * 1000,\n};\n\nexport function registerAdvisorRoutes(app: Hono, pool: Pool, longQueryThreshold: number, store?: TimeseriesStore) {\n app.get(\"/api/advisor\", async (c) => {\n try { return c.json(await getAdvisorReport(pool, longQueryThreshold)); }\n catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.get(\"/api/advisor/ignored\", (c) => {\n try { return c.json(getIgnoredIssues()); }\n catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.post(\"/api/advisor/ignore\", async (c) => {\n try {\n const body = await c.req.json();\n const issueId = body?.issueId;\n if (!issueId) return c.json({ error: \"issueId required\" }, 400);\n ignoreIssue(issueId);\n return c.json({ ok: true });\n } catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.delete(\"/api/advisor/ignore/:issueId\", (c) => {\n try {\n const issueId = c.req.param(\"issueId\");\n unignoreIssue(issueId);\n return c.json({ ok: true });\n } catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.get(\"/api/advisor/history\", (c) => {\n if (!store) return c.json([]);\n try {\n const range = c.req.query(\"range\") || \"24h\";\n const rangeMs = RANGE_MAP[range] || RANGE_MAP[\"24h\"];\n const now = Date.now();\n const data = store.query(\"health_score\", now - rangeMs, now);\n return c.json(data);\n } catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.post(\"/api/fix\", async (c) => {\n try {\n const body = await c.req.json();\n const sql = body?.sql?.trim();\n if (!sql) return c.json({ error: \"sql field required\" }, 400);\n if (!isSafeFix(sql)) return c.json({ error: \"Operation not allowed. Only VACUUM, ANALYZE, REINDEX, CREATE/DROP INDEX CONCURRENTLY, pg_terminate_backend, pg_cancel_backend, and EXPLAIN ANALYZE are permitted.\" }, 403);\n const client = await pool.connect();\n try {\n const start = Date.now();\n const result = await client.query(sql);\n const duration = Date.now() - start;\n return c.json({ ok: true, duration, rowCount: result.rowCount, rows: result.rows || [] });\n } finally {\n client.release();\n }\n } catch (err: any) {\n return c.json({ error: err.message }, 500);\n }\n });\n}\n","import type { Hono } from \"hono\";\nimport type { Pool } from \"pg\";\nimport type { SchemaTracker } from \"../schema-tracker.js\";\nimport { getSchemaTables, getSchemaTableDetail, getSchemaIndexes, getSchemaFunctions, getSchemaExtensions, getSchemaEnums } from \"../queries/schema.js\";\n\nexport function registerSchemaRoutes(app: Hono, pool: Pool, schemaTracker: SchemaTracker) {\n app.get(\"/api/schema/tables\", async (c) => {\n try { return c.json(await getSchemaTables(pool)); }\n catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.get(\"/api/schema/tables/:name\", async (c) => {\n try {\n const name = c.req.param(\"name\");\n const detail = await getSchemaTableDetail(pool, name);\n if (!detail) return c.json({ error: \"Table not found\" }, 404);\n return c.json(detail);\n } catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.get(\"/api/schema/indexes\", async (c) => {\n try { return c.json(await getSchemaIndexes(pool)); }\n catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.get(\"/api/schema/functions\", async (c) => {\n try { return c.json(await getSchemaFunctions(pool)); }\n catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.get(\"/api/schema/extensions\", async (c) => {\n try { return c.json(await getSchemaExtensions(pool)); }\n catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.get(\"/api/schema/enums\", async (c) => {\n try { return c.json(await getSchemaEnums(pool)); }\n catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n // Schema change tracking endpoints\n app.get(\"/api/schema/history\", (c) => {\n try {\n const limit = parseInt(c.req.query(\"limit\") || \"30\");\n return c.json(schemaTracker.getHistory(limit));\n } catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.get(\"/api/schema/changes\", (c) => {\n try {\n const since = c.req.query(\"since\");\n return c.json(schemaTracker.getChanges(since ? parseInt(since) : undefined));\n } catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.get(\"/api/schema/changes/latest\", (c) => {\n try { return c.json(schemaTracker.getLatestChanges()); }\n catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.get(\"/api/schema/diff\", (c) => {\n try {\n const from = parseInt(c.req.query(\"from\") || \"0\");\n const to = parseInt(c.req.query(\"to\") || \"0\");\n if (!from || !to) return c.json({ error: \"from and to params required\" }, 400);\n const diff = schemaTracker.getDiff(from, to);\n if (!diff) return c.json({ error: \"Snapshot not found\" }, 404);\n return c.json(diff);\n } catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.post(\"/api/schema/snapshot\", async (c) => {\n try {\n const result = await schemaTracker.takeSnapshot();\n return c.json(result);\n } catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n}\n","import type { Hono } from \"hono\";\nimport type { AlertManager } from \"../alerts.js\";\n\nexport function registerAlertsRoutes(app: Hono, alertManager: AlertManager) {\n app.get(\"/api/alerts/rules\", (c) => {\n try { return c.json(alertManager.getRules()); }\n catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.post(\"/api/alerts/rules\", async (c) => {\n try {\n const body = await c.req.json();\n const rule = alertManager.addRule(body);\n return c.json(rule, 201);\n } catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.put(\"/api/alerts/rules/:id\", async (c) => {\n try {\n const id = parseInt(c.req.param(\"id\"));\n const body = await c.req.json();\n const ok = alertManager.updateRule(id, body);\n if (!ok) return c.json({ error: \"Rule not found\" }, 404);\n return c.json({ ok: true });\n } catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.delete(\"/api/alerts/rules/:id\", (c) => {\n try {\n const id = parseInt(c.req.param(\"id\"));\n const ok = alertManager.deleteRule(id);\n if (!ok) return c.json({ error: \"Rule not found\" }, 404);\n return c.json({ ok: true });\n } catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.get(\"/api/alerts/webhook-info\", (c) => {\n try {\n const url = alertManager.getWebhookUrl();\n const type = alertManager.getWebhookType();\n const masked = url ? url.replace(/\\/[^/]{8,}$/, \"/****\") : null;\n return c.json({ url: masked, type: type || \"none\", configured: !!url });\n } catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.post(\"/api/alerts/test-webhook\", async (c) => {\n try {\n const result = await alertManager.sendTestWebhook();\n return c.json(result, result.ok ? 200 : 400);\n } catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n\n app.get(\"/api/alerts/history\", (c) => {\n try {\n const limit = parseInt(c.req.query(\"limit\") || \"50\");\n return c.json(alertManager.getHistory(limit));\n } catch (err: any) { return c.json({ error: err.message }, 500); }\n });\n}\n","// query-analyzer.ts — deep EXPLAIN plan analysis with auto index suggestions\n\nimport type { Pool } from \"pg\";\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport interface PlanNodeSummary {\n nodeType: string;\n table?: string;\n totalCost: number;\n actualRows?: number;\n actualTime?: number; // ms\n filter?: string;\n}\n\nexport interface SeqScanInfo {\n table: string;\n rowCount: number;\n filter?: string; // filter condition from explain output\n suggestion?: string;\n}\n\nexport interface IndexSuggestion {\n table: string;\n columns: string[];\n reason: string;\n sql: string; // CREATE INDEX CONCURRENTLY …\n estimatedBenefit: \"high\" | \"medium\" | \"low\";\n}\n\nexport interface ExplainAnalysis {\n planNodes: PlanNodeSummary[];\n seqScans: SeqScanInfo[];\n missingIndexes: IndexSuggestion[];\n costEstimate: {\n totalCost: number;\n actualTime?: number;\n planningTime?: number;\n };\n recommendations: string[];\n}\n\nexport interface QueryRegressionInfo {\n queryId: string; // queryid from pg_stat_statements\n currentMeanMs: number;\n previousMeanMs: number;\n changePercent: number;\n degradedAt?: string; // approximate timestamp\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\n/**\n * Recursively walk a plan tree (EXPLAIN FORMAT JSON) and collect every node.\n * Each node looks like { \"Node Type\": \"...\", \"Plans\": [...], ... }\n */\nfunction collectNodes(node: any, acc: any[] = []): any[] {\n if (!node || typeof node !== \"object\") return acc;\n acc.push(node);\n const plans = node[\"Plans\"] ?? node[\"plans\"];\n if (Array.isArray(plans)) {\n for (const child of plans) collectNodes(child, acc);\n }\n return acc;\n}\n\n/**\n * Extract simple column names from a Postgres filter expression.\n * Handles patterns like:\n * (col = $1)\n * (col > $1)\n * (col IS NULL)\n * (col IS NOT NULL)\n * (col ~~ '%foo%') -- LIKE\n */\nfunction extractColumnsFromFilter(filter: string): string[] {\n // Match identifiers that appear before comparison operators\n const colPattern = /\\(?\"?([a-z_][a-z0-9_]*)\"?\\s*(?:=|<|>|<=|>=|<>|!=|IS\\s+(?:NOT\\s+)?NULL|~~|!~~)/gi;\n const found = new Set<string>();\n let m: RegExpExecArray | null;\n while ((m = colPattern.exec(filter)) !== null) {\n const col = m[1].toLowerCase();\n // Skip Postgres internal names\n if (![\"and\", \"or\", \"not\", \"true\", \"false\", \"null\"].includes(col)) {\n found.add(col);\n }\n }\n return Array.from(found);\n}\n\n/**\n * Fetch the list of indexed column sets for a given table from pg_indexes.\n * Returns an array of column name arrays (one per index).\n */\nasync function getExistingIndexColumns(pool: Pool, tableName: string): Promise<string[][]> {\n try {\n // Query pg_indexes to get index definitions\n const r = await pool.query(\n `SELECT indexdef FROM pg_indexes WHERE tablename = $1`,\n [tableName]\n );\n return r.rows.map((row: any) => {\n // Parse column list from: ... ON table (col1, col2, ...)\n const m = /\\(([^)]+)\\)/.exec(row.indexdef);\n if (!m) return [] as string[];\n return m[1]\n .split(\",\")\n .map((c: string) => c.trim().replace(/^\"|\"$/g, \"\").toLowerCase());\n });\n } catch {\n return [];\n }\n}\n\n/**\n * Benefit rating based on estimated row count.\n */\nfunction rateBenefit(rowCount: number): \"high\" | \"medium\" | \"low\" {\n if (rowCount > 100_000) return \"high\";\n if (rowCount >= 10_000) return \"medium\";\n return \"low\";\n}\n\n/**\n * Format a large number as human-readable (1.2M, 50K, etc.)\n */\nfunction fmtRows(n: number): string {\n if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;\n if (n >= 1_000) return `${(n / 1_000).toFixed(0)}K`;\n return String(n);\n}\n\n// ─── Core analysis ────────────────────────────────────────────────────────────\n\n/**\n * Analyse a EXPLAIN (FORMAT JSON) result and return rich diagnostics.\n *\n * @param explainJson - The value of `r.rows[0][\"QUERY PLAN\"]` (an array with one plan object)\n * @param pool - Optional PG pool; without it only static analysis is performed\n */\nexport async function analyzeExplainPlan(\n explainJson: any,\n pool?: Pool | null\n): Promise<ExplainAnalysis> {\n const result: ExplainAnalysis = {\n planNodes: [],\n seqScans: [],\n missingIndexes: [],\n costEstimate: { totalCost: 0 },\n recommendations: [],\n };\n\n if (!explainJson || !Array.isArray(explainJson) || explainJson.length === 0) {\n return result;\n }\n\n const topLevel = explainJson[0];\n const planRoot = topLevel?.[\"Plan\"] ?? topLevel?.[\"plan\"];\n\n // Planning / execution times from top-level\n const planningTime: number | undefined = topLevel?.[\"Planning Time\"] ?? undefined;\n const executionTime: number | undefined = topLevel?.[\"Execution Time\"] ?? undefined;\n\n if (!planRoot) return result;\n\n // Collect all nodes\n const allNodes = collectNodes(planRoot);\n\n // Build planNodes summary\n result.planNodes = allNodes.map((n: any) => {\n const s: PlanNodeSummary = {\n nodeType: n[\"Node Type\"] ?? \"Unknown\",\n totalCost: n[\"Total Cost\"] ?? 0,\n };\n if (n[\"Relation Name\"]) s.table = n[\"Relation Name\"];\n if (n[\"Actual Rows\"] !== undefined) s.actualRows = n[\"Actual Rows\"];\n if (n[\"Actual Total Time\"] !== undefined) s.actualTime = n[\"Actual Total Time\"];\n if (n[\"Filter\"]) s.filter = n[\"Filter\"];\n return s;\n });\n\n // Cost estimate from root node\n result.costEstimate = {\n totalCost: planRoot[\"Total Cost\"] ?? 0,\n actualTime: executionTime,\n planningTime,\n };\n\n // ── Seq Scan analysis ──────────────────────────────────────────────────────\n const seqScanNodes = allNodes.filter((n: any) => n[\"Node Type\"] === \"Seq Scan\");\n\n for (const node of seqScanNodes) {\n const table: string = node[\"Relation Name\"] ?? \"unknown\";\n const rowCount: number = node[\"Plan Rows\"] ?? node[\"Actual Rows\"] ?? 0;\n const filter: string | undefined = node[\"Filter\"];\n\n const info: SeqScanInfo = { table, rowCount, filter };\n\n if (rowCount > 10_000) {\n info.suggestion = filter\n ? `Consider adding an index to support the filter on ${table}`\n : `Full table scan on large table ${table} — review query`;\n }\n\n result.seqScans.push(info);\n }\n\n // ── Missing index inference ────────────────────────────────────────────────\n for (const scan of result.seqScans) {\n if (!scan.filter) continue;\n\n const cols = extractColumnsFromFilter(scan.filter);\n if (cols.length === 0) continue;\n\n // Check existing indexes (needs DB)\n let existingIndexCols: string[][] = [];\n if (pool) {\n existingIndexCols = await getExistingIndexColumns(pool, scan.table);\n }\n\n // Filter out columns already covered as the leading column of an existing index\n const uncoveredCols = cols.filter(\n (col) => !existingIndexCols.some((idxCols) => idxCols.length > 0 && idxCols[0] === col)\n );\n\n if (uncoveredCols.length === 0) continue;\n\n const benefit = rateBenefit(scan.rowCount);\n\n if (uncoveredCols.length >= 2) {\n // Suggest a composite index\n const idxName = `idx_${scan.table}_${uncoveredCols.join(\"_\")}`;\n const sql = `CREATE INDEX CONCURRENTLY ${idxName} ON ${scan.table} (${uncoveredCols.join(\", \")})`;\n result.missingIndexes.push({\n table: scan.table,\n columns: uncoveredCols,\n reason: `Seq Scan with multi-column filter (${uncoveredCols.join(\", \")}) on ${fmtRows(scan.rowCount)} rows — composite index preferred`,\n sql,\n estimatedBenefit: benefit,\n });\n } else {\n // Single column\n const col = uncoveredCols[0];\n const idxName = `idx_${scan.table}_${col}`;\n const sql = `CREATE INDEX CONCURRENTLY ${idxName} ON ${scan.table} (${col})`;\n result.missingIndexes.push({\n table: scan.table,\n columns: [col],\n reason: `Seq Scan with Filter on ${col} (${fmtRows(scan.rowCount)} rows)`,\n sql,\n estimatedBenefit: benefit,\n });\n }\n }\n\n // ── Recommendations ────────────────────────────────────────────────────────\n for (const scan of result.seqScans) {\n if (scan.rowCount > 10_000) {\n const filterPart = scan.filter\n ? ` — consider adding index on ${extractColumnsFromFilter(scan.filter).join(\", \") || \"filter columns\"}`\n : \" — no filter; full scan may be intentional\";\n result.recommendations.push(\n `Seq Scan on ${scan.table} (${fmtRows(scan.rowCount)} rows)${filterPart}`\n );\n }\n }\n\n if (planningTime !== undefined) {\n const label = planningTime > 10 ? \"high — check statistics\" : \"normal\";\n result.recommendations.push(`Planning time ${planningTime.toFixed(1)}ms — ${label}`);\n }\n\n if (result.missingIndexes.length === 0 && result.seqScans.length === 0) {\n result.recommendations.push(\"No obvious sequential scans detected — query looks efficient\");\n }\n\n return result;\n}\n\n// ─── Regression detection ─────────────────────────────────────────────────────\n\n/**\n * Detect queries whose mean execution time has increased by more than 50%\n * compared to the earliest snapshot in the query_stats store for the given window.\n *\n * This is a best-effort function; it silently returns [] if pg_stat_statements\n * is unavailable or the query_stats store doesn't have enough history.\n *\n * @param pool - PG pool (used to read pg_stat_statements)\n * @param statsDb - Optional better-sqlite3 Database with query_stats table\n * @param windowHours - How far back to compare (default 24 h)\n */\nexport async function detectQueryRegressions(\n pool: Pool,\n statsDb?: any | null,\n windowHours = 24\n): Promise<QueryRegressionInfo[]> {\n try {\n // ── 1. Check pg_stat_statements is available ───────────────────────────\n const extCheck = await pool.query(\n \"SELECT 1 FROM pg_extension WHERE extname = 'pg_stat_statements'\"\n );\n if (extCheck.rows.length === 0) return [];\n\n // ── 2. Get current snapshot from pg_stat_statements ───────────────────\n const current = await pool.query(`\n SELECT queryid::text AS queryid, mean_exec_time\n FROM pg_stat_statements\n WHERE query NOT LIKE '%pg_stat%'\n AND queryid IS NOT NULL\n `);\n\n const currentMap = new Map<string, number>();\n for (const row of current.rows) {\n currentMap.set(row.queryid, parseFloat(row.mean_exec_time));\n }\n\n if (!statsDb) return [];\n\n // ── 3. Fetch historical baselines from SQLite query_stats ─────────────\n const windowMs = windowHours * 60 * 60 * 1000;\n const since = Date.now() - windowMs;\n\n let historical: { queryid: string; mean_exec_time: number; timestamp: number }[];\n try {\n historical = statsDb\n .prepare(\n `SELECT queryid, mean_exec_time, timestamp\n FROM query_stats\n WHERE timestamp >= ?\n ORDER BY queryid, timestamp ASC`\n )\n .all(since) as any[];\n } catch {\n return [];\n }\n\n // Keep only the *earliest* record per queryid in the window\n const baselineMap = new Map<string, { meanMs: number; timestamp: number }>();\n for (const row of historical) {\n if (!baselineMap.has(row.queryid)) {\n baselineMap.set(row.queryid, {\n meanMs: row.mean_exec_time,\n timestamp: row.timestamp,\n });\n }\n }\n\n // ── 4. Detect regressions > 50% ────────────────────────────────────────\n const regressions: QueryRegressionInfo[] = [];\n\n for (const [queryId, baseline] of baselineMap) {\n const currentMean = currentMap.get(queryId);\n if (currentMean === undefined || baseline.meanMs === 0) continue;\n\n const changePercent =\n ((currentMean - baseline.meanMs) / baseline.meanMs) * 100;\n\n if (changePercent > 50) {\n regressions.push({\n queryId,\n currentMeanMs: currentMean,\n previousMeanMs: baseline.meanMs,\n changePercent: Math.round(changePercent),\n degradedAt: new Date(baseline.timestamp).toISOString(),\n });\n }\n }\n\n return regressions.sort((a, b) => b.changePercent - a.changePercent);\n } catch {\n return [];\n }\n}\n","import type { Hono } from \"hono\";\nimport type { Pool } from \"pg\";\nimport { analyzeExplainPlan } from \"../query-analyzer.js\";\n\nconst DDL_PATTERN = /\\b(CREATE|DROP|ALTER|TRUNCATE|GRANT|REVOKE)\\b/i;\n\nexport function registerExplainRoutes(app: Hono, pool: Pool) {\n app.post(\"/api/explain\", async (c) => {\n try {\n const body = await c.req.json();\n const query = body?.query?.trim();\n if (!query) return c.json({ error: \"Missing query\" }, 400);\n if (DDL_PATTERN.test(query)) return c.json({ error: \"DDL statements are not allowed\" }, 400);\n if (!/^\\s*SELECT\\b/i.test(query)) return c.json({ error: \"Only SELECT queries can be explained for safety. DELETE/UPDATE/INSERT are blocked.\" }, 400);\n\n const client = await pool.connect();\n try {\n await client.query(\"SET statement_timeout = '30s'\");\n await client.query(\"BEGIN\");\n try {\n const r = await client.query(`EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON) ${query}`);\n await client.query(\"ROLLBACK\");\n await client.query(\"RESET statement_timeout\");\n\n const plan = r.rows[0][\"QUERY PLAN\"];\n\n // Deep analysis (best-effort; never throws)\n let analysis = null;\n try {\n analysis = await analyzeExplainPlan(plan, pool);\n } catch {\n // analysis unavailable — return plan only\n }\n\n return c.json({ plan, analysis });\n } catch (err: any) {\n await client.query(\"ROLLBACK\").catch(() => {});\n await client.query(\"RESET statement_timeout\").catch(() => {});\n return c.json({ error: err.message }, 400);\n }\n } finally {\n client.release();\n }\n } catch (err: any) {\n return c.json({ error: err.message }, 500);\n }\n });\n}\n","import type { TimeseriesStore } from \"./timeseries.js\";\n\nexport interface DiskPrediction {\n currentBytes: number;\n growthRatePerDay: number;\n predictedFullDate: Date | null;\n daysUntilFull: number | null;\n confidence: number;\n}\n\n/**\n * Simple linear regression: y = mx + b\n * Returns { slope, intercept, r2 }\n */\nexport function linearRegression(points: { x: number; y: number }[]): { slope: number; intercept: number; r2: number } {\n const n = points.length;\n if (n < 2) return { slope: 0, intercept: 0, r2: 0 };\n\n let sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0, sumY2 = 0;\n for (const p of points) {\n sumX += p.x;\n sumY += p.y;\n sumXY += p.x * p.y;\n sumX2 += p.x * p.x;\n sumY2 += p.y * p.y;\n }\n\n const denom = n * sumX2 - sumX * sumX;\n if (denom === 0) return { slope: 0, intercept: sumY / n, r2: 0 };\n\n const slope = (n * sumXY - sumX * sumY) / denom;\n const intercept = (sumY - slope * sumX) / n;\n\n // R² calculation\n const meanY = sumY / n;\n let ssTot = 0, ssRes = 0;\n for (const p of points) {\n ssTot += (p.y - meanY) ** 2;\n ssRes += (p.y - (slope * p.x + intercept)) ** 2;\n }\n const r2 = ssTot === 0 ? 1 : Math.max(0, 1 - ssRes / ssTot);\n\n return { slope, intercept, r2 };\n}\n\nexport class DiskPredictor {\n /**\n * Predict disk growth based on historical metric data.\n * @param store TimeseriesStore instance\n * @param metric Metric name (e.g. \"db_size_bytes\")\n * @param daysAhead How many days to project ahead\n * @param maxDiskBytes Optional max disk capacity for \"days until full\" calc\n */\n predict(store: TimeseriesStore, metric: string, daysAhead: number, maxDiskBytes?: number): DiskPrediction | null {\n const now = Date.now();\n // Get all available data (up to 30 days)\n const data = store.query(metric, now - 30 * 24 * 60 * 60 * 1000, now);\n\n if (data.length < 2) return null;\n\n // Need at least 24 hours of data\n const timeSpanMs = data[data.length - 1].timestamp - data[0].timestamp;\n if (timeSpanMs < 24 * 60 * 60 * 1000) return null;\n\n const currentBytes = data[data.length - 1].value;\n\n // Normalize timestamps to days from first point\n const t0 = data[0].timestamp;\n const points = data.map(d => ({\n x: (d.timestamp - t0) / (24 * 60 * 60 * 1000), // days\n y: d.value,\n }));\n\n const { slope, r2 } = linearRegression(points);\n const growthRatePerDay = slope; // bytes per day\n\n let predictedFullDate: Date | null = null;\n let daysUntilFull: number | null = null;\n\n if (maxDiskBytes && growthRatePerDay > 0) {\n const remainingBytes = maxDiskBytes - currentBytes;\n daysUntilFull = remainingBytes / growthRatePerDay;\n if (daysUntilFull > 0 && daysUntilFull < 365 * 10) {\n predictedFullDate = new Date(now + daysUntilFull * 24 * 60 * 60 * 1000);\n }\n }\n\n return {\n currentBytes,\n growthRatePerDay,\n predictedFullDate,\n daysUntilFull: daysUntilFull !== null && daysUntilFull > 0 ? daysUntilFull : null,\n confidence: r2,\n };\n }\n}\n","import type { Hono } from \"hono\";\nimport type { Pool } from \"pg\";\nimport type { TimeseriesStore } from \"../timeseries.js\";\nimport { DiskPredictor } from \"../disk-prediction.js\";\n\nconst RANGE_MAP: Record<string, number> = {\n \"24h\": 24 * 60 * 60 * 1000,\n \"7d\": 7 * 24 * 60 * 60 * 1000,\n \"30d\": 30 * 24 * 60 * 60 * 1000,\n};\n\nexport function registerDiskRoutes(app: Hono, pool: Pool, store: TimeseriesStore) {\n const predictor = new DiskPredictor();\n\n app.get(\"/api/disk/usage\", async (c) => {\n try {\n const client = await pool.connect();\n try {\n // Database size + data directory\n const dbRes = await client.query(`\n SELECT pg_database_size(current_database()) AS db_size,\n (SELECT setting FROM pg_settings WHERE name = 'data_directory') AS data_dir\n `);\n const { db_size, data_dir } = dbRes.rows[0];\n\n // Tablespace sizes\n const tsRes = await client.query(`SELECT spcname, pg_tablespace_size(oid) AS size FROM pg_tablespace`);\n const tablespaces = tsRes.rows.map((r: any) => ({\n name: r.spcname,\n size: parseInt(r.size),\n }));\n\n // Top 20 largest tables\n const tableRes = await client.query(`\n SELECT schemaname, relname,\n pg_total_relation_size(quote_ident(schemaname) || '.' || quote_ident(relname)) as total_size,\n pg_relation_size(quote_ident(schemaname) || '.' || quote_ident(relname)) as table_size,\n pg_indexes_size(quote_ident(schemaname) || '.' || quote_ident(relname)) as index_size\n FROM pg_stat_user_tables\n ORDER BY pg_total_relation_size(quote_ident(schemaname) || '.' || quote_ident(relname)) DESC\n LIMIT 20\n `);\n const tables = tableRes.rows.map((r: any) => ({\n schema: r.schemaname,\n name: r.relname,\n totalSize: parseInt(r.total_size),\n tableSize: parseInt(r.table_size),\n indexSize: parseInt(r.index_size),\n }));\n\n return c.json({\n dbSize: parseInt(db_size),\n dataDir: data_dir,\n tablespaces,\n tables,\n });\n } finally {\n client.release();\n }\n } catch (err: any) {\n return c.json({ error: err.message }, 500);\n }\n });\n\n app.get(\"/api/disk/prediction\", (c) => {\n try {\n const days = parseInt(c.req.query(\"days\") || \"30\");\n const maxDisk = c.req.query(\"maxDisk\") ? parseInt(c.req.query(\"maxDisk\")!) : undefined;\n const prediction = predictor.predict(store, \"db_size_bytes\", days, maxDisk);\n return c.json({ prediction });\n } catch (err: any) {\n return c.json({ error: err.message }, 500);\n }\n });\n\n app.get(\"/api/disk/table-history/:table\", (c) => {\n try {\n const table = c.req.param(\"table\");\n const range = c.req.query(\"range\") || \"24h\";\n const rangeMs = RANGE_MAP[range] || RANGE_MAP[\"24h\"];\n const now = Date.now();\n const data = store.query(`table_size:${table}`, now - rangeMs, now);\n return c.json(data);\n } catch (err: any) {\n return c.json({ error: err.message }, 500);\n }\n });\n\n app.get(\"/api/disk/history\", (c) => {\n try {\n const range = c.req.query(\"range\") || \"24h\";\n const rangeMs = RANGE_MAP[range] || RANGE_MAP[\"24h\"];\n const now = Date.now();\n const data = store.query(\"db_size_bytes\", now - rangeMs, now);\n return c.json(data);\n } catch (err: any) {\n return c.json({ error: err.message }, 500);\n }\n });\n}\n","import type Database from \"better-sqlite3\";\nimport type { Pool } from \"pg\";\n\nconst DEFAULT_RETENTION_DAYS = 7;\n\nexport interface QueryStatRow {\n timestamp: number;\n queryid: string;\n query: string;\n calls: number;\n total_exec_time: number;\n mean_exec_time: number;\n min_exec_time: number;\n max_exec_time: number;\n rows: number;\n shared_blks_hit: number;\n shared_blks_read: number;\n}\n\nexport interface TopQuery {\n queryid: string;\n query: string;\n total_calls: number;\n total_exec_time: number;\n mean_exec_time: number;\n total_rows: number;\n}\n\ninterface CumulativeRow {\n queryid: string;\n query: string;\n calls: number;\n total_exec_time: number;\n mean_exec_time: number;\n min_exec_time: number;\n max_exec_time: number;\n rows: number;\n shared_blks_hit: number;\n shared_blks_read: number;\n}\n\nexport class QueryStatsStore {\n private db: Database.Database;\n private insertStmt: Database.Statement;\n private retentionMs: number;\n private prev: Map<string, CumulativeRow> = new Map();\n private timer: ReturnType<typeof setInterval> | null = null;\n private pruneTimer: ReturnType<typeof setInterval> | null = null;\n\n constructor(db: Database.Database, retentionDays = DEFAULT_RETENTION_DAYS) {\n this.db = db;\n this.retentionMs = retentionDays * 24 * 60 * 60 * 1000;\n\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS query_stats (\n timestamp INTEGER NOT NULL,\n queryid TEXT NOT NULL,\n query TEXT,\n calls INTEGER,\n total_exec_time REAL,\n mean_exec_time REAL,\n min_exec_time REAL,\n max_exec_time REAL,\n rows INTEGER,\n shared_blks_hit INTEGER,\n shared_blks_read INTEGER\n );\n CREATE INDEX IF NOT EXISTS idx_qs_queryid_ts ON query_stats(queryid, timestamp);\n `);\n\n this.insertStmt = this.db.prepare(\n `INSERT INTO query_stats (timestamp, queryid, query, calls, total_exec_time, mean_exec_time, min_exec_time, max_exec_time, rows, shared_blks_hit, shared_blks_read)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`\n );\n }\n\n startPeriodicSnapshot(pool: Pool, intervalMs = 5 * 60 * 1000): void {\n this.snapshot(pool).catch((err) =>\n console.error(\"[query-stats] Initial snapshot failed:\", err.message)\n );\n this.timer = setInterval(() => {\n this.snapshot(pool).catch((err) =>\n console.error(\"[query-stats] Snapshot failed:\", err.message)\n );\n }, intervalMs);\n // Prune once per hour\n this.pruneTimer = setInterval(() => this.prune(), 60 * 60 * 1000);\n }\n\n stop(): void {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n if (this.pruneTimer) {\n clearInterval(this.pruneTimer);\n this.pruneTimer = null;\n }\n }\n\n async snapshot(pool: Pool): Promise<number> {\n const client = await pool.connect();\n try {\n // Check if pg_stat_statements is available\n const extCheck = await client.query(\n \"SELECT 1 FROM pg_extension WHERE extname = 'pg_stat_statements'\"\n );\n if (extCheck.rows.length === 0) return 0;\n\n const r = await client.query(`\n SELECT\n queryid::text,\n query,\n calls::int,\n total_exec_time,\n mean_exec_time,\n min_exec_time,\n max_exec_time,\n rows::int,\n shared_blks_hit::int,\n shared_blks_read::int\n FROM pg_stat_statements\n WHERE query NOT LIKE '%pg_stat%'\n AND query NOT LIKE '%pg_catalog%'\n AND queryid IS NOT NULL\n `);\n\n const now = Date.now();\n const hasPrev = this.prev.size > 0;\n let count = 0;\n\n const tx = this.db.transaction((rows: CumulativeRow[]) => {\n for (const row of rows) {\n const prev = this.prev.get(row.queryid);\n if (hasPrev && prev) {\n const deltaCalls = Math.max(0, row.calls - prev.calls);\n if (deltaCalls === 0) continue; // no new activity\n const deltaTime = Math.max(0, row.total_exec_time - prev.total_exec_time);\n const deltaRows = Math.max(0, row.rows - prev.rows);\n const deltaHit = Math.max(0, row.shared_blks_hit - prev.shared_blks_hit);\n const deltaRead = Math.max(0, row.shared_blks_read - prev.shared_blks_read);\n const meanTime = deltaCalls > 0 ? deltaTime / deltaCalls : 0;\n\n this.insertStmt.run(\n now, row.queryid, row.query,\n deltaCalls, deltaTime, meanTime,\n row.min_exec_time, row.max_exec_time,\n deltaRows, deltaHit, deltaRead\n );\n count++;\n } else if (!hasPrev) {\n // First snapshot: store cumulative values\n this.insertStmt.run(\n now, row.queryid, row.query,\n row.calls, row.total_exec_time, row.mean_exec_time,\n row.min_exec_time, row.max_exec_time,\n row.rows, row.shared_blks_hit, row.shared_blks_read\n );\n count++;\n }\n }\n });\n\n tx(r.rows);\n\n // Update prev state\n this.prev.clear();\n for (const row of r.rows) {\n this.prev.set(row.queryid, row);\n }\n\n return count;\n } catch (err) {\n console.error(\"[query-stats] Error snapshotting:\", (err as Error).message);\n return 0;\n } finally {\n client.release();\n }\n }\n\n /** Insert a row directly (for testing) */\n insertRow(row: QueryStatRow): void {\n this.insertStmt.run(\n row.timestamp, row.queryid, row.query,\n row.calls, row.total_exec_time, row.mean_exec_time,\n row.min_exec_time, row.max_exec_time,\n row.rows, row.shared_blks_hit, row.shared_blks_read\n );\n }\n\n getTrend(queryid: string, startMs: number, endMs?: number): QueryStatRow[] {\n const end = endMs ?? Date.now();\n return this.db\n .prepare(\n `SELECT timestamp, queryid, query, calls, total_exec_time, mean_exec_time,\n min_exec_time, max_exec_time, rows, shared_blks_hit, shared_blks_read\n FROM query_stats\n WHERE queryid = ? AND timestamp >= ? AND timestamp <= ?\n ORDER BY timestamp`\n )\n .all(queryid, startMs, end) as QueryStatRow[];\n }\n\n getTopQueries(\n startMs: number,\n endMs: number,\n orderBy: \"total_time\" | \"mean_time\" | \"calls\" = \"total_time\",\n limit = 20\n ): TopQuery[] {\n const orderCol =\n orderBy === \"total_time\" ? \"SUM(total_exec_time)\"\n : orderBy === \"calls\" ? \"SUM(calls)\"\n : \"AVG(mean_exec_time)\";\n\n return this.db\n .prepare(\n `SELECT queryid, \n MAX(query) as query,\n SUM(calls) as total_calls,\n SUM(total_exec_time) as total_exec_time,\n AVG(mean_exec_time) as mean_exec_time,\n SUM(rows) as total_rows\n FROM query_stats\n WHERE timestamp >= ? AND timestamp <= ?\n GROUP BY queryid\n ORDER BY ${orderCol} DESC\n LIMIT ?`\n )\n .all(startMs, endMs, limit) as TopQuery[];\n }\n\n prune(retentionMs?: number): number {\n const cutoff = Date.now() - (retentionMs ?? this.retentionMs);\n const info = this.db.prepare(\"DELETE FROM query_stats WHERE timestamp < ?\").run(cutoff);\n return info.changes;\n }\n\n close(): void {\n // No-op: DB lifecycle managed externally\n }\n}\n","import type { Hono } from \"hono\";\nimport type { QueryStatsStore } from \"../query-stats.js\";\n\nconst RANGE_MAP: Record<string, number> = {\n \"1h\": 60 * 60 * 1000,\n \"6h\": 6 * 60 * 60 * 1000,\n \"24h\": 24 * 60 * 60 * 1000,\n \"7d\": 7 * 24 * 60 * 60 * 1000,\n};\n\nexport function registerQueryStatsRoutes(app: Hono, store: QueryStatsStore) {\n app.get(\"/api/query-stats/top\", (c) => {\n try {\n const range = c.req.query(\"range\") || \"1h\";\n const orderBy = (c.req.query(\"orderBy\") || \"total_time\") as \"total_time\" | \"mean_time\" | \"calls\";\n const limit = parseInt(c.req.query(\"limit\") || \"20\", 10);\n const rangeMs = RANGE_MAP[range] || RANGE_MAP[\"1h\"];\n const now = Date.now();\n const data = store.getTopQueries(now - rangeMs, now, orderBy, limit);\n return c.json(data);\n } catch (err: any) {\n return c.json({ error: err.message }, 500);\n }\n });\n\n app.get(\"/api/query-stats/trend/:queryid\", (c) => {\n try {\n const queryid = c.req.param(\"queryid\");\n const range = c.req.query(\"range\") || \"1h\";\n const rangeMs = RANGE_MAP[range] || RANGE_MAP[\"1h\"];\n const now = Date.now();\n const data = store.getTrend(queryid, now - rangeMs, now);\n return c.json(data);\n } catch (err: any) {\n return c.json({ error: err.message }, 500);\n }\n });\n}\n","import type { Hono } from \"hono\";\nimport type { Pool } from \"pg\";\nimport { getOverview } from \"../queries/overview.js\";\nimport { getAdvisorReport, gradeFromScore } from \"../advisor.js\";\n\nexport function registerExportRoutes(app: Hono, pool: Pool, longQueryThreshold: number) {\n app.get(\"/api/export\", async (c) => {\n const format = c.req.query(\"format\") || \"json\";\n\n try {\n const [overview, advisor] = await Promise.all([\n getOverview(pool),\n getAdvisorReport(pool, longQueryThreshold),\n ]);\n\n if (format === \"md\") {\n const lines: string[] = [];\n lines.push(`# pg-dash Health Report`);\n lines.push(`\\nGenerated: ${new Date().toISOString()}\\n`);\n lines.push(`## Overview\\n`);\n lines.push(`- **PostgreSQL**: ${overview.version}`);\n lines.push(`- **Database Size**: ${overview.dbSize}`);\n lines.push(`- **Connections**: ${overview.connections.active} active / ${overview.connections.idle} idle / ${overview.connections.max} max`);\n lines.push(`\\n## Health Score: ${advisor.score}/100 (Grade: ${advisor.grade})\\n`);\n lines.push(`### Category Breakdown\\n`);\n lines.push(`| Category | Grade | Score | Issues |`);\n lines.push(`|----------|-------|-------|--------|`);\n for (const [cat, b] of Object.entries(advisor.breakdown)) {\n lines.push(`| ${cat} | ${b.grade} | ${b.score}/100 | ${b.count} |`);\n }\n if (advisor.issues.length > 0) {\n lines.push(`\\n### Issues (${advisor.issues.length})\\n`);\n for (const issue of advisor.issues) {\n const icon = issue.severity === \"critical\" ? \"🔴\" : issue.severity === \"warning\" ? \"🟡\" : \"🔵\";\n lines.push(`#### ${icon} [${issue.severity}] ${issue.title}\\n`);\n lines.push(`${issue.description}\\n`);\n lines.push(`**Impact**: ${issue.impact}\\n`);\n lines.push(`**Fix**:\\n\\`\\`\\`sql\\n${issue.fix}\\n\\`\\`\\`\\n`);\n }\n } else {\n lines.push(`\\n✅ No issues found!\\n`);\n }\n const md = lines.join(\"\\n\");\n c.header(\"Content-Type\", \"text/markdown; charset=utf-8\");\n c.header(\"Content-Disposition\", `attachment; filename=\"pg-dash-report-${new Date().toISOString().slice(0, 10)}.md\"`);\n return c.body(md);\n }\n\n // Default: JSON\n const data = { overview, advisor, exportedAt: new Date().toISOString() };\n c.header(\"Content-Disposition\", `attachment; filename=\"pg-dash-report-${new Date().toISOString().slice(0, 10)}.json\"`);\n return c.json(data);\n } catch (err: any) {\n return c.json({ error: err.message }, 500);\n }\n });\n}\n"],"mappings":"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","names":["fs","path","Pool","path","fs","os","Database","path","os","fs","RANGE_MAP","RANGE_MAP","DEFAULT_RETENTION_DAYS","RANGE_MAP","Database","path","os","fs","fs","path","fileURLToPath","__dirname","Pool","getAdvisorReport","saveSnapshot","loadSnapshot","diffSnapshots","os","analyzeMigration","Database","diffEnvironments","formatTextDiff","formatMdDiff"]}