# eventsource

[![npm version](https://img.shields.io/npm/v/eventsource.svg?style=flat-square)](https://www.npmjs.com/package/eventsource)[![npm bundle size](https://img.shields.io/bundlephobia/minzip/eventsource?style=flat-square)](https://bundlephobia.com/result?p=eventsource)[![npm weekly downloads](https://img.shields.io/npm/dw/eventsource.svg?style=flat-square)](https://www.npmjs.com/package/eventsource)

WhatWG/W3C-compatible [server-sent events/eventsource](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events) client. The module attempts to implement an absolute minimal amount of features/changes beyond the specification.

If you're looking for a modern alternative with a less constrained API, check out the [`eventsource-client` package](https://www.npmjs.com/package/eventsource-client).

## Installation

```bash
npm install --save eventsource
```

## Supported engines

- Node.js >= 22.12
- Chrome >= 84
- Safari >= 15
- Firefox >= 105
- Edge >= 84
- Deno >= 2
- Bun >= 1.1.23
- Cloudflare Workers: [partial](#cloudflare-workers)

Basically, any environment that supports:

- [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch)
- [ReadableStream](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream)
- [TextDecoder](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder)
- [URL](https://developer.mozilla.org/en-US/docs/Web/API/URL)
- [Event](https://developer.mozilla.org/en-US/docs/Web/API/Event), [MessageEvent](https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent), [EventTarget](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget)
- [Private class fields, methods, and accessors](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_elements)
- [Symbol.for](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/for)

If you need to support runtimes without private class fields, methods and accessors, try the `4.x` branch/version range.

If you need to support even older runtimes, try the `2.x` branch/version range (note: 2.x branch is primarily targeted at Node.js, not browsers).

## Usage

```ts
import {EventSource} from 'eventsource'

const es = new EventSource('https://my-server.com/sse')

/*
 * This will listen for events with the field `event: notice`.
 */
es.addEventListener('notice', (event) => {
  console.log(event.data)
})

/*
 * This will listen for events with the field `event: update`.
 */
es.addEventListener('update', (event) => {
  console.log(event.data)
})

/*
 * The event "message" is a special case, as it will capture events _without_ an
 * event field, as well as events that have the specific type `event: message`.
 * It will not trigger on any other event type.
 */
es.addEventListener('message', (event) => {
  console.log(event.data)
})

/**
 * To explicitly close the connection, call the `close` method.
 * This will prevent any reconnection from happening.
 */
setTimeout(() => {
  es.close()
}, 10_000)
```

### TypeScript

Make sure you have configured your TSConfig so it matches the environment you are targetting. If you are targetting browsers, this would be `dom`:

```jsonc
{
  "compilerOptions": {
    "lib": ["dom"],
  },
}
```

If you're using Node.js, ensure you have `@types/node` installed (and it is version 22 or higher). Cloudflare workers have `@cloudflare/workers-types` etc.

The following errors are caused by targetting an environment that does not have the necessary types available:

```
error TS2304: Cannot find name 'Event'.
error TS2304: Cannot find name 'EventTarget'.
error TS2304: Cannot find name 'MessageEvent'.
```

### Cloudflare Workers

Cloudflare Workers require `compatibilityDate: '2024-11-11'` or later, and `addEventListener()` instead of the `onmessage`, `onopen` and `onerror` properties.

workerd's `EventTarget` dispatches `on<type>` handler properties itself, on top of the listener this module registers, so a handler assigned that way is called twice per event - and assigning `null` only removes one of the two registrations, so it does not unsubscribe ([workerd#6022](https://github.com/cloudflare/workerd/issues/6022)).

```js
// Called twice per message on Cloudflare Workers
eventSource.onmessage = (event) => console.log(event.data)

// Use this instead
eventSource.addEventListener('message', (event) => console.log(event.data))
```

workerd also drops `origin` and `lastEventId` from the `MessageEvent` constructor. This module assigns them itself, so both are correct on Workers today; the workaround goes away once [workerd#6995](https://github.com/cloudflare/workerd/pull/6995) lands.

## Migrating from v1 / v2

See [MIGRATION.md](MIGRATION.md#v2-to-v3) for a detailed migration guide.

## Extensions to the WhatWG/W3C API

### Message and code properties on errors

The `error` event has a `message` and `code` property that can be used to get more information about the error. In the specification, the Event

```ts
es.addEventListener('error', (err) => {
  if (err.code === 401 || err.code === 403) {
    console.log('not authorized')
  }
})
```

### Limit parser buffer size

The parser buffers up to 100 MB while waiting for a complete EventSource line. To change that limit, pass `maxBufferSize` in the constructor options:

```ts
const es = new EventSource('https://my-server.com/sse', {
  maxBufferSize: 10 * 1024 * 1024, // 10 MB
})
```

If the limit is exceeded, the connection fails and emits an `error` event, and will not reconnect. Prefer servers that emit smaller chunks or newlines more frequently over increasing this limit.

### Specify `fetch` implementation

The `EventSource` constructor accepts an optional `fetch` property in the second argument that can be used to specify the `fetch` implementation to use.

This can be useful in environments where the global `fetch` function is not available - but it can also be used to alter the request/response behaviour.

#### Setting HTTP request headers

```ts
const es = new EventSource('https://my-server.com/sse', {
  fetch: (input, init) =>
    fetch(input, {
      ...init,
      headers: {
        ...init.headers,
        Authorization: 'Bearer myToken',
      },
    }),
})
```

#### HTTP/HTTPS proxy

Use a package like [`undici`](https://github.com/nodejs/undici) to add proxy support, either through environment variables or explicit configuration.

```ts
// npm install undici --save
import {fetch, EnvHttpProxyAgent} from 'undici'

const proxyAgent = new EnvHttpProxyAgent()

const es = new EventSource('https://my-server.com/sse', {
  fetch: (input, init) => fetch(input, {...init, dispatcher: proxyAgent}),
})
```

#### Using HTTP2

Use a package like [`undici`](https://github.com/nodejs/undici) that supports HTTP2.

```ts
// npm install undici --save
import {Agent, fetch} from 'undici'

const http2Dispatcher = new Agent({allowH2: true})

const es = new EventSource('https://my-server.com/sse', {
  fetch: (url, init) => fetch(url, {...init, dispatcher: http2Dispatcher}),
})
```

#### Allow unauthorized HTTPS requests

Use a package like [`undici`](https://github.com/nodejs/undici) for more control of fetch options through the use of an [`Agent`](https://undici.nodejs.org/#/docs/api/Agent.md).

```ts
// npm install undici --save
import {fetch, Agent} from 'undici'

const unsafeAgent = new Agent({
  connect: {
    rejectUnauthorized: false,
  },
})

await fetch('https://my-server.com/sse', {
  dispatcher: unsafeAgent,
})
```

#### Feature checking

For library authors, you may want to feature-check if an EventSource implementation supports passing a custom `fetch` implementation (such as this library does). We declare (from [v4.1.0](https://github.com/EventSource/eventsource/releases/tag/v4.1.0) and onwards) a non-enumerable symbol on the EventSource class to indicate this, named `eventsource.supports-fetch-override`. It can be used downstream such as:

```ts
function yourLibrary(options) {
  const OurEventSource = options.polyfills.EventSource || globalThis.EventSource

  if (OurEventSource && Symbol.for('eventsource.supports-fetch-override') in OurEventSource) {
    // We can safely assume this supports overriding/specifying `fetch`!
    const es = new OurEventSource('https://some.url', {
      fetch: (input, init) =>
        fetch(input, {
          ...init,
          headers: {
            ...(init.headers || {}),
            'x-some-header': 'foo',
          },
        }),
    })
    // …
  }
}
```

## License

MIT-licensed. See [LICENSE](LICENSE).
