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

@onwardz/js

v0.4.2

Published

Embed peer-to-peer chat rooms powered by Onwardz

Downloads

1,272

Readme

@onwardz/js

Embed real-time peer-to-peer chat rooms into any web app.
Built on WebRTC + WebRTC Data Channels — no infrastructure cost for data.


Installation

npm install @onwardz/js

Quick start

1. Create a room and issue a token (server-side)

Use @onwardz/node on your backend to create a room and issue a short-lived token so browser clients can join without exposing your API key:

import { OnwardzClient } from '@onwardz/node';

const onwardz = new OnwardzClient({ apiKey: process.env.ONWARDZ_API_KEY });

const { room_id } = await onwardz.createRoom({ max_participants: 10 });
const { token }   = await onwardz.issueToken(room_id);
// pass room_id and token to the browser

2. Join from the browser

import { OnwardzRoom } from '@onwardz/js';

const room = new OnwardzRoom({
  roomId: 'abc-123',
  token: 'eyJ...', // from your backend
});

room.on('peer:joined', ({ peerId }) => console.log('joined:', peerId));
room.on('peer:left',   ({ peerId }) => console.log('left:',   peerId));
room.on('message',     ({ from, text }) => console.log(from, ':', text));

const { peerId, peers } = await room.join();
console.log('my peer id:', peerId);
console.log('already in room:', peers);

room.send('hello world');

// later…
await room.leave();

Drop-in Web Component

No framework needed:

<script type="module">
  import '@onwardz/js/element';
</script>

<onwardz-room
  room-id="abc-123"
  token="eyJ..."
  local-name="Alex"
></onwardz-room>

The component renders a full video+chat embed (video grid, media controls, chat sidebar).

Sizing

<onwardz-room> fills the viewport by default (height: 100dvh), like a full-screen call — no parent height or extra CSS required.

To embed it in a fixed-size box instead, set the --onwardz-room-height (and optionally --onwardz-room-width) custom properties:

onwardz-room {
  --onwardz-room-height: 600px;
  --onwardz-room-width: 900px;
}

A plain height rule on the onwardz-room selector is ignored — the element's shadow :host style wins on specificity. Use the custom properties above to resize it.

Attributes

| Attribute | Type | | Description | |---|---|---|---| | room-id | string | ✅ | Room ID to join | | token | string | ✅ | JWT token issued server-side via issueToken() | | local-name | string | optional | Display name shown to other participants | | server-url | string | optional | Override for self-hosted deployments. Defaults to https://api.onwardz.com/rpc | | show-feedback | boolean | optional | Show a post-call feedback overlay when the user hangs up |

Events

| Event | Detail | Description | |---|---|---| | onwardz-hangup | — | The local user clicked "End call" (fires after the feedback overlay is dismissed if show-feedback is set) |


API Reference

new OnwardzRoom(options)

| Option | Type | Default | Description | |---|---|---|---| | roomId | string | — | ✅ Room ID to join | | token | string | — | ✅ JWT room token issued server-side via rooms.issue_token | | serverUrl | string | https://api.onwardz.com/rpc | Override for self-hosted deployments | | localName | string | — | Display name announced to peers on connect | | localStream | MediaStream | — | Camera/mic stream to share with peers | | iceServers | object[] | — | Custom STUN/TURN config |

Methods

| Method | Returns | Description | |---|---|---| | join() | Promise<{ peerId, peers }> | Join the room and start the WebRTC mesh. Returns this peer's ID and the list of peer IDs already in the room at join time. | | send(text) | void | Broadcast a text message to all peers | | startScreenShare(stream) | Promise<void> | Share a screen stream | | stopScreenShare() | void | Stop screen sharing | | leave() | Promise<void> | Leave the room and clean up | | on(event, handler) | this | Register an event listener (chainable) |

Properties

| Property | Type | Description | |---|---|---| | peerId | string\|null | This peer's ID (available after join()) | | roomPeers | string[] | Peer IDs acknowledged by the server — available immediately after join() resolves and kept in sync as peers join/leave | | connectedPeers | string[] | Peer IDs with an open WebRTC data channel (a lagging subset of roomPeers) | | showBranding | boolean | true on free-tier accounts — show the Onwardz branding badge |

Events

| Event | Detail | Description | |---|---|---| | peer:joined | { peerId } | A peer connected | | peer:left | { peerId } | A peer disconnected | | message | { from, text } | A peer sent a text message | | peer:track | { peerId, stream } | Remote camera stream arrived | | peer:screen-track | { peerId, stream } | Remote screen share started | | peer:screen-stop | { peerId } | Remote peer stopped screen sharing |

Error codes

Import RpcErrors to handle server errors without hardcoding numbers:

import { OnwardzRoom, RpcErrors } from '@onwardz/js';

try {
  await room.join();
} catch (err) {
  if (err.code === RpcErrors.ROOM_FULL)    showToast('Room is full');
  if (err.code === RpcErrors.ROOM_EXPIRED) showToast('This room has expired');
  if (err.code === RpcErrors.CAP_REACHED)  showToast('Usage limit reached — upgrade your plan');
  if (err.code === RpcErrors.FORBIDDEN)    showToast('Invalid or expired token');
}

| Constant | Code | Meaning | |---|---|---| | RpcErrors.ROOM_NOT_FOUND | -32000 | Room does not exist or was deleted | | RpcErrors.PEER_NOT_FOUND | -32001 | Peer ID not in signaling state | | RpcErrors.FORBIDDEN | -32003 | Invalid/missing API key or token | | RpcErrors.ROOM_FULL | -32004 | Room has reached its participant cap | | RpcErrors.ROOM_EXPIRED | -32005 | Room's expiry timestamp has passed | | RpcErrors.CAP_REACHED | -32006 | Account monthly minute quota exhausted |


With Media (camera/mic)

const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });

const room = new OnwardzRoom({
  roomId: 'abc-123',
  token: '...',
  localStream: stream,
});

room.on('peer:track', ({ peerId, stream }) => {
  const video = document.createElement('video');
  video.srcObject = stream;
  video.autoplay = true;
  document.body.appendChild(video);
});

await room.join();

Self-hosted deployments

If you run your own Onwardz server, pass serverUrl explicitly:

const room = new OnwardzRoom({
  roomId: 'abc-123',
  token: '...',
  serverUrl: 'https://api.your-domain.com/rpc',
});

License

MIT