{"version":3,"sources":["../src/index.ts","../src/internal/HostedClient.ts"],"sourcesContent":["export type StreamKind = \"camera\" | \"screen\" | \"audio\";\n\nexport interface StreamShareOptions {\n  userId: string;\n  signalingUrl?: string;\n}\n\nexport interface RemoteStreamEvent {\n  userId: string;\n  stream: MediaStream;\n}\n\nexport interface StreamShareClient {\n  subscribe(userId: string): Promise<void>;\n  startSession(sessionId: string): Promise<void>;\n  joinSession(sessionId: string): Promise<void>;\n  publishStream(stream: MediaStream, kind?: StreamKind): Promise<void>;\n  onRemoteStream(listener: (event: RemoteStreamEvent) => void): () => void;\n  close(): Promise<void>;\n}\n\nexport { createStreamShareClient } from \"./internal/HostedClient\";\n","import type {\n  RemoteStreamEvent,\n  StreamKind,\n  StreamShareClient,\n  StreamShareOptions,\n} from \"../index\";\n\nexport function createStreamShareClient(\n  options: StreamShareOptions\n): StreamShareClient {\n  return new HostedStreamShareClient(options);\n}\n\ntype SignalMessage =\n  | { type: \"subscribed\"; userId: string }\n  | { type: \"peers\"; peers: string[] }\n  | { type: \"peer-joined\"; userId: string }\n  | { type: \"peer-left\"; userId: string }\n  | { type: \"offer\"; from: string; sdp: string }\n  | { type: \"answer\"; from: string; sdp: string }\n  | { type: \"ice\"; from: string; candidate: RTCIceCandidateInit }\n  | { type: \"error\"; message: string };\n\nclass HostedStreamShareClient implements StreamShareClient {\n  private readonly userId: string;\n  private readonly signalingUrl: string;\n  private ws?: WebSocket;\n  private sessionId?: string;\n  private localStream?: MediaStream;\n  private remoteListeners: Set<(event: RemoteStreamEvent) => void> = new Set();\n  private peerConnections = new Map<\n    string,\n    RTCPeerConnection & { _pendingCandidates?: RTCIceCandidateInit[] }\n  >();\n  private remoteStreams = new Map<string, MediaStream>();\n  private useHostedService: boolean;\n\n  constructor(options: StreamShareOptions) {\n    this.userId = options.userId;\n    // Default to hosted service, allow custom override\n    this.signalingUrl = options.signalingUrl ?? \"wss://signal.streamshare.dev\";\n    this.useHostedService = !options.signalingUrl; // Only use hosted if no custom URL provided\n  }\n\n  private ensureSocket(): Promise<void> {\n    if (this.ws && this.ws.readyState === WebSocket.OPEN)\n      return Promise.resolve();\n    return new Promise((resolve, reject) => {\n      const ws = new WebSocket(this.signalingUrl);\n      this.ws = ws;\n\n      const timeout = setTimeout(() => {\n        if (this.useHostedService) {\n          console.log(\"Hosted service unavailable, falling back to P2P mode\");\n          // Fall back to direct P2P without server\n          resolve();\n        } else {\n          reject(new Error(\"Custom signaling server unavailable\"));\n        }\n      }, 5000);\n\n      ws.onopen = () => {\n        clearTimeout(timeout);\n        this.send({ type: \"subscribe\", userId: this.userId });\n        resolve();\n      };\n\n      ws.onmessage = (ev) => this.onSignal(JSON.parse(ev.data as string));\n\n      ws.onerror = () => {\n        clearTimeout(timeout);\n        if (this.useHostedService) {\n          console.log(\"Hosted service error, falling back to P2P mode\");\n          resolve();\n        } else {\n          reject(new Error(\"Signaling server connection failed\"));\n        }\n      };\n\n      ws.onclose = () => {\n        for (const pc of this.peerConnections.values()) pc.close();\n        this.peerConnections.clear();\n        this.remoteStreams.clear();\n      };\n    });\n  }\n\n  private send(msg: any) {\n    if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n      this.ws.send(JSON.stringify(msg));\n    }\n  }\n\n  async subscribe(userId: string): Promise<void> {\n    void userId;\n    await this.ensureSocket();\n  }\n\n  async startSession(sessionId: string): Promise<void> {\n    await this.ensureSocket();\n    this.sessionId = sessionId;\n    this.send({ type: \"start-session\", sessionId });\n  }\n\n  async joinSession(sessionId: string): Promise<void> {\n    await this.ensureSocket();\n    this.sessionId = sessionId;\n    this.send({ type: \"join-session\", sessionId });\n  }\n\n  async publishStream(\n    stream: MediaStream,\n    kind: StreamKind = \"camera\"\n  ): Promise<void> {\n    void kind;\n    this.localStream = stream;\n\n    // If no server connection, create a simple broadcast mode\n    if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n      console.log(\n        \"Broadcasting in P2P mode - other users will connect directly\"\n      );\n      // Store stream for when peers connect\n      return;\n    }\n\n    // With server: add tracks to existing peers and renegotiate\n    for (const [peerId, pc] of this.peerConnections) {\n      this.addTracksToPeer(pc, stream);\n      await this.createAndSendOffer(peerId, pc);\n    }\n  }\n\n  onRemoteStream(listener: (event: RemoteStreamEvent) => void): () => void {\n    this.remoteListeners.add(listener);\n    return () => this.remoteListeners.delete(listener);\n  }\n\n  private emitRemote(event: RemoteStreamEvent) {\n    for (const listener of this.remoteListeners) listener(event);\n  }\n\n  private async onSignal(msg: SignalMessage) {\n    try {\n      if (msg.type === \"peers\") {\n        for (const peerId of msg.peers) {\n          const pc = this.getOrCreatePeer(peerId);\n          if (this.localStream) this.addTracksToPeer(pc, this.localStream);\n          await this.createAndSendOffer(peerId, pc);\n        }\n        return;\n      }\n\n      if (msg.type === \"peer-joined\") {\n        this.getOrCreatePeer(msg.userId);\n        return;\n      }\n\n      if (msg.type === \"peer-left\") {\n        const pc = this.peerConnections.get(msg.userId);\n        pc?.close();\n        this.peerConnections.delete(msg.userId);\n        this.remoteStreams.delete(msg.userId);\n        return;\n      }\n\n      if (msg.type === \"offer\") {\n        const pc = this.getOrCreatePeer(msg.from);\n        if (pc.signalingState !== \"stable\") {\n          console.warn(\n            `Peer ${msg.from} not in stable state:`,\n            pc.signalingState\n          );\n          return;\n        }\n        await pc.setRemoteDescription({ type: \"offer\", sdp: msg.sdp });\n        if (pc._pendingCandidates) {\n          for (const candidate of pc._pendingCandidates) {\n            await pc.addIceCandidate(candidate);\n          }\n          pc._pendingCandidates = [];\n        }\n        if (this.localStream) this.addTracksToPeer(pc, this.localStream);\n        const answer = await pc.createAnswer();\n        await pc.setLocalDescription(answer);\n        this.send({ type: \"answer\", to: msg.from, sdp: answer.sdp! });\n        return;\n      }\n\n      if (msg.type === \"answer\") {\n        const pc = this.peerConnections.get(msg.from);\n        if (!pc) return;\n        if (pc.signalingState !== \"have-local-offer\") {\n          console.warn(\n            `Peer ${msg.from} not expecting answer, state:`,\n            pc.signalingState\n          );\n          return;\n        }\n        await pc.setRemoteDescription({ type: \"answer\", sdp: msg.sdp });\n        if (pc._pendingCandidates) {\n          for (const candidate of pc._pendingCandidates) {\n            await pc.addIceCandidate(candidate);\n          }\n          pc._pendingCandidates = [];\n        }\n        return;\n      }\n\n      if (msg.type === \"ice\") {\n        const pc = this.peerConnections.get(msg.from);\n        if (!pc) return;\n        if (pc.remoteDescription) {\n          await pc.addIceCandidate(msg.candidate);\n        } else {\n          if (!pc._pendingCandidates) pc._pendingCandidates = [];\n          pc._pendingCandidates.push(msg.candidate);\n        }\n        return;\n      }\n    } catch (error) {\n      console.error(\"Error handling signal:\", error);\n    }\n  }\n\n  private getOrCreatePeer(\n    peerId: string\n  ): RTCPeerConnection & { _pendingCandidates?: RTCIceCandidateInit[] } {\n    let pc = this.peerConnections.get(peerId);\n    if (pc) return pc;\n\n    pc = new RTCPeerConnection({\n      iceServers: [\n        { urls: \"stun:stun.l.google.com:19302\" },\n        // Add hosted TURN servers for better connectivity\n        {\n          urls: \"turn:turn.streamshare.dev:3478\",\n          username: \"streamshare\",\n          credential: \"public\",\n        },\n      ],\n    }) as RTCPeerConnection & { _pendingCandidates?: RTCIceCandidateInit[] };\n\n    pc.onicecandidate = (e) => {\n      if (e.candidate)\n        this.send({ type: \"ice\", to: peerId, candidate: e.candidate });\n    };\n\n    pc.ontrack = (e) => {\n      let stream = this.remoteStreams.get(peerId);\n      if (!stream) {\n        stream = new MediaStream();\n        this.remoteStreams.set(peerId, stream);\n        // Only emit event when creating new stream\n        stream.addTrack(e.track);\n        this.emitRemote({ userId: peerId, stream });\n      } else {\n        // Just add track to existing stream, don't emit again\n        stream.addTrack(e.track);\n      }\n    };\n\n    pc.onconnectionstatechange = () => {\n      if (pc?.connectionState === \"failed\") pc.restartIce();\n    };\n\n    if (this.localStream) this.addTracksToPeer(pc, this.localStream);\n    this.peerConnections.set(peerId, pc);\n    return pc;\n  }\n\n  private addTracksToPeer(pc: RTCPeerConnection, stream: MediaStream) {\n    const existing = new Set(pc.getSenders().map((s) => s.track?.id));\n    for (const track of stream.getTracks()) {\n      if (existing.has(track.id)) continue;\n      pc.addTrack(track, stream);\n    }\n  }\n\n  private async createAndSendOffer(peerId: string, pc: RTCPeerConnection) {\n    const offer = await pc.createOffer({\n      offerToReceiveAudio: true,\n      offerToReceiveVideo: true,\n    });\n    await pc.setLocalDescription(offer);\n    this.send({ type: \"offer\", to: peerId, sdp: offer.sdp! });\n  }\n\n  async close(): Promise<void> {\n    if (this.sessionId) this.send({ type: \"leave-session\" });\n    for (const pc of this.peerConnections.values()) pc.close();\n    this.peerConnections.clear();\n    this.remoteStreams.clear();\n    this.ws?.close();\n  }\n}\n"],"mappings":"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","names":[]}