Flow Studio
Client-Side Append
executionStore.appendLog(entry) is the single entry point for adding log entries to the client state. It is idempotent — if an entry with the same logId already exists, it is silently skipped. This prevents duplicates from SignalR retransmissions or historical API loads.
appendLog Implementation
// executionStore.ts
appendLog: (entry: LogEntry) => set(state => {
// Idempotency check — skip if logId already in array
if (state.logs.some(l => l.logId === entry.logId)) {
return state; // Return unchanged state — no re-render triggered
}
return {
logs: [...state.logs, entry]
};
}),
// Batch append (called for historical loads and large SignalR batches)
appendLogs: (entries: LogEntry[]) => set(state => {
const existingIds = new Set(state.logs.map(l => l.logId));
const newEntries = entries.filter(e => !existingIds.has(e.logId));
if (newEntries.length === 0) return state; // No change
return {
logs: [...state.logs, ...newEntries]
};
})
SignalR Handler
// useExecutionSignalR.ts — called on every NodeLogEmitted event
connection.on('NodeLogEmitted', (payload: { entries: LogEntry[] }) => {
useExecutionStore.getState().appendLogs(payload.entries);
// Batch append — more efficient than calling appendLog N times
});
Why Idempotency Matters
Three scenarios can cause the same log entry to arrive twice on the client:
| Scenario | How Duplicate Occurs |
|---|---|
| SignalR reconnect | Client reconnects mid-execution and replays missed events — some may already be in the array |
| Historical load after live view | Historical API returns all logs — some were already streamed live |
| Multiple observer tabs | If two tabs are open for the same execution, each receives events — mergeable if shared store |
logId is a server-assigned GUID. It is generated in
SignalRLogSink before the entry enters the buffer. The client never generates or modifies logIds — it only reads them for deduplication.