const TRANSIENT_STATUS_CODES = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504, 520, 521, 522, 523, 524]);
const TRANSIENT_PG_SQLSTATES = /* @__PURE__ */ new Set([
  "40001",
  // serialization_failure
  "40P01",
  // deadlock_detected
  "55P03",
  // lock_not_available
  "53300",
  // too_many_connections
  "53400",
  // configuration_limit_exceeded
  "57P01",
  // admin_shutdown
  "57P02",
  // crash_shutdown
  "57P03",
  // cannot_connect_now (db starting up)
  "08000",
  // connection_exception
  "08001",
  // sqlclient_unable_to_establish_sqlconnection
  "08003",
  // connection_does_not_exist
  "08006"
  // connection_failure
]);
const TRANSIENT_CODE_PATTERNS = [
  /ECONNRESET/i,
  /ETIMEDOUT/i,
  /ECONNREFUSED/i,
  /ENOTFOUND/i,
  /EAI_AGAIN/i,
  /socket hang up/i,
  /network timeout/i,
  /\btimed?\s*out\b/i,
  /service unavailable/i,
  /rate limit/i,
  /too many requests/i,
  /temporarily unavailable/i,
  /try again later/i,
  // imapflow surfaces these on TLS/socket-level drops that almost always
  // recover on the next attempt. Treating them as permanent kills the
  // channel after a single bad packet (regression observed during the demo:
  // a network hiccup put a freshly-connected mailbox into a non-recoverable
  // `error` state until the operator manually clicked "Retry").
  /unexpected close/i,
  /connection not available/i,
  /connection closed/i,
  /server closed connection/i,
  // Postgres transient failures surfaced as text by the ORM wrapper (the
  // SQLSTATE on `.code` is the primary signal; these catch wrapped errors that
  // only carry the message). See TRANSIENT_PG_SQLSTATES.
  /deadlock detected/i,
  /could not serialize access/i,
  /connection terminated/i,
  /too many clients already/i,
  /the database system is (starting up|shutting down|in recovery)/i
];
function classifyOutboundError(error) {
  if (!error) {
    return { transient: false, message: "Unknown error" };
  }
  if (error instanceof Error) {
    const explicit = error.transient;
    const status = error.status;
    if (explicit !== void 0) {
      return { transient: Boolean(explicit), status, message: error.message };
    }
    const code = error.code;
    if (typeof code === "string" && TRANSIENT_PG_SQLSTATES.has(code)) {
      return { transient: true, status, message: error.message };
    }
    if (typeof status === "number") {
      return {
        transient: TRANSIENT_STATUS_CODES.has(status),
        status,
        message: error.message
      };
    }
    const haystack = `${error.name} ${error.message}`;
    const matchesPattern = TRANSIENT_CODE_PATTERNS.some((pattern) => pattern.test(haystack));
    return { transient: matchesPattern, message: error.message };
  }
  return { transient: false, message: String(error) };
}
function isReauthError(classification) {
  return classification.status === 401 || /unauthorized|invalid_grant|requires_reauth/i.test(classification.message);
}
function computeBackoffMs(attemptNumber) {
  const base = 1e3 * Math.pow(2, Math.max(0, attemptNumber - 1));
  const capped = Math.min(base, 6e4);
  const jitter = Math.floor(Math.random() * 500);
  return capped + jitter;
}
export {
  classifyOutboundError,
  computeBackoffMs,
  isReauthError
};
//# sourceMappingURL=error-classification.js.map
