@@ -180,218 +180,6 @@ export function parseDuration(log: LogWithDuration): number | null {
180180 return Number . isFinite ( durationCandidate ) ? durationCandidate : null
181181}
182182
183- interface TraceSpan {
184- output ?: Record < string , unknown >
185- status ?: string
186- error ?: unknown
187- }
188-
189- interface BlockExecution {
190- outputData ?: unknown
191- errorMessage ?: string
192- }
193-
194- interface LogWithExecutionData {
195- executionData ?: {
196- finalOutput ?: unknown
197- traceSpans ?: TraceSpan [ ]
198- blockExecutions ?: BlockExecution [ ]
199- output ?: unknown
200- }
201- output ?: string
202- message ?: string
203- }
204-
205- /**
206- * Extract output from various sources in execution data.
207- * Checks multiple locations in priority order:
208- * 1. executionData.finalOutput
209- * 2. output (as string)
210- * 3. executionData.traceSpans (iterates through spans)
211- * 4. executionData.blockExecutions (last block)
212- * 5. message (fallback)
213- * @param log - Log object containing execution data
214- * @returns Extracted output value or null
215- */
216- export function extractOutput ( log : LogWithExecutionData ) : unknown {
217- let output : unknown = null
218-
219- // Check finalOutput first
220- if ( log . executionData ?. finalOutput !== undefined ) {
221- output = log . executionData . finalOutput
222- }
223-
224- // Check direct output field
225- if ( typeof log . output === 'string' ) {
226- output = log . output
227- } else if ( log . executionData ?. traceSpans && Array . isArray ( log . executionData . traceSpans ) ) {
228- // Search through trace spans
229- const spans = log . executionData . traceSpans
230- for ( let i = spans . length - 1 ; i >= 0 ; i -- ) {
231- const s = spans [ i ]
232- if ( s ?. output && Object . keys ( s . output ) . length > 0 ) {
233- output = s . output
234- break
235- }
236- const outputWithError = s ?. output as Record < string , unknown > | undefined
237- if ( s ?. status === 'error' && ( outputWithError ?. error || s ?. error ) ) {
238- output = outputWithError ?. error || s . error
239- break
240- }
241- }
242- // Fallback to executionData.output
243- if ( ! output && log . executionData ?. output ) {
244- output = log . executionData . output
245- }
246- }
247-
248- // Check block executions
249- if ( ! output ) {
250- const blockExecutions = log . executionData ?. blockExecutions
251- if ( Array . isArray ( blockExecutions ) && blockExecutions . length > 0 ) {
252- const lastBlock = blockExecutions [ blockExecutions . length - 1 ]
253- output = lastBlock ?. outputData || lastBlock ?. errorMessage || null
254- }
255- }
256-
257- // Final fallback to message
258- if ( ! output ) {
259- output = log . message || null
260- }
261-
262- return output
263- }
264-
265- /** Execution log cost breakdown */
266- interface ExecutionCost {
267- input : number
268- output : number
269- total : number
270- }
271-
272- /** Mapped execution log format for UI consumption */
273- export interface ExecutionLog {
274- id : string
275- executionId : string
276- startedAt : string
277- level : string
278- status : string
279- trigger : string
280- triggerUserId : string | null
281- triggerInputs ?: unknown
282- outputs ?: unknown
283- errorMessage : string | null
284- duration : number | null
285- cost : ExecutionCost | null
286- workflowName ?: string
287- workflowColor ?: string
288- hasPendingPause ?: boolean
289- }
290-
291- /** Raw API log response structure */
292- interface RawLogResponse extends LogWithDuration , LogWithExecutionData {
293- id : string
294- executionId : string
295- startedAt ?: string
296- endedAt ?: string
297- createdAt ?: string
298- level ?: string
299- status ?: string
300- trigger ?: string
301- triggerUserId ?: string | null
302- error ?: string
303- cost ?: {
304- input ?: number
305- output ?: number
306- total ?: number
307- }
308- workflowName ?: string
309- workflowColor ?: string
310- workflow ?: {
311- name ?: string
312- color ?: string
313- }
314- hasPendingPause ?: boolean
315- }
316-
317- /**
318- * Convert raw API log response to ExecutionLog format.
319- * @param log - Raw log response from API
320- * @returns Formatted execution log
321- */
322- export function mapToExecutionLog ( log : RawLogResponse ) : ExecutionLog {
323- const started = log . startedAt
324- ? new Date ( log . startedAt )
325- : log . endedAt
326- ? new Date ( log . endedAt )
327- : null
328-
329- const startedAt =
330- started && ! Number . isNaN ( started . getTime ( ) ) ? started . toISOString ( ) : new Date ( ) . toISOString ( )
331-
332- const duration = parseDuration ( log )
333- const output = extractOutput ( log )
334-
335- return {
336- id : log . id ,
337- executionId : log . executionId ,
338- startedAt,
339- level : log . level || 'info' ,
340- status : log . status || 'completed' ,
341- trigger : log . trigger || 'manual' ,
342- triggerUserId : log . triggerUserId || null ,
343- triggerInputs : undefined ,
344- outputs : output || undefined ,
345- errorMessage : log . error || null ,
346- duration,
347- cost : log . cost
348- ? {
349- input : log . cost . input || 0 ,
350- output : log . cost . output || 0 ,
351- total : log . cost . total || 0 ,
352- }
353- : null ,
354- workflowName : log . workflowName || log . workflow ?. name ,
355- workflowColor : log . workflowColor || log . workflow ?. color ,
356- hasPendingPause : log . hasPendingPause === true ,
357- }
358- }
359-
360- /**
361- * Alternative version that uses createdAt as fallback for startedAt.
362- * Used in some API responses.
363- * @param log - Raw log response from API
364- * @returns Formatted execution log
365- */
366- export function mapToExecutionLogAlt ( log : RawLogResponse ) : ExecutionLog {
367- const duration = parseDuration ( log )
368- const output = extractOutput ( log )
369-
370- return {
371- id : log . id ,
372- executionId : log . executionId ,
373- startedAt : log . createdAt || log . startedAt || new Date ( ) . toISOString ( ) ,
374- level : log . level || 'info' ,
375- status : log . status || 'completed' ,
376- trigger : log . trigger || 'manual' ,
377- triggerUserId : log . triggerUserId || null ,
378- triggerInputs : undefined ,
379- outputs : output || undefined ,
380- errorMessage : log . error || null ,
381- duration,
382- cost : log . cost
383- ? {
384- input : log . cost . input || 0 ,
385- output : log . cost . output || 0 ,
386- total : log . cost . total || 0 ,
387- }
388- : null ,
389- workflowName : log . workflow ?. name ,
390- workflowColor : log . workflow ?. color ,
391- hasPendingPause : log . hasPendingPause === true ,
392- }
393- }
394-
395183/**
396184 * Format latency value for display in dashboard UI
397185 * @param ms - Latency in milliseconds (number)
0 commit comments