Spaces:
Sleeping
Sleeping
/** | |
* 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; | |
} | |
} |