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

@mena-stream/client

v1.0.1

Published

MENA-Stream JavaScript SDK

Readme

@mena-stream/client

JavaScript/TypeScript SDK for MENA-Stream WebRTC API.

npm version License: MIT GitHub


What is MENA-Stream?

MENA-Stream is a production-grade WebRTC CPaaS platform built for the Arab market.

Architecture:

  • This SDK — Open-source (MIT). Handles all WebRTC complexity in the browser.
  • Backend — Built on mediasoup, extended with proprietary optimizations for low-latency real-time communication. Available via Cloud API or Self-Hosted Enterprise.

Benchmarks

Tested on a $29/month server:

| Metric | Result | |------------------------------|:---------:| | Concurrent Users (Signaling) | 1,642 | | Concurrent WebRTC Producers | 200 | | Server CPU at peak | 13% | | HTTP Failure Rate | 0% | | Success Rate | 100% |


Installation

npm install @mena-stream/client

Or via CDN:

<script src="https://cdn.jsdelivr.net/npm/@mena-stream/client/dist/mena-stream-sdk.js"></script>

Quick Start

import MenaStreamClient from '@mena-stream/client';

// Initialize
const client = new MenaStreamClient({
  apiKey: 'sk_live_...',
  baseUrl: 'https://api.mena-stream.com'
});

// Create a room
const room = await client.createRoom({ name: 'My Meeting' });

// Join the room
const session = await room.join({ displayName: 'Ahmed' });

// Enable camera & microphone
const stream = await session.enableCamera();
document.getElementById('local-video').srcObject = stream;

API Reference

new MenaStreamClient(config)

const client = new MenaStreamClient({
  apiKey: 'sk_live_...',   // Required — get from dashboard.mena-stream.com
  baseUrl: 'https://...',  // Optional — defaults to MENA-Stream Cloud
  wsUrl:   'wss://...',    // Optional — defaults to baseUrl with ws protocol
});

client.createRoom(opts)

Create a new video room.

const room = await client.createRoom({
  name:            'Weekly Standup', // Required
  maxParticipants: 50,               // Optional — default: 50
  expiresIn:       3600,             // Optional — seconds, default: 3600
});

console.log(room.id);       // Room ID
console.log(room.name);     // Room name
console.log(room.joinUrl);  // Shareable join URL

client.getRoom(roomId)

Get an existing room by ID.

const room = await client.getRoom('room_id_here');

room.join(opts)

Join a room and start a WebRTC session.

const session = await room.join({
  displayName: 'Ahmed',          // Required
  role: 'participant',           // Optional: 'participant' | 'moderator'
});

console.log(session.participantId); // Your participant ID

Roles:

  • participant — Regular user. Will wait for admission if host has enabled it.
  • moderator — Host. Enables admission control automatically.

room.close()

Close the room and disconnect all participants.

await room.close();

session.enableCamera(opts)

Enable camera and microphone.

const stream = await session.enableCamera({
  video: true,  // Optional — default: true
  audio: true,  // Optional — default: true
});

// Display local video
document.getElementById('local-video').srcObject = stream;

session.shareScreen()

Start screen sharing.

const stream = await session.shareScreen();
document.getElementById('screen-video').srcObject = stream;

session.stopScreenShare()

Stop screen sharing.

session.stopScreenShare();

session.consume(producerId)

Consume a remote participant's media track.

session.addEventListener('newProducer', async ({ detail }) => {
  const track = await session.consume(detail.producerId);

  if (detail.kind === 'video') {
    const video = document.createElement('video');
    video.srcObject = new MediaStream([track]);
    video.autoplay = true;
    document.body.appendChild(video);
  } else {
    const audio = document.createElement('audio');
    audio.srcObject = new MediaStream([track]);
    audio.autoplay = true;
    document.body.appendChild(audio);
  }
});

session.setAudioEnabled(enabled)

