npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@launchdarkly/eventsource

v0.2.1

Published

W3C compliant EventSource (server-sent events) client built on fetch, used by LaunchDarkly SDKs

Readme

LaunchDarkly EventSource for JavaScript SDKs

NPM Actions Status Documentation NPM NPM

[!CAUTION] This package is in pre-release and not subject to backwards compatibility guarantees. The API may change based on feedback.

Pin to a specific minor version and review the changelog before upgrading.

This package contains a W3C-compliant EventSource (server-sent events) client built on fetch(), ReadableStream and AbortController.

This package is intended to be used by LaunchDarkly SDKs and not as a general EventSource implementation.

This package is derived from LaunchDarkly's launchdarkly-eventsource package, which is a fork of the eventsource npm package. See LICENSE for the original license terms.

The stream parser at src/parser is a fork of the eventsource-parser npm package, version 4.1.1. It is maintained in this repository; see the third-party notices in LICENSE for its original MIT license terms.

Install

npm install @launchdarkly/eventsource

Quick usage

import { createEventSource } from '@launchdarkly/eventsource';

const es = createEventSource('https://example.com/stream', {
  initialRetryDelayMillis: 1000,
  maxBackoffMillis: 30000,
  jitterRatio: 0.5,
});

// The connection attempt starts before createEventSource returns. Attach
// listeners immediately after the call, before any other code runs, to avoid
// missing an event.
es.onopen = (event) => {
  console.log('connected', event.headers);
};
es.onerror = (event) => {
  if (event?.status === 401) {
    console.log('not authorized');
  }
};
es.addEventListener('message', (event) => {
  console.log('received', event.data);
});

// Later, when the stream is no longer needed:
es.close();

The EventSource instance

createEventSource returns an object that satisfies the exported EventSource interface:

  • readyState -- the connection state: CONNECTING (0), OPEN (1), or CLOSED (2). The package exports these constants.
  • url -- the URL of the most recent connection attempt. With a urlBuilder it can change between attempts.
  • reconnectInterval -- mirrors the most recent server retry: field, in milliseconds; starts at 1000. The retry strategy owns the actual reconnect timing; this slot only supplies the fallback delay for a reconnect whose strategy call throws.
  • close() -- permanently closes the stream, invokes onclose, and dispatches the closed event. Idempotent.

Unlike the standard EventSource, there is no onmessage slot; it is left out on purpose. Register message listeners with addEventListener('message', ...).

Options reference

This section documents the behavior of createEventSource's second argument for maintainers of this package and its consumers. The authoritative definitions, including any behavior not covered here, are the TSDoc comments on EventSourceInitDict in src/types.ts.

Events

Listeners attach in two ways:

  • The onopen, onerror, onretrying, and onclose members are plain writable slots, one listener each. Assigning a slot never affects listeners registered through addEventListener. For one dispatched event, the matching slot runs first, then the addEventListener listeners. onclose is an exception. Only the public close() method invokes it, never event dispatch.
  • addEventListener(type, listener) and removeEventListener(type, listener) register and remove any number of listeners per event type, including the SSE event types a server names through the event: field.

Listeners are typed by event type. For the event types this implementation itself dispatches, the payload type comes from the exported EventSourceEventMap. A server-named SSE event carries a MessageEvent.

Beyond the standard open/message/error events, this implementation dispatches:

  • closed: the stream has been permanently closed, either by a non-retryable error or by calling close(). When close() closes the stream, the onclose slot is invoked at the same point, before the closed listeners; a non-retryable error dispatches closed without invoking onclose.
  • end: the server ended the stream cleanly, with a complete response body. Not reported as an error, but still passed to errorFilter for retry purposes. A mid-stream connection drop -- a reset, or a socket that closes without completing the response -- surfaces from the fetch transport as a read failure, so it dispatches error and invokes onerror. A caller that treats end and error differently must expect error for an incomplete close.
  • retrying: after an error, indicates a reconnect is scheduled. The event's delayMillis property gives the delay.

The open event's headers property carries the HTTP response headers from the stream, as a plain object:

{
  'content-type': 'text/event-stream; charset=utf-8',
  'transfer-encoding': 'chunked',
  'cache-control': 'no-cache, no-store, must-revalidate',
}

The error event carries status/message for HTTP errors:

es.onerror = (err) => {
  if (err?.status === 401 || err?.status === 403) {
    console.log('not authorized');
  }
};

A server-sent SSE frame named error also dispatches under the error type, with a MessageEvent payload; only that frame carries a string data property.

An exception thrown by the onopen, onerror, or onretrying slot does not stop dispatch to the addEventListener listeners for that event. An exception from a registered listener does not stop the parse of the current chunk. Neither stops the stream's own reconnection logic; the exception surfaces later, asynchronously, as an uncaught error.

Retry delay: backoff and jitter

  • initialRetryDelayMillis -- base delay before the first reconnect attempt (default 1000ms).
  • maxBackoffMillis -- if set, the delay grows exponentially on each successive retry, up to this ceiling.
  • jitterRatio -- if set, each computed delay is randomly reduced by up to this fraction.
  • retryResetIntervalMillis -- how long the current connection must have been delivering events, measured from its first event, before the backoff counter resets to the initial delay. A connection that fails before it delivers an event does not count as healthy.

Without maxBackoffMillis and jitterRatio, every retry waits the same base delay. Set both, so that clients which lose their connections at the same time -- for example, in a server outage -- do not all reconnect at the same time:

