UNPKG

@azure/core-sse

Version:

Implementation of the Server-sent events protocol for Node.js and browsers.

159 lines 6.69 kB
// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. import { __asyncGenerator, __asyncValues, __await } from "tslib"; import { createStream, ensureAsyncIterable } from "./utils.js"; var ControlChars; (function (ControlChars) { ControlChars[ControlChars["NewLine"] = 10] = "NewLine"; ControlChars[ControlChars["CarriageReturn"] = 13] = "CarriageReturn"; ControlChars[ControlChars["Space"] = 32] = "Space"; ControlChars[ControlChars["Colon"] = 58] = "Colon"; })(ControlChars || (ControlChars = {})); export function createSseStream(chunkStream) { const { cancel, iterable } = ensureAsyncIterable(chunkStream); const asyncIter = toMessage(toLine(iterable)); return createStream(asyncIter, cancel); } function concatBuffer(a, b) { const res = new Uint8Array(a.length + b.length); res.set(a); res.set(b, a.length); return res; } function createMessage() { return { data: undefined, event: "", id: "", retry: undefined, }; } function toLine(chunkIter) { return __asyncGenerator(this, arguments, function* toLine_1() { var _a, e_1, _b, _c; let buf; let bufIdx = 0; let fieldLen = -1; let discardTrailingNewline = false; try { for (var _d = true, chunkIter_1 = __asyncValues(chunkIter), chunkIter_1_1; chunkIter_1_1 = yield __await(chunkIter_1.next()), _a = chunkIter_1_1.done, !_a; _d = true) { _c = chunkIter_1_1.value; _d = false; const chunk = _c; if (buf === undefined) { buf = chunk; bufIdx = 0; fieldLen = -1; } else { buf = concatBuffer(buf, chunk); } const bufLen = buf.length; let start = 0; while (bufIdx < bufLen) { if (discardTrailingNewline) { if (buf[bufIdx] === ControlChars.NewLine) { start = ++bufIdx; } discardTrailingNewline = false; } let end = -1; for (; bufIdx < bufLen && end === -1; ++bufIdx) { switch (buf[bufIdx]) { case ControlChars.Colon: if (fieldLen === -1) { fieldLen = bufIdx - start; } break; case ControlChars.CarriageReturn: // We need to discard the trailing newline if any but can't do // that now because we need to dispatch the current line first. discardTrailingNewline = true; end = bufIdx; break; case ControlChars.NewLine: end = bufIdx; break; } } if (end === -1) { // We reached the end of the buffer but the line hasn't ended. // Wait for the next chunk and then continue parsing: break; } yield yield __await({ line: buf.subarray(start, end), fieldLen }); start = bufIdx; // we're now on the next line fieldLen = -1; } if (start === bufLen) { buf = undefined; } else if (start !== 0) { // discard already processed lines buf = buf.subarray(start); bufIdx -= start; } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (!_d && !_a && (_b = chunkIter_1.return)) yield __await(_b.call(chunkIter_1)); } finally { if (e_1) throw e_1.error; } } }); } function toMessage(lineIter) { return __asyncGenerator(this, arguments, function* toMessage_1() { var _a, e_2, _b, _c; let message = createMessage(); const decoder = new TextDecoder(); try { for (var _d = true, lineIter_1 = __asyncValues(lineIter), lineIter_1_1; lineIter_1_1 = yield __await(lineIter_1.next()), _a = lineIter_1_1.done, !_a; _d = true) { _c = lineIter_1_1.value; _d = false; const { line, fieldLen } = _c; if (line.length === 0 && message.data !== undefined) { // empty line denotes end of message. Yield and start a new message: yield yield __await(message); message = createMessage(); } else if (fieldLen > 0) { // exclude comments and lines with no values // line is of format "<field>:<value>" or "<field>: <value>" // https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation const field = decoder.decode(line.subarray(0, fieldLen)); const valueOffset = fieldLen + (line[fieldLen + 1] === ControlChars.Space ? 2 : 1); const value = decoder.decode(line.subarray(valueOffset)); switch (field) { case "data": message.data = message.data ? message.data + "\n" + value : value; break; case "event": message.event = value; break; case "id": message.id = value; break; case "retry": { const retry = parseInt(value, 10); if (!isNaN(retry)) { message.retry = retry; } break; } } } } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (!_d && !_a && (_b = lineIter_1.return)) yield __await(_b.call(lineIter_1)); } finally { if (e_2) throw e_2.error; } } }); } //# sourceMappingURL=sse.js.map