Files
chatGPTBox/src/utils/eventsource-parser.mjs
T
2023-10-30 23:40:33 +08:00

131 lines
3.5 KiB
JavaScript

// https://www.npmjs.com/package/eventsource-parser/v/1.1.1
function createParser(onParse) {
let isFirstChunk
let bytes
let buffer
let startingPosition
let startingFieldLength
let eventId
let eventName
let data
reset()
return {
feed,
reset,
}
function reset() {
isFirstChunk = true
bytes = []
buffer = ''
startingPosition = 0
startingFieldLength = -1
eventId = void 0
eventName = void 0
data = ''
}
function feed(chunk) {
bytes = bytes.concat(Array.from(chunk))
buffer = new TextDecoder().decode(new Uint8Array(bytes))
if (isFirstChunk && hasBom(buffer)) {
buffer = buffer.slice(BOM.length)
}
isFirstChunk = false
const length = buffer.length
let position = 0
let discardTrailingNewline = false
while (position < length) {
if (discardTrailingNewline) {
if (buffer[position] === '\n') {
++position
}
discardTrailingNewline = false
}
let lineLength = -1
let fieldLength = startingFieldLength
let character
for (let index = startingPosition; lineLength < 0 && index < length; ++index) {
character = buffer[index]
if (character === ':' && fieldLength < 0) {
fieldLength = index - position
} else if (character === '\r') {
discardTrailingNewline = true
lineLength = index - position
} else if (character === '\n') {
lineLength = index - position
}
}
if (lineLength < 0) {
startingPosition = length - position
startingFieldLength = fieldLength
break
} else {
startingPosition = 0
startingFieldLength = -1
}
parseEventStreamLine(buffer, position, fieldLength, lineLength)
position += lineLength + 1
}
if (position === length) {
bytes = []
buffer = ''
} else if (position > 0) {
bytes = bytes.slice(new TextEncoder().encode(buffer.slice(0, position)).length)
buffer = buffer.slice(position)
}
}
function parseEventStreamLine(lineBuffer, index, fieldLength, lineLength) {
if (lineLength === 0) {
if (data.length > 0) {
onParse({
type: 'event',
id: eventId,
event: eventName || void 0,
data: data.slice(0, -1),
// remove trailing newline
})
data = ''
eventId = void 0
}
eventName = void 0
return
}
const noValue = fieldLength < 0
const field = lineBuffer.slice(index, index + (noValue ? lineLength : fieldLength))
let step = 0
if (noValue) {
step = lineLength
} else if (lineBuffer[index + fieldLength + 1] === ' ') {
step = fieldLength + 2
} else {
step = fieldLength + 1
}
const position = index + step
const valueLength = lineLength - step
const value = lineBuffer.slice(position, position + valueLength).toString()
if (field === 'data') {
data += value ? ''.concat(value, '\n') : '\n'
} else if (field === 'event') {
eventName = value
} else if (field === 'id' && !value.includes('\0')) {
eventId = value
} else if (field === 'retry') {
const retry = parseInt(value, 10)
if (!Number.isNaN(retry)) {
onParse({
type: 'reconnect-interval',
value: retry,
})
}
}
}
}
const BOM = [239, 187, 191]
function hasBom(buffer) {
return BOM.every((charCode, index) => buffer.charCodeAt(index) === charCode)
}
export { createParser }