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

@grndd.systems/ts-firebase-rtc

v1.0.0

Published

Pluggable WebRTC library with Firebase signaling abstraction

Downloads

110

Readme

@grndd-systems/zk-proof-rtc

A pluggable TypeScript library for WebRTC communication between web and mobile applications, with built-in Firebase signaling support.

Features

  • 🔌 Pluggable Signaling Architecture - Abstract signaling interface with Firebase implementation included
  • 🎯 Framework-Agnostic Core - Use with vanilla JS, React, Vue, Angular, or any framework
  • ⚛️ React Hooks - Ready-to-use React hooks for seamless integration
  • 📦 Tree-Shakeable - Only bundle what you use with ESM/CJS dual output
  • 🔒 Full TypeScript - Strict typing with comprehensive type definitions
  • 🔥 Firebase Ready - Built-in Firebase Realtime Database signaling
  • 🧊 Trickle ICE - Efficient incremental ICE candidate exchange
  • 🎨 Type-Safe Events - Strongly-typed event emitter for all connection events

Installation

npm install @grndd-systems/zk-proof-rtc

Peer Dependencies

Depending on your use case, you may need to install peer dependencies:

# For React hooks
npm install react

# For Firebase signaling
npm install firebase

Quick Start

React with Firebase (Recommended)

import { useWebRTCWithFirebase, generatePeerId } from '@grndd-systems/zk-proof-rtc';

function DesktopApp() {
  const { state, createOffer, send, onMessage } = useWebRTCWithFirebase({
    firebaseConfig: {
      apiKey: 'YOUR_API_KEY',
      authDomain: 'YOUR_AUTH_DOMAIN',
      databaseURL: 'YOUR_DATABASE_URL',
      projectId: 'YOUR_PROJECT_ID',
    },
    webrtcConfig: {
      debug: true, // Enable logging
    },
  });

  useEffect(() => {
    onMessage((data) => {
      console.log('Received:', data);
    });
  }, [onMessage]);

  const handleConnect = async () => {
    const peerId = generatePeerId();
    await createOffer(peerId);

    // Show QR code or share peerId with mobile app
    console.log('Share this ID:', peerId);
  };

  return (
    <div>
      <button onClick={handleConnect} disabled={state.isConnected}>
        Create Connection
      </button>
      <p>Status: {state.state}</p>
      {state.isConnected && (
        <button onClick={() => send({ type: 'greeting', message: 'Hello!' })}>
          Send Message
        </button>
      )}
    </div>
  );
}

Mobile/Answerer Side

import { useWebRTCWithFirebase } from '@grndd-systems/zk-proof-rtc';

function MobileApp({ peerId }: { peerId: string }) {
  const { state, createAnswer, send, onMessage } = useWebRTCWithFirebase({
    firebaseConfig: { /* same config */ },
  });

  useEffect(() => {
    // Auto-connect when peerId is available
    createAnswer(peerId);
  }, [peerId, createAnswer]);

  useEffect(() => {
    onMessage((data) => {
      console.log('Received:', data);
    });
  }, [onMessage]);

  return <div>Status: {state.state}</div>;
}

Usage

Vanilla JavaScript

import {
  WebRTCConnection,
  FirebaseSignalingClient,
} from '@grndd-systems/zk-proof-rtc/core';

// Create signaling client
const signalingClient = new FirebaseSignalingClient({
  firebaseConfig: {
    apiKey: 'YOUR_API_KEY',
    // ... other config
  },
});

// Create connection
const connection = new WebRTCConnection(signalingClient, {
  debug: true,
  iceGatheringTimeout: 3000,
});

// Listen for messages
connection.on('message', ({ data }) => {
  console.log('Received:', data);
});

// Listen for state changes
connection.on('state:change', ({ state }) => {
  console.log('Connection state:', state);
});

// Create offer (desktop)
const peerId = await connection.createOffer('peer-123');
console.log('Share this ID:', peerId);

// Send data
connection.send({ type: 'test', payload: 'Hello!' });

Custom Signaling Backend

Implement the SignalingClient interface to use your own signaling server:

import { SignalingClient } from '@grndd-systems/zk-proof-rtc/core';

class MyCustomSignaling extends SignalingClient {
  async initialize() {
    // Connect to your signaling server
  }

  async createOffer(peerId, offer, metadata) {
    // Upload offer to your server
  }

  async getOffer(peerId) {
    // Retrieve offer from your server
  }

  // ... implement other methods
}

// Use with WebRTCConnection
const connection = new WebRTCConnection(new MyCustomSignaling());

React Hook with Custom Signaling

import { useWebRTCConnection } from '@grndd-systems/zk-proof-rtc/react';
import { MyCustomSignaling } from './my-signaling';

function MyComponent() {
  const signalingClient = useMemo(() => new MyCustomSignaling(), []);

  const { state, createOffer, send, onMessage } = useWebRTCConnection({
    signalingClient,
    config: { debug: true },
  });

  // ... use the hook
}

API Reference

WebRTCConnection

Framework-agnostic WebRTC connection manager.

Constructor:

new WebRTCConnection(signalingClient: SignalingClient, config?: PartialWebRTCConfig)

Methods:

  • createOffer(peerId: string, metadata?: SessionMetadata): Promise<string> - Initialize as offerer
  • createAnswer(peerId: string): Promise<void> - Initialize as answerer
  • send(data: any): boolean - Send data through data channel
  • close(): Promise<void> - Close connection and cleanup
  • getState(): ConnectionState - Get current connection state
  • getPeerId(): string | null - Get peer ID
  • isConnected(): boolean - Check if connected

