index.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. class ParseError extends Error {
  2. constructor(message, options) {
  3. super(message), this.name = "ParseError", this.type = options.type, this.field = options.field, this.value = options.value, this.line = options.line;
  4. }
  5. }
  6. function noop(_arg) {
  7. }
  8. function createParser(callbacks) {
  9. if (typeof callbacks == "function")
  10. throw new TypeError(
  11. "`callbacks` must be an object, got a function instead. Did you mean `{onEvent: fn}`?"
  12. );
  13. const { onEvent = noop, onError = noop, onRetry = noop, onComment } = callbacks;
  14. let incompleteLine = "", isFirstChunk = !0, id, data = "", eventType = "";
  15. function feed(newChunk) {
  16. const chunk = isFirstChunk ? newChunk.replace(/^\xEF\xBB\xBF/, "") : newChunk, [complete, incomplete] = splitLines(`${incompleteLine}${chunk}`);
  17. for (const line of complete)
  18. parseLine(line);
  19. incompleteLine = incomplete, isFirstChunk = !1;
  20. }
  21. function parseLine(line) {
  22. if (line === "") {
  23. dispatchEvent();
  24. return;
  25. }
  26. if (line.startsWith(":")) {
  27. onComment && onComment(line.slice(line.startsWith(": ") ? 2 : 1));
  28. return;
  29. }
  30. const fieldSeparatorIndex = line.indexOf(":");
  31. if (fieldSeparatorIndex !== -1) {
  32. const field = line.slice(0, fieldSeparatorIndex), offset = line[fieldSeparatorIndex + 1] === " " ? 2 : 1, value = line.slice(fieldSeparatorIndex + offset);
  33. processField(field, value, line);
  34. return;
  35. }
  36. processField(line, "", line);
  37. }
  38. function processField(field, value, line) {
  39. switch (field) {
  40. case "event":
  41. eventType = value;
  42. break;
  43. case "data":
  44. data = `${data}${value}
  45. `;
  46. break;
  47. case "id":
  48. id = value.includes("\0") ? void 0 : value;
  49. break;
  50. case "retry":
  51. /^\d+$/.test(value) ? onRetry(parseInt(value, 10)) : onError(
  52. new ParseError(`Invalid \`retry\` value: "${value}"`, {
  53. type: "invalid-retry",
  54. value,
  55. line
  56. })
  57. );
  58. break;
  59. default:
  60. onError(
  61. new ParseError(
  62. `Unknown field "${field.length > 20 ? `${field.slice(0, 20)}\u2026` : field}"`,
  63. { type: "unknown-field", field, value, line }
  64. )
  65. );
  66. break;
  67. }
  68. }
  69. function dispatchEvent() {
  70. data.length > 0 && onEvent({
  71. id,
  72. event: eventType || void 0,
  73. // If the data buffer's last character is a U+000A LINE FEED (LF) character,
  74. // then remove the last character from the data buffer.
  75. data: data.endsWith(`
  76. `) ? data.slice(0, -1) : data
  77. }), id = void 0, data = "", eventType = "";
  78. }
  79. function reset(options = {}) {
  80. incompleteLine && options.consume && parseLine(incompleteLine), isFirstChunk = !0, id = void 0, data = "", eventType = "", incompleteLine = "";
  81. }
  82. return { feed, reset };
  83. }
  84. function splitLines(chunk) {
  85. const lines = [];
  86. let incompleteLine = "", searchIndex = 0;
  87. for (; searchIndex < chunk.length; ) {
  88. const crIndex = chunk.indexOf("\r", searchIndex), lfIndex = chunk.indexOf(`
  89. `, searchIndex);
  90. let lineEnd = -1;
  91. if (crIndex !== -1 && lfIndex !== -1 ? lineEnd = Math.min(crIndex, lfIndex) : crIndex !== -1 ? crIndex === chunk.length - 1 ? lineEnd = -1 : lineEnd = crIndex : lfIndex !== -1 && (lineEnd = lfIndex), lineEnd === -1) {
  92. incompleteLine = chunk.slice(searchIndex);
  93. break;
  94. } else {
  95. const line = chunk.slice(searchIndex, lineEnd);
  96. lines.push(line), searchIndex = lineEnd + 1, chunk[searchIndex - 1] === "\r" && chunk[searchIndex] === `
  97. ` && searchIndex++;
  98. }
  99. }
  100. return [lines, incompleteLine];
  101. }
  102. export {
  103. ParseError,
  104. createParser
  105. };
  106. //# sourceMappingURL=index.js.map