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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@asitni/sip-simple

v1.0.0

Published

TypeScript SIP client library with WebRTC support

Readme

TypeScript SIP Client Library

A modern, fully-typed SIP client library built with TypeScript for browser-based VoIP applications. Features WebRTC integration, persistent call history, and comprehensive device management.

✨ Features

  • 🎯 Full TypeScript Support - Complete type safety with comprehensive type definitions
  • 📞 Complete SIP Functionality - Make, receive, hold, transfer calls with full SIP protocol support
  • 🎵 Advanced Audio Management - Device selection, mute/unmute, DTMF, volume control
  • 📱 WebRTC Integration - High-quality peer-to-peer audio communication
  • 💾 Persistent Storage - Call history with localStorage and memory fallback
  • 🔄 Auto-Reconnection - Smart reconnection with exponential backoff
  • 🎛️ Event-Driven Architecture - Comprehensive event system for real-time updates
  • 📊 Call Statistics - Detailed audio quality and connection analytics
  • 🏗️ Modern Build System - Multiple formats (ESM, CJS, UMD) with tree-shaking support
  • 🧪 Testing Ready - Built-in test utilities and comprehensive coverage

📦 Installation

npm install @your-org/sip-client sip.js

🚀 Quick Start

import { SIPModule, SIPConfig } from '@your-org/sip-client';

// Create SIP client instance
const sipClient = new SIPModule();

// Configure connection
const config: SIPConfig = {
  sipUri: 'sip:[email protected]',
  wsUri: 'wss://your-sip-server.com:8089/ws',
  password: 'your-password',
  displayName: 'Your Display Name'
};

// Setup event listeners
sipClient.on('connected', () => {
  console.log('✅ Connected to SIP server');
});

sipClient.on('registered', () => {
  console.log('📝 Successfully registered');
});

sipClient.on('incomingCall', (callData) => {
  console.log(`📞 Incoming call from: ${callData.number}`);
  
  // Answer the call
  sipClient.answer();
  
  // Or reject it
  // sipClient.reject();
});

sipClient.on('callAccepted', () => {
  console.log('🎉 Call established');
});

sipClient.on('callEnded', (data) => {
  console.log(`📴 Call ended: ${data.reason}`);
});

// Connect and start calling
async function start() {
  try {
    const result = await sipClient.connect(config);
    if (result.success) {
      console.log('🔌 Connected successfully');
      
      // Make a call
      await sipClient.makeCall('1234567890');
    }
  } catch (error) {
    console.error('❌ Connection failed:', error);
  }
}

start();

🔧 Configuration

Basic Configuration

interface SIPConfig {
  sipUri: string;              // SIP URI (sip:[email protected])
  wsUri: string;               // WebSocket URI (wss://server:port/path)
  password: string;            // Authentication password
  displayName?: string;        // Display name for calls
  iceServers?: RTCIceServer[]; // STUN/TURN servers
  connection?: ConnectionConfig;
  registrationExpires?: number; // Registration expiration (seconds)
}

Advanced Connection Settings

interface ConnectionConfig {
  connectionTimeout?: number;        // Connection timeout (ms) - default: 10000
  maxReconnectionAttempts?: number;  // Max reconnection attempts - default: 5
  reconnectionTimeout?: number;      // Reconnection delay (ms) - default: 4000
  registrationExpires?: number;      // Registration expiration - default: 300
}

Example with Advanced Settings

const config: SIPConfig = {
  sipUri: 'sip:[email protected]',
  wsUri: 'wss://sip.company.com:8089/ws',
  password: 'secure-password',
  displayName: 'Alice Johnson',
  iceServers: [
    { urls: 'stun:stun.l.google.com:19302' },
    { 
      urls: 'turn:your-turn-server.com:3478',
      username: 'turnuser',
      credential: 'turnpass'
    }
  ],
  connection: {
    connectionTimeout: 15000,
    maxReconnectionAttempts: 10,
    reconnectionTimeout: 2000,
    registrationExpires: 600
  }
};

📞 Core API

Connection Management

// Connect to SIP server
const result = await sipClient.connect(config);

// Disconnect
await sipClient.disconnect();

// Reconnect with last configuration
await sipClient.reconnect();

// Check connection status
const isConnected = sipClient.isConnected(); // boolean
const state = sipClient.getState(); // ModuleState

Call Management

// Make outgoing call
const result = await sipClient.makeCall('1234567890');
// With custom domain
await sipClient.makeCall('1234567890', 'custom-domain.com');

// Handle incoming calls
sipClient.on('incomingCall', async (callData) => {
  console.log(`Call from: ${callData.number}`);
  
  // Answer
  await sipClient.answer();
  
  // Or reject
  // await sipClient.reject();
});

