eventsource-parser

repository·main·Indexed 19 days ago

https://github.com/rexxars/eventsource-parser

A streaming, source-agnostic Server-Sent Events (SSE) parser designed as a low-level building block for clients and polyfills. It is environment-agnostic, supporting browsers, Node.js, and Deno. The library provides a `createParser` API for manual data feeding via `.feed()` and an `EventSourceParserStream` for `TransformStream` environments (modern browsers, Node 18+). Key features include configurable memory limits via `maxBufferSize`, handling of retry intervals and comments, and detailed `ParseError` types.

Tokens
5.5K
Snippets
19
Records
26
Agent score
67%

What's inside eventsource-parser

  1. Migrate from v2 to v3 (Standard Parser)

    main

    In v3, createParser no longer accepts a single callback function. Instead, it requires an object of callbacks. This change eliminates the need to manually check event.type to distinguish between data events and retry interval changes.

    Key Changes:

    • Callback Structure: Replace the single callback with an object containing onEvent and onRetry.
    • Type Renaming: ParsedEvent is now EventSourceMessage. Note that the type property has been removed from EventSourceMessage.
    • New Callbacks: You can now use onError to handle parsing errors and onComment to handle comments.

    Removed/Renamed Types:

    • ParsedEvent $\rightarrow$ EventSourceMessage
    • EventSourceParseCallback $\rightarrow$ replaced by the ParserCallbacks interface (via the onEvent property).
    • ReconnectInterval $\rightarrow$ removed (the onRetry callback now provides the number interval directly).
    • ParseEvent $\rightarrow$ removed (event types are now separated by callback type).
    import {createParser, type EventSourceMessage} from 'eventsource-parser'
    
    const parser = createParser({
      onEvent: (event: EventSourceMessage) => {
        // …handle event…
      },
      onRetry: (interval: number) => {
        // …handle retry interval change…
      },
      onError: (error: Error) => {
        // …handle parse error…
      },
      onComment: (comment: string) => {
        // …handle comment…
      },
    })
  2. Migrate from v2 to v3 (TransformStream variant)

    main

    If you are using EventSourceParserStream, the migration is seamless, but new configuration options are available to enhance functionality:

    • Retry Intervals: You can now subscribe to retry interval changes by providing an onRetry callback.
    • Error Handling: You can now handle errors via an onError callback or by setting onError to 'terminate' to stop the stream on a parse error. The default behavior is to ignore errors.
    • Comments: You can now capture comments encountered during parsing using the onComment callback.
    // Example of using new options in EventSourceParserStream
    const stream = new EventSourceParserStream({
      onRetry: (interval: number) => {
        // …handle retry interval change…
      },
      onError: (error: Error) => {
        // …handle parse error…
      },
      // OR
      // onError: 'terminate',
      onComment: (comment: string) => {
        // …handle comment…
      },
    })
  3. Handle parsing errors in EventSourceParserStream

    main

    When configuring onError, you can choose to either terminate the stream or handle the error manually.

    If you set onError: 'terminate', the stream will emit an error and stop. If you provide a custom function, you can perform side effects (like logging) without necessarily stopping the stream.

    Critical Note: If the parser exceeds the maxBufferSize, it is considered unrecoverable. In this specific case, the stream will always error out with a ParseError (specifically with the type max-buffer-size-exceeded), even if you provided a custom error handler or a value other than 'terminate'.

  4. Limit buffered memory with maxBufferSize

    main

    To prevent unbounded memory growth from misbehaving servers or proxies, you can set maxBufferSize (in characters) in the createParser options.

    If the combined size of the pending line buffer and the in-progress event's data buffer exceeds this limit, the parser emits a ParseError with type: 'max-buffer-size-exceeded' and terminates. Subsequent calls to .feed() will throw until .reset() is called.

    const parser = createParser({
      maxBufferSize: 1024 * 1024, // 1 MB
      onEvent(event) {
        // …
      },
      onError(error) {
        if (error.type === 'max-buffer-size-exceeded') {
          // Handle error
        }
      },
    })
  5. Handle SSE events and metadata via callbacks

    main

    The parser dispatches information through several configurable callbacks in ParserConfig:

    • onEvent: Called when a complete event is parsed. Receives an object with { id, event, data }.
    • onRetry: Called when a retry: [value] field is parsed. The value is an integer representing the reconnection time in milliseconds.
    • onComment: Called when a line starting with : is encountered. The callback receives the comment string (stripping the leading colon and optional space).
    • onError: Called when a ParseError occurs (e.g., max-buffer-size-exceeded, invalid-retry, or unknown-field).
  6. Handle retry intervals with onRetry

    main

    If the server sends a retry field in the event stream, the parser triggers the onRetry callback provided in the createParser options. This callback receives the requested retry interval in milliseconds.

    const parser = createParser({
      onRetry(retryInterval) {
        console.log('Server requested retry interval of %dms', retryInterval)
      },
      onEvent(event) {
        // …
      },
    })
  7. Use EventSourceParserStream for TransformStream environments

    main

    In environments supporting TransformStream (modern browsers, Node 18+), you can use EventSourceParserStream from the eventsource-parser/stream export. This allows you to pipe a readable stream through the parser.

    Events are delivered directly through the stream rather than via a callback. The constructor accepts onComment, onRetry, maxBufferSize, and onError (which can be a function or the string 'terminate').

    import {EventSourceParserStream} from 'eventsource-parser/stream'
    
    const eventStream = response.body
      .pipeThrough(new TextDecoderStream())
      .pipeThrough(new EventSourceParserStream({
        maxBufferSize: 1024 * 1024,
        onError: 'terminate',
      }))
  8. Handle comments with onComment

    main

    By default, the parser ignores lines starting with : (comments). To process them, provide an onComment callback. Note that leading whitespace is not stripped from the comment value (e.g., : comment results in comment).

    const parser = createParser({
      onComment(comment) {
        console.log('Received comment:', comment)
      },
      onEvent(event) {
        // …
      },
    })
  9. Handle parse errors with onError

    main

    The onError callback is triggered when the parser encounters an error. The error object follows the ParseError type.

    Common error types include:

    • invalid-field: Occurs when data is not shaped as field: value. Use error.line to find the offending line.
    • invalid-retry: Occurs when a retry interval is invalid.
    • max-buffer-size-exceeded: Occurs when the buffer exceeds maxBufferSize.
    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) {
        // …
      },
    })
  10. Use the createParser API

    main

    The core way to use the library is by creating a parser instance with createParser. You then manually feed chunks of data (partial or complete) into the parser using the .feed(chunk) method. The parser emits parsed messages via the onEvent callback once a complete message is received.

    To reuse a parser for a new stream, you must call .reset().

    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() // Your data source
    
    for await (const chunk of sseStream) {
      parser.feed(chunk)
    }
    
    // Reset to reuse for a new stream
    parser.reset()
  11. Configure EventSourceParserStream options

    main

    You can pass a StreamOptions object to the EventSourceParserStream constructor to control error handling, retry intervals, comments, and memory limits.

    Options

    OptionTypeDescription
    onError'terminate' | ((error: Error) => void)Defines behavior when a parsing error occurs. 'terminate' errors the stream and stops parsing. A custom function allows manual handling. Any other value ignores the error and continues. Note: If the error type is max-buffer-size-exceeded, the stream will always terminate regardless of this setting.
    onRetry(retry: number) => voidCallback triggered when a reconnection interval is sent from the server. The retry parameter is the number of milliseconds to wait.
    onComment(comment: string) => voidCallback triggered when a comment is encountered in the stream.
    maxBufferSizenumberThe maximum number of characters the parser is allowed to buffer. If exceeded, the stream is always errored because the parser becomes unrecoverable.
    // Example: Terminate stream on parsing errors
    const eventStream =
      response.body
        .pipeThrough(new TextDecoderStream())
        .pipeThrough(new EventSourceParserStream({ onError: 'terminate' }));