import { redactSensitiveText } from "../logging/redact.js"; export function extractErrorCode(err: unknown): string | undefined { if (!err || typeof err !== "object") { return undefined; } const code = (err as { code?: unknown }).code; if (typeof code === "string") { return code; } if (typeof code === "number") { return String(code); } return undefined; } export function readErrorName(err: unknown): string { if (!err || typeof err !== "object") { return ""; } const name = (err as { name?: unknown }).name; return typeof name === "string" ? name : ""; } export function collectErrorGraphCandidates( err: unknown, resolveNested?: (current: Record) => Iterable, ): unknown[] { const queue: unknown[] = [err]; const seen = new Set(); const candidates: unknown[] = []; while (queue.length > 0) { const current = queue.shift(); if (current == null || seen.has(current)) { continue; } seen.add(current); candidates.push(current); if (!current || typeof current !== "object" || !resolveNested) { continue; } for (const nested of resolveNested(current as Record)) { if (nested != null && !seen.has(nested)) { queue.push(nested); } } } return candidates; } /** * Type guard for NodeJS.ErrnoException (any error with a `code` property). */ export function isErrno(err: unknown): err is NodeJS.ErrnoException { return Boolean(err && typeof err === "object" && "code" in err); } /** * Check if an error has a specific errno code. */ export function hasErrnoCode(err: unknown, code: string): boolean { return isErrno(err) && err.code === code; } export function formatErrorMessage(err: unknown): string { let formatted: string; if (err instanceof Error) { formatted = err.message || err.name || "Error"; } else if (typeof err === "string") { formatted = err; } else if (typeof err === "number" || typeof err === "boolean" || typeof err === "bigint") { formatted = String(err); } else { try { formatted = JSON.stringify(err); } catch { formatted = Object.prototype.toString.call(err); } } // Security: best-effort token redaction before returning/logging. return redactSensitiveText(formatted); } export function formatUncaughtError(err: unknown): string { if (extractErrorCode(err) === "INVALID_CONFIG") { return formatErrorMessage(err); } if (err instanceof Error) { const stack = err.stack ?? err.message ?? err.name; return redactSensitiveText(stack); } return formatErrorMessage(err); }