From 7eddac5119d1db3cbf831c735c5b78f0c92728ef Mon Sep 17 00:00:00 2001 From: Siddharth Kumar Sah Date: Mon, 23 Mar 2026 01:36:57 +0800 Subject: [PATCH] feat(api): add SingleFileProgress type and SSE update function --- apps/api/src/routes/progress.ts | 42 +++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/apps/api/src/routes/progress.ts b/apps/api/src/routes/progress.ts index 59216f07..629ef3ba 100644 --- a/apps/api/src/routes/progress.ts +++ b/apps/api/src/routes/progress.ts @@ -19,11 +19,23 @@ export interface JobProgress { currentFile?: string; } +export interface SingleFileProgress { + jobId: string; + type: "single"; + phase: "processing" | "complete" | "failed"; + stage?: string; + percent: number; + error?: string; +} + /** In-memory store of job progress, keyed by jobId. */ const jobProgressStore = new Map(); /** SSE listeners waiting for updates, keyed by jobId. */ -const listeners = new Map void>>(); +const listeners = new Map< + string, + Set<(data: JobProgress | SingleFileProgress) => void> +>(); /** * Create or update progress for a job. @@ -46,6 +58,23 @@ export function updateJobProgress(progress: JobProgress): void { } } +export function updateSingleFileProgress( + progress: Omit, +): void { + const event: SingleFileProgress = { ...progress, type: "single" }; + const subs = listeners.get(progress.jobId); + if (subs) { + for (const cb of subs) { + cb(event); + } + if (progress.phase === "complete" || progress.phase === "failed") { + setTimeout(() => { + listeners.delete(progress.jobId); + }, 5000); + } + } +} + /** * Get current progress for a job. */ @@ -73,7 +102,7 @@ export async function registerProgressRoutes( }); // Helper to send an SSE message - const sendEvent = (data: JobProgress) => { + const sendEvent = (data: JobProgress | SingleFileProgress) => { reply.raw.write(`data: ${JSON.stringify(data)}\n\n`); }; @@ -95,9 +124,14 @@ export async function registerProgressRoutes( listeners.set(jobId, new Set()); } - const callback = (data: JobProgress) => { + const callback = (data: JobProgress | SingleFileProgress) => { sendEvent(data); - if (data.status === "completed" || data.status === "failed") { + if ( + ("status" in data && + (data.status === "completed" || data.status === "failed")) || + ("phase" in data && + (data.phase === "complete" || data.phase === "failed")) + ) { reply.raw.end(); } };