@indiekitai/pg-dash 0.4.1 → 0.4.2

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 CHANGED
@@ -970,7 +970,7 @@ var init_schema = __esm({
970
970
  });
971
971
 
972
972
  // src/server/schema-diff.ts
973
- function diffSnapshots(oldSnap, newSnap) {
973
+ function diffSchemaSnapshots(oldSnap, newSnap) {
974
974
  const changes = [];
975
975
  const oldTableMap = new Map(oldSnap.tables.map((t) => [`${t.schema}.${t.name}`, t]));
976
976
  const newTableMap = new Map(newSnap.tables.map((t) => [`${t.schema}.${t.name}`, t]));
@@ -1105,12 +1105,13 @@ async function buildLiveSnapshot(pool) {
1105
1105
  enums: enums.map((e) => ({ name: e.name, schema: e.schema, values: e.values }))
1106
1106
  };
1107
1107
  }
1108
- var SchemaTracker;
1108
+ var SNAPSHOT_RETENTION, SchemaTracker;
1109
1109
  var init_schema_tracker = __esm({
1110
1110
  "src/server/schema-tracker.ts"() {
1111
1111
  "use strict";
1112
1112
  init_schema();
1113
1113
  init_schema_diff();
1114
+ SNAPSHOT_RETENTION = 50;
1114
1115
  SchemaTracker = class {
1115
1116
  db;
1116
1117
  pool;
@@ -1147,11 +1148,19 @@ var init_schema_tracker = __esm({
1147
1148
  const json = JSON.stringify(snapshot);
1148
1149
  const info = this.db.prepare("INSERT INTO schema_snapshots (timestamp, snapshot) VALUES (?, ?)").run(now, json);
1149
1150
  const snapshotId = Number(info.lastInsertRowid);
1151
+ this.db.prepare(`
1152
+ DELETE FROM schema_snapshots
1153
+ WHERE id NOT IN (
1154
+ SELECT id FROM schema_snapshots
1155
+ ORDER BY timestamp DESC
1156
+ LIMIT ?
1157
+ )
1158
+ `).run(SNAPSHOT_RETENTION);
1150
1159
  const prev = this.db.prepare("SELECT snapshot FROM schema_snapshots WHERE id < ? ORDER BY id DESC LIMIT 1").get(snapshotId);
1151
1160
  let changes = [];
1152
1161
  if (prev) {
1153
1162
  const oldSnap = JSON.parse(prev.snapshot);
1154
- changes = diffSnapshots(oldSnap, snapshot);
1163
+ changes = diffSchemaSnapshots(oldSnap, snapshot);
1155
1164
  if (changes.length > 0) {
1156
1165
  const insert = this.db.prepare("INSERT INTO schema_changes (snapshot_id, timestamp, change_type, object_type, table_name, detail) VALUES (?, ?, ?, ?, ?, ?)");
1157
1166
  const tx = this.db.transaction((chs) => {
@@ -1198,7 +1207,7 @@ var init_schema_tracker = __esm({
1198
1207
  const from = this.db.prepare("SELECT snapshot FROM schema_snapshots WHERE id = ?").get(fromId);
1199
1208
  const to = this.db.prepare("SELECT snapshot FROM schema_snapshots WHERE id = ?").get(toId);
1200
1209
  if (!from || !to) return null;
1201
- return diffSnapshots(JSON.parse(from.snapshot), JSON.parse(to.snapshot));
1210
+ return diffSchemaSnapshots(JSON.parse(from.snapshot), JSON.parse(to.snapshot));
1202
1211
  }
1203
1212
  };
1204
1213
  }
@@ -1207,7 +1216,7 @@ var init_schema_tracker = __esm({
1207
1216
  // src/server/snapshot.ts
1208
1217
  var snapshot_exports = {};
1209
1218
  __export(snapshot_exports, {
1210
- diffSnapshots: () => diffSnapshots2,
1219
+ diffSnapshots: () => diffSnapshots,
1211
1220
  loadSnapshot: () => loadSnapshot,
1212
1221
  saveSnapshot: () => saveSnapshot
1213
1222
  });
@@ -1229,7 +1238,7 @@ function loadSnapshot(snapshotPath) {
1229
1238
  return null;
1230
1239
  }
1231
1240
  }
1232
- function diffSnapshots2(prev, current) {
1241
+ function diffSnapshots(prev, current) {
1233
1242
  const prevNormIds = new Set(prev.issues.map((i) => normalizeIssueId(i.id)));
1234
1243
  const currNormIds = new Set(current.issues.map((i) => normalizeIssueId(i.id)));
1235
1244
  const newIssues = current.issues.filter((i) => !prevNormIds.has(normalizeIssueId(i.id)));
@@ -1739,7 +1748,7 @@ async function diffEnvironments(sourceConn, targetConn, options) {
1739
1748
  const constraintDiffs = [];
1740
1749
  const enumDiffs = [];
1741
1750
  if (sourceSnap && targetSnap) {
1742
- const snapChanges = diffSnapshots(sourceSnap, targetSnap);
1751
+ const snapChanges = diffSchemaSnapshots(sourceSnap, targetSnap);
1743
1752
  for (const c of snapChanges) {
1744
1753
  if (c.object_type === "constraint") {
1745
1754
  constraintDiffs.push({
@@ -2417,9 +2426,14 @@ var SEVERITY_COLORS = {
2417
2426
  info: { hex: "#3498db", decimal: 3447003, emoji: "\u{1F535}" }
2418
2427
  };
2419
2428
  function detectWebhookType(url) {
2420
- if (url.includes("hooks.slack.com")) return "slack";
2421
- if (url.includes("discord.com/api/webhooks") || url.includes("discordapp.com")) return "discord";
2422
- return "generic";
2429
+ try {
2430
+ const { hostname } = new URL(url);
2431
+ if (hostname.endsWith("hooks.slack.com")) return "slack";
2432
+ if (hostname.endsWith("discord.com") || hostname.endsWith("discordapp.com")) return "discord";
2433
+ return "unknown";
2434
+ } catch {
2435
+ return "unknown";
2436
+ }
2423
2437
  }
2424
2438
  function formatSlackMessage(alert, rule) {
2425
2439
  const colors = SEVERITY_COLORS[rule.severity] || SEVERITY_COLORS.info;
@@ -3895,40 +3909,32 @@ async function startServer(opts) {
3895
3909
  if (Object.keys(snapshot).length > 0) {
3896
3910
  try {
3897
3911
  const alertMetrics = {};
3898
- if (snapshot.connections_total !== void 0) {
3899
- const client = await pool.connect();
3900
- try {
3901
- const r = await client.query("SELECT setting::int AS max FROM pg_settings WHERE name = 'max_connections'");
3912
+ const alertClient = await pool.connect();
3913
+ try {
3914
+ if (snapshot.connections_total !== void 0) {
3915
+ const r = await alertClient.query("SELECT setting::int AS max FROM pg_settings WHERE name = 'max_connections'");
3902
3916
  const max = r.rows[0]?.max || 100;
3903
3917
  alertMetrics.connection_util = snapshot.connections_total / max * 100;
3904
- } finally {
3905
- client.release();
3906
3918
  }
3907
- }
3908
- if (snapshot.cache_hit_ratio !== void 0) {
3909
- alertMetrics.cache_hit_pct = snapshot.cache_hit_ratio * 100;
3910
- }
3911
- try {
3912
- const client = await pool.connect();
3913
- try {
3914
- const r = await client.query(`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()`, [longQueryThreshold]);
3915
- alertMetrics.long_query_count = r.rows[0]?.c || 0;
3916
- } finally {
3917
- client.release();
3919
+ if (snapshot.cache_hit_ratio !== void 0) {
3920
+ alertMetrics.cache_hit_pct = snapshot.cache_hit_ratio * 100;
3918
3921
  }
3922
+ const [longQueriesResult, idleInTxResult] = await Promise.all([
3923
+ alertClient.query(
3924
+ `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()`,
3925
+ [longQueryThreshold]
3926
+ ),
3927
+ alertClient.query(
3928
+ `SELECT count(*)::int AS c FROM pg_stat_activity WHERE state = 'idle in transaction' AND now() - state_change > $1 * interval '1 minute'`,
3929
+ [longQueryThreshold]
3930
+ )
3931
+ ]);
3932
+ alertMetrics.long_query_count = longQueriesResult.rows[0]?.c || 0;
3933
+ alertMetrics.idle_in_tx_count = idleInTxResult.rows[0]?.c || 0;
3919
3934
  } catch (err) {
3920
- console.error("[alerts] Error checking long queries:", err.message);
3921
- }
3922
- try {
3923
- const client = await pool.connect();
3924
- try {
3925
- const r = await client.query(`SELECT count(*)::int AS c FROM pg_stat_activity WHERE state = 'idle in transaction' AND now() - state_change > $1 * interval '1 minute'`, [longQueryThreshold]);
3926
- alertMetrics.idle_in_tx_count = r.rows[0]?.c || 0;
3927
- } finally {
3928
- client.release();
3929
- }
3930
- } catch (err) {
3931
- console.error("[alerts] Error checking idle-in-tx:", err.message);
3935
+ console.error("[alerts] Error collecting alert metrics:", err.message);
3936
+ } finally {
3937
+ alertClient.release();
3932
3938
  }
3933
3939
  collectCycleCount++;
3934
3940
  if (collectCycleCount % 10 === 0) {
@@ -4140,7 +4146,7 @@ if (subcommand === "check") {
4140
4146
  const useDiff = values.diff || false;
4141
4147
  const { Pool: Pool3 } = await import("pg");
4142
4148
  const { getAdvisorReport: getAdvisorReport2 } = await Promise.resolve().then(() => (init_advisor(), advisor_exports));
4143
- const { saveSnapshot: saveSnapshot2, loadSnapshot: loadSnapshot2, diffSnapshots: diffSnapshots3 } = await Promise.resolve().then(() => (init_snapshot(), snapshot_exports));
4149
+ const { saveSnapshot: saveSnapshot2, loadSnapshot: loadSnapshot2, diffSnapshots: diffSnapshots2 } = await Promise.resolve().then(() => (init_snapshot(), snapshot_exports));
4144
4150
  const os4 = await import("os");
4145
4151
  const pool = new Pool3({ connectionString, connectionTimeoutMillis: 1e4 });
4146
4152
  const checkDataDir = values["data-dir"] || path5.join(os4.homedir(), ".pg-dash");
@@ -4152,7 +4158,7 @@ if (subcommand === "check") {
4152
4158
  if (useDiff) {
4153
4159
  const prev = loadSnapshot2(snapshotPath);
4154
4160
  if (prev) {
4155
- diff = diffSnapshots3(prev.result, report);
4161
+ diff = diffSnapshots2(prev.result, report);
4156
4162
  }
4157
4163
  saveSnapshot2(snapshotPath, report);
4158
4164
  }