import { KeyPair, IceCandidate } from '../api/client.js'; import { CryptoAdapter } from '../crypto/adapter.js'; import { WebRTCAdapter } from '../webrtc/adapter.js'; import { EventEmitter } from 'eventemitter3'; import { OffererConnection } from '../connections/offerer.js'; import { Peer, PeerOptions } from './peer.js'; import type { RondevuOptions, OfferOptions, OfferHandle, DiscoverOptions, DiscoverResult } from './rondevu-types.js'; export type { RondevuOptions, OfferContext, OfferFactory, OfferOptions, OfferHandle, ConnectionContext, DiscoverOptions, DiscoveredOffer, DiscoverResult, } from './rondevu-types.js'; export { ICE_SERVER_PRESETS } from './ice-config.js'; export type { IceServerPreset, IcePresetConfig } from './ice-config.js'; export type { PollAnswerEvent, PollIceEvent } from './polling-manager.js'; /** * Rondevu - Complete WebRTC signaling client with durable connections * * Uses a tags-based discovery system where offers have 1+ tags for matching. * Authentication uses Ed25519 public key cryptography - your public key IS your identity. * * @example * ```typescript * // Create and initialize Rondevu instance with preset ICE servers * const rondevu = await Rondevu.connect({ * apiUrl: 'https://signal.example.com', * iceServers: 'ipv4-turn' // Use preset: 'ipv4-turn', 'hostname-turns', 'google-stun', or 'relay-only' * }) * * // Create offers with tags for discovery * await rondevu.offer({ * tags: ['chat', 'video'], * maxOffers: 5 // Maintain up to 5 concurrent offers * }) * * // Start accepting connections (auto-fills offers and polls) * await rondevu.startFilling() * * // Listen for connections * rondevu.on('connection:opened', (offerId, connection) => { * connection.on('connected', () => console.log('Connected!')) * connection.on('message', (data) => console.log('Received:', data)) * connection.send('Hello!') * }) * * // Connect by discovering offers with matching tags * const connection = await rondevu.connect({ * tags: ['chat'] * }) * * connection.on('connected', () => { * console.log('Connected!') * connection.send('Hello!') * }) * * connection.on('message', (data) => { * console.log('Received:', data) * }) * * connection.on('reconnecting', (attempt) => { * console.log(`Reconnecting, attempt ${attempt}`) * }) * ``` */ export declare class Rondevu extends EventEmitter { private static readonly DEFAULT_API_URL; private static readonly DEFAULT_TTL_MS; private static readonly POLLING_INTERVAL_MS; private api; private readonly apiUrl; private keyPair; private cryptoAdapter?; private webrtcAdapter; private iceServers; private iceTransportPolicy?; private debugEnabled; private currentTags; private connectionConfig?; private offerPool; private pollingManager; private constructor(); /** * Internal debug logging - only logs if debug mode is enabled */ private debug; /** * Create and initialize a Rondevu client * * @example * ```typescript * const rondevu = await Rondevu.connect({}) // Uses default API URL * // or * const rondevu = await Rondevu.connect({ * apiUrl: 'https://custom.api.com' * }) * ``` */ static connect(options?: RondevuOptions): Promise; /** * Get the current public key (identity) */ getPublicKey(): string; /** * Get the full keypair (publicKey + privateKey) * Use this to persist keypair for future sessions * * ⚠️ SECURITY WARNING: * - The private key grants full access to this identity * - Store keypair securely (encrypted storage, never in logs) * - Never expose private key in URLs, console output, or error messages * - Treat the private key like a password or API key */ getKeyPair(): KeyPair; /** * Get the WebRTC adapter for creating peer connections * Used internally by offer pool and connections */ getWebRTCAdapter(): WebRTCAdapter; /** * Get the crypto adapter for signing/verification operations * Used for meta.json signing and other crypto operations */ getCryptoAdapter(): CryptoAdapter; /** * Default offer factory - creates a simple data channel connection * The RTCPeerConnection is created by Rondevu and passed in */ private defaultOfferFactory; /** * Create offers with tags for discovery (offerer/host side) * Auto-starts filling by default. Use the returned object to cancel. * * @example * ```typescript * // Auto-start (default) * const offer = await rondevu.offer({ * tags: ['chat', 'video'], * maxOffers: 5 * }) * // Later: offer.cancel() to stop * * // Manual start * await rondevu.offer({ tags: ['chat'], maxOffers: 5, autoStart: false }) * await rondevu.startFilling() * ``` */ offer(options: OfferOptions): Promise; /** * Start filling offers and polling for answers/ICE * Call this after offer() to begin accepting connections */ startFilling(): Promise; /** * Stop filling offers and polling * Closes all active peer connections */ stopFilling(): void; /** * Start the centralized polling manager * Use this when you need polling without offers (e.g., answerer connections) */ startPolling(): void; /** * Stop the centralized polling manager */ stopPolling(): void; /** * Check if polling is active */ isPolling(): boolean; /** * Get the count of active offers * @returns Number of active offers */ getOfferCount(): number; /** * Check if an offer is currently connected * @param offerId - The offer ID to check * @returns True if the offer exists and is connected */ isConnected(offerId: string): boolean; /** * Disconnect all active offers * Similar to stopFilling() but doesn't stop the polling/filling process */ disconnectAll(): void; /** * Update tags for new offers * Existing offers keep their old tags until they expire/rotate * New offers created during fill will use the updated tags * @param newTags - The new tags to use for future offers */ updateOfferTags(newTags: string[]): void; /** * Get the current publishing status * @returns Object with publishing state information */ getPublishStatus(): { active: boolean; offerCount: number; tags: string[] | null; }; /** * Create a peer connection with simplified DX * Returns a Peer object with clean state management and events * * @example * ```typescript * // Connect to any peer matching tags * const peer = await rondevu.peer({ tags: ['chat'] }) * * // Connect to specific peer by public key * const peer = await rondevu.peer({ * publicKey: 'abc123...', * tags: ['chat'] * }) * * peer.on('open', () => { * console.log('Connected to', peer.peerPublicKey) * peer.send('Hello!') * }) * * peer.on('message', (data) => { * console.log('Received:', data) * }) * * peer.on('state', (state, prevState) => { * console.log(`State: ${prevState} → ${state}`) * }) * * // Access underlying RTCPeerConnection * if (peer.peerConnection) { * console.log('ICE state:', peer.peerConnection.iceConnectionState) * } * ``` */ peer(options: PeerOptions): Promise; /** * Discover offers by tags * * @param tags - Tags to search for (OR logic - matches any tag) * @param options - Discovery options (pagination) * * @example * ```typescript * // Discover offers matching any of the tags * const result = await rondevu.discover(['chat', 'video']) * * // Paginated discovery * const result = await rondevu.discover(['chat'], { * limit: 20, * offset: 0 * }) * * // Access offers * for (const offer of result.offers) { * console.log(offer.publicKey, offer.tags) * } * ``` */ discover(tags: string[], options?: DiscoverOptions): Promise; /** * Count available offers by tags * Returns the count of available (unanswered, non-expired) offers for each tag * * @param tags - Tags to count offers for * @param unique - If true, count unique public keys instead of total offers * @returns Object mapping each tag to its count * * @example * ```typescript * const counts = await rondevu.countOffersByTags(['chat', 'video']) * console.log(counts.counts) // { chat: 5, video: 3 } * * // Count unique peers instead of total offers * const uniquePeers = await rondevu.countOffersByTags(['chat'], true) * ``` */ countOffersByTags(tags: string[], unique?: boolean): Promise<{ counts: Record; }>; /** * Post answer SDP to specific offer */ postOfferAnswer(offerId: string, sdp: string): Promise<{ success: boolean; offerId: string; }>; /** * Get answer SDP (offerer polls this) */ getOfferAnswer(offerId: string): Promise<{ sdp: string; offerId: string; answererPublicKey: string; answeredAt: number; } | null>; /** * Combined polling for answers and ICE candidates * Returns all answered offers and ICE candidates for all peer's offers since timestamp */ poll(since?: number): Promise<{ answers: Array<{ offerId: string; answererPublicKey: string; sdp: string; answeredAt: number; }>; iceCandidates: Record>; }>; /** * Add ICE candidates to specific offer */ addOfferIceCandidates(offerId: string, candidates: RTCIceCandidateInit[]): Promise<{ count: number; offerId: string; }>; /** * Get ICE candidates for specific offer (with polling support) */ getOfferIceCandidates(offerId: string, since?: number): Promise<{ candidates: IceCandidate[]; offerId: string; }>; /** * Get active connections (for offerer side) */ getActiveConnections(): Map; }