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

@nostr-wot/relay

v0.1.1

Published

Standalone Nostr relay utilities: pool management, query batching, stats, low-level helpers.

Readme

@nostr-wot/relay

Low-level Nostr relay utilities. A pool that manages WebSocket connections, a query batcher that merges concurrent filters, and a stats tracker that records per-relay latency / success rates. Extracted from the WoT SDK so any Nostr project can use these primitives without pulling in WoT scoring code.

Most apps want @nostr-wot/data instead — it builds on top of these primitives and exposes higher-level fetchers (profiles, notes, threads, follows). Reach for @nostr-wot/relay only when you need to roll your own data layer or instrument relay performance.

Install

npm i @nostr-wot/relay nostr-tools

Two entrypoints

| Import path | What's in it | |---|---| | @nostr-wot/relay | RelayPool, RelayManager, QueryBatcher, RelayStats | | @nostr-wot/relay/react | React provider + hooks for the above |

What's in the box

RelayPool

A drop-in replacement for nostr-tools' SimplePool with reconnect handling and pluggable WebSocket implementations (e.g. for binary-frame coercion behind compressing proxies).

import { RelayPool } from "@nostr-wot/relay";

const pool = new RelayPool({
  relays: ["wss://relay.damus.io", "wss://nos.lol"],
  websocketImplementation: MyWebSocket,
});

const sub = pool.subscribeMany(
  pool.relays(),
  [{ kinds: [1], limit: 50 }],
  {
    onevent: (e) => { /* ... */ },
    oneose: () => { /* ... */ },
  },
);

await pool.publish(pool.relays(), signedEvent);
sub.close();

RelayManager

Higher-level orchestrator: tracks which relays are connected, applies a per-relay reconnect policy, surfaces a status observable for UIs.

import { RelayManager } from "@nostr-wot/relay";

const manager = new RelayManager({
  relays: ["wss://relay.damus.io"],
  reconnectBackoffMs: [1000, 5000, 15000],
});

manager.onStatusChange((url, status) => {
  // status: "connecting" | "connected" | "closed" | "error"
});

await manager.connect();

QueryBatcher

Merges concurrent reads with the same relay-set into a single REQ within a debounce window. Useful when many components ask for different events at once and you want to coalesce them on the wire.

import { QueryBatcher } from "@nostr-wot/relay";

const batcher = new QueryBatcher(pool, {
  debounceMs: 50,
  subscriptionTimeoutMs: 8000,
});

const events = await batcher.querySync(
  [{ kinds: [0], authors: [pubkey] }],
  { relays: ["wss://relay.damus.io"], timeoutMs: 5000 },
);

If you're using @nostr-wot/data, the sharedCoalescer exported from there is a singleton QueryBatcher-equivalent that the entire SDK shares.

RelayStats

Per-relay metrics: latency, EOSE timings, success/error counts. Persists optionally via a pluggable RelayStatsPersistence (defaults to localStorage).

import { RelayStats } from "@nostr-wot/relay";

const stats = new RelayStats({
  persistence: { kind: "localStorage", namespace: "myapp" },
});

stats.recordLatency("wss://relay.damus.io", 142);
const metrics = stats.snapshot("wss://relay.damus.io");
// → { medianLatencyMs, successCount, errorCount, ... }

React (/react)

import { RelayPoolProvider, useRelayPool, useRelayStats } from "@nostr-wot/relay/react";

<RelayPoolProvider relays={["wss://relay.damus.io"]}>
  <App />
</RelayPoolProvider>;

function StatusBadge() {
  const { connectedCount, totalCount } = useRelayPool();
  return <span>{connectedCount}/{totalCount} relays</span>;
}

Types

interface RelayPoolOptions { relays: string[]; websocketImplementation?: typeof WebSocket; }
type RelayStatus = "connecting" | "connected" | "closed" | "error";
interface RelayMetrics { medianLatencyMs: number; successCount: number; errorCount: number; }

See src/types.ts for the full surface.

License

MIT