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 🙏

© 2024 – Pkg Stats / Ryan Hefner

event-source-plus

v0.1.8

Published

A better EventSource API

Downloads

9,423

Readme

Event Source Plus

A more configurable EventSource implementation that runs in browsers, NodeJS, and workers. The default browser EventSource API is too limited. Event Source Plus fixes that.

Features

  • Use any HTTP method
  • Send custom headers
  • Optionally change headers when retrying
  • Pass data as body or query params
  • Runs in browsers, NodeJS, and workers
  • First class typescript support
  • Automatic retry with exponential backoff and hooks for customizing behavior
  • ESM and CommonJS support

Table of Contents

Installation

# npm
npm i event-source-plus

# pnpm
pnpm i event-source-plus

Usage

Basic Request

import { EventSourcePlus } from "event-source-plus";

const eventSource = new EventSourcePlus("https://example.com");

eventSource.listen({
    onMessage(message) {
        console.log(message);
    },
});

Canceling Requests

The listen() method returns a controller that you can use to abort the request.

const controller = eventSource.listen({
    onMessage(message) {
        console.log(message);
    },
});
controller.abort();

Additional Options

The EventSourcePlus constructor allows you to pass additional fetch options such as method, body, and headers.

const eventSource = new EventSourcePlus("https://example.com", {
    method: "post",
    body: JSON.stringify({ message: "hello world" }),
    headers: {
        "Content-Type": "application/json",
    },
});

You can also pass in a custom fetch implementation, which is useful for environments that don't natively support fetch.

const eventSource = new EventSourcePlus("https://example.com", {
    fetch: myCustomFetch,
});

Working with Headers

Headers can be set by passing an object or a function. The function may return a header object or a promise that resolves to a header object.

// object syntax //
const eventSource = new EventSourcePlus("https://example.com", {
    // this value will remain the same for every request
    headers: {
        Authorization: "some-token",
    },
});

// function syntax //
function getHeaders() {
    return {
        Authorization: "some-token",
    };
}
const eventSource = new EventSourcePlus("https://example.com", {
    // this function will rerun every time a request is sent
    headers: getHeaders,
});

// async function syntax //
async function getHeaders() {
    const token = await getSomeToken();
    return {
        Authorization: token,
    };
}
const eventSource = new EventSourcePlus("https://example.com", {
    // this function will rerun every time a request is sent
    headers: getHeaders,
});

The function syntax is especially useful when dealing with authentication because it allows you to always get a fresh auth token. This usually a pain point when working other SSE client libraries.

Customizing Retry Behavior

By default this library will automatically retry the request indefinitely with exponential backoff maxing out at 30 seconds. Both those these values can be adjusted when initializing the EventSourcePlus class.

const eventSource = new EventSourcePlus("https://example.com", {
    // automatically retry up to 100 times (default is 'undefined')
    maxRetryCount: 100,
    // set exponential backoff to max out at 10000 ms (default is "30000")
    maxRetryInterval: 10000,
});

Additionally, you can abort the request inside listen hooks using the EventSourceController

// abort the request if we receive 10 server errors
let errCount = 0;
const controller = eventSource.listen({
    onMessage(data) {},
    onResponseError({ request, response, options }) {
        errCount++;
        if (errCount >= 10) {
            controller.abort();
        }
    },
});

Changing the Retry Strategy (Beta)

This library has two retry strategies. always and on-error.

always is the default. It will always attempt to keep the connection open after it has been closed. This is useful for most realtime applications which need to keep a persistent connection with the backend.

on-error will only retry if an error occurred. If an event stream was successfully received by the client it will not reconnect after the connection is closed. This is useful for short lived streams that have a fixed length (For example LLM response streams) since it means you no longer need to listen for a "DONE" event to close the connection.

To change the retry strategy simply update the retryStrategy option:

const eventSource = new EventSourcePlus("https://example.com", {
    retryStrategy: "on-error",
});

The on-error strategy is a BETA feature. If you are using this in your LLM applications or for other purposes please give feedback so that I can make sure all edge cases are being accounted for.

Listen Hooks

The listen() method has the following hooks:

  • onMessage
  • onRequest
  • onRequestError
  • onResponse
  • onResponseError

The only required hook is onMessage.

onMessage(message)

onMessage is called whenever receiving a new Server Sent Event from the server.

eventSource.listen({
    onMessage(message) {
        console.log(message);
    },
});

onRequest({ request, options })

onRequest is called as soon as a request is constructed. This allows you to modify the request or do simple logging.

eventSource.listen({
    onRequest({ request, options }) {
        console.log(request, options);

        // add current time query search params
        options.query = options.query || {};
        options.query.t = new Date();
    },
});

onRequestError({request, options, error})

onRequestError will be called when the request fails.

eventSource.listen({
    async onRequestError({ request, options, error }) {
        console.log(`[request error]`, request, error);
    },
});

Some example errors might be Connection refused or Failed to parse URL

onResponse({ request, options, response })

onResponse will be called after receiving a response from the server.

eventSource.listen({
    async onResponse({ request, response, options }) {
        console.log(`Received status code: ${response.status}`);
    },
});

onResponseError({ request, options, response })

onResponseError will fire if one of the following conditions have been met

  • response.ok is not true (i.e. server returned an error status code)
  • The Content-Type header sent by the server doesn't include text/event-stream
eventSource.listen({
    async onResponseError({ request, response, options }) {
        console.log(
            `[response error]`,
            request,
            response.status,
            response.body,
        );
    },
});

Contributing

Pull requests and issue reports are welcome.

Before submitting a PR please ensure that you have run the following commands and there are no errors.

pnpm run lint
pnpm run format

(For VSCode users "formatOnSave" is set to true. So the formatting step may be unnecessary)

Integration tests and unit tests get run by CI.