( rawEvents: ExecutionLog[], workflowStartTime?: number | null, )
| 143 | const MIN_TIMELINE_DURATION_MS = 1; |
| 144 | |
| 145 | const prepareTimelineEvents = ( |
| 146 | rawEvents: ExecutionLog[], |
| 147 | workflowStartTime?: number | null, |
| 148 | ): { |
| 149 | events: TimelineEvent[]; |
| 150 | totalDuration: number; |
| 151 | timelineStartTime: number | null; |
| 152 | } => { |
| 153 | if (!rawEvents || rawEvents.length === 0) { |
| 154 | return { |
| 155 | events: [], |
| 156 | totalDuration: 0, |
| 157 | timelineStartTime: workflowStartTime ?? null, |
| 158 | }; |
| 159 | } |
| 160 | |
| 161 | const sortedEvents = [...rawEvents].sort( |
| 162 | (a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime(), |
| 163 | ); |
| 164 | |
| 165 | if (sortedEvents.length === 0) { |
| 166 | return { |
| 167 | events: [], |
| 168 | totalDuration: 0, |
| 169 | timelineStartTime: workflowStartTime ?? 0, |
| 170 | }; |
| 171 | } |
| 172 | |
| 173 | // Use workflow start time if provided, otherwise use first event timestamp |
| 174 | // Always prefer workflowStartTime when available (it's the authoritative source) |
| 175 | const firstEventTime = new Date(sortedEvents[0].timestamp).getTime(); |
| 176 | const startTime = |
| 177 | workflowStartTime !== null && workflowStartTime !== undefined |
| 178 | ? workflowStartTime |
| 179 | : firstEventTime; |
| 180 | const endTime = new Date(sortedEvents[sortedEvents.length - 1].timestamp).getTime(); |
| 181 | const totalDuration = Math.max(endTime - startTime, MIN_TIMELINE_DURATION_MS); |
| 182 | |
| 183 | const events: TimelineEvent[] = sortedEvents.map((event, index) => { |
| 184 | const eventTime = new Date(event.timestamp).getTime(); |
| 185 | const offsetMs = eventTime - startTime; |
| 186 | |
| 187 | // Calculate duration based on next event or a default duration |
| 188 | let duration = 0; |
| 189 | if (index < sortedEvents.length - 1) { |
| 190 | const nextEventTime = new Date(sortedEvents[index + 1].timestamp).getTime(); |
| 191 | duration = Math.max(nextEventTime - eventTime, 100); // Minimum 100ms duration |
| 192 | } else { |
| 193 | duration = Math.max(totalDuration - offsetMs, 100); // For last event, use remaining time |
| 194 | } |
| 195 | |
| 196 | return { |
| 197 | ...event, |
| 198 | visualTime: totalDuration > 0 ? offsetMs / totalDuration : 0, |
| 199 | duration, |
| 200 | offsetMs, |
| 201 | }; |
| 202 | }); |
no outgoing calls
no test coverage detected