Mute or unmute microphone.

session.setAudioEnabled(false); // Mute
session.setAudioEnabled(true);  // Unmute

session.setVideoEnabled(enabled)

Enable or disable camera.

session.setVideoEnabled(false); // Camera off
session.setVideoEnabled(true);  // Camera on

session.getParticipants()

Get the list of current participants.

const participants = await session.getParticipants();
// Returns: [{ id, displayName, uptime }]

participants.forEach(p => {
  console.log(p.displayName, p.uptime + 's');
});

session.setupIntersectionObserver(callback)

Automatically adjust video quality based on visibility. High quality for visible videos, low quality for off-screen videos.

// Set up observer — callback returns consumerId for a participantId
session.setupIntersectionObserver((participantId) => {
  return consumerMap.get(participantId)?.videoConsumerId;
});

// Observe a video element
const observer = session.setupIntersectionObserver(getConsumerId);
const videoCard = document.getElementById(`video-${participantId}`);
videoCard.dataset.participantId = participantId;
observer.observe(videoCard);

session.disconnect()

Leave the room and clean up all resources.

session.disconnect();

Events

participantJoined

Fired when a new participant joins the room.

session.addEventListener('participantJoined', ({ detail }) => {
  console.log('Joined:', detail.participantId, detail.displayName);
});

participantLeft

Fired when a participant leaves the room.

session.addEventListener('participantLeft', ({ detail }) => {
  console.log('Left:', detail.participantId);
  // Remove their video element
  document.getElementById(`video-${detail.participantId}`)?.remove();
});

newProducer

Fired when a remote participant starts sending audio or video.

session.addEventListener('newProducer', async ({ detail }) => {
  // detail: { producerId, participantId, kind, appData }
  const track = await session.consume(detail.producerId);
  const stream = new MediaStream([track]);
  // Attach stream to video/audio element
});

activeSpeakers

Fired every 800ms with the list of active speakers.

session.addEventListener('activeSpeakers', ({ detail }) => {
  // detail: { speakers: [{ participantId, volume }] }
  const topSpeaker = detail.speakers[0];
  if (topSpeaker) {
    console.log('Speaking:', topSpeaker.participantId);
    // Highlight their video
    document.getElementById(`video-${topSpeaker.participantId}`)
      ?.classList.add('speaking');
  }
});

waitingForAdmission

Fired when the participant is waiting for the host's approval.

session.addEventListener('waitingForAdmission', () => {
  console.log('Waiting for host approval...');
  // Show waiting UI
});

admissionGranted

Fired when the host approves the admission request.

session.addEventListener('admissionGranted', async () => {
  console.log('Admitted! Starting camera...');
  const stream = await session.enableCamera();
  document.getElementById('local-video').srcObject = stream;
});

admissionDenied

Fired when the host denies the admission request.

session.addEventListener('admissionDenied', () => {
  console.log('Admission denied');
  session.disconnect();
});

admissionRequest

Fired on the moderator's session when a participant requests to join.

session.addEventListener('admissionRequest', ({ detail }) => {
  // detail: { participantId, displayName }
  console.log(`${detail.displayName} wants to join`);

  // Show approval UI
  showAdmissionDialog(detail.participantId, detail.displayName);
});

To approve or deny via WebSocket:

// Approve
session._ws.send(JSON.stringify({
  type: 'admitUser',
  id: String(Date.now()),
  data: { participantId: detail.participantId }
}));

// Deny
session._ws.send(JSON.stringify({
  type: 'denyUser',
  id: String(Date.now()),
  data: { participantId: detail.participantId }
}));

producerPaused

Fired when a remote participant mutes their microphone or camera.

session.addEventListener('producerPaused', ({ detail }) => {
  // detail: { participantId, producerId }
  // Show muted indicator
});

producerResumed

Fired when a remote participant unmutes.

session.addEventListener('producerResumed', ({ detail }) => {
  // Hide muted indicator
});

