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

@noego/wire

v0.0.1

Published

Relay client for multi-device messaging

Downloads

58

Readme

@noego/wire

Relay client for multi-device messaging via the Beacon protocol. Handles pairing, SSE streaming, reconnection, dedup, and heartbeat monitoring.

Zero runtime dependencies — uses built-in fetch and manual SSE parsing.

Quick Start

import { WireClient } from '@noego/wire';

// Register a new device (static — before you have a connection)
const registration = await WireClient.register('http://localhost:3080', {
  deviceName: 'My Desktop',
  deviceType: 'desktop',
});
// → { userId, deviceId, authToken, pairingCode, expiresAt }

// Claim a pairing code from another device
const claim = await WireClient.claim('http://localhost:3080', {
  pairingCode: 'ABC123',
  deviceName: 'My Phone',
});
// → { userId, deviceId, authToken, desktopDeviceId, desktopDeviceName }

// Create a connected client
const wire = new WireClient({
  beaconUrl: 'http://localhost:3080',
  deviceId: registration.deviceId,
  authToken: registration.authToken,
});

await wire.connect();

// Send messages
await wire.send({
  target: { kind: 'device', deviceId: 'dev_abc123' },
  type: 'chat.send-message',
  payload: { content: 'Hello' },
});

// Receive messages
wire.messages$.subscribe((msg) => {
  console.log(msg.type, msg.payload);
});

// Or use the event-based API
wire.on('message', (msg) => { /* ... */ });

// Refresh the session without losing registered handlers
await wire.reconnect();

// Cleanup
wire.close();

API

Static Methods

| Method | Purpose | |--------|---------| | WireClient.register(beaconUrl, opts) | Register a new device, get credentials + pairing code | | WireClient.claim(beaconUrl, opts) | Claim a pairing code, get credentials for paired device |

Instance Methods

| Method | Purpose | |--------|---------| | connect() | Create session + open SSE stream | | reconnect() | Refresh the session/stream on the same instance while preserving handlers | | send(input) | Send a message, get acknowledgement | | request(input, timeoutMs?) | Send + wait for correlated response | | on(event, handler) | Subscribe to events — returns unsubscribe function | | close() | Close connection, cancel pending requests |

Properties

| Property | Type | Purpose | |----------|------|---------| | isConnected | boolean | Connection state | | currentSessionId | string \| null | Active session ID | | messages$ | { subscribe(handler): { unsubscribe() } } | Observable-compatible message stream |

Events

| Event | Payload | When | |-------|---------|------| | open | — | SSE connection established | | message | RelayMessage | Message received | | close | — | Connection closed | | error | Error | Connection error | | reconnecting | — | Attempting reconnect |

Protocol Features

  • Reconnection: Exponential backoff (500ms → 5s) on connection loss
  • Heartbeat: Monitors server keepalive, reconnects on timeout (2.5× heartbeat interval)
  • Dedup: Client-side by messageId — monotonic, persisted cursor
  • Stream ordering: Validates streamSeq is strictly increasing, reconnects on violation
  • Request/response: Correlation via requestId with configurable timeout

Reconnect Behavior

Event handlers registered with on() and messages$ live on the WireClient instance.

  • Reusing the same instance preserves the listener registry.
  • Creating a new WireClient(...) creates a new, empty listener registry.
  • For routine transport recovery, call wire.reconnect().
  • If you intentionally called wire.close(), you can call wire.connect() again on the same instance.

Wrapper services should keep a single long-lived WireClient and reconnect that instance instead of replacing it:

class RealtimeSocket {
  private readonly wire = new WireClient(config);

  async connect(): Promise<void> {
    await this.wire.connect();
  }

  async recoverTransport(): Promise<void> {
    await this.wire.reconnect();
  }
}

Testing

npm test

Integration tests boot a real @noego/beacon server in-process — two wires exchange messages over real HTTP + SSE.