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

@nice-code/devtools-relay

v0.102.0

Published

Downloads

2,286

Readme

@nice-code/devtools-relay

Most operators should manage deployed sessions through @nice-code/devtools-cli, which stores the mint response, reports the relay's effective TTL, and prints paste-ready producer URLs. This package remains the reusable relay host/core.

Docs: nicecode.io — guides, integrations, and the full API surface. Working with an AI assistant? Point it at nicecode.io/llms-devtools.txt (the devtools suite) or nicecode.io/llms.txt (the whole stack) — the complete, current docs flattened into plain text.

A tiny, reusable WebSocket relay that lets the nice-* devtools span browser storage partitions — a private window, a different browser profile, a different browser, even a different device — which same-origin BroadcastChannel cannot cross (each partition is isolated by design, which is also why those environments connect as a distinct client).

App clients and the devtools window each dial the relay; it rooms them by an app name and fans every frame out to the room's other members. It is purely ephemeral — no history, no content logging.

It has two modes: local rooms (?key=, unauthenticated, same-machine trust — everything below until noted) and deployable, token-authenticated sessions for inspecting live staging deployments (@nice-code/devtools-relay/cloudflare — see "Deploy it" below).

Runs on Bun (Bun.serve) or Node (node:http + ws), picked at runtime by startDevtoolsRelayAuto.

Let Vite run it for you

Usually you should not run this by hand at all. Add @nice-code/devtools-vite to your Vite config and the relay starts on the first devtools click, with every local client discovering it — no port to configure, no second terminal:

import { niceDevtools } from "@nice-code/devtools-vite";
export default defineConfig({ plugins: [react(), niceDevtools()] });

Or run it yourself

bunx @nice-code/devtools-relay            # port 5199, binds 0.0.0.0
bunx @nice-code/devtools-relay --port 6000 --host 127.0.0.1

Environment fallbacks: DEVTOOLS_RELAY_PORT, DEVTOOLS_RELAY_HOST.

Then point your app's relay URL at it — relayUrl on the devtools config, or VITE_DEVTOOLS_RELAY_URL in the demos. An explicit URL bypasses discovery entirely. The devtools transport connects with ?key=<app-name> so unrelated apps on one relay never cross streams.

GET /health answers { "service": "nice-devtools-relay", "protocol": 1, "rooms": N }. The service marker is what lets a prober tell a relay apart from anything else that happens to hold the port — liveness is not identity.

Embed it

import { startDevtoolsRelayAuto } from "@nice-code/devtools-relay";

const relay = await startDevtoolsRelayAuto({ port: 5199 });
// …
relay.stop();

startDevtoolsRelay (Bun) and startDevtoolsRelayNode (Node) are exported directly when you know which host you want. Both reject with an EADDRINUSE error — recognisable via isAddressInUseError — when the port is taken, which a caller racing several dev servers onto one well-known port should treat as "someone else's relay won" rather than a failure. probeRelay(url) answers "relay" | "other" | "down".

Deploy it — authenticated sessions on Cloudflare

Local ?key= rooms are same-machine trust. For anything a network can reach — inspecting a deployed staging frontend/backend from a devtools window — the relay has a second, heavily-gated mode: sessions, served from a Cloudflare Worker + one Durable Object room per session via @nice-code/devtools-relay/cloudflare.

import { DurableObject } from "cloudflare:workers";
import { handleDevtoolsRelayRequest, serveDevtoolsRelayRoom } from "@nice-code/devtools-relay/cloudflare";

export class DevtoolsRelayRoomDO extends DurableObject {
  private room = serveDevtoolsRelayRoom(this.ctx);
  fetch(r: Request) { return this.room.fetch(r); }
  webSocketMessage(ws: WebSocket, m: string | ArrayBuffer) { return this.room.webSocketMessage(ws, m); }
  webSocketClose(ws: WebSocket) { return this.room.webSocketClose(ws); }
  alarm() { return this.room.alarm(); }
}

export default {
  fetch: (request: Request, env: Env) =>
    handleDevtoolsRelayRequest(request, {
      rooms: env.DEVTOOLS_RELAY_ROOM,
      adminSecret: env.RELAY_ADMIN_SECRET, // absent ⇒ the whole relay is inert (fail-closed)
    }),
};

An operator mints a session (POST /sessions, admin-Bearer-gated, constant-time compare) and hands its short-lived token to the staging producers and the observing window. The room enforces admission — env: "production" is refused unconditionally, then token, capacity, and TTL (an alarm evicts the room at expiry). Nothing content-bearing is ever persisted: storage holds only the admission record, so a killed session yields no history.

A ready-to-deploy example lives in example/cloudflare/ (worker + wrangler config). Full guide: nicecode.io/devtools/remote-sessions.

Extend it

The room/fan-out logic lives in DevtoolsRelayHub, which is transport-agnostic (it forwards opaque frames and knows nothing about WebSockets or the devtools protocol). Wrap it in whatever carrier you need — the Bun server here, or a Node ws server. (The Cloudflare host above deliberately does not use it: a hibernatable Durable Object cannot keep a room in memory, so its fan-out reads ctx.getWebSockets() instead.)

import { DevtoolsRelayHub } from "@nice-code/devtools-relay";

const hub = new DevtoolsRelayHub();
hub.join(roomKey, conn); // conn: { send(data: string): void }
hub.relay(conn, frame); // → every other member of conn's room
hub.leave(conn);

Security

The local mode (?key= rooms) is an unauthenticated, ephemeral relay meant for local/dev use. The CLI binds 0.0.0.0 by default so other devices on your LAN can reach it — pass --host 127.0.0.1 to keep it same-machine only. (A relay started for you by the Vite plugin binds loopback by default instead, on the grounds that a server nobody consciously started should not be reachable from the network.) Do not expose it to the public internet.

Anything deployed belongs to session mode (above): admin-gated minting, token-authenticated joins, per-room capacity, frame rate limits, short TTLs, and an unconditional refusal of production-declared participants. Neither mode holds anything at rest.