const es = createEventSource(url, {
  initialRetryDelayMillis: 2000, // the first retry waits 2 seconds
  maxBackoffMillis: 30000, // enables backoff, with a maximum of 30 seconds
  retryResetIntervalMillis: 60000, // backoff resets after 60 seconds of delivered events
  jitterRatio: 0.5, // each delay is reduced by a random amount of up to 50%
});

Custom retry delay strategy

The retryDelayStrategy option replaces the built-in retry timing with a caller-supplied object. The object must implement three methods:

  • nextRetryDelay(currentTimeMillis) -- called when a reconnect is about to be scheduled; returns the delay in milliseconds before the next attempt.
  • setGoodSince(goodSinceTimeMillis) -- called when the first event of a connection arrives, to mark the connection as delivering data.
  • setBaseDelay(delayMillis) -- called when the server sends a retry: field; the value reaches the strategy already validated as all ASCII digits and capped at one hour.

The timestamps the client passes come from one monotonic clock. They are not epoch times, and a strategy must only compare them with each other, never with Date.now().

When retryDelayStrategy is set it fully replaces the built-in behavior, so initialRetryDelayMillis, maxBackoffMillis, jitterRatio, and retryResetIntervalMillis have no effect. When the option is absent, the built-in options above apply.

Error retry behavior

By default, connection failures and I/O errors are always retried; HTTP error responses are retried only for 500, 502, 503, and 504. A 200 response that declares a Content-Type other than text/event-stream, or whose headers cannot be read, is also treated as an error. Its report carries no status, so the default filter retries it like a transient transport condition. A response with no Content-Type header is accepted, so a minimal injected transport can report no headers. Set errorFilter to override this -- it receives the error and returns true to retry or false to close the stream and raise error. A filter that throws is treated as if it returned false. The stream then closes cleanly and releases its connection, and the exception surfaces asynchronously as an uncaught error, like a throwing listener's. Each status other than 200, including a redirect status that the transport did not follow, is an HTTP error response.

const es = createEventSource(url, {
  // Retry every error except an unauthorized response.
  errorFilter: (err) => err.status !== 401,
});

Headers, method, and body

headers sets additional request headers, for example to send cookies or an initial Last-Event-ID value. Normally Cache-Control: no-cache and Accept: text/event-stream are also sent; skipDefaultHeaders: true sends only the headers you specify. This matters for a cross-origin request, because CORS restricts the headers a request can carry. method overrides the default GET; body sets a request body, for use with a non-GET method.

const es = createEventSource(url, {
  method: 'REPORT',
  body: JSON.stringify(context),
  headers: { Authorization: sdkKey },
  skipDefaultHeaders: true,
});

Read timeout

readTimeoutMillis drops and retries the connection if that many milliseconds elapse with no data received, guarding against a TCP connection that fails without an I/O error. When the server sends heartbeat data at a known interval -- such as a : comment line, which SSE ignores -- set the timeout longer than that interval.

Listener registry

Listener storage and dispatch for addEventListener registrations go through a registry created once per instance. The createEventRegistry option supplies the factory; when absent, the instance uses the internal Map-backed registry, whose factory is also exported as createDefaultEventRegistry. A substitute must satisfy the exported EventListenerRegistry interface -- for example, an SDK can supply one backed by Node's EventEmitter. The registry never sees the on* slots; they are plain members of the instance. The registry is trusted code. It observes every event the instance dispatches and controls what its listeners actually receive.

Credentials

withCredentials: true maps to fetch()'s credentials: 'include', so a cross-origin stream is opened with cookies and HTTP authentication.

Dynamic URLs

urlBuilder returns the URL to use for the next connection attempt and is called immediately before every request, including the first. This is how a query parameter that changes between attempts stays current across reconnects.

Injectable transport

The fetch option supplies the transport used to open the stream. When absent, the client uses the global fetch. The option only requires the structural subset of the fetch API that the client uses (see FetchLike in src/httpHelpers.ts), so a real fetch implementation satisfies it without casts. This is how the Node-based SDKs connect this client to node:http/node:https with their agent, proxy, and TLS configuration. The client itself knows nothing about any of those.

Contributing

See Contributing.

Verifying SDK build provenance with the SLSA framework

LaunchDarkly uses the SLSA framework (Supply-chain Levels for Software Artifacts) to help developers make their supply chain more secure by ensuring the authenticity and build integrity of our published SDK packages. To learn more, see the provenance guide.

About LaunchDarkly

  • LaunchDarkly is a continuous delivery platform that provides feature flags as a service and allows developers to iterate quickly and safely. We allow you to easily flag your features and manage them from the LaunchDarkly dashboard. With LaunchDarkly, you can:
    • Roll out a new feature to a subset of your users (like a group of users who opt-in to a beta tester group), gathering feedback and bug reports from real-world use cases.
    • Gradually roll out a feature to an increasing percentage of users, and track the effect that the feature has on key metrics (for instance, how likely is a user to complete a purchase if they have feature A versus feature B?).
    • Turn off a feature that you realize is causing performance problems in production, without needing to re-deploy, or even restart the application with a changed configuration file.
    • Grant access to certain features based on user attributes, like payment plan (eg: users on the 'gold' plan get access to more features than users in the 'silver' plan).
    • Disable parts of your application to facilitate maintenance, without taking everything offline.
  • LaunchDarkly provides feature flag SDKs for a wide variety of languages and technologies. Read our documentation for a complete list.
  • Explore LaunchDarkly