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

@atel-callcenter/sdk

v2.0.8

Published

ATEL Call Center SDK V2 - Framework and media-provider agnostic core with adapters for React and LiveKit

Downloads

642

Readme

ATEL Call Center SDK v2

Framework and Media Provider Agnostic Call Center SDK

A modern TypeScript SDK for building queue-based video call applications with clean separation between business logic (Core) and platform implementations (Adapters).

🏗️ Architecture

The SDK follows a strict CORE/ADAPTER pattern:

sdk-ts/
 ├─ core/                    # ✅ Framework & Media Agnostic
 │   ├─ events.ts            # Strongly-typed event system
 │   ├─ media-adapter.ts     # MediaAdapter interface
 │   ├─ state.ts             # Call state machine
 │   ├─ CustomerCore.ts      # Customer business logic
 │   └─ AgentCore.ts         # Agent business logic
 │
 ├─ adapters/
 │   ├─ web/                 # Web platform adapters
 │   │   └─ livekit/         # LiveKit media provider
 │   └─ react/               # React framework adapters
 │       ├─ customer/        # Customer React hooks
 │       └─ agent/           # Agent React hooks
 │
 └─ shared/                  # Shared utilities (sockets, etc)

Core Principles

  1. Core SDK is Framework-Agnostic: NO React, NO browser APIs
  2. Core SDK is Media-Agnostic: NO LiveKit, NO Agora, NO Twilio
  3. Dependency Injection: Adapters are injected via factory pattern
  4. Strongly-Typed Events: All events use discriminated unions
  5. State Machine: Call lifecycle managed with validated transitions

📦 Installation

npm install @atel-callcenter/sdk
# or
yarn add @atel-callcenter/sdk

🚀 Quick Start

Customer (React + LiveKit)

import {
  CustomerProvider,
  useCustomer,
} from '@atel-callcenter/sdk/react/customer';

function App() {
  return (
    <CustomerProvider config={{ url: 'http://localhost:3000' }}>
      <CustomerInterface />
    </CustomerProvider>
  );
}

function CustomerInterface() {
  const customer = useCustomer();

  const handleCall = async () => {
    await customer.requestCall({
      name: 'Jane Doe',
      queueId: 'sales',
    });
  };

  return (
    <div>
      <p>Status: {customer.currentCallState}</p>
      {customer.currentCallState === 'idle' && (
        <button onClick={handleCall}>Request Call</button>
      )}
      {customer.currentCallState === 'in-call' && (
        <button onClick={() => customer.endCall()}>End Call</button>
      )}
    </div>
  );
}

Agent (React + LiveKit)

import { AgentProvider, useAgent } from '@atel-callcenter/sdk/react/agent';

function App() {
  return (
    <AgentProvider config={{ url: 'http://localhost:3000' }}>
      <AgentDashboard />
    </AgentProvider>
  );
}

function AgentDashboard() {
  const agent = useAgent();

  useEffect(() => {
    agent.register({
      agentId: 'agent-123',
      agentName: 'John Smith',
      queueIds: ['sales', 'support'],
    });
  }, []);

  const handleAccept = () => {
    agent.acceptCall();
  };

  const handleReject = () => {
    agent.rejectCall('Not available');
  };

  return (
    <div>
      <p>Status: {agent.status}</p>
      {agent.incomingCall && (
        <div>
          <p>Call from: {agent.incomingCall.customerName}</p>
          <button onClick={handleAccept}>Accept</button>
          <button onClick={handleReject}>Reject</button>
        </div>
      )}
      {agent.currentCallState === 'in-call' && (
        <button onClick={() => agent.endCall()}>End Call</button>
      )}
    </div>
  );
}

## 📖 Advanced Usage

### Using Core SDK Directly (Framework-Agnostic)

For non-React applications or custom integrations:

