SPB Git forge

spb/cancerindex

Public
37commits 1branches 0releases
2.9 MBsize
maindefault branch
10 days agolast push
TypeScript 97.2% SQL 1.5% CSS 0.6% JavaScript 0.5%
1.9 KB · 29 lines typescript
Raw Blame History
1/** Pure error classification for database failures (no server-only import so it is unit-testable). */23/** Thrown when the database itself is unreachable (connection refused, auth, pool exhausted…). */4export class DatabaseUnavailableError extends Error {5  readonly code: string | undefined;6  constructor(cause: unknown) {7    const e = cause as { message?: string; code?: string } | undefined;8    super(`Database unavailable${e?.code ? ` (${e.code})` : ''}: ${e?.message ?? String(cause)}`);9    this.name = 'DatabaseUnavailableError';10    this.code = e?.code;11  }12}1314// Node socket errors + postgres.js connection errors + PostgreSQL SQLSTATE classes 08 (connection15// exception), 28 (auth), 3D (bad database), 53 (insufficient resources) and 57P (operator intervention).16const CONNECTION_CODES = new Set(['ECONNREFUSED', 'ECONNRESET', 'ETIMEDOUT', 'ENOTFOUND', 'EHOSTUNREACH', 'EPIPE', 'EAI_AGAIN', 'CONNECT_TIMEOUT', 'CONNECTION_CLOSED', 'CONNECTION_ENDED', 'CONNECTION_DESTROYED']);17const CONNECTION_SQLSTATE = /^(08|28|3D|53|57P)/;18const CONNECTION_MESSAGE = /ECONNREFUSED|ECONNRESET|ETIMEDOUT|ENOTFOUND|connection (refused|terminated|closed|ended)|CONNECT_TIMEOUT|too many connections|password authentication failed/i;1920/** True when the error means "the database is unreachable" rather than "this query/table is wrong". */21export function isConnectionError(err: unknown, depth = 0): boolean {22  if (!err || typeof err !== 'object' || depth > 4) return false;23  const e = err as { code?: unknown; errno?: unknown; message?: unknown; cause?: unknown };24  const code = typeof e.code === 'string' ? e.code : typeof e.errno === 'string' ? e.errno : '';25  if (code && (CONNECTION_CODES.has(code) || CONNECTION_SQLSTATE.test(code))) return true;26  if (typeof e.message === 'string' && CONNECTION_MESSAGE.test(e.message)) return true;27  return e.cause != null && e.cause !== err ? isConnectionError(e.cause, depth + 1) : false;28}29