init project

This commit is contained in:
root
2026-07-31 13:12:54 -04:00
parent 0da92d5e02
commit f3863f760c
7215 changed files with 1860260 additions and 1 deletions
+177
View File
@@ -0,0 +1,177 @@
"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;
}
}
const LF = 10, CR = 13, SPACE = 32;
function noop(_arg) {
}
function createParser(config) {
if (typeof config == "function")
throw new TypeError(
"`config` must be an object, got a function instead. Did you mean `createParser({onEvent: fn})`?"
);
const { onEvent = noop, onError = noop, onRetry = noop, onComment, maxBufferSize } = config, pendingFragments = [];
let pendingFragmentsLength = 0, isFirstChunk = !0, id, data = "", dataLines = 0, eventType, terminated = !1;
function feed(chunk) {
if (terminated)
throw new Error(
"Cannot feed parser: it was terminated after exceeding the configured max buffer size. Call `reset()` to resume parsing."
);
if (isFirstChunk && (isFirstChunk = !1, chunk.charCodeAt(0) === 239 && chunk.charCodeAt(1) === 187 && chunk.charCodeAt(2) === 191 && (chunk = chunk.slice(3))), pendingFragments.length === 0) {
const trailing2 = processLines(chunk);
trailing2 !== "" && (pendingFragments.push(trailing2), pendingFragmentsLength = trailing2.length), checkBufferSize();
return;
}
if (chunk.indexOf(`
`) === -1 && chunk.indexOf("\r") === -1) {
pendingFragments.push(chunk), pendingFragmentsLength += chunk.length, checkBufferSize();
return;
}
pendingFragments.push(chunk);
const input = pendingFragments.join("");
pendingFragments.length = 0, pendingFragmentsLength = 0;
const trailing = processLines(input);
trailing !== "" && (pendingFragments.push(trailing), pendingFragmentsLength = trailing.length), checkBufferSize();
}
function checkBufferSize() {
maxBufferSize !== void 0 && (pendingFragmentsLength + data.length <= maxBufferSize || (terminated = !0, pendingFragments.length = 0, pendingFragmentsLength = 0, id = void 0, data = "", dataLines = 0, eventType = void 0, onError(
new ParseError(`Buffered data exceeded max buffer size of ${maxBufferSize} characters`, {
type: "max-buffer-size-exceeded"
})
)));
}
function processLines(chunk) {
let searchIndex = 0;
if (chunk.indexOf("\r") === -1) {
let lfIndex = chunk.indexOf(`
`, searchIndex);
for (; lfIndex !== -1; ) {
if (searchIndex === lfIndex) {
dataLines > 0 && onEvent({ id, event: eventType, data }), id = void 0, data = "", dataLines = 0, eventType = void 0, searchIndex = lfIndex + 1, lfIndex = chunk.indexOf(`
`, searchIndex);
continue;
}
const firstCharCode = chunk.charCodeAt(searchIndex);
if (isDataPrefix(chunk, searchIndex, firstCharCode)) {
const valueStart = chunk.charCodeAt(searchIndex + 5) === SPACE ? searchIndex + 6 : searchIndex + 5, value = chunk.slice(valueStart, lfIndex);
if (dataLines === 0 && chunk.charCodeAt(lfIndex + 1) === LF) {
onEvent({ id, event: eventType, data: value }), id = void 0, data = "", eventType = void 0, searchIndex = lfIndex + 2, lfIndex = chunk.indexOf(`
`, searchIndex);
continue;
}
data = dataLines === 0 ? value : `${data}
${value}`, dataLines++;
} else isEventPrefix(chunk, searchIndex, firstCharCode) ? eventType = chunk.slice(
chunk.charCodeAt(searchIndex + 6) === SPACE ? searchIndex + 7 : searchIndex + 6,
lfIndex
) || void 0 : parseLine(chunk, searchIndex, lfIndex);
searchIndex = lfIndex + 1, lfIndex = chunk.indexOf(`
`, searchIndex);
}
return chunk.slice(searchIndex);
}
for (; searchIndex < chunk.length; ) {
const crIndex = chunk.indexOf("\r", searchIndex), lfIndex = chunk.indexOf(`
`, searchIndex);
let lineEnd = -1;
if (crIndex !== -1 && lfIndex !== -1 ? lineEnd = crIndex < lfIndex ? crIndex : lfIndex : crIndex !== -1 ? crIndex === chunk.length - 1 ? lineEnd = -1 : lineEnd = crIndex : lfIndex !== -1 && (lineEnd = lfIndex), lineEnd === -1)
break;
parseLine(chunk, searchIndex, lineEnd), searchIndex = lineEnd + 1, chunk.charCodeAt(searchIndex - 1) === CR && chunk.charCodeAt(searchIndex) === LF && searchIndex++;
}
return chunk.slice(searchIndex);
}
function parseLine(chunk, start, end) {
if (start === end) {
dispatchEvent();
return;
}
const firstCharCode = chunk.charCodeAt(start);
if (isDataPrefix(chunk, start, firstCharCode)) {
const valueStart = chunk.charCodeAt(start + 5) === SPACE ? start + 6 : start + 5, value2 = chunk.slice(valueStart, end);
data = dataLines === 0 ? value2 : `${data}
${value2}`, dataLines++;
return;
}
if (isEventPrefix(chunk, start, firstCharCode)) {
eventType = chunk.slice(chunk.charCodeAt(start + 6) === SPACE ? start + 7 : start + 6, end) || void 0;
return;
}
if (firstCharCode === 105 && chunk.charCodeAt(start + 1) === 100 && chunk.charCodeAt(start + 2) === 58) {
const value2 = chunk.slice(chunk.charCodeAt(start + 3) === SPACE ? start + 4 : start + 3, end);
id = value2.includes("\0") ? void 0 : value2;
return;
}
if (firstCharCode === 58) {
if (onComment) {
const line2 = chunk.slice(start, end);
onComment(line2.slice(chunk.charCodeAt(start + 1) === SPACE ? 2 : 1));
}
return;
}
const line = chunk.slice(start, end), fieldSeparatorIndex = line.indexOf(":");
if (fieldSeparatorIndex === -1) {
processField(line, "", line);
return;
}
const field = line.slice(0, fieldSeparatorIndex), offset = line.charCodeAt(fieldSeparatorIndex + 1) === SPACE ? 2 : 1, value = line.slice(fieldSeparatorIndex + offset);
processField(field, value, line);
}
function processField(field, value, line) {
switch (field) {
case "event":
eventType = value || void 0;
break;
case "data":
data = dataLines === 0 ? value : `${data}
${value}`, dataLines++;
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() {
dataLines > 0 && onEvent({
id,
event: eventType,
data
}), id = void 0, data = "", dataLines = 0, eventType = void 0;
}
function reset(options = {}) {
if (options.consume && pendingFragments.length > 0) {
const incompleteLine = pendingFragments.join("");
parseLine(incompleteLine, 0, incompleteLine.length);
}
isFirstChunk = !0, id = void 0, data = "", dataLines = 0, eventType = void 0, pendingFragments.length = 0, pendingFragmentsLength = 0, terminated = !1;
}
return { feed, reset };
}
function isDataPrefix(chunk, i, firstCharCode) {
return firstCharCode === 100 && chunk.charCodeAt(i + 1) === 97 && chunk.charCodeAt(i + 2) === 116 && chunk.charCodeAt(i + 3) === 97 && chunk.charCodeAt(i + 4) === 58;
}
function isEventPrefix(chunk, i, firstCharCode) {
return firstCharCode === 101 && chunk.charCodeAt(i + 1) === 118 && chunk.charCodeAt(i + 2) === 101 && chunk.charCodeAt(i + 3) === 110 && chunk.charCodeAt(i + 4) === 116 && chunk.charCodeAt(i + 5) === 58;
}
exports.ParseError = ParseError;
exports.createParser = createParser;
//# sourceMappingURL=index.cjs.map
File diff suppressed because one or more lines are too long
+169
View File
@@ -0,0 +1,169 @@
/**
* Creates a new EventSource parser.
*
* @param config - Parser configuration. Accepts callbacks (see {@link ParserCallbacks})
* and options like `maxBufferSize` (see {@link ParserConfig}).
*
* @returns A new EventSource parser, with `feed` and `reset` methods.
* @public
*/
export declare function createParser(config: ParserConfig): EventSourceParser;
/**
* The type of error that occurred.
* @public
*/
export declare type ErrorType =
| "invalid-retry"
| "unknown-field"
| "max-buffer-size-exceeded";
/**
* 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;
}
/**
* Configuration accepted by {@link createParser}. Extends {@link ParserCallbacks} with
* additional options that control parser behavior.
*
* @public
*/
export declare interface ParserConfig extends ParserCallbacks {
/**
* Maximum number of characters the parser is allowed to buffer across calls to `feed()`.
*
* Two unbounded surfaces exist in a streaming SSE parser:
* - A partial line that has not yet been terminated by `\n`, `\r`, or `\r\n`.
* - A multi-line event whose terminating blank line has not yet arrived (each `data:`
* field gets appended to the buffered event).
*
* When the combined size of these buffers exceeds `maxBufferSize`, the parser emits a
* `ParseError` with `type: 'max-buffer-size-exceeded'` to `onError` and becomes
* terminated — subsequent calls to `feed()` will throw until `reset()` is called.
* This protects against unbounded memory growth from malformed or malicious streams.
*
* @defaultValue `undefined` (unbounded)
*/
maxBufferSize?: number | undefined;
}
export {};
+169
View File
@@ -0,0 +1,169 @@
/**
* Creates a new EventSource parser.
*
* @param config - Parser configuration. Accepts callbacks (see {@link ParserCallbacks})
* and options like `maxBufferSize` (see {@link ParserConfig}).
*
* @returns A new EventSource parser, with `feed` and `reset` methods.
* @public
*/
export declare function createParser(config: ParserConfig): EventSourceParser;
/**
* The type of error that occurred.
* @public
*/
export declare type ErrorType =
| "invalid-retry"
| "unknown-field"
| "max-buffer-size-exceeded";
/**
* 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;
}
/**
* Configuration accepted by {@link createParser}. Extends {@link ParserCallbacks} with
* additional options that control parser behavior.
*
* @public
*/
export declare interface ParserConfig extends ParserCallbacks {
/**
* Maximum number of characters the parser is allowed to buffer across calls to `feed()`.
*
* Two unbounded surfaces exist in a streaming SSE parser:
* - A partial line that has not yet been terminated by `\n`, `\r`, or `\r\n`.
* - A multi-line event whose terminating blank line has not yet arrived (each `data:`
* field gets appended to the buffered event).
*
* When the combined size of these buffers exceeds `maxBufferSize`, the parser emits a
* `ParseError` with `type: 'max-buffer-size-exceeded'` to `onError` and becomes
* terminated — subsequent calls to `feed()` will throw until `reset()` is called.
* This protects against unbounded memory growth from malformed or malicious streams.
*
* @defaultValue `undefined` (unbounded)
*/
maxBufferSize?: number | undefined;
}
export {};
+177
View File
@@ -0,0 +1,177 @@
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;
}
}
const LF = 10, CR = 13, SPACE = 32;
function noop(_arg) {
}
function createParser(config) {
if (typeof config == "function")
throw new TypeError(
"`config` must be an object, got a function instead. Did you mean `createParser({onEvent: fn})`?"
);
const { onEvent = noop, onError = noop, onRetry = noop, onComment, maxBufferSize } = config, pendingFragments = [];
let pendingFragmentsLength = 0, isFirstChunk = !0, id, data = "", dataLines = 0, eventType, terminated = !1;
function feed(chunk) {
if (terminated)
throw new Error(
"Cannot feed parser: it was terminated after exceeding the configured max buffer size. Call `reset()` to resume parsing."
);
if (isFirstChunk && (isFirstChunk = !1, chunk.charCodeAt(0) === 239 && chunk.charCodeAt(1) === 187 && chunk.charCodeAt(2) === 191 && (chunk = chunk.slice(3))), pendingFragments.length === 0) {
const trailing2 = processLines(chunk);
trailing2 !== "" && (pendingFragments.push(trailing2), pendingFragmentsLength = trailing2.length), checkBufferSize();
return;
}
if (chunk.indexOf(`
`) === -1 && chunk.indexOf("\r") === -1) {
pendingFragments.push(chunk), pendingFragmentsLength += chunk.length, checkBufferSize();
return;
}
pendingFragments.push(chunk);
const input = pendingFragments.join("");
pendingFragments.length = 0, pendingFragmentsLength = 0;
const trailing = processLines(input);
trailing !== "" && (pendingFragments.push(trailing), pendingFragmentsLength = trailing.length), checkBufferSize();
}
function checkBufferSize() {
maxBufferSize !== void 0 && (pendingFragmentsLength + data.length <= maxBufferSize || (terminated = !0, pendingFragments.length = 0, pendingFragmentsLength = 0, id = void 0, data = "", dataLines = 0, eventType = void 0, onError(
new ParseError(`Buffered data exceeded max buffer size of ${maxBufferSize} characters`, {
type: "max-buffer-size-exceeded"
})
)));
}
function processLines(chunk) {
let searchIndex = 0;
if (chunk.indexOf("\r") === -1) {
let lfIndex = chunk.indexOf(`
`, searchIndex);
for (; lfIndex !== -1; ) {
if (searchIndex === lfIndex) {
dataLines > 0 && onEvent({ id, event: eventType, data }), id = void 0, data = "", dataLines = 0, eventType = void 0, searchIndex = lfIndex + 1, lfIndex = chunk.indexOf(`
`, searchIndex);
continue;
}
const firstCharCode = chunk.charCodeAt(searchIndex);
if (isDataPrefix(chunk, searchIndex, firstCharCode)) {
const valueStart = chunk.charCodeAt(searchIndex + 5) === SPACE ? searchIndex + 6 : searchIndex + 5, value = chunk.slice(valueStart, lfIndex);
if (dataLines === 0 && chunk.charCodeAt(lfIndex + 1) === LF) {
onEvent({ id, event: eventType, data: value }), id = void 0, data = "", eventType = void 0, searchIndex = lfIndex + 2, lfIndex = chunk.indexOf(`
`, searchIndex);
continue;
}
data = dataLines === 0 ? value : `${data}
${value}`, dataLines++;
} else isEventPrefix(chunk, searchIndex, firstCharCode) ? eventType = chunk.slice(
chunk.charCodeAt(searchIndex + 6) === SPACE ? searchIndex + 7 : searchIndex + 6,
lfIndex
) || void 0 : parseLine(chunk, searchIndex, lfIndex);
searchIndex = lfIndex + 1, lfIndex = chunk.indexOf(`
`, searchIndex);
}
return chunk.slice(searchIndex);
}
for (; searchIndex < chunk.length; ) {
const crIndex = chunk.indexOf("\r", searchIndex), lfIndex = chunk.indexOf(`
`, searchIndex);
let lineEnd = -1;
if (crIndex !== -1 && lfIndex !== -1 ? lineEnd = crIndex < lfIndex ? crIndex : lfIndex : crIndex !== -1 ? crIndex === chunk.length - 1 ? lineEnd = -1 : lineEnd = crIndex : lfIndex !== -1 && (lineEnd = lfIndex), lineEnd === -1)
break;
parseLine(chunk, searchIndex, lineEnd), searchIndex = lineEnd + 1, chunk.charCodeAt(searchIndex - 1) === CR && chunk.charCodeAt(searchIndex) === LF && searchIndex++;
}
return chunk.slice(searchIndex);
}
function parseLine(chunk, start, end) {
if (start === end) {
dispatchEvent();
return;
}
const firstCharCode = chunk.charCodeAt(start);
if (isDataPrefix(chunk, start, firstCharCode)) {
const valueStart = chunk.charCodeAt(start + 5) === SPACE ? start + 6 : start + 5, value2 = chunk.slice(valueStart, end);
data = dataLines === 0 ? value2 : `${data}
${value2}`, dataLines++;
return;
}
if (isEventPrefix(chunk, start, firstCharCode)) {
eventType = chunk.slice(chunk.charCodeAt(start + 6) === SPACE ? start + 7 : start + 6, end) || void 0;
return;
}
if (firstCharCode === 105 && chunk.charCodeAt(start + 1) === 100 && chunk.charCodeAt(start + 2) === 58) {
const value2 = chunk.slice(chunk.charCodeAt(start + 3) === SPACE ? start + 4 : start + 3, end);
id = value2.includes("\0") ? void 0 : value2;
return;
}
if (firstCharCode === 58) {
if (onComment) {
const line2 = chunk.slice(start, end);
onComment(line2.slice(chunk.charCodeAt(start + 1) === SPACE ? 2 : 1));
}
return;
}
const line = chunk.slice(start, end), fieldSeparatorIndex = line.indexOf(":");
if (fieldSeparatorIndex === -1) {
processField(line, "", line);
return;
}
const field = line.slice(0, fieldSeparatorIndex), offset = line.charCodeAt(fieldSeparatorIndex + 1) === SPACE ? 2 : 1, value = line.slice(fieldSeparatorIndex + offset);
processField(field, value, line);
}
function processField(field, value, line) {
switch (field) {
case "event":
eventType = value || void 0;
break;
case "data":
data = dataLines === 0 ? value : `${data}
${value}`, dataLines++;
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() {
dataLines > 0 && onEvent({
id,
event: eventType,
data
}), id = void 0, data = "", dataLines = 0, eventType = void 0;
}
function reset(options = {}) {
if (options.consume && pendingFragments.length > 0) {
const incompleteLine = pendingFragments.join("");
parseLine(incompleteLine, 0, incompleteLine.length);
}
isFirstChunk = !0, id = void 0, data = "", dataLines = 0, eventType = void 0, pendingFragments.length = 0, pendingFragmentsLength = 0, terminated = !1;
}
return { feed, reset };
}
function isDataPrefix(chunk, i, firstCharCode) {
return firstCharCode === 100 && chunk.charCodeAt(i + 1) === 97 && chunk.charCodeAt(i + 2) === 116 && chunk.charCodeAt(i + 3) === 97 && chunk.charCodeAt(i + 4) === 58;
}
function isEventPrefix(chunk, i, firstCharCode) {
return firstCharCode === 101 && chunk.charCodeAt(i + 1) === 118 && chunk.charCodeAt(i + 2) === 101 && chunk.charCodeAt(i + 3) === 110 && chunk.charCodeAt(i + 4) === 116 && chunk.charCodeAt(i + 5) === 58;
}
export {
ParseError,
createParser
};
//# sourceMappingURL=index.js.map
File diff suppressed because one or more lines are too long
+29
View File
@@ -0,0 +1,29 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: !0 });
var index = require("./index.cjs");
class EventSourceParserStream extends TransformStream {
constructor({ onError, onRetry, onComment, maxBufferSize } = {}) {
let parser;
super({
start(controller) {
parser = index.createParser({
onEvent: (event) => {
controller.enqueue(event);
},
onError(error) {
typeof onError == "function" && onError(error), (onError === "terminate" || error.type === "max-buffer-size-exceeded") && controller.error(error);
},
onRetry,
onComment,
maxBufferSize
});
},
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 * Maximum number of characters the parser is allowed to buffer across calls to `feed()`.\n * See {@link ParserConfig.maxBufferSize} for details.\n *\n * When the limit is exceeded, the stream is always errored (regardless of the `onError`\n * setting) since the underlying parser is unrecoverable without a `reset()`.\n *\n * @defaultValue `undefined` (unbounded)\n */\n maxBufferSize?: number | 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({onError: 'terminate'}))\n * ```\n *\n * @public\n */\nexport class EventSourceParserStream extends TransformStream<string, EventSourceMessage> {\n constructor({onError, onRetry, onComment, maxBufferSize}: 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 (typeof onError === 'function') {\n onError(error)\n }\n\n // `max-buffer-size-exceeded` is fatal — the parser is unusable until\n // `reset()`, which the stream wrapper has no way to call. Always\n // terminate the stream in that case so consumers see the meaningful\n // `ParseError` instead of an opaque \"cannot feed terminated parser\"\n // throw from the next chunk.\n if (onError === 'terminate' || error.type === 'max-buffer-size-exceeded') {\n controller.error(error)\n }\n\n // Ignore by default\n },\n onRetry,\n onComment,\n maxBufferSize,\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":";;;AAmEO,MAAM,gCAAgC,gBAA4C;AAAA,EACvF,YAAY,EAAC,SAAS,SAAS,WAAW,cAAA,IAAgC,IAAI;AAC5E,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,mBAAO,WAAY,cACrB,QAAQ,KAAK,IAQX,YAAY,eAAe,MAAM,SAAS,+BAC5C,WAAW,MAAM,KAAK;AAAA,UAI1B;AAAA,UACA;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;;;"}
+134
View File
@@ -0,0 +1,134 @@
/**
* The type of error that occurred.
* @public
*/
export declare type ErrorType =
| "invalid-retry"
| "unknown-field"
| "max-buffer-size-exceeded";
/**
* 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({onError: 'terminate'}))
* ```
*
* @public
*/
export declare class EventSourceParserStream extends TransformStream<
string,
EventSourceMessage
> {
constructor({ onError, onRetry, onComment, maxBufferSize }?: 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;
/**
* Maximum number of characters the parser is allowed to buffer across calls to `feed()`.
* See {@link ParserConfig.maxBufferSize} for details.
*
* When the limit is exceeded, the stream is always errored (regardless of the `onError`
* setting) since the underlying parser is unrecoverable without a `reset()`.
*
* @defaultValue `undefined` (unbounded)
*/
maxBufferSize?: number | undefined;
}
export {};
+134
View File
@@ -0,0 +1,134 @@
/**
* The type of error that occurred.
* @public
*/
export declare type ErrorType =
| "invalid-retry"
| "unknown-field"
| "max-buffer-size-exceeded";
/**
* 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({onError: 'terminate'}))
* ```
*
* @public
*/
export declare class EventSourceParserStream extends TransformStream<
string,
EventSourceMessage
> {
constructor({ onError, onRetry, onComment, maxBufferSize }?: 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;
/**
* Maximum number of characters the parser is allowed to buffer across calls to `feed()`.
* See {@link ParserConfig.maxBufferSize} for details.
*
* When the limit is exceeded, the stream is always errored (regardless of the `onError`
* setting) since the underlying parser is unrecoverable without a `reset()`.
*
* @defaultValue `undefined` (unbounded)
*/
maxBufferSize?: number | undefined;
}
export {};
+30
View File
@@ -0,0 +1,30 @@
import { createParser } from "./index.js";
import { ParseError } from "./index.js";
class EventSourceParserStream extends TransformStream {
constructor({ onError, onRetry, onComment, maxBufferSize } = {}) {
let parser;
super({
start(controller) {
parser = createParser({
onEvent: (event) => {
controller.enqueue(event);
},
onError(error) {
typeof onError == "function" && onError(error), (onError === "terminate" || error.type === "max-buffer-size-exceeded") && controller.error(error);
},
onRetry,
onComment,
maxBufferSize
});
},
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 * Maximum number of characters the parser is allowed to buffer across calls to `feed()`.\n * See {@link ParserConfig.maxBufferSize} for details.\n *\n * When the limit is exceeded, the stream is always errored (regardless of the `onError`\n * setting) since the underlying parser is unrecoverable without a `reset()`.\n *\n * @defaultValue `undefined` (unbounded)\n */\n maxBufferSize?: number | 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({onError: 'terminate'}))\n * ```\n *\n * @public\n */\nexport class EventSourceParserStream extends TransformStream<string, EventSourceMessage> {\n constructor({onError, onRetry, onComment, maxBufferSize}: 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 (typeof onError === 'function') {\n onError(error)\n }\n\n // `max-buffer-size-exceeded` is fatal — the parser is unusable until\n // `reset()`, which the stream wrapper has no way to call. Always\n // terminate the stream in that case so consumers see the meaningful\n // `ParseError` instead of an opaque \"cannot feed terminated parser\"\n // throw from the next chunk.\n if (onError === 'terminate' || error.type === 'max-buffer-size-exceeded') {\n controller.error(error)\n }\n\n // Ignore by default\n },\n onRetry,\n onComment,\n maxBufferSize,\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":";;AAmEO,MAAM,gCAAgC,gBAA4C;AAAA,EACvF,YAAY,EAAC,SAAS,SAAS,WAAW,cAAA,IAAgC,IAAI;AAC5E,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,mBAAO,WAAY,cACrB,QAAQ,KAAK,IAQX,YAAY,eAAe,MAAM,SAAS,+BAC5C,WAAW,MAAM,KAAK;AAAA,UAI1B;AAAA,UACA;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;"}