```typescript
import { CustomerClient } from '@atel-callcenter/sdk/core/CustomerCore';
import { createLiveKitAdapter } from '@atel-callcenter/sdk/adapters/web';

// Create customer client with LiveKit adapter
const customer = new CustomerClient({
  url: 'http://localhost:3000',
  mediaAdapterFactory: createLiveKitAdapter,
});

// Listen to strongly-typed events
customer.on('CALL_ACCEPTED', (event) => {
  console.log('Call accepted:', event.ticketId);
  console.log('Media info:', event.media);
});

customer.on('MEDIA_READY', () => {
  console.log('Media connection ready');
});

customer.on('CALL_ENDED', (event) => {
  console.log('Call ended:', event.reason);
});

// Connect and request call
customer.connect();
await customer.requestCall({
  name: 'Jane Doe',
  queueId: 'sales',
});

Custom Media Adapter

Implement MediaAdapter interface for other providers (Agora, Twilio, etc):

import {
  MediaAdapter,
  MediaInfo,
} from '@atel-callcenter/sdk/core/media-adapter';

export class AgoraMediaAdapter implements MediaAdapter {
  private client: AgoraRTCClient | null = null;

  async connect(mediaInfo: MediaInfo): Promise<void> {
    // Implement Agora connection logic
    this.client = AgoraRTC.createClient({ mode: 'rtc', codec: 'vp8' });
    await this.client.join(
      mediaInfo.roomUrl,
      mediaInfo.roomName,
      mediaInfo.token,
    );
    this.onReadyCallback?.();
  }

  async disconnect(): Promise<void> {
    await this.client?.leave();
    this.client = null;
  }

  onReady(callback: () => void): void {
    this.onReadyCallback = callback;
  }

  // ... implement other methods
}

// Use custom adapter
const customer = new CustomerClient({
  url: 'http://localhost:3000',
  mediaAdapterFactory: () => new AgoraMediaAdapter(),
});

Event System

All events are strongly-typed discriminated unions:

import type { CallEvent } from '@atel-callcenter/sdk/core/events';

customer.on('CALL_ACCEPTED', (event) => {
  // TypeScript knows this is CallAcceptedEvent
  console.log(event.ticketId);
  console.log(event.media.provider); // 'livekit'
  console.log(event.media.roomUrl);
});

customer.on('ERROR', (event) => {
  // TypeScript knows this is ErrorEvent
  console.log(event.code);
  console.log(event.message);
});

🔄 Call State Machine

The SDK manages call lifecycle through a state machine:

idle → requesting → ringing → connecting → in-call → ending → ended
                                                            ↓
                                                          failed

Access current state:

console.log(customer.currentCallState); // 'idle' | 'requesting' | ...
console.log(agent.currentCallState);

📚 API Reference

CustomerClient

Constructor:

  • new CustomerClient(config: CustomerConfig)

Methods:

  • connect(): void - Connect to signaling server
  • disconnect(): Promise<void> - Disconnect and cleanup
  • requestCall(request: CallRequest): Promise<void> - Request a call
  • endCall(): Promise<void> - End current call

Properties:

  • currentCallState: CallState - Current call state
  • ticketId?: string - Current ticket ID
  • queueId?: string - Current queue ID
  • name?: string - Customer name

Events:

  • CALL_REQUESTED - Call request sent
  • CALL_RINGING - Waiting for agent
  • CALL_ACCEPTED - Agent accepted, media info provided
  • CALL_REJECTED - Agent rejected call
  • CALL_ENDED - Call ended by either party
  • MEDIA_READY - Media connection established
  • MEDIA_DISCONNECTED - Media connection lost
  • ERROR - Error occurred

AgentClient

Constructor:

  • new AgentClient(config: AgentConfig)

Methods:

  • connect(): void - Connect to signaling server
  • disconnect(): Promise<void> - Disconnect and cleanup
  • register(registration: AgentRegistration): Promise<void> - Register as agent
  • unregister(): Promise<void> - Unregister agent
  • acceptCall(): Promise<void> - Accept incoming call
  • rejectCall(reason?: string): void - Reject incoming call
  • endCall(): Promise<void> - End current call

Properties:

  • status: AgentStatus - 'offline' | 'available' | 'busy'
  • currentCallState: CallState - Current call state
  • agentId?: string - Agent ID
  • agentName?: string - Agent name
  • queueIds: string[] - Registered queue IDs
  • incomingCall?: IncomingCallData - Incoming call info

Events:

  • Same as CustomerClient, plus legacy events for backward compatibility

🎯 Migration from v1

If upgrading from SDK v1:

  1. Import Paths Changed:

    // Before
    import { CustomerCore } from '@atel-callcenter/sdk/customer';
    
    // After
    import { CustomerClient } from '@atel-callcenter/sdk/core/CustomerCore';
    // Or use React adapters (recommended)
    import { useCustomer } from '@atel-callcenter/sdk/react/customer';
  2. Event Names Changed:

    // Before
    customer.on('callAccepted', ...);
    
    // After
    customer.on('CALL_ACCEPTED', ...); // Strongly-typed
  3. Media Adapter Required:

    // Before - LiveKit was hardcoded
    const customer = new CustomerCore({ url: '...' });
    
    // After - Inject media adapter
    const customer = new CustomerClient({
      url: '...',
      mediaAdapterFactory: createLiveKitAdapter,
    });
    // Or use React Provider (handles this automatically)

🛠️ Development

# Install dependencies
npm install

# Build
npm run build

# Watch mode
npm run watch

# Clean
npm run clean

📄 License

MIT

// Customer import { CustomerCore } from '@atel-callcenter/sdk/customer';

const customer = new CustomerCore({ url: 'http://localhost:3000' });

customer.on('callAccepted', (data) => { console.log('Connected to:', data.agentName); });

customer.connect(); await customer.requestCall({ customerName: 'Customer Name', queueId: 'sales', });


## Package Exports

- `@atel-callcenter/sdk` - Main entry (exports all)
- `@atel-callcenter/sdk/shared` - Shared core only
- `@atel-callcenter/sdk/agent` - Agent core only
- `@atel-callcenter/sdk/customer` - Customer core only
- `@atel-callcenter/sdk/react/agent` - React agent adapter
- `@atel-callcenter/sdk/react/customer` - React customer adapter

## Design Principles

1. **Shared Core** - Only low-level infrastructure (socket, events, types)
2. **Role Separation** - Agent and Customer logic completely independent
3. **Framework Agnostic** - Cores work with any framework
4. **Adapter Pattern** - React adapters manage lifecycle and provide hooks
5. **Clean Dependencies** - No circular deps, no role-based conditionals