interface SSEMessage {
event?: string
data: string
id?: string
retry?: number
}
async function parseSSE(url: string) {
const stream = await ofetch(url, { responseType: 'stream' })
const reader = stream.getReader()
const decoder = new TextDecoder()
let buffer = ''
let currentMessage: Partial<SSEMessage> = {}
while (true) {
const { done, value } = await reader.read()
if (done) break
buffer += decoder.decode(value, { stream: true })
const lines = buffer.split('\n')
buffer = lines.pop() || ''
for (const line of lines) {
if (line === '') {
// Empty line = message complete
if (currentMessage.data) {
yield currentMessage as SSEMessage
}
currentMessage = {}
continue
}
const colonIndex = line.indexOf(':')
if (colonIndex === -1) continue
const field = line.slice(0, colonIndex)
const value = line.slice(colonIndex + 1).trim()
switch (field) {
case 'event':
currentMessage.event = value
break
case 'data':
currentMessage.data = (currentMessage.data || '') + value
break
case 'id':
currentMessage.id = value
break
case 'retry':
currentMessage.retry = parseInt(value, 10)
break
}
}
}
}
// Usage
for await (const message of parseSSE('/api/events')) {
console.log('Event:', message.event)
console.log('Data:', message.data)
}