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

osamlive

v1.0.1

Published

Official SDK for OsamLive — video/audio calling and live streaming for web, Next.js, Node.js, and React Native.

Readme

OsamLive

Video calling, group calls, and live streaming (with OBS Studio / RTMP support) for your own app — the same model as ZegoCloud or Agora, built on the open-source LiveKit media engine. This package is the SDK: it mints join tokens and manages rooms through your OsamLive project, and gives you a ready-made React layer for the actual call UI.

  • Works everywhere on the server: Node.js, Next.js (API routes, Server Actions, Route Handlers), Express, serverless functions, even Edge runtimes — it's built on nothing but the global fetch.
  • Works in the browser: a React hook + component for Next.js/React/Vite apps.
  • Works in React Native: the server SDK runs fine inside a React Native app too (again, just fetch), and pairs with LiveKit's own official React Native SDK for the actual call screen — see the React Native section.

Compatibility

  • React: 16.8 and up — 16, 17, 18, and 19 all work (osamlive/react only uses hooks and JSX that have existed since 16.8; nothing 18/19-only). The published types import plain ReactElement/CSSProperties from react rather than the newer React.JSX namespace, so they resolve cleanly under @types/react for any of those versions too.
  • TypeScript: no minimum version pinned — the shipped .d.ts files avoid anything recent enough to trip up older compilers. Using this from plain JavaScript (no TypeScript at all) works fine too; the types are there if you want them, not required.
  • livekit-client: 1.x and 2.x. The server SDK (osamlive) doesn't depend on it at all — only osamlive/react does, and only on the stable core API (Room, Participant, Track, RoomEvent) that hasn't changed across that range.
  • Node.js: 18+ (needs global fetch; polyfill it yourself on 16 if you're stuck there).

Install

npm install osamlive

If you're building the call UI in a browser (React/Next.js), also install LiveKit's client SDK, which this package's React helpers wrap:

npm install livekit-client

The two halves of this SDK

| | Where it runs | What it needs | Import | |---|---|---|---| | Server SDK | Your backend only | Your project's App Secret | import OsamLive from "osamlive" | | React helpers | Browser / client component | A { token, wsUrl } pair from your backend | import { useOsamLiveRoom } from "osamlive/react" |

Never put your App Secret in browser or mobile app code. The flow is always:

Your client (browser/app)  →  your backend  →  osamlive server SDK  →  OsamLive
       ▲                                                                   │
       └──────────────────── { token, wsUrl } ────────────────────────────┘

Your backend mints a fresh, short-lived token per real user right before they join a call. Never share one token across users, and never mint tokens ahead of time and store them.

Getting your credentials

  1. Log in to your OsamLive dashboard as a developer.
  2. Create a project. You'll be shown an App Key exactly once — <appId>.<secret>. Save it somewhere safe (an environment variable, a secrets manager).
# .env (server-side only — never NEXT_PUBLIC_/EXPO_PUBLIC_/etc.)
OSAMLIVE_APP_KEY=proj_xxxxxxxxxxxxxxxx.yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
OSAMLIVE_BASE_URL=https://live.yourdomain.com

OSAMLIVE_BASE_URL is the domain of your OsamLive deployment (self-hosted or the hosted service you were given). If you're self-hosting, this is the domain you set up for the dashboard/API — see your OsamLive service's own deployment docs.

Quick start — Node.js

import { OsamLive } from "osamlive";

const client = new OsamLive({
  appKey: process.env.OSAMLIVE_APP_KEY,
  baseUrl: process.env.OSAMLIVE_BASE_URL,
});

const { token, wsUrl } = await client.createToken({
  roomName: "team-standup",
  identity: currentUser.id,
  name: currentUser.name,
});

// Send { token, wsUrl } to whichever client (browser/app) is about to join.

Next.js

API route / Route Handler (app/api/call-token/route.ts) — this is where the App Key actually lives:

import { NextResponse } from "next/server";
import { OsamLive } from "osamlive";
import { getCurrentUser } from "@/lib/auth"; // however you already do this

const client = new OsamLive({
  appKey: process.env.OSAMLIVE_APP_KEY!,
  baseUrl: process.env.OSAMLIVE_BASE_URL!,
});

export async function POST(req: Request) {
  const user = await getCurrentUser(req);
  if (!user) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });

  const { roomName } = await req.json();

  const { token, wsUrl } = await client.createToken({
    roomName,
    identity: user.id,
    name: user.name,
  });

  return NextResponse.json({ token, wsUrl });
}

Client component — fetches a token from your own route above, then joins:

"use client";

import { useEffect, useState } from "react";
import { useOsamLiveRoom, ParticipantView } from "osamlive/react";

export default function CallRoom({ roomName }: { roomName: string }) {
  const { connect, disconnect, isConnected, participants, toggleCamera, toggleMicrophone } =
    useOsamLiveRoom();
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    (async () => {
      const res = await fetch("/api/call-token", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ roomName }),
      });
      const { token, wsUrl } = await res.json();
      await connect({ token, wsUrl }).catch((err) => setError(err.message));
    })();
    return () => disconnect();
  }, [roomName]);

  if (error) return <p>Could not join call: {error}</p>;

  return (
    <div>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(200px, 1fr))", gap: 12 }}>
        {participants.map((p) => (
          <ParticipantView
            key={p.identity}
            participant={p}
            mirror={p.isLocal}
            style={{ aspectRatio: "16 / 9", borderRadius: 12, overflow: "hidden", background: "#111" }}
          />
        ))}
      </div>

      {isConnected && (
        <div style={{ marginTop: 12, display: "flex", gap: 8 }}>
          <button onClick={toggleCamera}>Toggle camera</button>
          <button onClick={toggleMicrophone}>Toggle mic</button>
          <button onClick={disconnect}>Leave call</button>
        </div>
      )}
    </div>
  );
}