disconnected

Fired when the session is disconnected.

session.addEventListener('disconnected', ({ detail }) => {
  console.log('Disconnected, code:', detail.code);
});

Complete Example

import MenaStreamClient from '@mena-stream/client';

const client = new MenaStreamClient({
  apiKey: 'sk_live_...',
  baseUrl: 'https://api.mena-stream.com'
});

async function startMeeting() {
  // Create room as moderator (enables admission control)
  const room = await client.createRoom({ name: 'Team Meeting' });
  const session = await room.join({
    displayName: 'Ahmed',
    role: 'moderator'
  });

  // Local video
  const stream = await session.enableCamera();
  document.getElementById('local').srcObject = stream;

  // Remote participants
  session.addEventListener('newProducer', async ({ detail }) => {
    const track = await session.consume(detail.producerId);
    if (detail.kind === 'video') {
      const video = document.createElement('video');
      video.srcObject = new MediaStream([track]);
      video.autoplay = true;
      document.getElementById('grid').appendChild(video);
    }
  });

  // Participant joined/left
  session.addEventListener('participantJoined', ({ detail }) => {
    console.log(detail.displayName, 'joined');
  });

  session.addEventListener('participantLeft', ({ detail }) => {
    document.getElementById(`v-${detail.participantId}`)?.remove();
  });

  // Active speaker
  session.addEventListener('activeSpeakers', ({ detail }) => {
    if (detail.speakers[0]) {
      highlightSpeaker(detail.speakers[0].participantId);
    }
  });

  // Admission requests
  session.addEventListener('admissionRequest', ({ detail }) => {
    if (confirm(`${detail.displayName} wants to join. Admit?`)) {
      session._ws.send(JSON.stringify({
        type: 'admitUser',
        id: String(Date.now()),
        data: { participantId: detail.participantId }
      }));
    }
  });

  // Controls
  document.getElementById('mute').onclick = () =>
    session.setAudioEnabled(false);
  document.getElementById('unmute').onclick = () =>
    session.setAudioEnabled(true);
  document.getElementById('leave').onclick = () =>
    session.disconnect();
}

startMeeting();

Performance Features

| Feature | Description | Benefit | |------------------------|------------------------------------|------------------------------| | Simulcast | 3 quality layers (100/300/900 Kbps)| Auto quality adaptation | | SVC via VP9 | Scalable Video Coding S3T3 | 40% less bandwidth | | Audio DTX | No packets during silence | 60% audio bandwidth saving | | ActiveSpeaker | 800ms detection interval | Auto-highlight speaker | | Producer Pause | Zero bytes when muted | Instant bandwidth saving | | Intersection Observer | HD for visible, SD for hidden | Up to 80% bandwidth saving | | Transport Pool | Pre-allocated transports | 70% faster room join | | PipeTransport | Cross-worker media routing | Scales across 8 CPU cores |


Pricing

| Plan | Minutes/Month | Price | |------------|---------------|--------------| | Free | 1,000 | Free forever | | Starter | 10,000 | $29/month | | Growth | 100,000 | $99/month | | Scale | 500,000 | $299/month | | Enterprise | Unlimited | Custom |

Minutes = participants × minutes (3 users × 10 min = 30 minutes)

Payment: mada, Visa, Mastercard, Apple Pay


Self-Hosted Enterprise

Deploy MENA-Stream on your own servers with a license key.

# Your servers. Your data. Fixed monthly cost.
docker compose up -d

Contact: [email protected]


Links

  • Site: https://mena-stream.com
  • GitHub: https://github.com/salahaldain-abduljalil/mena_stream_project
  • npm: https://www.npmjs.com/package/@mena-stream/client

License

MIT — The SDK is free and open-source.

The MENA-Stream backend infrastructure is proprietary. This SDK connects to it.

Copyright (c) 2026 Salah Aldain Abduljalil