execution-formatter.ts•4.57 kB
/**
* Execution Formatter Utilities
*
* This module provides utility functions for formatting execution data
* in a consistent, user-friendly manner.
*/
import { Execution } from '../types/index.js';
/**
* Format basic execution information for display
*
* @param execution Execution object
* @returns Formatted execution summary
*/
export function formatExecutionSummary(execution: Execution): Record<string, any> {
// Calculate duration
const startedAt = new Date(execution.startedAt);
const stoppedAt = execution.stoppedAt ? new Date(execution.stoppedAt) : new Date();
const durationMs = stoppedAt.getTime() - startedAt.getTime();
const durationSeconds = Math.round(durationMs / 1000);
// Create status indicator emoji
const statusIndicator = getStatusIndicator(execution.status);
return {
id: execution.id,
workflowId: execution.workflowId,
status: `${statusIndicator} ${execution.status}`,
startedAt: execution.startedAt,
stoppedAt: execution.stoppedAt || 'In progress',
duration: `${durationSeconds}s`,
finished: execution.finished
};
}
/**
* Format detailed execution information including node results
*
* @param execution Execution object
* @returns Formatted execution details
*/
export function formatExecutionDetails(execution: Execution): Record<string, any> {
const summary = formatExecutionSummary(execution);
// Extract node results
const nodeResults: Record<string, any> = {};
if (execution.data?.resultData?.runData) {
for (const [nodeName, nodeData] of Object.entries(execution.data.resultData.runData)) {
try {
// Get the last output
const lastOutput = Array.isArray(nodeData) && nodeData.length > 0
? nodeData[nodeData.length - 1]
: null;
if (lastOutput && lastOutput.data && Array.isArray(lastOutput.data.main)) {
// Extract the output data
const outputData = lastOutput.data.main.length > 0
? lastOutput.data.main[0]
: [];
nodeResults[nodeName] = {
status: lastOutput.status,
items: outputData.length,
data: outputData.slice(0, 3), // Limit to first 3 items to avoid overwhelming response
};
}
} catch (error) {
nodeResults[nodeName] = { error: 'Failed to parse node output' };
}
}
}
// Add node results and error information to the summary
return {
...summary,
mode: execution.mode,
nodeResults: nodeResults,
// Include error information if present
error: execution.data?.resultData && 'error' in execution.data.resultData
? {
message: (execution.data.resultData as any).error?.message,
stack: (execution.data.resultData as any).error?.stack,
}
: undefined,
};
}
/**
* Get appropriate status indicator emoji based on execution status
*
* @param status Execution status string
* @returns Status indicator emoji
*/
export function getStatusIndicator(status: string): string {
switch (status) {
case 'success':
return '✅'; // Success
case 'error':
return '❌'; // Error
case 'waiting':
return '⏳'; // Waiting
case 'canceled':
return '🛑'; // Canceled
default:
return '⏱️'; // In progress or unknown
}
}
/**
* Summarize execution results for more compact display
*
* @param executions Array of execution objects
* @param limit Maximum number of executions to include
* @returns Summary of execution results
*/
export function summarizeExecutions(executions: Execution[], limit: number = 10): Record<string, any> {
const limitedExecutions = executions.slice(0, limit);
// Group executions by status
const byStatus: Record<string, number> = {};
limitedExecutions.forEach(execution => {
const status = execution.status || 'unknown';
byStatus[status] = (byStatus[status] || 0) + 1;
});
// Calculate success rate
const totalCount = limitedExecutions.length;
const successCount = byStatus.success || 0;
const successRate = totalCount > 0 ? Math.round((successCount / totalCount) * 100) : 0;
return {
total: totalCount,
byStatus: Object.entries(byStatus).map(([status, count]) => ({
status: `${getStatusIndicator(status)} ${status}`,
count,
percentage: totalCount > 0 ? Math.round((count / totalCount) * 100) : 0
})),
successRate: `${successRate}%`,
displayed: limitedExecutions.length,
totalAvailable: executions.length
};
}