proteinea / src /server /event-log.ts
Mahmoud Eljendy
feat: Antibody Studio — AI-native antibody design workspace by Proteinea
30cc31a
/**
* In-memory event log per task.
*
* Each task accumulates StreamEvents in an append-only buffer. Readers poll
* with an offset ("give me events from index N onward") so they can reconnect
* mid-stream without replaying from scratch.
*
* The log is ephemeral — it lives for the duration of the Node process. Once a
* run completes the events stay in memory for late reconnects but are garbage-
* collected when the task is explicitly cleared.
*/
import type { StreamEvent } from "@/lib/types";
export interface EventEntry {
/** Monotonically increasing index within this task's log. */
index: number;
/** Epoch-ms when the event was appended. */
ts: number;
event: StreamEvent;
}
interface TaskLog {
entries: EventEntry[];
done: boolean;
}
const logs = new Map<string, TaskLog>();
function ensureLog(taskId: string): TaskLog {
let log = logs.get(taskId);
if (!log) {
log = { entries: [], done: false };
logs.set(taskId, log);
}
return log;
}
/** Append a StreamEvent to the task's log. */
export function appendEvent(taskId: string, event: StreamEvent): EventEntry {
const log = ensureLog(taskId);
const entry: EventEntry = {
index: log.entries.length,
ts: Date.now(),
event,
};
log.entries.push(entry);
return entry;
}
/** Mark the log as complete — no more events will arrive. */
export function markDone(taskId: string): void {
const log = logs.get(taskId);
if (log) log.done = true;
}
/** Read events starting at `fromIndex`. Returns entries + whether the log is done. */
export function readEvents(
taskId: string,
fromIndex = 0,
): { entries: EventEntry[]; done: boolean } {
const log = logs.get(taskId);
if (!log) return { entries: [], done: false };
return {
entries: log.entries.slice(fromIndex),
done: log.done,
};
}
/** Whether the task's log has been marked done. */
export function isDone(taskId: string): boolean {
return logs.get(taskId)?.done ?? false;
}
/** Total number of events in the log. */
export function eventCount(taskId: string): number {
return logs.get(taskId)?.entries.length ?? 0;
}
/** Remove a task's log to free memory. */
export function clearLog(taskId: string): void {
logs.delete(taskId);
}