File size: 2,862 Bytes
4e54efb e4ce2a0 4e54efb |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
/**
* 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;
}
} |