|
function createParser(onParse) { |
|
let isFirstChunk; |
|
let buffer; |
|
let startingPosition; |
|
let startingFieldLength; |
|
let eventId; |
|
let eventName; |
|
let data; |
|
reset(); |
|
return { |
|
feed, |
|
reset |
|
}; |
|
function reset() { |
|
isFirstChunk = true; |
|
buffer = ""; |
|
startingPosition = 0; |
|
startingFieldLength = -1; |
|
eventId = void 0; |
|
eventName = void 0; |
|
data = ""; |
|
} |
|
function feed(chunk) { |
|
buffer = buffer ? buffer + chunk : chunk; |
|
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) { |
|
buffer = ""; |
|
} else if (position > 0) { |
|
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) |
|
|
|
}); |
|
|
|
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 }; |
|
|
|
|