Plain React (Vite, CRA, etc.)

Same as the Next.js client component above — osamlive/react doesn't depend on Next.js at all. Just point your fetch("/api/call-token") call at wherever your backend (any Node server, not necessarily Next.js) exposes the token-minting endpoint.

React Native

The server SDK (osamlive, not osamlive/react) is plain fetch-based JavaScript, so it also runs inside a React Native app's JS engine without any changes — but exactly as on web, it should still only ever run on your backend, not inside the app bundle (a compiled mobile app is not a secure place for a secret either — it can be decompiled). Have your backend expose the same kind of token endpoint shown in the Next.js example above, and call it from the app with fetch.

For the actual call screen, pair the token your backend gives you with LiveKit's own official React Native SDK, @livekit/react-native (it's the native counterpart to the livekit-client the web helpers in this package wrap — OsamLive is a LiveKit-based service, so any LiveKit-compatible client SDK works against it):

npm install @livekit/react-native @livekit/react-native-webrtc livekit-client
npx pod-install # iOS
import { useState, useEffect } from "react";
import { LiveKitRoom, useTracks, VideoTrack } from "@livekit/react-native";
import { Track } from "livekit-client";
import { View, Button } from "react-native";

export function CallScreen({ roomName }: { roomName: string }) {
  const [creds, setCreds] = useState<{ token: string; wsUrl: string } | null>(null);

  useEffect(() => {
    fetch("https://your-backend.com/api/call-token", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ roomName }),
    })
      .then((res) => res.json())
      .then(setCreds);
  }, [roomName]);

  if (!creds) return null;

  return (
    <LiveKitRoom serverUrl={creds.wsUrl} token={creds.token} connect audio video>
      <ParticipantGrid />
    </LiveKitRoom>
  );
}

function ParticipantGrid() {
  const tracks = useTracks([Track.Source.Camera]);
  return (
    <View style={{ flex: 1 }}>
      {tracks.map((t) => (
        <VideoTrack key={t.publication.trackSid} trackRef={t} style={{ flex: 1 }} />
      ))}
    </View>
  );
}

This React Native example follows @livekit/react-native's own documented API — check that package's docs for the exact hooks/props on the version you install, since native SDKs move faster than this README. The one thing specific to OsamLive here is the token: however you fetch { token, wsUrl } from your backend is the only OsamLive-specific part; everything else is standard LiveKit React Native usage.

OBS Studio / RTMP streaming

Instead of a browser/app publishing camera+mic, let an external encoder stream into the room:

const ingress = await client.createIngress({
  roomName: "team-standup",
  identity: "host",
  name: "Host",
});

console.log(ingress.serverUrl); // paste into OBS → Settings → Stream → Server
console.log(ingress.streamKey); // paste into OBS → Settings → Stream → Stream Key

// When the broadcast ends:
await client.stopIngress(ingress.ingressId);

Viewers who already joined via createToken see the OBS feed exactly like any other publishing participant — no extra code needed on their side.

API reference

new OsamLive(options)

| Option | Type | Required | Description | |---|---|---|---| | appKey | string | one of appKey or appId+secret | <appId>.<secret>, shown once at project creation | | appId | string | | Alternative to appKey | | secret | string | | Alternative to appKey | | baseUrl | string | recommended | Your OsamLive deployment's URL |

client.createToken({ roomName, identity, name?, canPublish?, canSubscribe? })

→ Promise<{ token: string; wsUrl: string; iceServers?: IceServer[] }>. Mint a join token for one participant. canPublish/canSubscribe default to true; set canPublish: false for a view-only participant. iceServers is present only if your OsamLive deployment has TURN configured — pass it straight through to connect() (see below) for reliable connectivity behind restrictive NATs/firewalls.

client.createRoom({ roomName })

→ Promise<{ roomName: string }>. Optional — a room is created automatically the first time someone joins with a token for it.

client.endRoom(roomName)

→ Promise<void>. Ends the room immediately, disconnecting everyone in it.

client.createIngress({ roomName, identity, name? })

→ Promise<{ ingressId: string; serverUrl: string; streamKey: string }>. See OBS Studio / RTMP streaming above.

client.stopIngress(ingressId)

→ Promise<void>.

Errors

Every method throws OsamLiveError ({ name: "OsamLiveError", message, status }) on failure — wrap calls in try/catch and read .message for a user-facing reason and .status for the HTTP status code.

osamlive/react

  • useOsamLiveRoom() → { room, isConnected, isConnecting, error, participants, connect, disconnect, toggleCamera, toggleMicrophone, isCameraEnabled, isMicrophoneEnabled }. connect({ token, wsUrl, publish?, iceServers? }) — pass iceServers straight through from createToken's result for reliable connectivity if your deployment has TURN configured.
  • <ParticipantView participant={p} mirror? className? style? /> — renders one participant's video (and, for remote participants, audio). Set mirror only on the local participant's own tile.

License

MIT