Commit Inicial

This commit is contained in:
jfeng committed 2026-03-25 09:47:08 -03:00
commit 9636921d99
4064 files changed
+517144

No files matched your search

+106
View File
@@ -0,0 +1,106 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: !0 });
class ParseError extends Error {
constructor(message, options) {
super(message), this.name = "ParseError", this.type = options.type, this.field = options.field, this.value = options.value, this.line = options.line;
}
}
function noop(_arg) {
}
function createParser(callbacks) {
if (typeof callbacks == "function")
throw new TypeError(
"`callbacks` must be an object, got a function instead. Did you mean `{onEvent: fn}`?"
);
const { onEvent = noop, onError = noop, onRetry = noop, onComment } = callbacks;
let incompleteLine = "", isFirstChunk = !0, id, data = "", eventType = "";
function feed(newChunk) {
const chunk = isFirstChunk ? newChunk.replace(/^\xEF\xBB\xBF/, "") : newChunk, [complete, incomplete] = splitLines(`${incompleteLine}${chunk}`);
for (const line of complete)
parseLine(line);
incompleteLine = incomplete, isFirstChunk = !1;
}
function parseLine(line) {
if (line === "") {
dispatchEvent();
return;
}
if (line.startsWith(":")) {
onComment && onComment(line.slice(line.startsWith(": ") ? 2 : 1));
return;
}
const fieldSeparatorIndex = line.indexOf(":");
if (fieldSeparatorIndex !== -1) {
const field = line.slice(0, fieldSeparatorIndex), offset = line[fieldSeparatorIndex + 1] === " " ? 2 : 1, value = line.slice(fieldSeparatorIndex + offset);
processField(field, value, line);
return;
}
processField(line, "", line);
}
function processField(field, value, line) {
switch (field) {
case "event":
eventType = value;
break;
case "data":
data = `${data}${value}
`;
break;
case "id":
id = value.includes("\0") ? void 0 : value;
break;
case "retry":
/^\d+$/.test(value) ? onRetry(parseInt(value, 10)) : onError(
new ParseError(`Invalid \`retry\` value: "${value}"`, {
type: "invalid-retry",
value,
line
})
);
break;
default:
onError(
new ParseError(
`Unknown field "${field.length > 20 ? `${field.slice(0, 20)}\u2026` : field}"`,
{ type: "unknown-field", field, value, line }
)
);
break;
}
}
function dispatchEvent() {
data.length > 0 && onEvent({
id,
event: eventType || void 0,
// If the data buffer's last character is a U+000A LINE FEED (LF) character,
// then remove the last character from the data buffer.
data: data.endsWith(`
`) ? data.slice(0, -1) : data
}), id = void 0, data = "", eventType = "";
}
function reset(options = {}) {
incompleteLine && options.consume && parseLine(incompleteLine), isFirstChunk = !0, id = void 0, data = "", eventType = "", incompleteLine = "";
}
return { feed, reset };
}
function splitLines(chunk) {
const lines = [];
let incompleteLine = "", searchIndex = 0;
for (; searchIndex < chunk.length; ) {
const crIndex = chunk.indexOf("\r", searchIndex), lfIndex = chunk.indexOf(`
`, searchIndex);
let lineEnd = -1;
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) {
incompleteLine = chunk.slice(searchIndex);
break;
} else {
const line = chunk.slice(searchIndex, lineEnd);
lines.push(line), searchIndex = lineEnd + 1, chunk[searchIndex - 1] === "\r" && chunk[searchIndex] === `
` && searchIndex++;
}
}
return [lines, incompleteLine];
}
exports.ParseError = ParseError;
exports.createParser = createParser;
//# sourceMappingURL=index.cjs.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.cjs","sources":["../src/errors.ts","../src/parse.ts"],"sourcesContent":["/**\n * The type of error that occurred.\n * @public\n */\nexport type ErrorType = 'invalid-retry' | 'unknown-field'\n\n/**\n * Error thrown when encountering an issue during parsing.\n *\n * @public\n */\nexport class ParseError extends Error {\n /**\n * The type of error that occurred.\n */\n type: ErrorType\n\n /**\n * In the case of an unknown field encountered in the stream, this will be the field name.\n */\n field?: string | undefined\n\n /**\n * In the case of an unknown field encountered in the stream, this will be the value of the field.\n */\n value?: string | undefined\n\n /**\n * The line that caused the error, if available.\n */\n line?: string | undefined\n\n constructor(\n message: string,\n options: {type: ErrorType; field?: string; value?: string; line?: string},\n ) {\n super(message)\n this.name = 'ParseError'\n this.type = options.type\n this.field = options.field\n this.value = options.value\n this.line = options.line\n }\n}\n","/**\n * EventSource/Server-Sent Events parser\n * @see https://html.spec.whatwg.org/multipage/server-sent-events.html\n */\nimport {ParseError} from './errors.ts'\nimport type {EventSourceParser, ParserCallbacks} from './types.ts'\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nfunction noop(_arg: unknown) {\n // intentional noop\n}\n\n/**\n * Creates a new EventSource parser.\n *\n * @param callbacks - Callbacks to invoke on different parsing events:\n * - `onEvent` when a new event is parsed\n * - `onError` when an error occurs\n * - `onRetry` when a new reconnection interval has been sent from the server\n * - `onComment` when a comment is encountered in the stream\n *\n * @returns A new EventSource parser, with `parse` and `reset` methods.\n * @public\n */\nexport function createParser(callbacks: ParserCallbacks): EventSourceParser {\n if (typeof callbacks === 'function') {\n throw new TypeError(\n '`callbacks` must be an object, got a function instead. Did you mean `{onEvent: fn}`?',\n )\n }\n\n const {onEvent = noop, onError = noop, onRetry = noop, onComment} = callbacks\n\n let incompleteLine = ''\n\n let isFirstChunk = true\n let id: string | undefined\n let data = ''\n let eventType = ''\n\n function feed(newChunk: string) {\n // Strip any UTF8 byte order mark (BOM) at the start of the stream\n const chunk = isFirstChunk ? newChunk.replace(/^\\xEF\\xBB\\xBF/, '') : newChunk\n\n // If there was a previous incomplete line, append it to the new chunk,\n // so we may process it together as a new (hopefully complete) chunk.\n const [complete, incomplete] = splitLines(`${incompleteLine}${chunk}`)\n\n for (const line of complete) {\n parseLine(line)\n }\n\n incompleteLine = incomplete\n isFirstChunk = false\n }\n\n function parseLine(line: string) {\n // If the line is empty (a blank line), dispatch the event\n if (line === '') {\n dispatchEvent()\n return\n }\n\n // If the line starts with a U+003A COLON character (:), ignore the line.\n if (line.startsWith(':')) {\n if (onComment) {\n onComment(line.slice(line.startsWith(': ') ? 2 : 1))\n }\n return\n }\n\n // If the line contains a U+003A COLON character (:)\n const fieldSeparatorIndex = line.indexOf(':')\n if (fieldSeparatorIndex !== -1) {\n // Collect the characters on the line before the first U+003A COLON character (:),\n // and let `field` be that string.\n const field = line.slice(0, fieldSeparatorIndex)\n\n // Collect the characters on the line after the first U+003A COLON character (:),\n // and let `value` be that string. If value starts with a U+0020 SPACE character,\n // remove it from value.\n const offset = line[fieldSeparatorIndex + 1] === ' ' ? 2 : 1\n const value = line.slice(fieldSeparatorIndex + offset)\n\n processField(field, value, line)\n return\n }\n\n // Otherwise, the string is not empty but does not contain a U+003A COLON character (:)\n // Process the field using the whole line as the field name, and an empty string as the field value.\n // 👆 This is according to spec. That means that a line that has the value `data` will result in\n // a newline being added to the current `data` buffer, for instance.\n processField(line, '', line)\n }\n\n function processField(field: string, value: string, line: string) {\n // Field names must be compared literally, with no case folding performed.\n switch (field) {\n case 'event':\n // Set the `event type` buffer to field value\n eventType = value\n break\n case 'data':\n // Append the field value to the `data` buffer, then append a single U+000A LINE FEED(LF)\n // character to the `data` buffer.\n data = `${data}${value}\\n`\n Line truncated
+144
View File
@@ -0,0 +1,144 @@
/**
* Creates a new EventSource parser.
*
* @param callbacks - Callbacks to invoke on different parsing events:
* - `onEvent` when a new event is parsed
* - `onError` when an error occurs
* - `onRetry` when a new reconnection interval has been sent from the server
* - `onComment` when a comment is encountered in the stream
*
* @returns A new EventSource parser, with `parse` and `reset` methods.
* @public
*/
export declare function createParser(callbacks: ParserCallbacks): EventSourceParser
/**
* The type of error that occurred.
* @public
*/
export declare type ErrorType = 'invalid-retry' | 'unknown-field'
/**
* A parsed EventSource message event
*
* @public
*/
export declare interface EventSourceMessage {
/**
* The event type sent from the server. Note that this differs from the browser `EventSource`
* implementation in that browsers will default this to `message`, whereas this parser will
* leave this as `undefined` if not explicitly declared.
*/
event?: string | undefined
/**
* ID of the message, if any was provided by the server. Can be used by clients to keep the
* last received message ID in sync when reconnecting.
*/
id?: string | undefined
/**
* The data received for this message
*/
data: string
}
/**
* EventSource parser instance.
*
* Needs to be reset between reconnections/when switching data source, using the `reset()` method.
*
* @public
*/
export declare interface EventSourceParser {
/**
* Feeds the parser another chunk. The method _does not_ return a parsed message.
* Instead, callbacks passed when creating the parser will be triggered once we see enough data
* for a valid/invalid parsing step (see {@link ParserCallbacks}).
*
* @param chunk - The chunk to parse. Can be a partial, eg in the case of streaming messages.
* @public
*/
feed(chunk: string): void
/**
* Resets the parser state. This is required when you have a new stream of messages -
* for instance in the case of a client being disconnected and reconnecting.
*
* Previously received, incomplete data will NOT be parsed unless you pass `consume: true`,
* which tells the parser to attempt to consume any incomplete data as if it ended with a newline
* character. This is useful for cases when a server sends a non-EventSource message that you
* want to be able to react to in an `onError` callback.
*
* @public
*/
reset(options?: {consume?: boolean}): void
}
/**
* Error thrown when encountering an issue during parsing.
*
* @public
*/
export declare class ParseError extends Error {
/**
* The type of error that occurred.
*/
type: ErrorType
/**
* In the case of an unknown field encountered in the stream, this will be the field name.
*/
field?: string | undefined
/**
* In the case of an unknown field encountered in the stream, this will be the value of the field.
*/
value?: string | undefined
/**
* The line that caused the error, if available.
*/
line?: string | undefined
constructor(
message: string,
options: {
type: ErrorType
field?: string
value?: string
line?: string
},
)
}
/**
* Callbacks that can be passed to the parser to handle different types of parsed messages
* and errors.
*
* @public
*/
export declare interface ParserCallbacks {
/**
* Callback for when a new event/message is parsed from the stream.
* This is the main callback that clients will use to handle incoming messages.
*
* @param event - The parsed event/message
*/
onEvent?: ((event: EventSourceMessage) => void) | undefined
/**
* Callback for when the server sends a new reconnection interval through the `retry` field.
*
* @param retry - The number of milliseconds to wait before reconnecting.
*/
onRetry?: ((retry: number) => void) | undefined
/**
* Callback for when a comment is encountered in the stream.
*
* @param comment - The comment encountered in the stream.
*/
onComment?: ((comment: string) => void) | undefined
/**
* Callback for when an error occurs during parsing. This is a catch-all for any errors
* that occur during parsing, and can be used to handle them in a custom way. Most clients
* tend to silently ignore any errors and instead retry, but it can be helpful to log/debug.
*
* @param error - The error that occurred during parsing
*/
onError?: ((error: ParseError) => void) | undefined
}
export {}
+144
View File
@@ -0,0 +1,144 @@
/**
* Creates a new EventSource parser.
*
* @param callbacks - Callbacks to invoke on different parsing events:
* - `onEvent` when a new event is parsed
* - `onError` when an error occurs
* - `onRetry` when a new reconnection interval has been sent from the server
* - `onComment` when a comment is encountered in the stream
*
* @returns A new EventSource parser, with `parse` and `reset` methods.
* @public
*/
export declare function createParser(callbacks: ParserCallbacks): EventSourceParser
/**
* The type of error that occurred.
* @public
*/
export declare type ErrorType = 'invalid-retry' | 'unknown-field'
/**
* A parsed EventSource message event
*
* @public
*/
export declare interface EventSourceMessage {
/**
* The event type sent from the server. Note that this differs from the browser `EventSource`
* implementation in that browsers will default this to `message`, whereas this parser will
* leave this as `undefined` if not explicitly declared.
*/
event?: string | undefined
/**
* ID of the message, if any was provided by the server. Can be used by clients to keep the
* last received message ID in sync when reconnecting.
*/
id?: string | undefined
/**
* The data received for this message
*/
data: string
}
/**
* EventSource parser instance.
*
* Needs to be reset between reconnections/when switching data source, using the `reset()` method.
*
* @public
*/
export declare interface EventSourceParser {
/**
* Feeds the parser another chunk. The method _does not_ return a parsed message.
* Instead, callbacks passed when creating the parser will be triggered once we see enough data
* for a valid/invalid parsing step (see {@link ParserCallbacks}).
*
* @param chunk - The chunk to parse. Can be a partial, eg in the case of streaming messages.
* @public
*/
feed(chunk: string): void
/**
* Resets the parser state. This is required when you have a new stream of messages -
* for instance in the case of a client being disconnected and reconnecting.
*
* Previously received, incomplete data will NOT be parsed unless you pass `consume: true`,
* which tells the parser to attempt to consume any incomplete data as if it ended with a newline
* character. This is useful for cases when a server sends a non-EventSource message that you
* want to be able to react to in an `onError` callback.
*
* @public
*/
reset(options?: {consume?: boolean}): void
}
/**
* Error thrown when encountering an issue during parsing.
*
* @public
*/
export declare class ParseError extends Error {
/**
* The type of error that occurred.
*/
type: ErrorType
/**
* In the case of an unknown field encountered in the stream, this will be the field name.
*/
field?: string | undefined
/**
* In the case of an unknown field encountered in the stream, this will be the value of the field.
*/
value?: string | undefined
/**
* The line that caused the error, if available.
*/
line?: string | undefined
constructor(
message: string,
options: {
type: ErrorType
field?: string
value?: string
line?: string
},
)
}
/**
* Callbacks that can be passed to the parser to handle different types of parsed messages
* and errors.
*
* @public
*/
export declare interface ParserCallbacks {
/**
* Callback for when a new event/message is parsed from the stream.
* This is the main callback that clients will use to handle incoming messages.
*
* @param event - The parsed event/message
*/
onEvent?: ((event: EventSourceMessage) => void) | undefined
/**
* Callback for when the server sends a new reconnection interval through the `retry` field.
*
* @param retry - The number of milliseconds to wait before reconnecting.
*/
onRetry?: ((retry: number) => void) | undefined
/**
* Callback for when a comment is encountered in the stream.
*
* @param comment - The comment encountered in the stream.
*/
onComment?: ((comment: string) => void) | undefined
/**
* Callback for when an error occurs during parsing. This is a catch-all for any errors
* that occur during parsing, and can be used to handle them in a custom way. Most clients
* tend to silently ignore any errors and instead retry, but it can be helpful to log/debug.
*
* @param error - The error that occurred during parsing
*/
onError?: ((error: ParseError) => void) | undefined
}
export {}
+106
View File
@@ -0,0 +1,106 @@
class ParseError extends Error {
constructor(message, options) {
super(message), this.name = "ParseError", this.type = options.type, this.field = options.field, this.value = options.value, this.line = options.line;
}
}
function noop(_arg) {
}
function createParser(callbacks) {
if (typeof callbacks == "function")
throw new TypeError(
"`callbacks` must be an object, got a function instead. Did you mean `{onEvent: fn}`?"
);
const { onEvent = noop, onError = noop, onRetry = noop, onComment } = callbacks;
let incompleteLine = "", isFirstChunk = !0, id, data = "", eventType = "";
function feed(newChunk) {
const chunk = isFirstChunk ? newChunk.replace(/^\xEF\xBB\xBF/, "") : newChunk, [complete, incomplete] = splitLines(`${incompleteLine}${chunk}`);
for (const line of complete)
parseLine(line);
incompleteLine = incomplete, isFirstChunk = !1;
}
function parseLine(line) {
if (line === "") {
dispatchEvent();
return;
}
if (line.startsWith(":")) {
onComment && onComment(line.slice(line.startsWith(": ") ? 2 : 1));
return;
}
const fieldSeparatorIndex = line.indexOf(":");
if (fieldSeparatorIndex !== -1) {
const field = line.slice(0, fieldSeparatorIndex), offset = line[fieldSeparatorIndex + 1] === " " ? 2 : 1, value = line.slice(fieldSeparatorIndex + offset);
processField(field, value, line);
return;
}
processField(line, "", line);
}
function processField(field, value, line) {
switch (field) {
case "event":
eventType = value;
break;
case "data":
data = `${data}${value}
`;
break;
case "id":
id = value.includes("\0") ? void 0 : value;
break;
case "retry":
/^\d+$/.test(value) ? onRetry(parseInt(value, 10)) : onError(
new ParseError(`Invalid \`retry\` value: "${value}"`, {
type: "invalid-retry",
value,
line
})
);
break;
default:
onError(
new ParseError(
`Unknown field "${field.length > 20 ? `${field.slice(0, 20)}\u2026` : field}"`,
{ type: "unknown-field", field, value, line }
)
);
break;
}
}
function dispatchEvent() {
data.length > 0 && onEvent({
id,
event: eventType || void 0,
// If the data buffer's last character is a U+000A LINE FEED (LF) character,
// then remove the last character from the data buffer.
data: data.endsWith(`
`) ? data.slice(0, -1) : data
}), id = void 0, data = "", eventType = "";
}
function reset(options = {}) {
incompleteLine && options.consume && parseLine(incompleteLine), isFirstChunk = !0, id = void 0, data = "", eventType = "", incompleteLine = "";
}
return { feed, reset };
}
function splitLines(chunk) {
const lines = [];
let incompleteLine = "", searchIndex = 0;
for (; searchIndex < chunk.length; ) {
const crIndex = chunk.indexOf("\r", searchIndex), lfIndex = chunk.indexOf(`
`, searchIndex);
let lineEnd = -1;
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) {
incompleteLine = chunk.slice(searchIndex);
break;
} else {
const line = chunk.slice(searchIndex, lineEnd);
lines.push(line), searchIndex = lineEnd + 1, chunk[searchIndex - 1] === "\r" && chunk[searchIndex] === `
` && searchIndex++;
}
}
return [lines, incompleteLine];
}
export {
ParseError,
createParser
};
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sources":["../src/errors.ts","../src/parse.ts"],"sourcesContent":["/**\n * The type of error that occurred.\n * @public\n */\nexport type ErrorType = 'invalid-retry' | 'unknown-field'\n\n/**\n * Error thrown when encountering an issue during parsing.\n *\n * @public\n */\nexport class ParseError extends Error {\n /**\n * The type of error that occurred.\n */\n type: ErrorType\n\n /**\n * In the case of an unknown field encountered in the stream, this will be the field name.\n */\n field?: string | undefined\n\n /**\n * In the case of an unknown field encountered in the stream, this will be the value of the field.\n */\n value?: string | undefined\n\n /**\n * The line that caused the error, if available.\n */\n line?: string | undefined\n\n constructor(\n message: string,\n options: {type: ErrorType; field?: string; value?: string; line?: string},\n ) {\n super(message)\n this.name = 'ParseError'\n this.type = options.type\n this.field = options.field\n this.value = options.value\n this.line = options.line\n }\n}\n","/**\n * EventSource/Server-Sent Events parser\n * @see https://html.spec.whatwg.org/multipage/server-sent-events.html\n */\nimport {ParseError} from './errors.ts'\nimport type {EventSourceParser, ParserCallbacks} from './types.ts'\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nfunction noop(_arg: unknown) {\n // intentional noop\n}\n\n/**\n * Creates a new EventSource parser.\n *\n * @param callbacks - Callbacks to invoke on different parsing events:\n * - `onEvent` when a new event is parsed\n * - `onError` when an error occurs\n * - `onRetry` when a new reconnection interval has been sent from the server\n * - `onComment` when a comment is encountered in the stream\n *\n * @returns A new EventSource parser, with `parse` and `reset` methods.\n * @public\n */\nexport function createParser(callbacks: ParserCallbacks): EventSourceParser {\n if (typeof callbacks === 'function') {\n throw new TypeError(\n '`callbacks` must be an object, got a function instead. Did you mean `{onEvent: fn}`?',\n )\n }\n\n const {onEvent = noop, onError = noop, onRetry = noop, onComment} = callbacks\n\n let incompleteLine = ''\n\n let isFirstChunk = true\n let id: string | undefined\n let data = ''\n let eventType = ''\n\n function feed(newChunk: string) {\n // Strip any UTF8 byte order mark (BOM) at the start of the stream\n const chunk = isFirstChunk ? newChunk.replace(/^\\xEF\\xBB\\xBF/, '') : newChunk\n\n // If there was a previous incomplete line, append it to the new chunk,\n // so we may process it together as a new (hopefully complete) chunk.\n const [complete, incomplete] = splitLines(`${incompleteLine}${chunk}`)\n\n for (const line of complete) {\n parseLine(line)\n }\n\n incompleteLine = incomplete\n isFirstChunk = false\n }\n\n function parseLine(line: string) {\n // If the line is empty (a blank line), dispatch the event\n if (line === '') {\n dispatchEvent()\n return\n }\n\n // If the line starts with a U+003A COLON character (:), ignore the line.\n if (line.startsWith(':')) {\n if (onComment) {\n onComment(line.slice(line.startsWith(': ') ? 2 : 1))\n }\n return\n }\n\n // If the line contains a U+003A COLON character (:)\n const fieldSeparatorIndex = line.indexOf(':')\n if (fieldSeparatorIndex !== -1) {\n // Collect the characters on the line before the first U+003A COLON character (:),\n // and let `field` be that string.\n const field = line.slice(0, fieldSeparatorIndex)\n\n // Collect the characters on the line after the first U+003A COLON character (:),\n // and let `value` be that string. If value starts with a U+0020 SPACE character,\n // remove it from value.\n const offset = line[fieldSeparatorIndex + 1] === ' ' ? 2 : 1\n const value = line.slice(fieldSeparatorIndex + offset)\n\n processField(field, value, line)\n return\n }\n\n // Otherwise, the string is not empty but does not contain a U+003A COLON character (:)\n // Process the field using the whole line as the field name, and an empty string as the field value.\n // 👆 This is according to spec. That means that a line that has the value `data` will result in\n // a newline being added to the current `data` buffer, for instance.\n processField(line, '', line)\n }\n\n function processField(field: string, value: string, line: string) {\n // Field names must be compared literally, with no case folding performed.\n switch (field) {\n case 'event':\n // Set the `event type` buffer to field value\n eventType = value\n break\n case 'data':\n // Append the field value to the `data` buffer, then append a single U+000A LINE FEED(LF)\n // character to the `data` buffer.\n data = `${data}${value}\\n`\n Line truncated
+28
View File
@@ -0,0 +1,28 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: !0 });
var index = require("./index.cjs");
class EventSourceParserStream extends TransformStream {
constructor({ onError, onRetry, onComment } = {}) {
let parser;
super({
start(controller) {
parser = index.createParser({
onEvent: (event) => {
controller.enqueue(event);
},
onError(error) {
onError === "terminate" ? controller.error(error) : typeof onError == "function" && onError(error);
},
onRetry,
onComment
});
},
transform(chunk) {
parser.feed(chunk);
}
});
}
}
exports.ParseError = index.ParseError;
exports.EventSourceParserStream = EventSourceParserStream;
//# sourceMappingURL=stream.cjs.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"stream.cjs","sources":["../src/stream.ts"],"sourcesContent":["import {createParser} from './parse.ts'\nimport type {EventSourceMessage, EventSourceParser} from './types.ts'\n\n/**\n * Options for the EventSourceParserStream.\n *\n * @public\n */\nexport interface StreamOptions {\n /**\n * Behavior when a parsing error occurs.\n *\n * - A custom function can be provided to handle the error.\n * - `'terminate'` will error the stream and stop parsing.\n * - Any other value will ignore the error and continue parsing.\n *\n * @defaultValue `undefined`\n */\n onError?: ('terminate' | ((error: Error) => void)) | undefined\n\n /**\n * Callback for when a reconnection interval is sent from the server.\n *\n * @param retry - The number of milliseconds to wait before reconnecting.\n */\n onRetry?: ((retry: number) => void) | undefined\n\n /**\n * Callback for when a comment is encountered in the stream.\n *\n * @param comment - The comment encountered in the stream.\n */\n onComment?: ((comment: string) => void) | undefined\n}\n\n/**\n * A TransformStream that ingests a stream of strings and produces a stream of `EventSourceMessage`.\n *\n * @example Basic usage\n * ```\n * const eventStream =\n * response.body\n * .pipeThrough(new TextDecoderStream())\n * .pipeThrough(new EventSourceParserStream())\n * ```\n *\n * @example Terminate stream on parsing errors\n * ```\n * const eventStream =\n * response.body\n * .pipeThrough(new TextDecoderStream())\n * .pipeThrough(new EventSourceParserStream({terminateOnError: true}))\n * ```\n *\n * @public\n */\nexport class EventSourceParserStream extends TransformStream<string, EventSourceMessage> {\n constructor({onError, onRetry, onComment}: StreamOptions = {}) {\n let parser!: EventSourceParser\n\n super({\n start(controller) {\n parser = createParser({\n onEvent: (event) => {\n controller.enqueue(event)\n },\n onError(error) {\n if (onError === 'terminate') {\n controller.error(error)\n } else if (typeof onError === 'function') {\n onError(error)\n }\n\n // Ignore by default\n },\n onRetry,\n onComment,\n })\n },\n transform(chunk) {\n parser.feed(chunk)\n },\n })\n }\n}\n\nexport {type ErrorType, ParseError} from './errors.ts'\nexport type {EventSourceMessage} from './types.ts'\n"],"names":["createParser"],"mappings":";;;AAwDO,MAAM,gCAAgC,gBAA4C;AAAA,EACvF,YAAY,EAAC,SAAS,SAAS,UAAA,IAA4B,CAAA,GAAI;AAC7D,QAAI;AAEJ,UAAM;AAAA,MACJ,MAAM,YAAY;AAChB,iBAASA,MAAAA,aAAa;AAAA,UACpB,SAAS,CAAC,UAAU;AAClB,uBAAW,QAAQ,KAAK;AAAA,UAC1B;AAAA,UACA,QAAQ,OAAO;AACT,wBAAY,cACd,WAAW,MAAM,KAAK,IACb,OAAO,WAAY,cAC5B,QAAQ,KAAK;AAAA,UAIjB;AAAA,UACA;AAAA,UACA;AAAA,QAAA,CACD;AAAA,MACH;AAAA,MACA,UAAU,OAAO;AACf,eAAO,KAAK,KAAK;AAAA,MACnB;AAAA,IAAA,CACD;AAAA,EACH;AACF;;;"}
+118
View File
@@ -0,0 +1,118 @@
/**
* The type of error that occurred.
* @public
*/
export declare type ErrorType = 'invalid-retry' | 'unknown-field'
/**
* A parsed EventSource message event
*
* @public
*/
export declare interface EventSourceMessage {
/**
* The event type sent from the server. Note that this differs from the browser `EventSource`
* implementation in that browsers will default this to `message`, whereas this parser will
* leave this as `undefined` if not explicitly declared.
*/
event?: string | undefined
/**
* ID of the message, if any was provided by the server. Can be used by clients to keep the
* last received message ID in sync when reconnecting.
*/
id?: string | undefined
/**
* The data received for this message
*/
data: string
}
/**
* A TransformStream that ingests a stream of strings and produces a stream of `EventSourceMessage`.
*
* @example Basic usage
* ```
* const eventStream =
* response.body
* .pipeThrough(new TextDecoderStream())
* .pipeThrough(new EventSourceParserStream())
* ```
*
* @example Terminate stream on parsing errors
* ```
* const eventStream =
* response.body
* .pipeThrough(new TextDecoderStream())
* .pipeThrough(new EventSourceParserStream({terminateOnError: true}))
* ```
*
* @public
*/
export declare class EventSourceParserStream extends TransformStream<string, EventSourceMessage> {
constructor({onError, onRetry, onComment}?: StreamOptions)
}
/**
* Error thrown when encountering an issue during parsing.
*
* @public
*/
export declare class ParseError extends Error {
/**
* The type of error that occurred.
*/
type: ErrorType
/**
* In the case of an unknown field encountered in the stream, this will be the field name.
*/
field?: string | undefined
/**
* In the case of an unknown field encountered in the stream, this will be the value of the field.
*/
value?: string | undefined
/**
* The line that caused the error, if available.
*/
line?: string | undefined
constructor(
message: string,
options: {
type: ErrorType
field?: string
value?: string
line?: string
},
)
}
/**
* Options for the EventSourceParserStream.
*
* @public
*/
export declare interface StreamOptions {
/**
* Behavior when a parsing error occurs.
*
* - A custom function can be provided to handle the error.
* - `'terminate'` will error the stream and stop parsing.
* - Any other value will ignore the error and continue parsing.
*
* @defaultValue `undefined`
*/
onError?: ('terminate' | ((error: Error) => void)) | undefined
/**
* Callback for when a reconnection interval is sent from the server.
*
* @param retry - The number of milliseconds to wait before reconnecting.
*/
onRetry?: ((retry: number) => void) | undefined
/**
* Callback for when a comment is encountered in the stream.
*
* @param comment - The comment encountered in the stream.
*/
onComment?: ((comment: string) => void) | undefined
}
export {}
+118
View File
@@ -0,0 +1,118 @@
/**
* The type of error that occurred.
* @public
*/
export declare type ErrorType = 'invalid-retry' | 'unknown-field'
/**
* A parsed EventSource message event
*
* @public
*/
export declare interface EventSourceMessage {
/**
* The event type sent from the server. Note that this differs from the browser `EventSource`
* implementation in that browsers will default this to `message`, whereas this parser will
* leave this as `undefined` if not explicitly declared.
*/
event?: string | undefined
/**
* ID of the message, if any was provided by the server. Can be used by clients to keep the
* last received message ID in sync when reconnecting.
*/
id?: string | undefined
/**
* The data received for this message
*/
data: string
}
/**
* A TransformStream that ingests a stream of strings and produces a stream of `EventSourceMessage`.
*
* @example Basic usage
* ```
* const eventStream =
* response.body
* .pipeThrough(new TextDecoderStream())
* .pipeThrough(new EventSourceParserStream())
* ```
*
* @example Terminate stream on parsing errors
* ```
* const eventStream =
* response.body
* .pipeThrough(new TextDecoderStream())
* .pipeThrough(new EventSourceParserStream({terminateOnError: true}))
* ```
*
* @public
*/
export declare class EventSourceParserStream extends TransformStream<string, EventSourceMessage> {
constructor({onError, onRetry, onComment}?: StreamOptions)
}
/**
* Error thrown when encountering an issue during parsing.
*
* @public
*/
export declare class ParseError extends Error {
/**
* The type of error that occurred.
*/
type: ErrorType
/**
* In the case of an unknown field encountered in the stream, this will be the field name.
*/
field?: string | undefined
/**
* In the case of an unknown field encountered in the stream, this will be the value of the field.
*/
value?: string | undefined
/**
* The line that caused the error, if available.
*/
line?: string | undefined
constructor(
message: string,
options: {
type: ErrorType
field?: string
value?: string
line?: string
},
)
}
/**
* Options for the EventSourceParserStream.
*
* @public
*/
export declare interface StreamOptions {
/**
* Behavior when a parsing error occurs.
*
* - A custom function can be provided to handle the error.
* - `'terminate'` will error the stream and stop parsing.
* - Any other value will ignore the error and continue parsing.
*
* @defaultValue `undefined`
*/
onError?: ('terminate' | ((error: Error) => void)) | undefined
/**
* Callback for when a reconnection interval is sent from the server.
*
* @param retry - The number of milliseconds to wait before reconnecting.
*/
onRetry?: ((retry: number) => void) | undefined
/**
* Callback for when a comment is encountered in the stream.
*
* @param comment - The comment encountered in the stream.
*/
onComment?: ((comment: string) => void) | undefined
}
export {}
+29
View File
@@ -0,0 +1,29 @@
import { createParser } from "./index.js";
import { ParseError } from "./index.js";
class EventSourceParserStream extends TransformStream {
constructor({ onError, onRetry, onComment } = {}) {
let parser;
super({
start(controller) {
parser = createParser({
onEvent: (event) => {
controller.enqueue(event);
},
onError(error) {
onError === "terminate" ? controller.error(error) : typeof onError == "function" && onError(error);
},
onRetry,
onComment
});
},
transform(chunk) {
parser.feed(chunk);
}
});
}
}
export {
EventSourceParserStream,
ParseError
};
//# sourceMappingURL=stream.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"stream.js","sources":["../src/stream.ts"],"sourcesContent":["import {createParser} from './parse.ts'\nimport type {EventSourceMessage, EventSourceParser} from './types.ts'\n\n/**\n * Options for the EventSourceParserStream.\n *\n * @public\n */\nexport interface StreamOptions {\n /**\n * Behavior when a parsing error occurs.\n *\n * - A custom function can be provided to handle the error.\n * - `'terminate'` will error the stream and stop parsing.\n * - Any other value will ignore the error and continue parsing.\n *\n * @defaultValue `undefined`\n */\n onError?: ('terminate' | ((error: Error) => void)) | undefined\n\n /**\n * Callback for when a reconnection interval is sent from the server.\n *\n * @param retry - The number of milliseconds to wait before reconnecting.\n */\n onRetry?: ((retry: number) => void) | undefined\n\n /**\n * Callback for when a comment is encountered in the stream.\n *\n * @param comment - The comment encountered in the stream.\n */\n onComment?: ((comment: string) => void) | undefined\n}\n\n/**\n * A TransformStream that ingests a stream of strings and produces a stream of `EventSourceMessage`.\n *\n * @example Basic usage\n * ```\n * const eventStream =\n * response.body\n * .pipeThrough(new TextDecoderStream())\n * .pipeThrough(new EventSourceParserStream())\n * ```\n *\n * @example Terminate stream on parsing errors\n * ```\n * const eventStream =\n * response.body\n * .pipeThrough(new TextDecoderStream())\n * .pipeThrough(new EventSourceParserStream({terminateOnError: true}))\n * ```\n *\n * @public\n */\nexport class EventSourceParserStream extends TransformStream<string, EventSourceMessage> {\n constructor({onError, onRetry, onComment}: StreamOptions = {}) {\n let parser!: EventSourceParser\n\n super({\n start(controller) {\n parser = createParser({\n onEvent: (event) => {\n controller.enqueue(event)\n },\n onError(error) {\n if (onError === 'terminate') {\n controller.error(error)\n } else if (typeof onError === 'function') {\n onError(error)\n }\n\n // Ignore by default\n },\n onRetry,\n onComment,\n })\n },\n transform(chunk) {\n parser.feed(chunk)\n },\n })\n }\n}\n\nexport {type ErrorType, ParseError} from './errors.ts'\nexport type {EventSourceMessage} from './types.ts'\n"],"names":[],"mappings":";;AAwDO,MAAM,gCAAgC,gBAA4C;AAAA,EACvF,YAAY,EAAC,SAAS,SAAS,UAAA,IAA4B,CAAA,GAAI;AAC7D,QAAI;AAEJ,UAAM;AAAA,MACJ,MAAM,YAAY;AAChB,iBAAS,aAAa;AAAA,UACpB,SAAS,CAAC,UAAU;AAClB,uBAAW,QAAQ,KAAK;AAAA,UAC1B;AAAA,UACA,QAAQ,OAAO;AACT,wBAAY,cACd,WAAW,MAAM,KAAK,IACb,OAAO,WAAY,cAC5B,QAAQ,KAAK;AAAA,UAIjB;AAAA,UACA;AAAA,UACA;AAAA,QAAA,CACD;AAAA,MACH;AAAA,MACA,UAAU,OAAO;AACf,eAAO,KAAK,KAAK;AAAA,MACnB;AAAA,IAAA,CACD;AAAA,EACH;AACF;"}