// Call control
await sipClient.hangup();           // End call
await sipClient.hold();             // Put on hold
await sipClient.unhold();           // Remove from hold
await sipClient.transfer('5551234'); // Transfer call

// Check call status
const hasActiveCall = sipClient.hasActiveCall();
const callState = sipClient.getCallState();

Audio Control

// Microphone control
const muteResult = sipClient.toggleMute();
console.log(`Muted: ${muteResult.muted}`);

// Volume control (0.0 - 1.0)
sipClient.setVolume(0.8);
const currentVolume = sipClient.getVolume();

// DTMF tones
sipClient.sendDTMF('1');           // Send single digit
sipClient.sendDTMF('*', 200, 100); // Custom duration and gap

// Check audio status
const isMuted = sipClient.isMuted();

Device Management

// Get available devices
const devices = await sipClient.getAvailableDevices();
console.log('Microphones:', devices.microphones);
console.log('Speakers:', devices.speakers);

// Set devices
await sipClient.setMicrophone(deviceId);  // null for default
await sipClient.setSpeaker(deviceId);

// Get current devices
const current = sipClient.getCurrentDevices();

// Test devices
const micTest = await sipClient.testMicrophone(deviceId);
if (micTest.success) {
  console.log('Audio level:', micTest.getLevel?.()); // 0-100
  micTest.stop?.(); // Stop test
}

await sipClient.testSpeaker(deviceId); // Plays test tone

// Check speaker selection support
const supportsSelection = sipClient.isSpeakerSelectionSupported();

📊 Call History & Statistics

// Get call history
const history = sipClient.getCallHistory(10); // Last 10 calls
history.forEach(call => {
  console.log(`${call.direction} call ${call.number} - ${call.duration}ms`);
});

// Get call statistics
const stats = sipClient.getCallStatistics();
console.log(`Total calls: ${stats.totalCalls}`);
console.log(`Answered: ${stats.answeredCalls}`);
console.log(`Average duration: ${stats.averageDuration}ms`);

// Clear history
sipClient.clearCallHistory();

// Get real-time call stats
const callStats = await sipClient.getCallStats();
if (callStats) {
  console.log(`Audio quality - Jitter: ${callStats.jitter}ms`);
  console.log(`Packets lost: ${callStats.packetsLost}`);
}

🎛️ Event System

Connection Events

sipClient.on('connected', () => {
  // Connected to SIP server transport
});

sipClient.on('registered', () => {
  // Successfully registered with SIP server
});

sipClient.on('disconnected', () => {
  // Disconnected from SIP server
});

sipClient.on('registrationFailed', (error: string) => {
  // Registration failed
});

Call Events

sipClient.on('callStarted', (callData: CallData) => {
  // Outgoing call initiated
});

sipClient.on('incomingCall', (callData: CallData) => {
  // Incoming call received
});

sipClient.on('callProgress', () => {
  // Call is ringing/progressing
});

sipClient.on('callAccepted', () => {
  // Call was answered/established
});

sipClient.on('callEnded', (data: { reason: CallEndReason }) => {
  // Call ended - reasons: 'remote_hangup', 'local_hangup', 'cancelled', 'rejected', 'terminated'
});

sipClient.on('callAnswered', () => {
  // You answered an incoming call
});

sipClient.on('callRejected', () => {
  // You rejected an incoming call
});

Audio & Control Events

sipClient.on('muteToggled', (data: { muted: boolean }) => {
  // Microphone mute status changed
});

sipClient.on('callHeld', () => {
  // Call put on hold
});

sipClient.on('callUnheld', () => {
  // Call removed from hold
});

sipClient.on('dtmfSent', (data: { digit: string }) => {
  // DTMF tone sent
});

Transfer Events

sipClient.on('callRefer', (data: { referTo?: string; referredBy?: string }) => {
  // Call transfer initiated
});

sipClient.on('transferAccepted', () => {
  // Transfer accepted
});

sipClient.on('transferRejected', () => {
  // Transfer rejected
});

System Events

sipClient.on('error', (error: string) => {
  // Error occurred
});

sipClient.on('stateChanged', (data: { oldState: ModuleState; newState: ModuleState }) => {
  // Module state changed
});

🏗️ Advanced Usage

Custom Event Handling

import { SIPModule, CallEndReason } from '@your-org/sip-client';

class CustomSIPHandler {
  private sipClient: SIPModule;
  
  constructor() {
    this.sipClient = new SIPModule();
    this.setupEvents();
  }
  
  private setupEvents() {
    // Auto-answer incoming calls
    this.sipClient.on('incomingCall', (callData) => {
      if (this.shouldAutoAnswer(callData.number)) {
        this.sipClient.answer();
      }
    });
    
    // Auto-redial on call failure
    this.sipClient.on('callEnded', (data) => {
      if (data.reason === CallEndReason.REJECTED && this.shouldRetry()) {
        setTimeout(() => this.retryCall(), 5000);
      }
    });
  }
  
