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

@wtfalch/calls

v0.2.0

Published

Framework-independent SDK for Agora calls: session control (admission, guest links, moderation) and a media-ready browser call handle.

Readme

@wtfalch/calls

Framework-independent Calls SDK, in two entries.

@wtfalch/calls is the control client: session admission, guest links, moderation and guest credential status/rotation.

import { createCallsClient } from '@wtfalch/calls';
const calls = createCallsClient({ baseUrl: 'https://calls.example' });
await calls.joinAdmission(sessionId, { video: false, requestId: crypto.randomUUID() });

waiting(sessionId) lists the guests waiting in the lobby, oldest first, for a host who holds calls.participants:admit. record(sessionId) reads what happened in a call: when it started and ended, and when each participant was admitted and left. Anyone who was in the call can read it, and so can holders of calls.records:read. A guest's copy carries no member's actorId.

Member requests use same-origin cookies and exact-origin requests. Guest link redemption returns a handle whose opaque credential remains private to the handle. Errors are typed CallsError; requests are bounded and are never retried automatically.

@wtfalch/calls/browser is the media-ready call handle. join() returns a CallHandle with:

  • state() and subscribe() — a snapshot now and again on every change. It carries participants, the roster as { participantId, self, displayName, actorId?, media? }, so a changing roster needs no polling. media is { microphone, camera, screen, speaking, quality }, present when the adapter reports it, and a subscriber hears it change.
  • publish('audio' | 'video' | 'screen') and stopShare().
  • setMicrophoneEnabled() and setMode() — mute and camera without leaving the call.
  • attach(participantId, 'camera' | 'screen', videoElement) draws that participant's video and returns its detach. Your own id gives a local preview.
  • devices() and useDevice(kind, deviceId) — list and switch microphone, camera and speaker.
  • ready() and leave().

When the call ends, the last snapshot a subscriber hears has ended: { reason }: left (you called leave()), admission_expired (a guest was never let in), control_refused (joining failed), session_ended (the host ended it, or its window closed), participant_removed (a moderator removed you) or admission_lapsed (your own admission or credential expired, or lost eligibility). connection_unavailable remains for the cases the service still cannot tell apart: a race with a sweep that has not run yet, or a still-admitted row whose binding/media credential alone lapsed.

The handle renders nothing itself. You inject the media adapter; @wtfalch/calls/livekit below is the shipped one. With an adapter that lacks attach, devices or useDevice, those methods throw an error with code media_unsupported.

displayName is untrusted text. A guest declares its own and the service bounds it by length only, so whatever draws it owns the escaping.

The handle also takes an endpoint store, so this entry touches no device and picks no storage itself. It needs the peer dependency @matrix-org/[email protected], which is required rather than optional: browser.mjs imports the crypto machine statically, so importing this entry without the peer throws ERR_MODULE_NOT_FOUND before any of your code runs. It is a peer and not a dependency because the engine needs exactly one instance of it.

@wtfalch/calls/livekit

A real media adapter for @wtfalch/calls/browser, built on livekit-client. It owns devices, tracks and the LiveKit room; createCallEndpoint owns authority, keys and lifecycle. It needs the peer dependency [email protected], and it is optional: only import this entry if you use it.

livekit-client's E2EE worker is your file to serve, not this package's. Create it once and hand it in:

import { createLiveKitAdapter } from '@wtfalch/calls/livekit';
import { createCallEndpoint } from '@wtfalch/calls/browser';

const worker = new Worker(
  new URL('livekit-client/dist/livekit-client.e2ee.worker.mjs', import.meta.url),
  { type: 'module' },
);
const endpoint = createCallEndpoint({ adapter: createLiveKitAdapter({ worker }), store });
const call = await endpoint.join(/* ... */);
const detach = call.attach(participantId, 'camera', videoElement);

The adapter never terminates that worker, so the same instance carries through every reconnect the engine drives. Terminate it yourself when you are done with the endpoint for good.

attach() throws an error with code track_unavailable when that source is not published and subscribed yet. Call it again once the participant's media shows the source on. Mute keeps the capture, so unmuting never asks for the microphone again. Remote audio plays on its own; there is nothing to attach for it.

For a test, pass capture: { audio, video, screen }, each a function returning a MediaStreamTrack, to publish synthetic media instead of opening real devices.