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

@stackline/sse

v1.0.1

Published

Universal, spec-correct and memory-safe Server-Sent Events toolkit for AI streaming, browsers, servers and edge runtimes

Readme

@stackline/sse

npm version npm downloads CI CodeQL license

One zero-dependency toolkit for consuming, parsing, encoding, serving, and reconnecting Server-Sent Events. It is designed for AI token streams, live interfaces, serverless runtimes, browsers, and Node.js services.

npm install @stackline/sse

Why this package

SSE projects commonly combine one parser package, another encoder, a stale fetch wrapper, and custom server code. @stackline/sse gives those layers one consistent contract:

  • WHATWG-compatible incremental parsing of strings and UTF-8 bytes;
  • pull-based async iteration with real stream backpressure;
  • fetch streaming with POST, auth headers, retries, timeouts, and resume IDs;
  • safe event encoding that rejects CRLF and Last-Event-ID injection;
  • Web Stream and Response helpers for edge and server runtimes;
  • bounded line, event, and callback queues by default;
  • ESM, CommonJS, browser global, TypeScript 3.9 through 7, Deno, and Bun;
  • zero runtime dependencies.

AI streaming

import { fetchSSE } from '@stackline/sse';

const controller = new AbortController();

for await (const event of fetchSSE('https://api.example.com/responses', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.API_TOKEN}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ model: 'example-model', stream: true }),
  signal: controller.signal,
  connectTimeout: 10_000,
  idleTimeout: 45_000,
  totalTimeout: 5 * 60_000,
  retry: {
    retries: 3,
    minDelay: 500,
    maxDelay: 10_000
  }
})) {
  if (event.data === '[DONE]') break;
  console.log(event.event, JSON.parse(event.data));
}

fetchSSE accepts all ordinary fetch request options. Node.js 18 and newer provide fetch; Node.js 14 and 16 can pass an implementation with fetch.

Parse any stream

Async iterator

import { decodeSSE } from '@stackline/sse';

const response = await fetch('/events');

for await (const event of decodeSSE(response)) {
  console.log(event.event, event.data, event.lastEventId);
}

The source can be a Response, ReadableStream, AsyncIterable, or ordinary Iterable of string and Uint8Array chunks.

Incremental callback parser

import { createParser } from '@stackline/sse';

const parser = createParser({
  onEvent(event) {
    console.log(event.data);
  },
  onRetry(milliseconds) {
    console.log('Server retry interval:', milliseconds);
  }
});

parser.feed('id: 7\ndata: first chunk\n');
parser.feed('data: second chunk\n\n');

Each event contains:

interface SSEEvent<T = string> {
  data: T;
  event?: string;
  id?: string;         // ID field in this event block
  lastEventId: string; // committed resume ID, including inherited IDs
}

An id-only block commits lastEventId even when no message is dispatched. That detail matters when a connection closes immediately after a checkpoint.

JSON streams

import { decodeJSON } from '@stackline/sse';

for await (const event of decodeJSON(response, {
  doneSentinel: '[DONE]'
})) {
  console.log(event.data); // parsed JSON value
}

Invalid JSON throws SSEParseError. Set ignoreInvalidJSON: true only when a mixed text and JSON protocol intentionally requires it.

Encode events

import { encodeJSON, encodeSSE } from '@stackline/sse';

encodeSSE({
  id: '42',
  event: 'delta',
  retry: 3000,
  data: 'line one\nline two'
});

encodeJSON({ token: 'hello' }, { event: 'delta', id: '43' });

id and event values cannot contain line breaks. IDs also reject NUL. This prevents a value from injecting additional SSE fields or HTTP resume headers.

Serve events

Response from an async generator

import { eventStreamResponse } from '@stackline/sse';

async function* updates() {
  yield { event: 'ready', data: 'connected', id: '1' };
  yield { event: 'delta', data: 'hello', id: '2' };
}

export function GET() {
  return eventStreamResponse(updates());
}

The response includes text/event-stream, no-cache, no-transform, and X-Accel-Buffering: no headers unless the caller overrides them.

Push channel

import { createSSEChannel } from '@stackline/sse';

const channel = createSSEChannel({
  heartbeatInterval: 15_000
});

const response = channel.toResponse();

if (!channel.sendJSON({ progress: 25 }, { event: 'progress' })) {
  await channel.ready;
}

channel.close();

send and sendJSON return false when the stream applies backpressure. Wait for channel.ready before producing more data.

Reconnection behavior

