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

@flefebvre/next-sse

v0.1.0

Published

Typed, authorization-aware pub/sub over Server-Sent Events for Next.js, gap-free across reconnects, redeploys and the server-render handoff.

Downloads

59

Readme

@flefebvre/next-sse

Typed, authorization-aware pub/sub over Server-Sent Events for the Next.js App Router — on one Instance, or on many behind a load balancer, with the same code.

Requires Next.js ≥ 16.3.0, React ≥ 19.2.0, Node ≥ 22.12. ESM only, App Router only.

Why

Keeping an SSE stream alive in production is where the work is. Proxies buffer the response and the page appears to hang. Connections die without the client noticing. Messages published while a client was reconnecting, or while the app was being redeployed, are gone. A reconnect replays what the client already had. Between a server component rendering its data and the stream going live there is a window in which updates vanish. Fan-out breaks the moment the app runs on more than one process. Rolling deploys hang on open streams.

This library is those problems, solved once. The claim is gap-free delivery: no Message lost or delivered twice across reconnects, redeploys, or the server-render handoff. Channels are declared in one server-only module with their event schemas and their authorization rule beside them, so publishing and receiving are type-checked end to end; the transport is chosen from the environment, and the manual ships inside the package.

Install

pnpm add @flefebvre/next-sse

redis (node-redis v6) comes along as a plain dependency, imported lazily: an app on the Memory Transport never loads it. Set REDIS_URL and the same code runs across many Instances on Redis or Valkey. A schema library is optional — any Standard Schema validator works, and asType<T>() covers events that need types without validation.

For agents

Add this line to your AGENTS.md or CLAUDE.md:

For @flefebvre/next-sse, read `node_modules/@flefebvre/next-sse/docs/index.md` first — it is the
manual for the exact installed version.

The full manual ships in the package, so what an agent reads is version-locked to the code next to it. There is no separate skill content to go stale, and nothing prints at install time.

Quickstart

1. Declare a Channel

// lib/sse.ts
import { initSSE } from "@flefebvre/next-sse/server";
import { z } from "zod";

import { sessionFrom, type Session } from "./session";

const { channel, createSSE } = initSSE({
  context: (request: Request): Session => sessionFrom(request.headers.get("cookie")),
  key: (session) => session.userId,
});

export const sse = createSSE({
  chat: channel({
    pattern: "chat:{roomId}",
    events: { message: z.object({ id: z.string(), text: z.string().min(1) }) },
    authorize: (params, session) => session.rooms.includes(params.roomId),
  }),
});

2. Serve the route

// app/api/sse/route.ts
import { sse } from "@/lib/sse";

export const { GET } = sse.handlers;

Mount <SSEProvider sse={sse}> once in a layout, and create the client half with createClient<typeof sse>() in a 'use client' module.

3. Publish from a server action

// app/actions.ts
"use server";

export async function send(roomId: string, text: string) {
  const message = appendMessage(roomId, text);
  await sse.chat({ roomId }).message(message);

  return message;
}

4. Render it live

// app/page.tsx (Server Component)
const messages = await sse.snapshot(() => listMessages(roomId));

return <Messages roomId={roomId} snapshot={messages} />;
// app/messages.tsx (Client Component)
const { state, status } = useChannelState(channels.chat({ roomId }), snapshot, {
  message: upsertBy("id"),
});

sse.snapshot takes the log's position before reading the data, so anything published between that read and the stream going live is replayed to the page — exactly once. The overlap is absorbed by upsertBy("id").

The package

Six subpaths, and no root export.

| Import from | Holds | Docs | | ------------------- | ----------------------------------------------------------------------------- | --------------------------------- | | /server | initSSE, SSEProvider, toClientChannels, asType, InvalidPayloadError | server.md | | /client | createClient, SSEClientProvider, upsertBy | client.md | | /transport | the Transport contract, its errors, createPublisher | transport.md | | /transport/memory | createMemoryTransport | transport.md | | /transport/redis | createRedisTransport, createRedisPublisher | transport.md | | /testing | transportContract, FakeEventSource, snapshotOf | testing.md |

/server resolves to the implementation only under the react-server condition; imported anywhere else it throws at module load, so a Client Component cannot pull the Registry — and whatever the Registry imports — into the browser bundle.

Going further

  • Setup — install to first Message, with a Done-when checklist.
  • Core concepts — the eleven words the API uses.
  • Patterns — ten recipes, each linking the example app that runs it.
  • Deployment — proxy checklist, multi-Instance, redeploys, environment.
  • Wire format — publish into an app from any language.
  • When not to use SSE — and what to use instead.

On Redis and Valkey: the integration suite exercises standalone servers, a real three-master cluster and a real master-plus-sentinel on every push (Redis 8 and Valkey 8; the floor is Redis 7.0 / Valkey 8), the last two through an injected createCluster or createSentinel client. Failover of the blocking reader is the one thing not covered.

License

MIT