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

@emdzej/swsrs-client

v0.2.2

Published

Client SDK for swsrs (Simple WebSocket Relay Service): admin API + peer connection helpers for browser and Node 22+.

Readme

@emdzej/swsrs-client

TypeScript client for the swsrs relay. Works in browsers and Node 22+. Zero runtime dependencies — uses native fetch and WebSocket.

Install

pnpm add @emdzej/swsrs-client    # or: npm install / yarn add

This package is developed and published with pnpm. Consumers can install it with any package manager — the published artifact is the same.

Admin API

import { AdminClient } from "@emdzej/swsrs-client";

const admin = new AdminClient({
  baseURL: "https://relay.example.com",
  token: async () => await getOIDCToken(),  // called per request, may refresh
});

const session = await admin.createSession();
// { id, initiator_token, responder_token, expires_at, ... }

const status = await admin.getSession(session.id);
const all    = await admin.listSessions();
await admin.deleteSession(session.id);

token may be a sync function, async function, or plain string-returning function. It is invoked on every request so you can rotate without rebuilding the client.

Peer connection

dial and accept are wire-identical — the names express caller intent. Both return a PeerConnection wrapping a native WebSocket.

import { dial } from "@emdzej/swsrs-client";

const conn = await dial({
  relayURL: "wss://relay.example.com",  // http(s):// auto-upgraded
  sessionId: session.id,
  token: session.initiator_token,
});

conn.socket.addEventListener("message", (e) => {
  // e.data is ArrayBuffer (socket.binaryType defaults to "arraybuffer")
  console.log("got", new Uint8Array(e.data as ArrayBuffer));
});

conn.send(new TextEncoder().encode("hello"));

await conn.closed;  // resolves on disconnect with the CloseEvent

Why the token is in the URL

Browsers cannot set the Authorization header on a WebSocket upgrade, so the SDK passes ?token= instead. The swsrs server accepts either form. Tokens are short-lived and bound to a specific slot — but you should still always serve wss://, never ws://, to keep them out of plaintext.

Cancellation

Pass an AbortSignal to abort the handshake or close an open connection:

const ctrl = new AbortController();
setTimeout(() => ctrl.abort(), 5_000);

await dial({ relayURL, sessionId, token, signal: ctrl.signal });

End-to-end example: probe ↔ UI

// === UI side (browser) ===
const admin = new AdminClient({ baseURL: "https://relay.example.com", token: getOIDC });
const session = await admin.createSession();

// hand session.responder_token to the probe via your existing control plane
await myBackend.tellProbe(probeId, { sessionId: session.id, token: session.responder_token });

const conn = await dial({
  relayURL: "wss://relay.example.com",
  sessionId: session.id,
  token: session.initiator_token,
});

conn.socket.addEventListener("message", handleProbeData);
conn.send(encode({ cmd: "runCheck" }));

API reference

class AdminClient

  • new AdminClient({ baseURL, token, fetch? })
  • createSession(signal?): Promise<Session>
  • getSession(id, signal?): Promise<SessionStatus>
  • listSessions(signal?): Promise<SessionStatus[]>
  • deleteSession(id, signal?): Promise<void>

function dial(opts: PeerOptions): Promise<PeerConnection>

function accept(opts: PeerOptions): Promise<PeerConnection>

interface PeerOptions {
  relayURL: string;
  sessionId: string;
  token: string;
  signal?: AbortSignal;
  WebSocketImpl?: typeof WebSocket;  // tests
  protocols?: string | string[];
}

interface PeerConnection {
  readonly socket: WebSocket;
  readonly opened: Promise<void>;
  readonly closed: Promise<CloseEvent>;
  send(data: ArrayBufferLike | ArrayBufferView | Blob | string): void;
  close(code?: number, reason?: string): void;
}

Auth helpers

Don't have an OIDC token? Discover the relay's IdP and run device flow:

import { discoverConfig, deviceLogin, AdminClient } from "@emdzej/swsrs-client";
import { FileTokenStore } from "@emdzej/swsrs-client/node"; // Node only

const config = await discoverConfig("https://relay.example.com");
// config.issuer, config.token_endpoint, config.device_authorization_endpoint, ...

const tok = await deviceLogin({
  config,
  onPrompt: (p) => {
    console.log(`Visit ${p.verificationUri} and enter ${p.userCode}`);
  },
});

const store = new FileTokenStore();        // defaults to ~/.config/swsrs/credentials.json
await store.save(tok);

const admin = new AdminClient({
  baseURL: "https://relay.example.com",
  token: async () => {
    const t = await store.load();
    if (!t) throw new Error("no token; run device login again");
    return t.access_token;
  },
});

discoverConfig() throws AuthDisabledError when the server is running with --no-auth — callers can treat that as "no token needed".

Browser caveat

deviceLogin() runs in Node and in Electron, but most IdPs don't enable CORS on the token endpoint for device flow (it was designed for CLIs). If you call it from a browser context you'll see a CORS error. Browser apps should run their own auth-code + PKCE flow with an IdP-supported library and pass the resulting access token to AdminClient.token.

Known limitations

  • No transparent reconnect within the peer-wait grace window.
  • No application-layer pings (browsers can't send WS protocol pings). For long-lived sessions, ensure the other peer or the server pings.

License

MIT.