'use client'; import { AlertCircle, Copy } from 'lucide-react'; import { useToast } from '../../lib/toast'; export type StructuredErrorRecord = Record & { message?: string; stack?: string; }; /** * Check whether `value` looks like a structured error object we can render * with the error block. Some persisted workflow errors only include a * `message`, while runtime errors usually also include `stack`. */ export function isStructuredError( value: unknown ): value is StructuredErrorRecord { return ( value != null && typeof value === 'object' && (typeof (value as Record).message === 'string' || typeof (value as Record).stack === 'string') ); } /** * Narrower guard kept for callers that specifically need a stack trace. */ export function isStructuredErrorWithStack( value: unknown ): value is StructuredErrorRecord & { stack: string } { return ( isStructuredError(value) && typeof (value as StructuredErrorRecord).stack === 'string' ); } /** * Renders a structured error as a visually distinct error block. Shows the * error message with an alert icon at the top, separated from the stack trace * or full message below. */ export function ErrorStackBlock({ value }: { value: StructuredErrorRecord }) { const toast = useToast(); const stack = typeof value.stack === 'string' ? value.stack : undefined; const message = typeof value.message === 'string' ? value.message : undefined; const body = stack ?? message ?? ''; // V8's `Error.stack` already starts with `Name: message`; message-only // errors use the message as the body so long single-line failures remain // readable even when the header truncates. const copyText = message && stack && !stack.includes(message) ? `${message}\n\n${stack}` : body; return (
{message && (

{message}

)} {body && (
          {body}
        
)}
); }