/** * Performs a POST request and handles the response as a Server-Sent Events (SSE) stream. * The standard EventSource API does not support POST requests, so we use fetch. * * @param {string} url The URL to send the POST request to. * @param {object} body The JSON body for the POST request. * @param {object} callbacks An object containing callback functions. * @param {(data: object) => void} callbacks.onMessage A function called for each message received. * @param {(error: Error) => void} callbacks.onError A function called if an error occurs. */ export async function postWithSSE(url, body, callbacks) { const { onMessage, onError } = callbacks; try { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'text/event-stream' // Politely ask for an event stream }, body: JSON.stringify(body), }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } if (!response.body) { throw new Error('Response body is null.'); } const reader = response.body.getReader(); const decoder = new TextDecoder(); let buffer = ''; while (true) { const { value, done } = await reader.read(); // Decode the chunk of data and add it to our buffer. // The `stream: true` option is important for multi-byte characters. const chunk = decoder.decode(value, { stream: true }); buffer += chunk; // SSE messages are separated by double newlines (`\n\n`). // A single chunk from the stream might contain multiple messages or a partial message. // We process all complete messages in the buffer. let boundary; while ((boundary = buffer.indexOf('\n\n')) !== -1) { const messageString = buffer.substring(0, boundary); buffer = buffer.substring(boundary + 2); // Remove the processed message from the buffer // Skip empty keep-alive messages if (messageString.trim() === '') { continue; } // SSE "data:" lines. Your server only uses `data:`. // We remove the "data: " prefix to get the JSON payload. if (messageString.startsWith('data:')) { const jsonData = messageString.substring('data: '.length); try { const parsedData = JSON.parse(jsonData); if (parsedData.status === "complete") return parsedData; else onMessage(parsedData); } catch (e) { console.error("Failed to parse JSON from SSE message:", jsonData, e); // Optionally call the onError callback for parsing errors if (onError) onError(new Error("Failed to parse JSON from SSE message.")); } } } } } catch (error) { throw error; } }