fetchSSE follows SSE resume semantics and adds explicit production controls:

  • sends Accept: text/event-stream and Cache-Control: no-store behavior;
  • commits and forwards Last-Event-ID on reconnect;
  • honors valid retry: fields and Retry-After headers;
  • retries network failures and HTTP 408, 425, 429, 500, 502, 503, and 504;
  • rejects other HTTP statuses and incorrect content types;
  • uses exponential backoff with full jitter by default;
  • stops permanently on HTTP 204;
  • never replays a streaming request body without bodyFactory.

Native EventSource reconnects indefinitely, so the default retry budget is also unlimited. Production applications should pass an AbortSignal, a finite retry.retries, or totalTimeout.

const options = {
  retry: {
    retries: 5,
    minDelay: 500,
    maxDelay: 30_000,
    factor: 2,
    jitter: 'full'
  },
  onRetry({ delay, reconnects, error }) {
    console.warn({ delay, reconnects, error });
  }
};

For a body that must be recreated on every attempt, bodyFactory receives the attempt number, committed resume ID, and that attempt's abort signal:

const options = {
  bodyFactory({ attempt, lastEventId, signal }) {
    return createUploadStream({ attempt, lastEventId, signal });
  }
};

When the input is a Request, its headers are preserved unless options.headers explicitly replaces them. The SSE Accept and resume headers are then merged case-insensitively.

Memory safety

The parser is bounded by default:

| Limit | Default | Purpose | | --- | ---: | --- | | maxLineLength | 1 MiB | unterminated or oversized field line | | maxEventSize | 1 MiB | accumulated multiline event | | maxQueuedEvents | 4096 | callback burst inside one feed slice | | feedSize | 16 KiB | limits work admitted before yielding |

Raise a limit explicitly for a trusted protocol that carries larger events. Limit failures terminate the parser with a stable ERR_SSE_* code.

Migration

From eventsource-parser

Direct dependency:

npm install @stackline/sse

The familiar API is available:

import { createParser } from '@stackline/sse';

For a low-change trial, npm aliases preserve the old import name:

npm install eventsource-parser@npm:@stackline/sse

createParser({ onEvent, onRetry, onComment, onError, maxBufferSize }) is supported. The additional lastEventId property follows WHATWG resume semantics. Security limits are enabled by default, unlike unbounded parsers.

From @microsoft/fetch-event-source

npm install @stackline/sse
import { fetchEventSource } from '@stackline/sse';

await fetchEventSource('/events', {
  onopen(response) {},
  onmessage(event) {},
  onclose(context) {},
  onerror(error) {}
});

An alias can support staged migration:

npm install @microsoft/fetch-event-source@npm:@stackline/sse

The callback names are supported. openWhenHidden is accepted but this package does not silently disconnect a healthy stream when a page becomes hidden.

Runtime matrix

| Runtime | Parser / encoder | Fetch client | Server helpers | | --- | --- | --- | --- | | Modern browsers | Yes | Yes | Yes | | Node.js 18+ | Yes | Yes | Yes | | Node.js 14 / 16 | Yes | Inject fetch | Inject Web Streams if needed | | Deno 2 | Yes | Yes | Yes | | Bun | Yes | Yes | Yes | | Cloudflare Workers | Yes | Yes | Yes |

The package ships ESM, CommonJS, a browser IIFE, and declarations tested with TypeScript 3.9, 4.7, 4.9, 5.x, 6.x, and 7.x.

Errors

| Class | Code | Meaning | | --- | --- | --- | | SSEParseError | ERR_SSE_PARSE and specific variants | malformed or limited stream | | SSEEncodeError | ERR_SSE_ENCODE | unsafe or unsupported output field | | SSEHTTPError | ERR_SSE_HTTP | rejected HTTP response | | SSETimeoutError | ERR_SSE_TIMEOUT | connect, idle, or total deadline | | SSERetryError | ERR_SSE_RETRY | finite reconnect budget exhausted | | SSEReplayError | ERR_SSE_BODY_REPLAY | non-replayable request body |

Package integrity

  • zero runtime dependencies;
  • no install scripts;
  • deterministic ESM, CommonJS, and browser builds;
  • CI tests Node.js 14 through 24, Windows, macOS, Linux, Deno, and Bun;
  • CodeQL, npm audit, registry signature verification, publint, and Are the Types Wrong checks;
  • release tarballs include SHA-512 checksums and a CycloneDX SBOM.

See SECURITY.md for vulnerability reporting and CONTRIBUTING.md for development instructions.

Adoption resources

The examples ship in the npm tarball. Network examples expose functions and do not send requests during installation or import.

License

MIT Copyright 2026 Alexandro Paixao Marques.