temp
This commit is contained in:
21
node_modules/eventsource-parser/LICENSE
generated
vendored
Normal file
21
node_modules/eventsource-parser/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Espen Hovlandsdal <espen@hovlandsdal.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
126
node_modules/eventsource-parser/README.md
generated
vendored
Normal file
126
node_modules/eventsource-parser/README.md
generated
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
# eventsource-parser
|
||||
|
||||
[](https://www.npmjs.com/package/eventsource-parser)[](https://bundlephobia.com/result?p=eventsource-parser)[](https://www.npmjs.com/package/eventsource-parser)
|
||||
|
||||
A streaming parser for [server-sent events/eventsource](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events), without any assumptions about how the actual stream of data is retrieved. It is intended to be a building block for [clients](https://github.com/rexxars/eventsource-client) and polyfills in javascript environments such as browsers, node.js and deno.
|
||||
|
||||
If you are looking for a modern client implementation, see [eventsource-client](https://github.com/rexxars/eventsource-client).
|
||||
|
||||
You create an instance of the parser, and _feed_ it chunks of data - partial or complete, and the parse emits parsed messages once it receives a complete message. A [TransformStream variant](#stream-usage) is also available for environments that support it (modern browsers, Node 18 and higher).
|
||||
|
||||
Other modules in the EventSource family:
|
||||
|
||||
- [eventsource-client](https://github.com/rexxars/eventsource-client): modern, feature rich eventsource client for browsers, node.js, bun, deno and other modern JavaScript environments.
|
||||
- [eventsource-encoder](https://github.com/rexxars/eventsource-encoder): encodes messages in the EventSource/Server-Sent Events format.
|
||||
- [eventsource](https://github.com/eventsource/eventsource): Node.js polyfill for the WhatWG EventSource API.
|
||||
|
||||
> [!NOTE]
|
||||
> Migrating from eventsource-parser 1.x/2.x? See the [migration guide](./MIGRATE-v3.md).
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install --save eventsource-parser
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```ts
|
||||
import {createParser, type EventSourceMessage} from 'eventsource-parser'
|
||||
|
||||
function onEvent(event: EventSourceMessage) {
|
||||
console.log('Received event!')
|
||||
console.log('id: %s', event.id || '<none>')
|
||||
console.log('event: %s', event.event || '<none>')
|
||||
console.log('data: %s', event.data)
|
||||
}
|
||||
|
||||
const parser = createParser({onEvent})
|
||||
const sseStream = getSomeReadableStream()
|
||||
|
||||
for await (const chunk of sseStream) {
|
||||
parser.feed(chunk)
|
||||
}
|
||||
|
||||
// If you want to re-use the parser for a new stream of events, make sure to reset it!
|
||||
parser.reset()
|
||||
console.log('Done!')
|
||||
```
|
||||
|
||||
### Retry intervals
|
||||
|
||||
If the server sends a `retry` field in the event stream, the parser will call any `onRetry` callback specified to the `createParser` function:
|
||||
|
||||
```ts
|
||||
const parser = createParser({
|
||||
onRetry(retryInterval) {
|
||||
console.log('Server requested retry interval of %dms', retryInterval)
|
||||
},
|
||||
onEvent(event) {
|
||||
// …
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### Parse errors
|
||||
|
||||
If the parser encounters an error while parsing, it will call any `onError` callback provided to the `createParser` function:
|
||||
|
||||
```ts
|
||||
import {type ParseError} from 'eventsource-parser'
|
||||
|
||||
const parser = createParser({
|
||||
onError(error: ParseError) {
|
||||
console.error('Error parsing event:', error)
|
||||
if (error.type === 'invalid-field') {
|
||||
console.error('Field name:', error.field)
|
||||
console.error('Field value:', error.value)
|
||||
console.error('Line:', error.line)
|
||||
} else if (error.type === 'invalid-retry') {
|
||||
console.error('Invalid retry interval:', error.value)
|
||||
}
|
||||
},
|
||||
onEvent(event) {
|
||||
// …
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
Note that `invalid-field` errors will usually be called for any invalid data - not only data shaped as `field: value`. This is because the EventSource specification says to treat anything prior to a `:` as the field name. Use the `error.line` property to get the full line that caused the error.
|
||||
|
||||
> [!NOTE]
|
||||
> When encountering the end of a stream, calling `.reset({consume: true})` on the parser to flush any remaining data and reset the parser state. This will trigger the `onError` callback if the pending data is not a valid event.
|
||||
|
||||
### Comments
|
||||
|
||||
The parser will ignore comments (lines starting with `:`) by default. If you want to handle comments, you can provide an `onComment` callback to the `createParser` function:
|
||||
|
||||
```ts
|
||||
const parser = createParser({
|
||||
onComment(comment) {
|
||||
console.log('Received comment:', comment)
|
||||
},
|
||||
onEvent(event) {
|
||||
// …
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> Leading whitespace is not stripped from comments, eg `: comment` will give ` comment` as the comment value, not `comment` (note the leading space).
|
||||
|
||||
## Stream usage
|
||||
|
||||
```ts
|
||||
import {EventSourceParserStream} from 'eventsource-parser/stream'
|
||||
|
||||
const eventStream = response.body
|
||||
.pipeThrough(new TextDecoderStream())
|
||||
.pipeThrough(new EventSourceParserStream())
|
||||
```
|
||||
|
||||
Note that the TransformStream is exposed under a separate export (`eventsource-parser/stream`), in order to maximize compatibility with environments that do not have the `TransformStream` constructor available.
|
||||
|
||||
## License
|
||||
|
||||
MIT © [Espen Hovlandsdal](https://espen.codes/)
|
||||
166
node_modules/eventsource-parser/dist/index.cjs
generated
vendored
Normal file
166
node_modules/eventsource-parser/dist/index.cjs
generated
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
"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(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, pendingFragments = [];
|
||||
let isFirstChunk = !0, id, data = "", dataLines = 0, eventType;
|
||||
function feed(chunk) {
|
||||
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);
|
||||
return;
|
||||
}
|
||||
if (chunk.indexOf(`
|
||||
`) === -1 && chunk.indexOf("\r") === -1) {
|
||||
pendingFragments.push(chunk);
|
||||
return;
|
||||
}
|
||||
pendingFragments.push(chunk);
|
||||
const input = pendingFragments.join("");
|
||||
pendingFragments.length = 0;
|
||||
const trailing = processLines(input);
|
||||
trailing !== "" && pendingFragments.push(trailing);
|
||||
}
|
||||
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;
|
||||
}
|
||||
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
|
||||
1
node_modules/eventsource-parser/dist/index.cjs.map
generated
vendored
Normal file
1
node_modules/eventsource-parser/dist/index.cjs.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
146
node_modules/eventsource-parser/dist/index.d.cts
generated
vendored
Normal file
146
node_modules/eventsource-parser/dist/index.d.cts
generated
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
/**
|
||||
* 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 {};
|
||||
146
node_modules/eventsource-parser/dist/index.d.ts
generated
vendored
Normal file
146
node_modules/eventsource-parser/dist/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
/**
|
||||
* 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 {};
|
||||
166
node_modules/eventsource-parser/dist/index.js
generated
vendored
Normal file
166
node_modules/eventsource-parser/dist/index.js
generated
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
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(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, pendingFragments = [];
|
||||
let isFirstChunk = !0, id, data = "", dataLines = 0, eventType;
|
||||
function feed(chunk) {
|
||||
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);
|
||||
return;
|
||||
}
|
||||
if (chunk.indexOf(`
|
||||
`) === -1 && chunk.indexOf("\r") === -1) {
|
||||
pendingFragments.push(chunk);
|
||||
return;
|
||||
}
|
||||
pendingFragments.push(chunk);
|
||||
const input = pendingFragments.join("");
|
||||
pendingFragments.length = 0;
|
||||
const trailing = processLines(input);
|
||||
trailing !== "" && pendingFragments.push(trailing);
|
||||
}
|
||||
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;
|
||||
}
|
||||
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
|
||||
1
node_modules/eventsource-parser/dist/index.js.map
generated
vendored
Normal file
1
node_modules/eventsource-parser/dist/index.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
28
node_modules/eventsource-parser/dist/stream.cjs
generated
vendored
Normal file
28
node_modules/eventsource-parser/dist/stream.cjs
generated
vendored
Normal 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
node_modules/eventsource-parser/dist/stream.cjs.map
generated
vendored
Normal file
1
node_modules/eventsource-parser/dist/stream.cjs.map
generated
vendored
Normal 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;;;"}
|
||||
121
node_modules/eventsource-parser/dist/stream.d.cts
generated
vendored
Normal file
121
node_modules/eventsource-parser/dist/stream.d.cts
generated
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* 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 {};
|
||||
121
node_modules/eventsource-parser/dist/stream.d.ts
generated
vendored
Normal file
121
node_modules/eventsource-parser/dist/stream.d.ts
generated
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* 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
node_modules/eventsource-parser/dist/stream.js
generated
vendored
Normal file
29
node_modules/eventsource-parser/dist/stream.js
generated
vendored
Normal 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
node_modules/eventsource-parser/dist/stream.js.map
generated
vendored
Normal file
1
node_modules/eventsource-parser/dist/stream.js.map
generated
vendored
Normal 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;"}
|
||||
92
node_modules/eventsource-parser/package.json
generated
vendored
Normal file
92
node_modules/eventsource-parser/package.json
generated
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
{
|
||||
"name": "eventsource-parser",
|
||||
"version": "3.0.8",
|
||||
"description": "Streaming, source-agnostic EventSource/Server-Sent Events parser",
|
||||
"keywords": [
|
||||
"eventsource",
|
||||
"server-sent-events",
|
||||
"sse"
|
||||
],
|
||||
"homepage": "https://github.com/rexxars/eventsource-parser#readme",
|
||||
"bugs": {
|
||||
"url": "https://github.com/rexxars/eventsource-parser/issues"
|
||||
},
|
||||
"license": "MIT",
|
||||
"author": "Espen Hovlandsdal <espen@hovlandsdal.com>",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+ssh://git@github.com/rexxars/eventsource-parser.git"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"!dist/stats.html",
|
||||
"!dist/index.min.js",
|
||||
"src",
|
||||
"stream.js"
|
||||
],
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"source": "./src/index.ts",
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.cjs",
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
"./stream": {
|
||||
"source": "./src/stream.ts",
|
||||
"import": "./dist/stream.js",
|
||||
"require": "./dist/stream.cjs",
|
||||
"default": "./dist/stream.js"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "pkg-utils build && pkg-utils --strict",
|
||||
"clean": "rimraf dist coverage",
|
||||
"check": "npm run clean && npm run format && npm run lint && npm run build && vitest run",
|
||||
"format": "oxfmt",
|
||||
"format:check": "oxfmt --check",
|
||||
"bench": "node --expose-gc --experimental-strip-types --no-warnings=ExperimentalWarning bench/parse.bench.ts",
|
||||
"bundle-size": "node --experimental-strip-types --no-warnings=ExperimentalWarning scripts/bundle-size.ts",
|
||||
"knip": "knip",
|
||||
"lint": "oxlint && tsc --noEmit",
|
||||
"posttest": "npm run lint",
|
||||
"prebuild": "npm run clean",
|
||||
"prepublishOnly": "npm run build",
|
||||
"test": "npm run test:node",
|
||||
"test:bun": "bun test",
|
||||
"test:deno": "deno run --allow-write --allow-net --allow-run --allow-sys --allow-ffi --allow-env --allow-read npm:vitest",
|
||||
"test:node": "vitest --reporter=verbose"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sanity/pkg-utils": "^10.4.15",
|
||||
"@sanity/semantic-release-preset": "^6.0.0",
|
||||
"@sanity/tsconfig": "^2.1.0",
|
||||
"@types/node": "^20.19.0",
|
||||
"eventsource-encoder": "^1.0.1",
|
||||
"knip": "^6.4.1",
|
||||
"mitata": "^1.0.34",
|
||||
"oxfmt": "^0.45.0",
|
||||
"oxlint": "^1.60.0",
|
||||
"rimraf": "^6.1.3",
|
||||
"rollup-plugin-visualizer": "^6.0.3",
|
||||
"semantic-release": "^25.0.3",
|
||||
"terser": "^5.46.1",
|
||||
"typescript": "^5.9.3",
|
||||
"vitest": "^4.1.4"
|
||||
},
|
||||
"browserslist": [
|
||||
"node >= 18",
|
||||
"chrome >= 71",
|
||||
"safari >= 14.1",
|
||||
"firefox >= 105",
|
||||
"edge >= 79"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
}
|
||||
44
node_modules/eventsource-parser/src/errors.ts
generated
vendored
Normal file
44
node_modules/eventsource-parser/src/errors.ts
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* The type of error that occurred.
|
||||
* @public
|
||||
*/
|
||||
export type ErrorType = 'invalid-retry' | 'unknown-field'
|
||||
|
||||
/**
|
||||
* Error thrown when encountering an issue during parsing.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export 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},
|
||||
) {
|
||||
super(message)
|
||||
this.name = 'ParseError'
|
||||
this.type = options.type
|
||||
this.field = options.field
|
||||
this.value = options.value
|
||||
this.line = options.line
|
||||
}
|
||||
}
|
||||
3
node_modules/eventsource-parser/src/index.ts
generated
vendored
Normal file
3
node_modules/eventsource-parser/src/index.ts
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export {type ErrorType, ParseError} from './errors.ts'
|
||||
export {createParser} from './parse.ts'
|
||||
export type {EventSourceMessage, EventSourceParser, ParserCallbacks} from './types.ts'
|
||||
395
node_modules/eventsource-parser/src/parse.ts
generated
vendored
Normal file
395
node_modules/eventsource-parser/src/parse.ts
generated
vendored
Normal file
@@ -0,0 +1,395 @@
|
||||
/**
|
||||
* EventSource/Server-Sent Events parser
|
||||
* @see https://html.spec.whatwg.org/multipage/server-sent-events.html
|
||||
*/
|
||||
import {ParseError} from './errors.ts'
|
||||
import type {EventSourceParser, ParserCallbacks} from './types.ts'
|
||||
|
||||
// ASCII codes used in the hot parsing paths.
|
||||
const LF = 10
|
||||
const CR = 13
|
||||
const SPACE = 32
|
||||
|
||||
// oxlint-disable-next-line no-unused-vars
|
||||
function noop(_arg: unknown) {
|
||||
// intentional noop
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 function createParser(callbacks: ParserCallbacks): EventSourceParser {
|
||||
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
|
||||
|
||||
// Trailing bytes from prior `feed()` calls that did not yet form a complete line.
|
||||
// Stored as an array of fragments and only joined when a line terminator arrives.
|
||||
// Concatenating per-feed (`prefix + chunk`) is O(N²) when a single SSE line spans
|
||||
// many chunks (e.g. a large `data:` payload streamed in tiny slices, or an MCP-style
|
||||
// server that emits one giant content block). Buffering as fragments + joining once
|
||||
// makes the same workload linear.
|
||||
const pendingFragments: string[] = []
|
||||
|
||||
let isFirstChunk = true
|
||||
let id: string | undefined
|
||||
let data = ''
|
||||
let dataLines = 0
|
||||
let eventType: string | undefined
|
||||
|
||||
/**
|
||||
* Feeds a chunk of the SSE stream to the parser. Any trailing bytes that do
|
||||
* not yet form a complete line are held back and prepended to the next chunk,
|
||||
* so callers can pass arbitrary slices of the stream without worrying about
|
||||
* line boundaries.
|
||||
*
|
||||
* Per the SSE spec, a UTF-8 BOM (0xEF 0xBB 0xBF) at the start of the very
|
||||
* first chunk is stripped before parsing.
|
||||
*
|
||||
* @see https://html.spec.whatwg.org/multipage/server-sent-events.html#parsing-an-event-stream
|
||||
*/
|
||||
function feed(chunk: string) {
|
||||
if (isFirstChunk) {
|
||||
isFirstChunk = false
|
||||
// Match and strip UTF-8 BOM from the start of the stream, if present.
|
||||
// (Per the spec, this is only valid at the very start of the stream)
|
||||
if (
|
||||
chunk.charCodeAt(0) === 0xef &&
|
||||
chunk.charCodeAt(1) === 0xbb &&
|
||||
chunk.charCodeAt(2) === 0xbf
|
||||
) {
|
||||
chunk = chunk.slice(3)
|
||||
}
|
||||
}
|
||||
|
||||
// Hot path: no buffered prefix from a prior partial line. Hand the chunk
|
||||
// straight to `processLines`, exactly like the original implementation.
|
||||
// Zero new work in the common case (every chunk ends with `\n\n`).
|
||||
if (pendingFragments.length === 0) {
|
||||
const trailing = processLines(chunk)
|
||||
if (trailing !== '') pendingFragments.push(trailing)
|
||||
return
|
||||
}
|
||||
|
||||
// We have a buffered prefix. If this chunk also has no terminator, append
|
||||
// to the buffer without concatenating — that's the O(N²) trap we're
|
||||
// avoiding (large single `data:` payload split across many tiny chunks).
|
||||
if (chunk.indexOf('\n') === -1 && chunk.indexOf('\r') === -1) {
|
||||
pendingFragments.push(chunk)
|
||||
return
|
||||
}
|
||||
|
||||
// Terminator arrived. Join the accumulated fragments + this chunk once,
|
||||
// process, and buffer any new trailing partial line.
|
||||
pendingFragments.push(chunk)
|
||||
const input = pendingFragments.join('')
|
||||
pendingFragments.length = 0
|
||||
const trailing = processLines(input)
|
||||
if (trailing !== '') pendingFragments.push(trailing)
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits `chunk` into SSE lines and dispatches each to the appropriate handler.
|
||||
* Returns any trailing bytes that did not terminate with a line break, so the
|
||||
* caller can prepend them to the next chunk.
|
||||
*
|
||||
* The SSE spec permits three line terminators: `\n`, `\r`, and `\r\n`. Real-world
|
||||
* streams almost always use plain `\n`, so we take a fast path when no `\r` is
|
||||
* present in the chunk. The slow path is spec-correct but does more work per line.
|
||||
*/
|
||||
function processLines(chunk: string): string {
|
||||
let searchIndex = 0
|
||||
|
||||
// Fast path: LF-only chunk (the common case for typical SSE servers).
|
||||
// We can scan forward with a single `indexOf('\n')` per line and inline
|
||||
// the hot-path branches for `data:` and `event:` without the CR bookkeeping
|
||||
// the slow path needs.
|
||||
if (chunk.indexOf('\r') === -1) {
|
||||
let lfIndex = chunk.indexOf('\n', searchIndex)
|
||||
while (lfIndex !== -1) {
|
||||
// Blank line: end-of-event marker. Dispatch the accumulated event (if any)
|
||||
// and reset the buffered fields. This is hoisted out of `parseLine` because
|
||||
// it's the single most common line shape after `data:` lines.
|
||||
if (searchIndex === lfIndex) {
|
||||
if (dataLines > 0) {
|
||||
onEvent({id, event: eventType, data})
|
||||
}
|
||||
id = undefined
|
||||
data = ''
|
||||
dataLines = 0
|
||||
eventType = undefined
|
||||
searchIndex = lfIndex + 1
|
||||
lfIndex = chunk.indexOf('\n', searchIndex)
|
||||
continue
|
||||
}
|
||||
const firstCharCode = chunk.charCodeAt(searchIndex)
|
||||
if (isDataPrefix(chunk, searchIndex, firstCharCode)) {
|
||||
// `data:` line — append the value to the event's data buffer.
|
||||
// 'data:'.length === 5, 'data: '.length === 6
|
||||
const valueStart =
|
||||
chunk.charCodeAt(searchIndex + 5) === SPACE ? searchIndex + 6 : searchIndex + 5
|
||||
const value = chunk.slice(valueStart, lfIndex)
|
||||
// Fast path within a fast path: if this is the first data line AND the
|
||||
// next char is another LF (i.e. `data:foo\n\n`), dispatch immediately
|
||||
// without ever writing to the `data` buffer. This is the shape of a
|
||||
// typical single-line SSE event (ChatGPT-style streams, etc.) and is
|
||||
// hot enough to be worth the duplication.
|
||||
if (dataLines === 0 && chunk.charCodeAt(lfIndex + 1) === LF) {
|
||||
onEvent({id, event: eventType, data: value})
|
||||
id = undefined
|
||||
data = ''
|
||||
eventType = undefined
|
||||
searchIndex = lfIndex + 2
|
||||
lfIndex = chunk.indexOf('\n', searchIndex)
|
||||
continue
|
||||
}
|
||||
// Multi-line data: concatenate with newline separator per spec.
|
||||
data = dataLines === 0 ? value : `${data}\n${value}`
|
||||
dataLines++
|
||||
} else if (isEventPrefix(chunk, searchIndex, firstCharCode)) {
|
||||
// `event:` line — set the event type for the next dispatch. Per spec,
|
||||
// an empty value resets `event type` to its default (undefined here).
|
||||
// 'event:'.length === 6, 'event: '.length === 7
|
||||
eventType =
|
||||
chunk.slice(
|
||||
chunk.charCodeAt(searchIndex + 6) === SPACE ? searchIndex + 7 : searchIndex + 6,
|
||||
lfIndex,
|
||||
) || undefined
|
||||
} else {
|
||||
// Everything else: `id:`, `retry:`, comment lines (`:` prefix), unknown
|
||||
// fields, or malformed lines. These are rarer and go through the full
|
||||
// per-line parser, which handles the SSE field grammar in detail.
|
||||
parseLine(chunk, searchIndex, lfIndex)
|
||||
}
|
||||
searchIndex = lfIndex + 1
|
||||
lfIndex = chunk.indexOf('\n', searchIndex)
|
||||
}
|
||||
return chunk.slice(searchIndex)
|
||||
}
|
||||
|
||||
// Slow path: the chunk contains at least one `\r`, so lines may be terminated
|
||||
// by `\r`, `\n`, or `\r\n`. We locate the next terminator by looking at both
|
||||
// the nearest `\r` and `\n` and picking whichever comes first.
|
||||
while (searchIndex < chunk.length) {
|
||||
const crIndex = chunk.indexOf('\r', searchIndex)
|
||||
const lfIndex = chunk.indexOf('\n', searchIndex)
|
||||
|
||||
let lineEnd = -1
|
||||
if (crIndex !== -1 && lfIndex !== -1) {
|
||||
lineEnd = crIndex < lfIndex ? crIndex : lfIndex
|
||||
} else if (crIndex !== -1) {
|
||||
// A trailing `\r` at the very end of the chunk is ambiguous: it could be
|
||||
// a bare-CR terminator, or the first half of a `\r\n` whose `\n` arrives
|
||||
// in the next chunk. Defer until we see more input.
|
||||
if (crIndex === chunk.length - 1) {
|
||||
lineEnd = -1
|
||||
} else {
|
||||
lineEnd = crIndex
|
||||
}
|
||||
} else if (lfIndex !== -1) {
|
||||
lineEnd = lfIndex
|
||||
}
|
||||
|
||||
if (lineEnd === -1) {
|
||||
break
|
||||
}
|
||||
|
||||
parseLine(chunk, searchIndex, lineEnd)
|
||||
searchIndex = lineEnd + 1
|
||||
// If we just consumed a `\r` and the next char is `\n`, skip it so the
|
||||
// pair is treated as a single terminator rather than an empty line.
|
||||
if (chunk.charCodeAt(searchIndex - 1) === CR && chunk.charCodeAt(searchIndex) === LF) {
|
||||
searchIndex++
|
||||
}
|
||||
}
|
||||
|
||||
return chunk.slice(searchIndex)
|
||||
}
|
||||
|
||||
function parseLine(chunk: string, start: number, end: number) {
|
||||
if (start === end) {
|
||||
dispatchEvent()
|
||||
return
|
||||
}
|
||||
|
||||
const firstCharCode = chunk.charCodeAt(start)
|
||||
|
||||
if (isDataPrefix(chunk, start, firstCharCode)) {
|
||||
// 'data:'.length === 5, 'data: '.length === 6
|
||||
const valueStart = chunk.charCodeAt(start + 5) === SPACE ? start + 6 : start + 5
|
||||
const value = chunk.slice(valueStart, end)
|
||||
data = dataLines === 0 ? value : `${data}\n${value}`
|
||||
dataLines++
|
||||
return
|
||||
}
|
||||
|
||||
if (isEventPrefix(chunk, start, firstCharCode)) {
|
||||
// 'event:'.length === 6, 'event: '.length === 7
|
||||
eventType =
|
||||
chunk.slice(chunk.charCodeAt(start + 6) === SPACE ? start + 7 : start + 6, end) || undefined
|
||||
return
|
||||
}
|
||||
|
||||
// Fast path for "id:" — 'i' = 105, 'd' = 100, ':' = 58
|
||||
if (
|
||||
firstCharCode === 105 &&
|
||||
chunk.charCodeAt(start + 1) === 100 &&
|
||||
chunk.charCodeAt(start + 2) === 58
|
||||
) {
|
||||
// 'id:'.length === 3, 'id: '.length === 4
|
||||
const value = chunk.slice(chunk.charCodeAt(start + 3) === SPACE ? start + 4 : start + 3, end)
|
||||
id = value.includes('\0') ? undefined : value
|
||||
return
|
||||
}
|
||||
|
||||
// Comment line — ':' = 58
|
||||
if (firstCharCode === 58) {
|
||||
if (onComment) {
|
||||
const line = chunk.slice(start, end)
|
||||
// skip ':' (+1), or ': ' (+2) when a space follows
|
||||
onComment(line.slice(chunk.charCodeAt(start + 1) === SPACE ? 2 : 1))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const line = chunk.slice(start, end)
|
||||
const fieldSeparatorIndex = line.indexOf(':')
|
||||
if (fieldSeparatorIndex === -1) {
|
||||
processField(line, '', line)
|
||||
return
|
||||
}
|
||||
|
||||
const field = line.slice(0, fieldSeparatorIndex)
|
||||
// skip ':' (+1), or ': ' (+2) when a space follows
|
||||
const offset = line.charCodeAt(fieldSeparatorIndex + 1) === SPACE ? 2 : 1
|
||||
const value = line.slice(fieldSeparatorIndex + offset)
|
||||
processField(field, value, line)
|
||||
}
|
||||
|
||||
function processField(field: string, value: string, line: string) {
|
||||
// Field names must be compared literally, with no case folding performed.
|
||||
switch (field) {
|
||||
case 'event':
|
||||
// Set the `event type` buffer to field value
|
||||
eventType = value || undefined
|
||||
break
|
||||
case 'data':
|
||||
data = dataLines === 0 ? value : `${data}\n${value}`
|
||||
dataLines++
|
||||
break
|
||||
case 'id':
|
||||
// If the field value does not contain U+0000 NULL, then set the `ID` buffer to
|
||||
// the field value. Otherwise, ignore the field.
|
||||
id = value.includes('\0') ? undefined : value
|
||||
break
|
||||
case 'retry':
|
||||
// If the field value consists of only ASCII digits, then interpret the field value as an
|
||||
// integer in base ten, and set the event stream's reconnection time to that integer.
|
||||
// Otherwise, ignore the field.
|
||||
if (/^\d+$/.test(value)) {
|
||||
onRetry(parseInt(value, 10))
|
||||
} else {
|
||||
onError(
|
||||
new ParseError(`Invalid \`retry\` value: "${value}"`, {
|
||||
type: 'invalid-retry',
|
||||
value,
|
||||
line,
|
||||
}),
|
||||
)
|
||||
}
|
||||
break
|
||||
default:
|
||||
// Otherwise, the field is ignored.
|
||||
onError(
|
||||
new ParseError(
|
||||
`Unknown field "${field.length > 20 ? `${field.slice(0, 20)}…` : field}"`,
|
||||
{type: 'unknown-field', field, value, line},
|
||||
),
|
||||
)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
function dispatchEvent() {
|
||||
if (dataLines > 0) {
|
||||
onEvent({
|
||||
id,
|
||||
event: eventType,
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
id = undefined
|
||||
data = ''
|
||||
dataLines = 0
|
||||
eventType = undefined
|
||||
}
|
||||
|
||||
function reset(options: {consume?: boolean} = {}) {
|
||||
if (options.consume && pendingFragments.length > 0) {
|
||||
const incompleteLine = pendingFragments.join('')
|
||||
parseLine(incompleteLine, 0, incompleteLine.length)
|
||||
}
|
||||
|
||||
isFirstChunk = true
|
||||
id = undefined
|
||||
data = ''
|
||||
dataLines = 0
|
||||
eventType = undefined
|
||||
pendingFragments.length = 0
|
||||
}
|
||||
|
||||
return {feed, reset}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if `chunk` starts with the literal `data:` at index `i`.
|
||||
*
|
||||
* Equivalent to `chunk.startsWith('data:', i)`, but benchmarks show this
|
||||
* hand-unrolled char-code comparison is ~20% faster on common event types.
|
||||
* The caller passes `firstCharCode` (the code at `i`) so it can be reused
|
||||
* across prefix checks.
|
||||
*
|
||||
* ASCII: 'd' = 100, 'a' = 97, 't' = 116, 'a' = 97, ':' = 58
|
||||
*/
|
||||
function isDataPrefix(chunk: string, i: number, firstCharCode: number): boolean {
|
||||
return (
|
||||
firstCharCode === 100 &&
|
||||
chunk.charCodeAt(i + 1) === 97 &&
|
||||
chunk.charCodeAt(i + 2) === 116 &&
|
||||
chunk.charCodeAt(i + 3) === 97 &&
|
||||
chunk.charCodeAt(i + 4) === 58
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if `chunk` starts with the literal `event:` at index `i`.
|
||||
*
|
||||
* See {@link isDataPrefix} for why this is hand-unrolled rather than using
|
||||
* `String.prototype.startsWith`.
|
||||
*
|
||||
* ASCII: 'e' = 101, 'v' = 118, 'e' = 101, 'n' = 110, 't' = 116, ':' = 58
|
||||
*/
|
||||
function isEventPrefix(chunk: string, i: number, firstCharCode: number): boolean {
|
||||
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
|
||||
)
|
||||
}
|
||||
88
node_modules/eventsource-parser/src/stream.ts
generated
vendored
Normal file
88
node_modules/eventsource-parser/src/stream.ts
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
import {createParser} from './parse.ts'
|
||||
import type {EventSourceMessage, EventSourceParser} from './types.ts'
|
||||
|
||||
/**
|
||||
* Options for the EventSourceParserStream.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export 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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 class EventSourceParserStream extends TransformStream<string, EventSourceMessage> {
|
||||
constructor({onError, onRetry, onComment}: StreamOptions = {}) {
|
||||
let parser!: EventSourceParser
|
||||
|
||||
super({
|
||||
start(controller) {
|
||||
parser = createParser({
|
||||
onEvent: (event) => {
|
||||
controller.enqueue(event)
|
||||
},
|
||||
onError(error) {
|
||||
if (onError === 'terminate') {
|
||||
controller.error(error)
|
||||
} else if (typeof onError === 'function') {
|
||||
onError(error)
|
||||
}
|
||||
|
||||
// Ignore by default
|
||||
},
|
||||
onRetry,
|
||||
onComment,
|
||||
})
|
||||
},
|
||||
transform(chunk) {
|
||||
parser.feed(chunk)
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export {type ErrorType, ParseError} from './errors.ts'
|
||||
export type {EventSourceMessage} from './types.ts'
|
||||
97
node_modules/eventsource-parser/src/types.ts
generated
vendored
Normal file
97
node_modules/eventsource-parser/src/types.ts
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
import type {ParseError} from './errors.ts'
|
||||
|
||||
/**
|
||||
* EventSource parser instance.
|
||||
*
|
||||
* Needs to be reset between reconnections/when switching data source, using the `reset()` method.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export 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
|
||||
}
|
||||
|
||||
/**
|
||||
* A parsed EventSource message event
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export 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
|
||||
}
|
||||
|
||||
/**
|
||||
* Callbacks that can be passed to the parser to handle different types of parsed messages
|
||||
* and errors.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export 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
|
||||
}
|
||||
2
node_modules/eventsource-parser/stream.js
generated
vendored
Normal file
2
node_modules/eventsource-parser/stream.js
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/* included for compatibility with react-native without package exports support */
|
||||
module.exports = require('./dist/stream.cjs')
|
||||
Reference in New Issue
Block a user