export interface CleanupDiagnostic { readonly operation: string; readonly error: unknown; } function describeValue(value: unknown): string { return value instanceof Error ? value.message : String(value); } export class FailureWithCleanup extends Error { readonly cleanupFailures: readonly CleanupDiagnostic[]; constructor(primary: unknown, cleanupFailures: readonly CleanupDiagnostic[]) { super(describeValue(primary), { cause: primary }); this.name = "FailureWithCleanup"; this.cleanupFailures = Object.freeze([...cleanupFailures]); } } export function appendCleanupFailure( failure: unknown, operation: string, cleanupError: unknown, ): FailureWithCleanup { const primary = failure instanceof FailureWithCleanup ? failure.cause : failure; const previous = failure instanceof FailureWithCleanup ? failure.cleanupFailures : []; return new FailureWithCleanup(primary, [ ...previous, Object.freeze({ operation, error: cleanupError }), ]); } export function describeFailure(failure: unknown): string { if (!(failure instanceof FailureWithCleanup)) { return describeValue(failure); } const diagnostics = failure.cleanupFailures .map(({ operation, error }) => `${operation}: ${describeValue(error)}`) .join("; "); return `${failure.message} (cleanup failures: ${diagnostics})`; }