/** * Server-Sent Events (SSE) endpoint for real-time extraction progress * * This endpoint streams extraction progress updates to the frontend * using the SSE protocol. Each event contains status updates, method attempts, * retry information, and final results. */ import { json, type RequestHandler } from '@sveltejs/kit'; import { extractTextAndThumbnail, type ProgressEvent } from '$lib/server/extraction'; import { extractRecipe } from '$lib/server/parser'; export const POST: RequestHandler = async ({ request }) => { const { url } = await request.json(); if (!url) { return json({ error: 'URL is required' }, { status: 400 }); } // Create a ReadableStream for SSE const stream = new ReadableStream({ async start(controller) { const encoder = new TextEncoder(); // Helper to send SSE message const sendEvent = (event: ProgressEvent) => { const data = JSON.stringify(event); const message = `event: progress\ndata: ${data}\n\n`; controller.enqueue(encoder.encode(message)); }; try { // Extract with progress callback const extracted = await extractTextAndThumbnail(url, sendEvent); // Parse recipe from extracted text sendEvent({ type: 'status', message: 'Parsing recipe...', timestamp: new Date().toISOString() }); const recipe = await extractRecipe(extracted.bodyText); // Send final result const completeEvent: ProgressEvent = { type: 'complete', message: 'Extraction and parsing completed', data: { recipe, thumbnail: extracted.thumbnail }, timestamp: new Date().toISOString() }; const completeMessage = `event: complete\ndata: ${JSON.stringify(completeEvent)}\n\n`; controller.enqueue(encoder.encode(completeMessage)); controller.close(); } catch (error) { // Send error event const errorEvent: ProgressEvent = { type: 'error', message: error instanceof Error ? error.message : 'Unknown error occurred', timestamp: new Date().toISOString() }; const errorMessage = `event: error\ndata: ${JSON.stringify(errorEvent)}\n\n`; controller.enqueue(encoder.encode(errorMessage)); controller.close(); } } }); // Return SSE response return new Response(stream, { headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', Connection: 'keep-alive' } }); };