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

@rantingjs/socket

v0.1.0

Published

A WebSocket server for ranting apps, speaking the Pusher protocol: broadcasting to browsers without a hosted service

Readme

@rantingjs/socket

A WebSocket server for ranting apps. It speaks the Pusher protocol, so browsers connect with the Pusher JS client or Echo, and the app broadcasts to it with the framework's pusher broadcaster: real-time events without a hosted service.

bun add @rantingjs/socket
bunx ranting install @rantingjs/socket --skip-add   # SOCKET_APP_ID / KEY / SECRET, BROADCAST_CONNECTION=socket
bunx ranting socket:start                          # ranting dev starts it too

The app broadcasts through the socket connection of config/broadcasting.ts (SOCKET_APP_ID, SOCKET_APP_KEY, SOCKET_APP_SECRET, SOCKET_HOST, SOCKET_PORT, SOCKET_SCHEME). In the browser:

const pusher = new Pusher(SOCKET_APP_KEY, {
  wsHost: 'localhost', wsPort: 8080, forceTLS: false, enabledTransports: ['ws', 'wss'], cluster: '',
  channelAuthorization: { endpoint: '/broadcasting/auth', transport: 'ajax' },
});
pusher.subscribe('private-orders.1').bind('OrderShipped', (order) => { ... });

Laravel Echo works too, configured as for Reverb (broadcaster: 'reverb'): the framework's README has the example.

What it serves

  • GET /app/<key>: the WebSocket. pusher:connection_established gives the socket id and the activity timeout; pusher:subscribe / unsubscribe, pusher:ping / pong.
  • Channels: public, private-* and presence-* (signed by the app's /broadcasting/auth), and cache-* (private-cache-*, presence-cache-*), which keep their last event for whoever subscribes next (pusher:cache_miss when there is none). Presence channels send the members on subscribing, and member_added / member_removed once per user, not per tab.
  • Client events (client-*) on private and presence channels, from members only (acceptClientEventsFrom: members, all or none); the sender does not get its own.
  • The HTTP API, signed as Pusher's libraries sign it: POST /apps/<id>/events (with info), POST .../batch_events, GET .../channels (filter_by_prefix, info=user_count,subscription_count), GET .../channels/<name>, GET .../channels/<name>/users, GET .../connections, POST .../users/<id>/terminate_connections, and GET /up.
  • Upkeep: every pruneEvery seconds (60), connections that did not answer the last ping get error 4201 and are dropped, then those quiet for longer than pingInterval are pinged.
  • Limits per app: allowed origins (*.example.com), maxConnections (4004), maxMessageSize (closes with 1009), and a rate limit on messages (4301; terminateOnLimit closes the connection).

Errors are pusher:error with Pusher's codes: 4001 unknown app, 4004 over quota, 4009 origin or signature refused, 4200 a malformed message, 4201 no pong, 4301 rate limit or client events off. Refusals in the 4000 range end the connection, so the client does not reconnect into the same refusal.

Config

bunx ranting publish @rantingjs/socket copies src/config/socket.ts:

| Key | Default | | |---|---|---| | server.host, server.port | SOCKET_SERVER_HOST 0.0.0.0, SOCKET_SERVER_PORT 8080 | Where it listens (--host, --port). | | server.path | SOCKET_SERVER_PATH, empty | A prefix for every route (--path /ws); the broadcaster's path follows it. | | server.tls | SOCKET_TLS_CERT, SOCKET_TLS_KEY | Certificate and key files: wss:// and https://. | | server.maxRequestSize | 10000 | Bytes an API request may carry (413). | | server.pruneEvery | 60 | Seconds between prune-and-ping rounds. | | server.scaling | off | enabled (SOCKET_SCALING_ENABLED), channel, the redis connection, timeout for counts. | | apps[] | one app from SOCKET_APP_* | id, key, secret, allowedOrigins, pingInterval (60), activityTimeout (30), maxConnections, maxMessageSize (10000), acceptClientEventsFrom, rateLimiting. |

Several servers

Behind a load balancer, turn on server.scaling: every event, user termination and count goes through Redis pub/sub, so a broadcast reaches subscribers on every server, presence members are counted once across servers, and the API answers with totals. Servers that do not answer within timeout seconds are left out of a count.

Commands and events

  • socket:start [--host] [--port] [--path] [--debug] runs the server; --debug prints every message in and out. Ctrl+C closes every connection first.
  • socket:restart tells running servers to close their connections and stop (checked every 5 seconds), for a supervisor to start the new code.

The server fires, through the app's events: MessageReceived and MessageSent (connection and message), ChannelCreated, ChannelRemoved and ConnectionPruned. Only events something listens to are made.

new SocketServer({ apps, port, ... }).start() runs it without the CLI (tests, or another process manager); server.broadcast(appId, channels, event, data) sends from the same process.