Events:

  • state:change - Connection state changed
  • datachannel:state - Data channel state changed
  • ice:state - ICE connection state changed
  • ice:candidate - ICE candidate generated
  • message - Data received
  • error - Error occurred
  • peer:connected - Peer connected
  • peer:disconnected - Peer disconnected

useWebRTCConnection(options)

React hook for WebRTC connection.

Options:

{
  signalingClient: SignalingClient;
  config?: PartialWebRTCConfig;
  autoCleanup?: boolean; // default: true
}

Returns:

{
  state: UseWebRTCConnectionState;
  createOffer: (peerId: string, metadata?: SessionMetadata) => Promise<string>;
  createAnswer: (peerId: string) => Promise<void>;
  send: (data: any) => boolean;
  onMessage: (callback: (data: any) => void) => void;
  close: () => Promise<void>;
}

useWebRTCWithFirebase(options)

Convenience hook for Firebase signaling.

Options:

{
  firebaseConfig?: FirebaseOptions;
  firebaseApp?: FirebaseApp; // Use existing app
  firebaseDatabase?: Database; // Use existing database
  basePath?: string; // default: 'signals'
  sessionTTL?: number; // default: 300000 (5 minutes)
  webrtcConfig?: PartialWebRTCConfig;
  autoCleanup?: boolean; // default: true
}

FirebaseSignalingClient

Firebase Realtime Database signaling implementation.

Constructor:

new FirebaseSignalingClient(config: FirebaseSignalingConfig)

Config:

{
  firebaseConfig: FirebaseOptions;
  firebaseApp?: FirebaseApp;
  firebaseDatabase?: Database;
  basePath?: string; // default: 'signals'
  sessionTTL?: number; // default: 300000
  debug?: boolean;
}

Configuration Options

interface WebRTCConfig {
  iceServers: RTCIceServer[]; // default: Google STUN servers
  channelName: string; // default: 'zkPassport'
  channelOptions: RTCDataChannelInit; // default: { ordered: true }
  iceGatheringTimeout: number; // default: 3000ms
  sessionTTL: number; // default: 300000ms (5 minutes)
  debug: boolean; // default: false
}

Architecture

@grndd-systems/zk-proof-rtc
├── core/                    # Framework-agnostic core
│   ├── WebRTCConnection     # Main connection manager
│   ├── EventEmitter         # Type-safe event system
│   ├── SignalingClient      # Abstract signaling interface
│   ├── FirebaseSignalingClient  # Firebase implementation
│   └── types                # TypeScript definitions
├── react/                   # React integration
│   ├── useWebRTCConnection
│   └── useWebRTCWithFirebase
└── utils/                   # Utilities
    ├── logger
    └── generatePeerId

Examples

Complete Desktop-Mobile Flow

Desktop (Offerer):

import { useWebRTCWithFirebase, generatePeerId } from '@grndd-systems/zk-proof-rtc';
import QRCode from 'qrcode';

function DesktopApp() {
  const [qrCode, setQrCode] = useState('');
  const { state, createOffer, send, onMessage } = useWebRTCWithFirebase({
    firebaseConfig: { /* ... */ },
    webrtcConfig: { debug: true },
  });

  useEffect(() => {
    onMessage((data) => {
      console.log('Received from mobile:', data);
      // Handle mobile response
    });
  }, [onMessage]);

  const handleConnect = async () => {
    const peerId = generatePeerId();
    await createOffer(peerId);

    // Generate QR code for mobile
    const qrData = JSON.stringify({ peerId, timestamp: Date.now() });
    const qrImage = await QRCode.toDataURL(qrData);
    setQrCode(qrImage);
  };

  const handleSendRequest = () => {
    send({
      type: 'proof_request',
      payload: {
        requestId: '123',
        proofType: 'age_check',
        minAge: 21,
      },
    });
  };

  return (
    <div>
      {!state.isConnected ? (
        <>
          <button onClick={handleConnect}>Generate QR Code</button>
          {qrCode && <img src={qrCode} alt="QR Code" />}
        </>
      ) : (
        <>
          <p>✅ Connected!</p>
          <button onClick={handleSendRequest}>Request Proof</button>
        </>
      )}
      <p>Status: {state.state}</p>
    </div>
  );
}

Mobile (Answerer):

import { useWebRTCWithFirebase } from '@grndd-systems/zk-proof-rtc';

function MobileApp() {
  const [scannedData, setScannedData] = useState<{ peerId: string } | null>(null);
  const { state, createAnswer, send, onMessage } = useWebRTCWithFirebase({
    firebaseConfig: { /* ... */ },
  });

  useEffect(() => {
    if (scannedData?.peerId) {
      createAnswer(scannedData.peerId);
    }
  }, [scannedData, createAnswer]);

  useEffect(() => {
    onMessage((data) => {
      console.log('Received from desktop:', data);

      if (data.type === 'proof_request') {
        // Generate and send proof
        const proof = generateProof(data.payload);
        send({
          type: 'proof_response',
          payload: proof,
        });
      }
    });
  }, [onMessage, send]);

  return (
    <div>
      {!scannedData ? (
        <QRScanner onScan={(data) => setScannedData(JSON.parse(data))} />
      ) : (
        <p>Status: {state.state}</p>
      )}
    </div>
  );
}

License

MIT

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Support

For issues and questions, please use the GitHub issue tracker.