  private shouldAutoAnswer(number: string): boolean {
    // Your logic here
    return this.whitelist.includes(number);
  }
}

Integration with React

import { useEffect, useState } from 'react';
import { SIPModule, SIPConfig, CallData } from '@your-org/sip-client';

export function useSIPClient(config: SIPConfig) {
  const [sipClient] = useState(() => new SIPModule());
  const [isConnected, setIsConnected] = useState(false);
  const [currentCall, setCurrentCall] = useState<CallData | null>(null);
  
  useEffect(() => {
    sipClient.on('connected', () => setIsConnected(true));
    sipClient.on('disconnected', () => setIsConnected(false));
    sipClient.on('incomingCall', setCurrentCall);
    sipClient.on('callEnded', () => setCurrentCall(null));
    
    sipClient.connect(config);
    
    return () => sipClient.destroy();
  }, []);
  
  return { sipClient, isConnected, currentCall };
}

🎯 TypeScript Types

Core Types

interface CallData {
  sessionId: string;
  number: string;
  direction: 'incoming' | 'outgoing';
  startTime: Date;
  establishedTime?: Date;
  endTime?: Date;
  state?: string;
}

interface OperationResult {
  success: boolean;
  error?: string;
  sessionId?: string;
  deviceId?: string;
}

interface AudioStats {
  bytesReceived: number;
  bytesSent: number;
  packetsLost: number;
  jitter: number;
  rtt: number;
  audioLevel: number;
}

Enums

enum CallEndReason {
  REMOTE_HANGUP = 'remote_hangup',
  LOCAL_HANGUP = 'local_hangup',
  CANCELLED = 'cancelled',
  REJECTED = 'rejected',
  TERMINATED = 'terminated'
}

enum ModuleState {
  IDLE = 'idle',
  CONNECTING = 'connecting',
  CONNECTED = 'connected',
  CALLING = 'calling',
  IN_CALL = 'in_call',
  DISCONNECTING = 'disconnecting',
  ERROR = 'error'
}

🏪 Storage & Persistence

The library automatically manages call history using localStorage with graceful fallback:

// Check storage availability
const hasStorage = sipClient.isStorageAvailable();

// History is automatically persisted
const history = sipClient.getCallHistory();

// Clear if needed
sipClient.clearCallHistory();

🔧 Error Handling

// Method-level error handling
const result = await sipClient.makeCall('1234567890');
if (!result.success) {
  console.error('Call failed:', result.error);
}

// Global error handling
sipClient.on('error', (error) => {
  console.error('SIP Error:', error);
  // Handle connection issues, call failures, etc.
});

🌐 Browser Support

  • Chrome/Chromium 60+
  • Firefox 60+
  • Safari 12+
  • Edge 79+

Requirements:

  • WebRTC API support
  • getUserMedia API support
  • WebSocket support
  • ES2020+ support

🚀 Performance

  • Tree-shakeable - Only import what you need
  • Memory efficient - Automatic cleanup and resource management
  • Optimized builds - Separate ESM, CJS, and UMD bundles
  • Lazy loading - sip.js loaded on demand

🧪 Testing

# Run tests
npm test

# Type checking
npm run type-check

# Build verification
npm run build

🔗 Integration Examples

Express.js Backend Integration

// server.js
import express from 'express';
import { SIPModule } from '@your-org/sip-client';

const app = express();
const sipClient = new SIPModule();

app.post('/api/call', async (req, res) => {
  const { number } = req.body;
  const result = await sipClient.makeCall(number);
  res.json(result);
});

WebSocket Notifications

// Send call events to WebSocket clients
sipClient.on('incomingCall', (callData) => {
  websocketServer.broadcast({
    type: 'incoming_call',
    data: callData
  });
});

📋 Migration Guide

From v0.x to v1.x

// Old way (v0.x)
import SIPClient from '@your-org/sip-client';
const client = new SIPClient();

// New way (v1.x)
import { SIPModule } from '@your-org/sip-client';
const client = new SIPModule();

🤝 Contributing

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/amazing-feature
  3. Make your changes and add tests
  4. Run tests: npm test
  5. Commit: git commit -m 'Add amazing feature'
  6. Push: git push origin feature/amazing-feature
  7. Submit a pull request

📄 License

MIT License - see LICENSE file for details.

📞 Support

🙏 Acknowledgments

  • Built on top of sip.js
  • WebRTC implementation inspired by modern standards
  • TypeScript best practices from the community

Made with ❤️ for the WebRTC community