@wetspace/wetrtc 3.0.1 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/README.md +156 -27
  2. package/dist/es/__test__/codec-preference.test.js +36 -0
  3. package/dist/es/__test__/codec-preference.test.js.map +1 -0
  4. package/dist/es/__test__/data-manager.test.js +60 -0
  5. package/dist/es/__test__/data-manager.test.js.map +1 -0
  6. package/dist/es/__test__/fsm.test.js +33 -0
  7. package/dist/es/__test__/fsm.test.js.map +1 -0
  8. package/dist/es/__test__/media-manager.test.js +41 -0
  9. package/dist/es/__test__/media-manager.test.js.map +1 -0
  10. package/dist/es/__test__/signal-manager.test.js +100 -0
  11. package/dist/es/__test__/signal-manager.test.js.map +1 -0
  12. package/dist/es/__test__/wetrtc-lifecycle.test.js +152 -0
  13. package/dist/es/__test__/wetrtc-lifecycle.test.js.map +1 -0
  14. package/dist/es/data/data-manager.d.ts +37 -0
  15. package/dist/es/data/data-manager.d.ts.map +1 -0
  16. package/dist/es/data/data-manager.js +282 -0
  17. package/dist/es/data/data-manager.js.map +1 -0
  18. package/dist/es/data/types.d.ts +34 -0
  19. package/dist/es/data/types.d.ts.map +1 -0
  20. package/dist/es/data/types.js +0 -0
  21. package/dist/es/disposable.d.ts +12 -0
  22. package/dist/es/disposable.d.ts.map +1 -0
  23. package/dist/es/disposable.js +36 -0
  24. package/dist/es/disposable.js.map +1 -0
  25. package/dist/es/fsm.d.ts +26 -0
  26. package/dist/es/fsm.d.ts.map +1 -0
  27. package/dist/es/fsm.js +63 -0
  28. package/dist/es/fsm.js.map +1 -0
  29. package/dist/es/index.d.ts +22 -0
  30. package/dist/es/index.d.ts.map +1 -0
  31. package/dist/es/index.js +48 -0
  32. package/dist/es/index.js.map +1 -0
  33. package/dist/es/media/audio-encoding.d.ts +10 -0
  34. package/dist/es/media/audio-encoding.d.ts.map +1 -0
  35. package/dist/es/media/audio-encoding.js +41 -0
  36. package/dist/es/media/audio-encoding.js.map +1 -0
  37. package/dist/es/media/codec-preference.d.ts +11 -0
  38. package/dist/es/media/codec-preference.d.ts.map +1 -0
  39. package/dist/es/media/codec-preference.js +77 -0
  40. package/dist/es/media/codec-preference.js.map +1 -0
  41. package/dist/es/media/encoding-utils.d.ts +2 -0
  42. package/dist/es/media/encoding-utils.d.ts.map +1 -0
  43. package/dist/es/media/encoding-utils.js +8 -0
  44. package/dist/es/media/encoding-utils.js.map +1 -0
  45. package/dist/es/media/media-manager.d.ts +39 -0
  46. package/dist/es/media/media-manager.d.ts.map +1 -0
  47. package/dist/es/media/media-manager.js +121 -0
  48. package/dist/es/media/media-manager.js.map +1 -0
  49. package/dist/es/media/types.d.ts +25 -0
  50. package/dist/es/media/types.d.ts.map +1 -0
  51. package/dist/es/media/types.js +0 -0
  52. package/dist/es/media/video-encoding.d.ts +12 -0
  53. package/dist/es/media/video-encoding.d.ts.map +1 -0
  54. package/dist/es/media/video-encoding.js +60 -0
  55. package/dist/es/media/video-encoding.js.map +1 -0
  56. package/dist/es/signal/signal-manager.d.ts +45 -0
  57. package/dist/es/signal/signal-manager.d.ts.map +1 -0
  58. package/dist/es/signal/signal-manager.js +250 -0
  59. package/dist/es/signal/signal-manager.js.map +1 -0
  60. package/dist/es/signal/types.d.ts +26 -0
  61. package/dist/es/signal/types.d.ts.map +1 -0
  62. package/dist/es/signal/types.js +8 -0
  63. package/dist/es/signal/types.js.map +1 -0
  64. package/dist/es/stats/stats-monitor.d.ts +32 -0
  65. package/dist/es/stats/stats-monitor.d.ts.map +1 -0
  66. package/dist/es/stats/stats-monitor.js +191 -0
  67. package/dist/es/stats/stats-monitor.js.map +1 -0
  68. package/dist/es/stats/types.d.ts +33 -0
  69. package/dist/es/stats/types.d.ts.map +1 -0
  70. package/dist/es/stats/types.js +0 -0
  71. package/dist/es/utils/types.d.ts +46 -0
  72. package/dist/es/utils/types.d.ts.map +1 -0
  73. package/dist/es/utils/types.js +80 -0
  74. package/dist/es/utils/types.js.map +1 -0
  75. package/dist/es/wetrtc.d.ts +92 -0
  76. package/dist/es/wetrtc.d.ts.map +1 -0
  77. package/dist/es/wetrtc.js +403 -0
  78. package/dist/es/wetrtc.js.map +1 -0
  79. package/dist/lib/__test__/codec-preference.test.js +34 -0
  80. package/dist/lib/__test__/codec-preference.test.js.map +1 -0
  81. package/dist/lib/__test__/data-manager.test.js +61 -0
  82. package/dist/lib/__test__/data-manager.test.js.map +1 -0
  83. package/dist/lib/__test__/fsm.test.js +34 -0
  84. package/dist/lib/__test__/fsm.test.js.map +1 -0
  85. package/dist/lib/__test__/media-manager.test.js +42 -0
  86. package/dist/lib/__test__/media-manager.test.js.map +1 -0
  87. package/dist/lib/__test__/signal-manager.test.js +101 -0
  88. package/dist/lib/__test__/signal-manager.test.js.map +1 -0
  89. package/dist/lib/__test__/wetrtc-lifecycle.test.js +153 -0
  90. package/dist/lib/__test__/wetrtc-lifecycle.test.js.map +1 -0
  91. package/dist/lib/data/data-manager.js +306 -0
  92. package/dist/lib/data/data-manager.js.map +1 -0
  93. package/dist/lib/data/types.js +18 -0
  94. package/dist/lib/data/types.js.map +1 -0
  95. package/dist/lib/disposable.js +60 -0
  96. package/dist/lib/disposable.js.map +1 -0
  97. package/dist/lib/fsm.js +87 -0
  98. package/dist/lib/fsm.js.map +1 -0
  99. package/dist/lib/index.js +75 -0
  100. package/dist/lib/index.js.map +1 -0
  101. package/dist/lib/media/audio-encoding.js +66 -0
  102. package/dist/lib/media/audio-encoding.js.map +1 -0
  103. package/dist/lib/media/codec-preference.js +106 -0
  104. package/dist/lib/media/codec-preference.js.map +1 -0
  105. package/dist/lib/media/encoding-utils.js +32 -0
  106. package/dist/lib/media/encoding-utils.js.map +1 -0
  107. package/dist/lib/media/media-manager.js +145 -0
  108. package/dist/lib/media/media-manager.js.map +1 -0
  109. package/dist/lib/media/types.js +18 -0
  110. package/dist/lib/media/types.js.map +1 -0
  111. package/dist/lib/media/video-encoding.js +87 -0
  112. package/dist/lib/media/video-encoding.js.map +1 -0
  113. package/dist/lib/signal/signal-manager.js +274 -0
  114. package/dist/lib/signal/signal-manager.js.map +1 -0
  115. package/dist/lib/signal/types.js +32 -0
  116. package/dist/lib/signal/types.js.map +1 -0
  117. package/dist/lib/stats/stats-monitor.js +215 -0
  118. package/dist/lib/stats/stats-monitor.js.map +1 -0
  119. package/dist/lib/stats/types.js +18 -0
  120. package/dist/lib/stats/types.js.map +1 -0
  121. package/dist/lib/utils/types.js +108 -0
  122. package/dist/lib/utils/types.js.map +1 -0
  123. package/dist/lib/wetrtc.js +415 -0
  124. package/dist/lib/wetrtc.js.map +1 -0
  125. package/package.json +38 -43
  126. package/es/core/constant.d.ts +0 -6
  127. package/es/core/hook.d.ts +0 -31
  128. package/es/core/index.d.ts +0 -39
  129. package/es/index.d.ts +0 -6
  130. package/es/index.js +0 -1
  131. package/es/libs/index.d.ts +0 -41
  132. package/es/libs/record.d.ts +0 -8
  133. package/lib/core/constant.d.ts +0 -6
  134. package/lib/core/hook.d.ts +0 -31
  135. package/lib/core/index.d.ts +0 -39
  136. package/lib/index.d.ts +0 -6
  137. package/lib/index.js +0 -1
  138. package/lib/libs/index.d.ts +0 -41
  139. package/lib/libs/record.d.ts +0 -8
@@ -0,0 +1 @@
1
+ {"version":3,"mappings":"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","names":[],"ignoreList":[],"sources":["../../src/wetrtc.ts"],"sourcesContent":["import { ConnectionStateMachine } from './fsm';\r\nimport type { ConnectionState } from './fsm';\r\nimport { DisposableStack, type IDisposable } from './disposable';\r\nimport { SignalManager } from './signal/signal-manager';\r\nimport type { SignalConfig } from './signal/signal-manager';\r\nimport { MediaManager } from './media/media-manager';\r\nimport type { DisplayMediaOptions } from './media/types';\r\nimport {\r\n applyVideoEncodingToConnection,\r\n applyVideoSenderEncoding,\r\n applyVideoTrackContentHint,\r\n type VideoEncodingOptions,\r\n} from './media/video-encoding';\r\nimport {\r\n applyAudioEncodingToConnection,\r\n applyAudioSenderEncoding,\r\n type AudioEncodingOptions,\r\n} from './media/audio-encoding';\r\nimport {\r\n applyH264CodecPreference,\r\n applyH264CodecPreferences,\r\n applyOpusCodecPreference,\r\n applyOpusCodecPreferences,\r\n type PreferredVideoCodec,\r\n type PreferredAudioCodec,\r\n} from './media/codec-preference';\r\nimport { DataManager } from './data/data-manager';\r\nimport type { DataChannelConfig } from './data/types';\r\nimport { StatsMonitor } from './stats/stats-monitor';\r\nimport { TypedEmitter, createLogger, type Logger } from './utils/types';\r\nimport type { SignalChannel } from './signal/types';\r\nimport type {\r\n WetRTCEvent,\r\n WetRTCEventMap,\r\n WetRTCError,\r\n WetRTCErrorCode,\r\n} from './utils/types';\r\n\r\nexport type { ConnectionState };\r\nexport type { WetRTCEvent, WetRTCEventMap, WetRTCError, WetRTCErrorCode };\r\nexport type { SignalChannel };\r\nexport type { VideoEncodingOptions } from './media/video-encoding';\r\nexport type { AudioEncodingOptions } from './media/audio-encoding';\r\nexport type { PreferredVideoCodec, PreferredAudioCodec } from './media/codec-preference';\r\n\r\nexport interface WetRTCConfig {\r\n signal: SignalChannel;\r\n direction?: 'sendonly' | 'recvonly' | 'sendrecv';\r\n iceServers?: RTCIceServer[];\r\n iceTransportPolicy?: RTCIceTransportPolicy;\r\n polite?: boolean;\r\n /** 是否主动发起 SDP 协商;默认 sendonly/sendrecv 为 true,recvonly 为 false */\r\n initiator?: boolean;\r\n dataChannels?: DataChannelConfig[];\r\n signalConfig?: SignalConfig;\r\n statsInterval?: number;\r\n logLevel?: 'debug' | 'info' | 'warn' | 'error' | 'none';\r\n reconnect?: false | {\r\n maxAttempts?: number;\r\n baseDelay?: number;\r\n maxDelay?: number;\r\n backoffMultiplier?: number;\r\n };\r\n /** 发送端视频编码低延迟参数(屏幕共享场景) */\r\n videoEncoding?: VideoEncodingOptions;\r\n /** 视频编解码器偏好;h264 优先使用 H.264(Electron/Chrome 下通常走硬件编码) */\r\n preferredVideoCodec?: PreferredVideoCodec;\r\n /** 发送端音频编码参数(码率、优先级等) */\r\n audioEncoding?: AudioEncodingOptions;\r\n /** 音频编解码器偏好;opus 优先使用 Opus(WebRTC 默认语音编解码器) */\r\n preferredAudioCodec?: PreferredAudioCodec;\r\n}\r\n\r\nconst DEFAULT_ICE_SERVERS: RTCIceServer[] = [\r\n { urls: 'stun:stun.l.google.com:19302' },\r\n];\r\n\r\nconst DEFAULT_RECONNECT = {\r\n maxAttempts: 5,\r\n baseDelay: 1000,\r\n maxDelay: 30_000,\r\n backoffMultiplier: 2,\r\n};\r\n\r\ntype ReconnectOptions = typeof DEFAULT_RECONNECT;\r\n\r\ntype NormalizedConfig = Required<Omit<WetRTCConfig, 'statsInterval' | 'logLevel' | 'iceTransportPolicy' | 'reconnect' | 'initiator' | 'videoEncoding' | 'preferredVideoCodec' | 'audioEncoding' | 'preferredAudioCodec'>> & {\r\n statsInterval: number;\r\n iceTransportPolicy?: RTCIceTransportPolicy;\r\n reconnect: false | ReconnectOptions;\r\n initiator: boolean;\r\n videoEncoding?: VideoEncodingOptions;\r\n preferredVideoCodec: PreferredVideoCodec;\r\n audioEncoding?: AudioEncodingOptions;\r\n preferredAudioCodec: PreferredAudioCodec;\r\n};\r\n\r\nexport class WetRTC implements IDisposable {\r\n readonly fsm: ConnectionStateMachine;\r\n readonly media: MediaManager;\r\n readonly data: DataManager;\r\n readonly stats: StatsMonitor;\r\n\r\n private events = new TypedEmitter<WetRTCEventMap>();\r\n private disposables = new DisposableStack();\r\n private logger: Logger;\r\n private pc: RTCPeerConnection | null = null;\r\n private signalManager: SignalManager;\r\n private config!: NormalizedConfig;\r\n private reconnectAttempt = 0;\r\n private pcListenerCleanup: (() => void) | null = null;\r\n private reconnecting = false;\r\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\r\n private screenStream: MediaStream | null = null;\r\n\r\n constructor(config: WetRTCConfig) {\r\n this.config = this.normalizeConfig(config);\r\n this.logger = createLogger(\r\n config.logLevel === 'debug' ? 0 :\r\n config.logLevel === 'info' ? 1 :\r\n config.logLevel === 'none' ? 4 : 2\r\n );\r\n\r\n this.fsm = new ConnectionStateMachine();\r\n this.media = new MediaManager();\r\n this.pc = this.createPeerConnection();\r\n this.data = new DataManager(this.pc);\r\n this.stats = new StatsMonitor(this.pc, {\r\n interval: this.config.statsInterval || undefined,\r\n });\r\n\r\n this.fsm.onChange((newState, prev) => {\r\n this.events.emit('statechange', newState, prev);\r\n });\r\n\r\n this.signalManager = new SignalManager(\r\n this.config.signal,\r\n this.pc,\r\n this.fsm,\r\n this.config.polite,\r\n this.config.signalConfig,\r\n );\r\n\r\n this.attachPcListeners(this.pc);\r\n\r\n if (this.config.dataChannels.length > 0) {\r\n this.data.configureChannels(this.config.dataChannels);\r\n }\r\n\r\n this.data.onMessage((data, label) => {\r\n const channel = label ? this.data.getChannel(label) : undefined;\r\n this.events.emit('message', data, channel as RTCDataChannel);\r\n });\r\n\r\n this.stats.onStats((snapshot) => {\r\n this.events.emit('stats', snapshot);\r\n });\r\n\r\n this.disposables.push(this.fsm);\r\n this.disposables.push(this.media);\r\n this.disposables.push(this.data);\r\n this.disposables.push(this.stats);\r\n\r\n this.logger.info('WetRTC initialized', { direction: this.config.direction });\r\n }\r\n\r\n async connect(): Promise<void> {\r\n if (!this.fsm.is('idle', 'failed')) {\r\n throw new Error(`Cannot connect in state \"${this.fsm.state}\"`);\r\n }\r\n\r\n this.reconnectAttempt = 0;\r\n return this.doConnect();\r\n }\r\n\r\n async disconnect(): Promise<void> {\r\n this.logger.info('Disconnecting');\r\n this.stats.stop();\r\n this.cancelReconnect();\r\n try {\r\n await this.config.signal.send({ type: 'bye' });\r\n } catch (err) {\r\n this.logger.warn('Failed to send bye:', err);\r\n }\r\n this.rebindPeerConnection();\r\n this.fsm.force('idle');\r\n }\r\n\r\n dispose(): void {\r\n this.logger.info('Disposing WetRTC');\r\n this.stats.stop();\r\n this.cancelReconnect();\r\n this.screenStream?.getTracks().forEach(track => track.stop());\r\n this.screenStream = null;\r\n this.pcListenerCleanup?.();\r\n this.pcListenerCleanup = null;\r\n this.signalManager.dispose();\r\n this.pc?.close();\r\n this.pc = null;\r\n this.disposables.dispose();\r\n this.events.removeAll();\r\n this.fsm.force('disposed');\r\n }\r\n\r\n on<E extends WetRTCEvent>(event: E, handler: WetRTCEventMap[E]): () => void {\r\n return this.events.on(event, handler);\r\n }\r\n\r\n once<E extends WetRTCEvent>(event: E, handler: WetRTCEventMap[E]): void {\r\n this.events.once(event, handler);\r\n }\r\n\r\n off<E extends WetRTCEvent>(event: E, handler: WetRTCEventMap[E]): void {\r\n this.events.off(event, handler);\r\n }\r\n\r\n get state(): ConnectionState {\r\n return this.fsm.state;\r\n }\r\n\r\n get peerConnection(): RTCPeerConnection | null {\r\n return this.pc;\r\n }\r\n\r\n addTrack(track: MediaStreamTrack, stream: MediaStream): void {\r\n if (track.kind === 'video' && this.config.videoEncoding) {\r\n applyVideoTrackContentHint(\r\n track,\r\n this.config.videoEncoding.contentHint ?? 'motion',\r\n );\r\n }\r\n this.media.addTrack(track, stream);\r\n this.addTrackToPeerConnection(track, stream);\r\n }\r\n\r\n removeTrack(track: MediaStreamTrack): void {\r\n const sender = this.pc?.getSenders().find(s => s.track === track);\r\n if (sender) {\r\n this.pc?.removeTrack(sender);\r\n }\r\n this.media.removeTrack(track);\r\n }\r\n\r\n async replaceTrack(\r\n oldTrack: MediaStreamTrack,\r\n newTrack: MediaStreamTrack,\r\n ): Promise<void> {\r\n const sender = this.pc?.getSenders().find(s => s.track === oldTrack);\r\n if (sender) {\r\n await sender.replaceTrack(newTrack);\r\n }\r\n this.media.replaceTrack(oldTrack, newTrack);\r\n }\r\n\r\n async shareScreen(options?: DisplayMediaOptions): Promise<MediaStream> {\r\n try {\r\n const stream = await this.media.getDisplayMedia(options);\r\n this.screenStream = stream;\r\n for (const track of stream.getTracks()) {\r\n this.addTrack(track, stream);\r\n }\r\n stream.getVideoTracks()[0]?.addEventListener('ended', () => {\r\n this.stopScreenShare();\r\n }, { once: true });\r\n return stream;\r\n } catch (err) {\r\n this.emitError('MEDIA_FAILED', (err as Error).message, true);\r\n throw err;\r\n }\r\n }\r\n\r\n stopScreenShare(): void {\r\n if (!this.screenStream) return;\r\n for (const track of this.screenStream.getTracks()) {\r\n this.removeTrack(track);\r\n }\r\n this.screenStream = null;\r\n }\r\n\r\n async switchCamera(deviceId?: string): Promise<MediaStream> {\r\n const stream = await this.media.getUserMedia({\r\n video: deviceId ? { deviceId: { exact: deviceId } } : true,\r\n audio: false,\r\n });\r\n const nextVideo = stream.getVideoTracks()[0];\r\n const currentVideo = this.media.getLocalTracks('video')[0];\r\n if (currentVideo && nextVideo) {\r\n await this.replaceVideoTrack(nextVideo);\r\n } else if (nextVideo) {\r\n this.addTrack(nextVideo, stream);\r\n }\r\n return stream;\r\n }\r\n\r\n async replaceVideoTrack(track: MediaStreamTrack): Promise<void> {\r\n const current = this.media.getLocalTracks('video')[0];\r\n if (!current) {\r\n const stream = new MediaStream([track]);\r\n this.addTrack(track, stream);\r\n return;\r\n }\r\n await this.replaceTrack(current, track);\r\n }\r\n\r\n // ── private ──\r\n\r\n private rebindPeerConnection(): RTCPeerConnection {\r\n this.pcListenerCleanup?.();\r\n this.pc?.close();\r\n\r\n const pc = this.createPeerConnection();\r\n this.pc = pc;\r\n this.signalManager.setPeerConnection(pc);\r\n this.data.setPeerConnection(pc);\r\n this.stats.setPeerConnection(pc);\r\n this.attachPcListeners(pc);\r\n this.restoreLocalTracks(pc);\r\n\r\n if (this.config.dataChannels.length > 0) {\r\n this.data.configureChannels(this.config.dataChannels);\r\n }\r\n\r\n return pc;\r\n }\r\n\r\n private attachPcListeners(pc: RTCPeerConnection): void {\r\n const onTrack = (ev: RTCTrackEvent) => {\r\n if (ev.streams[0]) {\r\n this.media.addRemoteStream(ev.streams[0]);\r\n }\r\n this.events.emit('track', ev);\r\n };\r\n\r\n const onDataChannel = (ev: RTCDataChannelEvent) => {\r\n this.data.registerRemoteChannel(ev);\r\n this.events.emit('datachannel', ev.channel);\r\n };\r\n\r\n const onIceDisconnected = () => {\r\n if (this.pc !== pc) return;\r\n if (pc.iceConnectionState === 'disconnected' && this.config.reconnect !== false) {\r\n this.scheduleReconnect();\r\n }\r\n };\r\n\r\n const onConnectionStateChange = () => {\r\n if (this.pc !== pc || pc.connectionState !== 'connected') return;\r\n if (this.config.videoEncoding) {\r\n void applyVideoEncodingToConnection(pc, this.config.videoEncoding);\r\n }\r\n if (this.config.audioEncoding) {\r\n void applyAudioEncodingToConnection(pc, this.config.audioEncoding);\r\n }\r\n };\r\n\r\n pc.addEventListener('track', onTrack);\r\n pc.addEventListener('datachannel', onDataChannel);\r\n pc.addEventListener('iceconnectionstatechange', onIceDisconnected);\r\n pc.addEventListener('connectionstatechange', onConnectionStateChange);\r\n\r\n this.pcListenerCleanup = () => {\r\n pc.removeEventListener('track', onTrack);\r\n pc.removeEventListener('datachannel', onDataChannel);\r\n pc.removeEventListener('iceconnectionstatechange', onIceDisconnected);\r\n pc.removeEventListener('connectionstatechange', onConnectionStateChange);\r\n };\r\n }\r\n\r\n private restoreLocalTracks(pc: RTCPeerConnection): void {\r\n const stream = this.media.getLocalStream();\r\n if (!stream) return;\r\n for (const track of this.media.getLocalTracks()) {\r\n this.addTrackToPeerConnection(track, stream, pc);\r\n }\r\n }\r\n\r\n private addTrackToPeerConnection(\r\n track: MediaStreamTrack,\r\n stream: MediaStream,\r\n pc = this.pc,\r\n ): void {\r\n if (!pc) return;\r\n const sender = pc.addTrack(track, stream);\r\n const transceiver = pc.getTransceivers().find(t => t.sender === sender);\r\n if (!transceiver) return;\r\n if (this.config.direction === 'sendonly') {\r\n transceiver.direction = 'sendonly';\r\n } else if (this.config.direction === 'sendrecv') {\r\n transceiver.direction = 'sendrecv';\r\n }\r\n\r\n if (track.kind === 'video' && this.config.videoEncoding) {\r\n void applyVideoSenderEncoding(sender, this.config.videoEncoding);\r\n }\r\n\r\n if (track.kind === 'audio' && this.config.audioEncoding) {\r\n void applyAudioSenderEncoding(sender, this.config.audioEncoding);\r\n }\r\n\r\n if (track.kind === 'video' && this.config.preferredVideoCodec === 'h264') {\r\n applyH264CodecPreference(transceiver);\r\n }\r\n\r\n if (track.kind === 'audio' && this.config.preferredAudioCodec === 'opus') {\r\n applyOpusCodecPreference(transceiver);\r\n }\r\n }\r\n\r\n private scheduleReconnect(): void {\r\n if (this.reconnecting) return;\r\n\r\n const { maxAttempts = 5, baseDelay = 1000, maxDelay = 30000, backoffMultiplier = 2 } =\r\n this.config.reconnect === false ? DEFAULT_RECONNECT : this.config.reconnect;\r\n\r\n if (this.reconnectAttempt >= maxAttempts) {\r\n this.logger.error('Max reconnect attempts reached');\r\n this.fsm.transition('failed');\r\n this.emitError('RECONNECT_FAILED', 'Max reconnect attempts reached', false);\r\n return;\r\n }\r\n\r\n this.reconnecting = true;\r\n this.reconnectAttempt++;\r\n const delay = Math.min(\r\n baseDelay * Math.pow(backoffMultiplier, this.reconnectAttempt - 1),\r\n maxDelay,\r\n );\r\n\r\n this.logger.info(`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempt}/${maxAttempts})`);\r\n this.events.emit('reconnecting', this.reconnectAttempt, maxAttempts);\r\n\r\n this.reconnectTimer = setTimeout(async () => {\r\n try {\r\n this.stats.stop();\r\n this.rebindPeerConnection();\r\n this.fsm.force('idle');\r\n await this.doConnect();\r\n this.reconnecting = false;\r\n this.reconnectTimer = null;\r\n } catch {\r\n this.reconnecting = false;\r\n this.reconnectTimer = null;\r\n this.scheduleReconnect();\r\n }\r\n }, delay);\r\n }\r\n\r\n private cancelReconnect(): void {\r\n this.reconnecting = false;\r\n if (this.reconnectTimer) {\r\n clearTimeout(this.reconnectTimer);\r\n this.reconnectTimer = null;\r\n }\r\n }\r\n\r\n private ensureTransceivers(): void {\r\n if (!this.pc || this.config.direction !== 'recvonly') return;\r\n if (this.pc.getTransceivers().length > 0) return;\r\n\r\n const videoTransceiver = this.pc.addTransceiver('video', { direction: 'recvonly' });\r\n const audioTransceiver = this.pc.addTransceiver('audio', { direction: 'recvonly' });\r\n\r\n if (this.config.preferredVideoCodec === 'h264') {\r\n applyH264CodecPreference(videoTransceiver);\r\n }\r\n if (this.config.preferredAudioCodec === 'opus') {\r\n applyOpusCodecPreference(audioTransceiver);\r\n }\r\n }\r\n\r\n private async doConnect(): Promise<void> {\r\n try {\r\n this.fsm.transition('signaling');\r\n this.stats.start();\r\n this.ensureTransceivers();\r\n\r\n if (this.config.preferredVideoCodec === 'h264' && this.pc) {\r\n applyH264CodecPreferences(this.pc);\r\n }\r\n if (this.config.preferredAudioCodec === 'opus' && this.pc) {\r\n applyOpusCodecPreferences(this.pc);\r\n }\r\n\r\n if (this.config.initiator) {\r\n this.data.initConfiguredChannels();\r\n await this.signalManager.createOffer();\r\n } else {\r\n this.logger.info('Waiting for remote offer (passive)');\r\n }\r\n } catch (err) {\r\n this.emitError('NEGOTIATION_FAILED', (err as Error).message, true);\r\n throw err;\r\n }\r\n }\r\n\r\n private createPeerConnection(): RTCPeerConnection {\r\n const config: RTCConfiguration = {\r\n iceServers: this.config.iceServers,\r\n };\r\n if (this.config.iceTransportPolicy) {\r\n config.iceTransportPolicy = this.config.iceTransportPolicy;\r\n }\r\n return new RTCPeerConnection(config);\r\n }\r\n\r\n private normalizeConfig(config: WetRTCConfig): NormalizedConfig {\r\n return {\r\n signal: config.signal,\r\n direction: config.direction ?? 'sendrecv',\r\n iceServers: config.iceServers ?? DEFAULT_ICE_SERVERS,\r\n iceTransportPolicy: config.iceTransportPolicy,\r\n polite: config.polite ?? (config.direction === 'sendonly'),\r\n initiator: config.initiator ?? (config.direction !== 'recvonly'),\r\n dataChannels: config.dataChannels ?? [],\r\n signalConfig: config.signalConfig ?? {},\r\n reconnect: config.reconnect === false ? false : { ...DEFAULT_RECONNECT, ...config.reconnect },\r\n statsInterval: config.statsInterval ?? 2000,\r\n videoEncoding: config.videoEncoding,\r\n preferredVideoCodec: config.preferredVideoCodec ?? 'auto',\r\n audioEncoding: config.audioEncoding,\r\n preferredAudioCodec: config.preferredAudioCodec ?? 'auto',\r\n };\r\n }\r\n\r\n private emitError(code: WetRTCErrorCode, message: string, recoverable: boolean): void {\r\n this.events.emit('error', {\r\n code,\r\n message: `[WetRTC] ${code}: ${message}`,\r\n recoverable,\r\n });\r\n }\r\n}\r\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJtYXBwaW5ncyI6IiIsIm5hbWVzIjpbXSwiaWdub3JlTGlzdCI6W10sInNvdXJjZXMiOltdLCJzb3VyY2VzQ29udGVudCI6W119"]}
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ var import_vitest = require("vitest");
3
+ var import_codec_preference = require("../media/codec-preference");
4
+ (0, import_vitest.describe)("sortVideoCodecsH264First", () => {
5
+ (0, import_vitest.it)("places H264 codecs before others", () => {
6
+ const codecs = [
7
+ { mimeType: "video/VP8", clockRate: 9e4, channels: void 0, sdpFmtpLine: void 0 },
8
+ { mimeType: "video/H264", clockRate: 9e4, channels: void 0, sdpFmtpLine: "level-asymmetry-allowed=1;packetization-mode=0" },
9
+ { mimeType: "video/VP9", clockRate: 9e4, channels: void 0, sdpFmtpLine: void 0 },
10
+ { mimeType: "video/H264", clockRate: 9e4, channels: void 0, sdpFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1" }
11
+ ];
12
+ const sorted = (0, import_codec_preference.sortVideoCodecsH264First)(codecs);
13
+ (0, import_vitest.expect)(sorted[0].mimeType).toBe("video/H264");
14
+ (0, import_vitest.expect)(sorted[0].sdpFmtpLine).toContain("packetization-mode=1");
15
+ (0, import_vitest.expect)(sorted[1].mimeType).toBe("video/H264");
16
+ (0, import_vitest.expect)(sorted[2].mimeType).toBe("video/VP8");
17
+ (0, import_vitest.expect)(sorted[3].mimeType).toBe("video/VP9");
18
+ });
19
+ });
20
+ (0, import_vitest.describe)("sortAudioCodecsOpusFirst", () => {
21
+ (0, import_vitest.it)("places Opus codecs before others", () => {
22
+ const codecs = [
23
+ { mimeType: "audio/PCMU", clockRate: 8e3, channels: 1, sdpFmtpLine: void 0 },
24
+ { mimeType: "audio/opus", clockRate: 48e3, channels: 2, sdpFmtpLine: "minptime=10;useinbandfec=1" },
25
+ { mimeType: "audio/PCMA", clockRate: 8e3, channels: 1, sdpFmtpLine: void 0 }
26
+ ];
27
+ const sorted = (0, import_codec_preference.sortAudioCodecsOpusFirst)(codecs);
28
+ (0, import_vitest.expect)(sorted[0].mimeType).toBe("audio/opus");
29
+ (0, import_vitest.expect)(sorted[1].mimeType).toBe("audio/PCMU");
30
+ (0, import_vitest.expect)(sorted[2].mimeType).toBe("audio/PCMA");
31
+ });
32
+ });
33
+
34
+ //# sourceMappingURL=codec-preference.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"mappings":";AAAA,oBAAqC;AACrC,8BAKO;AAAA,IAEP,wBAAS,4BAA4B,MAAM;AACzC,wBAAG,oCAAoC,MAAM;AAC3C,UAAM,SAAS;AAAA,MACb,EAAE,UAAU,aAAa,WAAW,KAAO,UAAU,QAAW,aAAa,OAAU;AAAA,MACvF,EAAE,UAAU,cAAc,WAAW,KAAO,UAAU,QAAW,aAAa,iDAAiD;AAAA,MAC/H,EAAE,UAAU,aAAa,WAAW,KAAO,UAAU,QAAW,aAAa,OAAU;AAAA,MACvF,EAAE,UAAU,cAAc,WAAW,KAAO,UAAU,QAAW,aAAa,iDAAiD;AAAA,IACjI;AAEA,UAAM,aAAS,kDAAyB,MAAM;AAE9C,8BAAO,OAAO,CAAC,EAAE,QAAQ,EAAE,KAAK,YAAY;AAC5C,8BAAO,OAAO,CAAC,EAAE,WAAW,EAAE,UAAU,sBAAsB;AAC9D,8BAAO,OAAO,CAAC,EAAE,QAAQ,EAAE,KAAK,YAAY;AAC5C,8BAAO,OAAO,CAAC,EAAE,QAAQ,EAAE,KAAK,WAAW;AAC3C,8BAAO,OAAO,CAAC,EAAE,QAAQ,EAAE,KAAK,WAAW;AAAA,EAC7C,CAAC;AACH,CAAC;AAAA,IAED,wBAAS,4BAA4B,MAAM;AACzC,wBAAG,oCAAoC,MAAM;AAC3C,UAAM,SAAS;AAAA,MACb,EAAE,UAAU,cAAc,WAAW,KAAM,UAAU,GAAG,aAAa,OAAU;AAAA,MAC/E,EAAE,UAAU,cAAc,WAAW,MAAO,UAAU,GAAG,aAAa,6BAA6B;AAAA,MACnG,EAAE,UAAU,cAAc,WAAW,KAAM,UAAU,GAAG,aAAa,OAAU;AAAA,IACjF;AAEA,UAAM,aAAS,kDAAyB,MAAM;AAE9C,8BAAO,OAAO,CAAC,EAAE,QAAQ,EAAE,KAAK,YAAY;AAC5C,8BAAO,OAAO,CAAC,EAAE,QAAQ,EAAE,KAAK,YAAY;AAC5C,8BAAO,OAAO,CAAC,EAAE,QAAQ,EAAE,KAAK,YAAY;AAAA,EAC9C,CAAC;AACH,CAAC","names":[],"ignoreList":[],"sources":["../../../src/__test__/codec-preference.test.ts"],"sourcesContent":["import { describe, expect, it } from 'vitest';\r\nimport {\r\n sortVideoCodecsH264First,\r\n sortAudioCodecsOpusFirst,\r\n type VideoRtpCodec,\r\n type AudioRtpCodec,\r\n} from '../media/codec-preference';\r\n\r\ndescribe('sortVideoCodecsH264First', () => {\r\n it('places H264 codecs before others', () => {\r\n const codecs = [\r\n { mimeType: 'video/VP8', clockRate: 90000, channels: undefined, sdpFmtpLine: undefined },\r\n { mimeType: 'video/H264', clockRate: 90000, channels: undefined, sdpFmtpLine: 'level-asymmetry-allowed=1;packetization-mode=0' },\r\n { mimeType: 'video/VP9', clockRate: 90000, channels: undefined, sdpFmtpLine: undefined },\r\n { mimeType: 'video/H264', clockRate: 90000, channels: undefined, sdpFmtpLine: 'level-asymmetry-allowed=1;packetization-mode=1' },\r\n ] as VideoRtpCodec[];\r\n\r\n const sorted = sortVideoCodecsH264First(codecs);\r\n\r\n expect(sorted[0].mimeType).toBe('video/H264');\r\n expect(sorted[0].sdpFmtpLine).toContain('packetization-mode=1');\r\n expect(sorted[1].mimeType).toBe('video/H264');\r\n expect(sorted[2].mimeType).toBe('video/VP8');\r\n expect(sorted[3].mimeType).toBe('video/VP9');\r\n });\r\n});\r\n\r\ndescribe('sortAudioCodecsOpusFirst', () => {\r\n it('places Opus codecs before others', () => {\r\n const codecs = [\r\n { mimeType: 'audio/PCMU', clockRate: 8000, channels: 1, sdpFmtpLine: undefined },\r\n { mimeType: 'audio/opus', clockRate: 48000, channels: 2, sdpFmtpLine: 'minptime=10;useinbandfec=1' },\r\n { mimeType: 'audio/PCMA', clockRate: 8000, channels: 1, sdpFmtpLine: undefined },\r\n ] as AudioRtpCodec[];\r\n\r\n const sorted = sortAudioCodecsOpusFirst(codecs);\r\n\r\n expect(sorted[0].mimeType).toBe('audio/opus');\r\n expect(sorted[1].mimeType).toBe('audio/PCMU');\r\n expect(sorted[2].mimeType).toBe('audio/PCMA');\r\n });\r\n});\r\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJtYXBwaW5ncyI6IiIsIm5hbWVzIjpbXSwiaWdub3JlTGlzdCI6W10sInNvdXJjZXMiOltdLCJzb3VyY2VzQ29udGVudCI6W119"]}
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ var import_vitest = require("vitest");
3
+ var import_data_manager = require("../data/data-manager");
4
+ function createChannel(label = "file-test") {
5
+ const listeners = /* @__PURE__ */ new Map();
6
+ return {
7
+ label,
8
+ readyState: "open",
9
+ bufferedAmount: 0,
10
+ bufferedAmountLowThreshold: 0,
11
+ send: import_vitest.vi.fn(),
12
+ close: import_vitest.vi.fn(),
13
+ addEventListener: import_vitest.vi.fn((type, fn) => {
14
+ if (!listeners.has(type))
15
+ listeners.set(type, /* @__PURE__ */ new Set());
16
+ listeners.get(type).add(fn);
17
+ }),
18
+ removeEventListener: import_vitest.vi.fn((type, fn) => {
19
+ listeners.get(type)?.delete(fn);
20
+ }),
21
+ onmessage: null,
22
+ onopen: null,
23
+ onclose: null,
24
+ onerror: null
25
+ };
26
+ }
27
+ function createPc(channel) {
28
+ return {
29
+ createDataChannel: import_vitest.vi.fn(() => channel)
30
+ };
31
+ }
32
+ (0, import_vitest.describe)("DataManager", () => {
33
+ (0, import_vitest.it)("receives a complete file", async () => {
34
+ const channel = createChannel();
35
+ const manager = new import_data_manager.DataManager(createPc(channel));
36
+ const handler = import_vitest.vi.fn();
37
+ manager.onFile(handler);
38
+ manager.createChannel("file-test");
39
+ channel.onmessage?.({ data: JSON.stringify({
40
+ type: "file-meta",
41
+ name: "hello.txt",
42
+ size: 5,
43
+ chunks: 1,
44
+ mimeType: "text/plain"
45
+ }) });
46
+ channel.onmessage?.({ data: new Blob(["hello"]) });
47
+ channel.onmessage?.({ data: JSON.stringify({ type: "file-complete", name: "hello.txt" }) });
48
+ (0, import_vitest.expect)(handler).toHaveBeenCalledTimes(1);
49
+ const [meta, blob] = handler.mock.calls[0];
50
+ (0, import_vitest.expect)(meta).toEqual({ name: "hello.txt", size: 5, type: "text/plain" });
51
+ (0, import_vitest.expect)(await blob.text()).toBe("hello");
52
+ });
53
+ (0, import_vitest.it)("rejects sendFile when file exceeds maxFileSize", async () => {
54
+ const channel = createChannel();
55
+ const manager = new import_data_manager.DataManager(createPc(channel));
56
+ const file = new File(["too large"], "large.txt");
57
+ await (0, import_vitest.expect)(manager.sendFile(file, { maxFileSize: 1 })).rejects.toThrow(/maxFileSize/);
58
+ });
59
+ });
60
+
61
+ //# sourceMappingURL=data-manager.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"mappings":";AAAA,oBAAyC;AACzC,0BAA4B;AAE5B,SAAS,cAAc,QAAQ,aAA6B;AAC1D,QAAM,YAAY,oBAAI,IAA2C;AACjE,SAAO;AAAA,IACL;AAAA,IACA,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,4BAA4B;AAAA,IAC5B,MAAM,iBAAG,GAAG;AAAA,IACZ,OAAO,iBAAG,GAAG;AAAA,IACb,kBAAkB,iBAAG,GAAG,CAAC,MAAc,OAAiC;AACtE,UAAI,CAAC,UAAU,IAAI,IAAI;AAAG,kBAAU,IAAI,MAAM,oBAAI,IAAI,CAAC;AACvD,gBAAU,IAAI,IAAI,EAAG,IAAI,EAAE;AAAA,IAC7B,CAAC;AAAA,IACD,qBAAqB,iBAAG,GAAG,CAAC,MAAc,OAAiC;AACzE,gBAAU,IAAI,IAAI,GAAG,OAAO,EAAE;AAAA,IAChC,CAAC;AAAA,IACD,WAAW;AAAA,IACX,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,SAAS;AAAA,EACX;AACF;AAEA,SAAS,SAAS,SAA4C;AAC5D,SAAO;AAAA,IACL,mBAAmB,iBAAG,GAAG,MAAM,OAAO;AAAA,EACxC;AACF;AAAA,IAEA,wBAAS,eAAe,MAAM;AAC5B,wBAAG,4BAA4B,YAAY;AACzC,UAAM,UAAU,cAAc;AAC9B,UAAM,UAAU,IAAI,gCAAY,SAAS,OAAO,CAAC;AACjD,UAAM,UAAU,iBAAG,GAAG;AAEtB,YAAQ,OAAO,OAAO;AACtB,YAAQ,cAAc,WAAW;AAEjC,YAAQ,YAAY,EAAE,MAAM,KAAK,UAAU;AAAA,MACzC,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,UAAU;AAAA,IACZ,CAAC,EAAE,CAAiB;AACpB,YAAQ,YAAY,EAAE,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,EAAE,CAAiB;AACjE,YAAQ,YAAY,EAAE,MAAM,KAAK,UAAU,EAAE,MAAM,iBAAiB,MAAM,YAAY,CAAC,EAAE,CAAiB;AAE1G,8BAAO,OAAO,EAAE,sBAAsB,CAAC;AACvC,UAAM,CAAC,MAAM,IAAI,IAAI,QAAQ,KAAK,MAAM,CAAC;AACzC,8BAAO,IAAI,EAAE,QAAQ,EAAE,MAAM,aAAa,MAAM,GAAG,MAAM,aAAa,CAAC;AACvE,8BAAO,MAAM,KAAK,KAAK,CAAC,EAAE,KAAK,OAAO;AAAA,EACxC,CAAC;AAED,wBAAG,kDAAkD,YAAY;AAC/D,UAAM,UAAU,cAAc;AAC9B,UAAM,UAAU,IAAI,gCAAY,SAAS,OAAO,CAAC;AACjD,UAAM,OAAO,IAAI,KAAK,CAAC,WAAW,GAAG,WAAW;AAEhD,cAAM,sBAAO,QAAQ,SAAS,MAAM,EAAE,aAAa,EAAE,CAAC,CAAC,EAAE,QAAQ,QAAQ,aAAa;AAAA,EACxF,CAAC;AACH,CAAC","names":[],"ignoreList":[],"sources":["../../../src/__test__/data-manager.test.ts"],"sourcesContent":["import { describe, expect, it, vi } from 'vitest';\r\nimport { DataManager } from '../data/data-manager';\r\n\r\nfunction createChannel(label = 'file-test'): RTCDataChannel {\r\n const listeners = new Map<string, Set<(...args: any[]) => void>>();\r\n return {\r\n label,\r\n readyState: 'open',\r\n bufferedAmount: 0,\r\n bufferedAmountLowThreshold: 0,\r\n send: vi.fn(),\r\n close: vi.fn(),\r\n addEventListener: vi.fn((type: string, fn: (...args: any[]) => void) => {\r\n if (!listeners.has(type)) listeners.set(type, new Set());\r\n listeners.get(type)!.add(fn);\r\n }),\r\n removeEventListener: vi.fn((type: string, fn: (...args: any[]) => void) => {\r\n listeners.get(type)?.delete(fn);\r\n }),\r\n onmessage: null,\r\n onopen: null,\r\n onclose: null,\r\n onerror: null,\r\n } as unknown as RTCDataChannel;\r\n}\r\n\r\nfunction createPc(channel: RTCDataChannel): RTCPeerConnection {\r\n return {\r\n createDataChannel: vi.fn(() => channel),\r\n } as unknown as RTCPeerConnection;\r\n}\r\n\r\ndescribe('DataManager', () => {\r\n it('receives a complete file', async () => {\r\n const channel = createChannel();\r\n const manager = new DataManager(createPc(channel));\r\n const handler = vi.fn();\r\n\r\n manager.onFile(handler);\r\n manager.createChannel('file-test');\r\n\r\n channel.onmessage?.({ data: JSON.stringify({\r\n type: 'file-meta',\r\n name: 'hello.txt',\r\n size: 5,\r\n chunks: 1,\r\n mimeType: 'text/plain',\r\n }) } as MessageEvent);\r\n channel.onmessage?.({ data: new Blob(['hello']) } as MessageEvent);\r\n channel.onmessage?.({ data: JSON.stringify({ type: 'file-complete', name: 'hello.txt' }) } as MessageEvent);\r\n\r\n expect(handler).toHaveBeenCalledTimes(1);\r\n const [meta, blob] = handler.mock.calls[0];\r\n expect(meta).toEqual({ name: 'hello.txt', size: 5, type: 'text/plain' });\r\n expect(await blob.text()).toBe('hello');\r\n });\r\n\r\n it('rejects sendFile when file exceeds maxFileSize', async () => {\r\n const channel = createChannel();\r\n const manager = new DataManager(createPc(channel));\r\n const file = new File(['too large'], 'large.txt');\r\n\r\n await expect(manager.sendFile(file, { maxFileSize: 1 })).rejects.toThrow(/maxFileSize/);\r\n });\r\n});\r\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJtYXBwaW5ncyI6IiIsIm5hbWVzIjpbXSwiaWdub3JlTGlzdCI6W10sInNvdXJjZXMiOltdLCJzb3VyY2VzQ29udGVudCI6W119"]}
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ var import_vitest = require("vitest");
3
+ var import_fsm = require("../fsm");
4
+ (0, import_vitest.describe)("ConnectionStateMachine", () => {
5
+ (0, import_vitest.it)("starts in idle", () => {
6
+ const fsm = new import_fsm.ConnectionStateMachine();
7
+ (0, import_vitest.expect)(fsm.state).toBe("idle");
8
+ });
9
+ (0, import_vitest.it)("allows idle → signaling → connecting → connected", () => {
10
+ const fsm = new import_fsm.ConnectionStateMachine();
11
+ fsm.transition("signaling");
12
+ fsm.transition("connecting");
13
+ fsm.transition("connected");
14
+ (0, import_vitest.expect)(fsm.state).toBe("connected");
15
+ });
16
+ (0, import_vitest.it)("throws on illegal transition idle → connected", () => {
17
+ const fsm = new import_fsm.ConnectionStateMachine();
18
+ (0, import_vitest.expect)(() => fsm.transition("connected")).toThrow(/Invalid state transition/);
19
+ });
20
+ (0, import_vitest.it)("force sets state without validation", () => {
21
+ const fsm = new import_fsm.ConnectionStateMachine();
22
+ fsm.force("connected");
23
+ (0, import_vitest.expect)(fsm.state).toBe("connected");
24
+ });
25
+ (0, import_vitest.it)("failed can return to idle", () => {
26
+ const fsm = new import_fsm.ConnectionStateMachine();
27
+ fsm.transition("signaling");
28
+ fsm.transition("failed");
29
+ fsm.transition("idle");
30
+ (0, import_vitest.expect)(fsm.state).toBe("idle");
31
+ });
32
+ });
33
+
34
+ //# sourceMappingURL=fsm.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"mappings":";AAAA,oBAAqC;AACrC,iBAAuC;AAAA,IAEvC,wBAAS,0BAA0B,MAAM;AACvC,wBAAG,kBAAkB,MAAM;AACzB,UAAM,MAAM,IAAI,kCAAuB;AACvC,8BAAO,IAAI,KAAK,EAAE,KAAK,MAAM;AAAA,EAC/B,CAAC;AAED,wBAAG,oDAAoD,MAAM;AAC3D,UAAM,MAAM,IAAI,kCAAuB;AACvC,QAAI,WAAW,WAAW;AAC1B,QAAI,WAAW,YAAY;AAC3B,QAAI,WAAW,WAAW;AAC1B,8BAAO,IAAI,KAAK,EAAE,KAAK,WAAW;AAAA,EACpC,CAAC;AAED,wBAAG,iDAAiD,MAAM;AACxD,UAAM,MAAM,IAAI,kCAAuB;AACvC,8BAAO,MAAM,IAAI,WAAW,WAAW,CAAC,EAAE,QAAQ,0BAA0B;AAAA,EAC9E,CAAC;AAED,wBAAG,uCAAuC,MAAM;AAC9C,UAAM,MAAM,IAAI,kCAAuB;AACvC,QAAI,MAAM,WAAW;AACrB,8BAAO,IAAI,KAAK,EAAE,KAAK,WAAW;AAAA,EACpC,CAAC;AAED,wBAAG,6BAA6B,MAAM;AACpC,UAAM,MAAM,IAAI,kCAAuB;AACvC,QAAI,WAAW,WAAW;AAC1B,QAAI,WAAW,QAAQ;AACvB,QAAI,WAAW,MAAM;AACrB,8BAAO,IAAI,KAAK,EAAE,KAAK,MAAM;AAAA,EAC/B,CAAC;AACH,CAAC","names":[],"ignoreList":[],"sources":["../../../src/__test__/fsm.test.ts"],"sourcesContent":["import { describe, it, expect } from 'vitest';\r\nimport { ConnectionStateMachine } from '../fsm';\r\n\r\ndescribe('ConnectionStateMachine', () => {\r\n it('starts in idle', () => {\r\n const fsm = new ConnectionStateMachine();\r\n expect(fsm.state).toBe('idle');\r\n });\r\n\r\n it('allows idle → signaling → connecting → connected', () => {\r\n const fsm = new ConnectionStateMachine();\r\n fsm.transition('signaling');\r\n fsm.transition('connecting');\r\n fsm.transition('connected');\r\n expect(fsm.state).toBe('connected');\r\n });\r\n\r\n it('throws on illegal transition idle → connected', () => {\r\n const fsm = new ConnectionStateMachine();\r\n expect(() => fsm.transition('connected')).toThrow(/Invalid state transition/);\r\n });\r\n\r\n it('force sets state without validation', () => {\r\n const fsm = new ConnectionStateMachine();\r\n fsm.force('connected');\r\n expect(fsm.state).toBe('connected');\r\n });\r\n\r\n it('failed can return to idle', () => {\r\n const fsm = new ConnectionStateMachine();\r\n fsm.transition('signaling');\r\n fsm.transition('failed');\r\n fsm.transition('idle');\r\n expect(fsm.state).toBe('idle');\r\n });\r\n});\r\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJtYXBwaW5ncyI6IiIsIm5hbWVzIjpbXSwiaWdub3JlTGlzdCI6W10sInNvdXJjZXMiOltdLCJzb3VyY2VzQ29udGVudCI6W119"]}
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ var import_vitest = require("vitest");
3
+ var import_media_manager = require("../media/media-manager");
4
+ (0, import_vitest.describe)("MediaManager", () => {
5
+ (0, import_vitest.afterEach)(() => {
6
+ import_vitest.vi.unstubAllGlobals();
7
+ });
8
+ (0, import_vitest.it)("maps display options to getDisplayMedia constraints", async () => {
9
+ const stream = {};
10
+ const getDisplayMedia = import_vitest.vi.fn().mockResolvedValue(stream);
11
+ import_vitest.vi.stubGlobal("navigator", {
12
+ mediaDevices: {
13
+ getDisplayMedia,
14
+ getUserMedia: import_vitest.vi.fn()
15
+ }
16
+ });
17
+ await import_media_manager.MediaManager.getDisplayMedia({
18
+ width: 1920,
19
+ height: 1080,
20
+ frameRate: 30,
21
+ audio: true
22
+ });
23
+ (0, import_vitest.expect)(getDisplayMedia).toHaveBeenCalledWith({
24
+ video: { width: 1920, height: 1080, frameRate: 30 },
25
+ audio: true
26
+ });
27
+ });
28
+ (0, import_vitest.it)("instance getDisplayMedia delegates to static implementation", async () => {
29
+ const stream = {};
30
+ const getDisplayMedia = import_vitest.vi.fn().mockResolvedValue(stream);
31
+ import_vitest.vi.stubGlobal("navigator", {
32
+ mediaDevices: {
33
+ getDisplayMedia,
34
+ getUserMedia: import_vitest.vi.fn()
35
+ }
36
+ });
37
+ const media = new import_media_manager.MediaManager();
38
+ await (0, import_vitest.expect)(media.getDisplayMedia({ audio: false })).resolves.toBe(stream);
39
+ });
40
+ });
41
+
42
+ //# sourceMappingURL=media-manager.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"mappings":";AAAA,oBAAoD;AACpD,2BAA6B;AAAA,IAE7B,wBAAS,gBAAgB,MAAM;AAC7B,+BAAU,MAAM;AACd,qBAAG,iBAAiB;AAAA,EACtB,CAAC;AAED,wBAAG,uDAAuD,YAAY;AACpE,UAAM,SAAS,CAAC;AAChB,UAAM,kBAAkB,iBAAG,GAAG,EAAE,kBAAkB,MAAM;AACxD,qBAAG,WAAW,aAAa;AAAA,MACzB,cAAc;AAAA,QACZ;AAAA,QACA,cAAc,iBAAG,GAAG;AAAA,MACtB;AAAA,IACF,CAAC;AAED,UAAM,kCAAa,gBAAgB;AAAA,MACjC,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,OAAO;AAAA,IACT,CAAC;AAED,8BAAO,eAAe,EAAE,qBAAqB;AAAA,MAC3C,OAAO,EAAE,OAAO,MAAM,QAAQ,MAAM,WAAW,GAAG;AAAA,MAClD,OAAO;AAAA,IACT,CAAC;AAAA,EACH,CAAC;AAED,wBAAG,+DAA+D,YAAY;AAC5E,UAAM,SAAS,CAAC;AAChB,UAAM,kBAAkB,iBAAG,GAAG,EAAE,kBAAkB,MAAM;AACxD,qBAAG,WAAW,aAAa;AAAA,MACzB,cAAc;AAAA,QACZ;AAAA,QACA,cAAc,iBAAG,GAAG;AAAA,MACtB;AAAA,IACF,CAAC;AAED,UAAM,QAAQ,IAAI,kCAAa;AAC/B,cAAM,sBAAO,MAAM,gBAAgB,EAAE,OAAO,MAAM,CAAC,CAAC,EAAE,SAAS,KAAK,MAAM;AAAA,EAC5E,CAAC;AACH,CAAC","names":[],"ignoreList":[],"sources":["../../../src/__test__/media-manager.test.ts"],"sourcesContent":["import { afterEach, describe, expect, it, vi } from 'vitest';\r\nimport { MediaManager } from '../media/media-manager';\r\n\r\ndescribe('MediaManager', () => {\r\n afterEach(() => {\r\n vi.unstubAllGlobals();\r\n });\r\n\r\n it('maps display options to getDisplayMedia constraints', async () => {\r\n const stream = {} as MediaStream;\r\n const getDisplayMedia = vi.fn().mockResolvedValue(stream);\r\n vi.stubGlobal('navigator', {\r\n mediaDevices: {\r\n getDisplayMedia,\r\n getUserMedia: vi.fn(),\r\n },\r\n });\r\n\r\n await MediaManager.getDisplayMedia({\r\n width: 1920,\r\n height: 1080,\r\n frameRate: 30,\r\n audio: true,\r\n });\r\n\r\n expect(getDisplayMedia).toHaveBeenCalledWith({\r\n video: { width: 1920, height: 1080, frameRate: 30 },\r\n audio: true,\r\n });\r\n });\r\n\r\n it('instance getDisplayMedia delegates to static implementation', async () => {\r\n const stream = {} as MediaStream;\r\n const getDisplayMedia = vi.fn().mockResolvedValue(stream);\r\n vi.stubGlobal('navigator', {\r\n mediaDevices: {\r\n getDisplayMedia,\r\n getUserMedia: vi.fn(),\r\n },\r\n });\r\n\r\n const media = new MediaManager();\r\n await expect(media.getDisplayMedia({ audio: false })).resolves.toBe(stream);\r\n });\r\n});\r\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJtYXBwaW5ncyI6IiIsIm5hbWVzIjpbXSwiaWdub3JlTGlzdCI6W10sInNvdXJjZXMiOltdLCJzb3VyY2VzQ29udGVudCI6W119"]}
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ var import_vitest = require("vitest");
3
+ var import_fsm = require("../fsm");
4
+ var import_signal_manager = require("../signal/signal-manager");
5
+ function createMockPc(overrides = {}) {
6
+ const listeners = /* @__PURE__ */ new Map();
7
+ return {
8
+ signalingState: "stable",
9
+ localDescription: null,
10
+ remoteDescription: null,
11
+ iceConnectionState: "new",
12
+ addEventListener: import_vitest.vi.fn((type, fn) => {
13
+ if (!listeners.has(type))
14
+ listeners.set(type, /* @__PURE__ */ new Set());
15
+ listeners.get(type).add(fn);
16
+ }),
17
+ removeEventListener: import_vitest.vi.fn((type, fn) => {
18
+ listeners.get(type)?.delete(fn);
19
+ }),
20
+ createOffer: import_vitest.vi.fn().mockResolvedValue({ type: "offer", sdp: "mock-offer" }),
21
+ createAnswer: import_vitest.vi.fn().mockResolvedValue({ type: "answer", sdp: "mock-answer" }),
22
+ setLocalDescription: import_vitest.vi.fn().mockImplementation(async (desc) => {
23
+ mock.localDescription = desc;
24
+ }),
25
+ setRemoteDescription: import_vitest.vi.fn().mockImplementation(async (desc) => {
26
+ mock.remoteDescription = desc;
27
+ }),
28
+ addIceCandidate: import_vitest.vi.fn().mockResolvedValue(void 0),
29
+ ...overrides
30
+ };
31
+ }
32
+ let mock;
33
+ function createSignalChannel() {
34
+ const handlers = [];
35
+ return {
36
+ handlers,
37
+ send: import_vitest.vi.fn().mockResolvedValue(void 0),
38
+ onMessage(handler) {
39
+ handlers.push(handler);
40
+ return () => {
41
+ const idx = handlers.indexOf(handler);
42
+ if (idx >= 0)
43
+ handlers.splice(idx, 1);
44
+ };
45
+ }
46
+ };
47
+ }
48
+ (0, import_vitest.describe)("SignalManager", () => {
49
+ let fsm;
50
+ let channel;
51
+ (0, import_vitest.beforeEach)(() => {
52
+ mock = createMockPc();
53
+ fsm = new import_fsm.ConnectionStateMachine();
54
+ channel = createSignalChannel();
55
+ });
56
+ (0, import_vitest.it)("createOffer sends offer and enters signaling", async () => {
57
+ const sm = new import_signal_manager.SignalManager(channel, mock, fsm, true);
58
+ await sm.createOffer();
59
+ (0, import_vitest.expect)(channel.send).toHaveBeenCalledWith(
60
+ import_vitest.expect.objectContaining({ type: "offer", sdp: "mock-offer" })
61
+ );
62
+ (0, import_vitest.expect)(fsm.state).toBe("signaling");
63
+ });
64
+ (0, import_vitest.it)("createOffer does not repeat signaling transition", async () => {
65
+ fsm.transition("signaling");
66
+ const sm = new import_signal_manager.SignalManager(channel, mock, fsm, true);
67
+ await (0, import_vitest.expect)(sm.createOffer()).resolves.toBeUndefined();
68
+ (0, import_vitest.expect)(fsm.state).toBe("signaling");
69
+ });
70
+ (0, import_vitest.it)("handleOffer sends answer", async () => {
71
+ const sm = new import_signal_manager.SignalManager(channel, mock, fsm, true);
72
+ await sm.handleOffer("remote-offer-sdp");
73
+ (0, import_vitest.expect)(mock.setRemoteDescription).toHaveBeenCalled();
74
+ (0, import_vitest.expect)(mock.createAnswer).toHaveBeenCalled();
75
+ (0, import_vitest.expect)(channel.send).toHaveBeenCalledWith(
76
+ import_vitest.expect.objectContaining({ type: "answer" })
77
+ );
78
+ });
79
+ (0, import_vitest.it)("setPeerConnection rebinds without throwing", () => {
80
+ const sm = new import_signal_manager.SignalManager(channel, mock, fsm, true);
81
+ const newPc = createMockPc();
82
+ (0, import_vitest.expect)(() => sm.setPeerConnection(newPc)).not.toThrow();
83
+ });
84
+ (0, import_vitest.it)("impolite peer ignores colliding offer", async () => {
85
+ mock = createMockPc({ signalingState: "have-local-offer" });
86
+ const sm = new import_signal_manager.SignalManager(channel, mock, fsm, false);
87
+ sm.makingOffer = true;
88
+ await sm.handleOffer("colliding-offer");
89
+ (0, import_vitest.expect)(mock.setRemoteDescription).not.toHaveBeenCalled();
90
+ });
91
+ (0, import_vitest.it)("polite peer rolls back colliding offer", async () => {
92
+ mock = createMockPc({ signalingState: "have-local-offer" });
93
+ const sm = new import_signal_manager.SignalManager(channel, mock, fsm, true);
94
+ sm.makingOffer = true;
95
+ await sm.handleOffer("colliding-offer");
96
+ (0, import_vitest.expect)(mock.setLocalDescription).toHaveBeenCalledWith({ type: "rollback" });
97
+ (0, import_vitest.expect)(mock.setRemoteDescription).toHaveBeenCalledWith({ type: "offer", sdp: "colliding-offer" });
98
+ });
99
+ });
100
+
101
+ //# sourceMappingURL=signal-manager.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"mappings":"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","names":[],"ignoreList":[],"sources":["../../../src/__test__/signal-manager.test.ts"],"sourcesContent":["import { describe, it, expect, vi, beforeEach } from 'vitest';\r\nimport { ConnectionStateMachine } from '../fsm';\r\nimport { SignalManager } from '../signal/signal-manager';\r\nimport type { SignalChannel } from '../signal/types';\r\n\r\nfunction createMockPc(overrides: Partial<RTCPeerConnection> = {}): RTCPeerConnection {\r\n const listeners = new Map<string, Set<EventListener>>();\r\n return {\r\n signalingState: 'stable',\r\n localDescription: null,\r\n remoteDescription: null,\r\n iceConnectionState: 'new',\r\n addEventListener: vi.fn((type: string, fn: EventListener) => {\r\n if (!listeners.has(type)) listeners.set(type, new Set());\r\n listeners.get(type)!.add(fn);\r\n }),\r\n removeEventListener: vi.fn((type: string, fn: EventListener) => {\r\n listeners.get(type)?.delete(fn);\r\n }),\r\n createOffer: vi.fn().mockResolvedValue({ type: 'offer', sdp: 'mock-offer' }),\r\n createAnswer: vi.fn().mockResolvedValue({ type: 'answer', sdp: 'mock-answer' }),\r\n setLocalDescription: vi.fn().mockImplementation(async (desc) => {\r\n (mock as any).localDescription = desc;\r\n }),\r\n setRemoteDescription: vi.fn().mockImplementation(async (desc) => {\r\n (mock as any).remoteDescription = desc;\r\n }),\r\n addIceCandidate: vi.fn().mockResolvedValue(undefined),\r\n ...overrides,\r\n } as unknown as RTCPeerConnection;\r\n}\r\n\r\nlet mock: RTCPeerConnection;\r\n\r\nfunction createSignalChannel(): SignalChannel & { handlers: Array<(msg: unknown) => void> } {\r\n const handlers: Array<(msg: unknown) => void> = [];\r\n return {\r\n handlers,\r\n send: vi.fn().mockResolvedValue(undefined),\r\n onMessage(handler: (msg: unknown) => void) {\r\n handlers.push(handler);\r\n return () => {\r\n const idx = handlers.indexOf(handler);\r\n if (idx >= 0) handlers.splice(idx, 1);\r\n };\r\n },\r\n };\r\n}\r\n\r\ndescribe('SignalManager', () => {\r\n let fsm: ConnectionStateMachine;\r\n let channel: ReturnType<typeof createSignalChannel>;\r\n\r\n beforeEach(() => {\r\n mock = createMockPc();\r\n fsm = new ConnectionStateMachine();\r\n channel = createSignalChannel();\r\n });\r\n\r\n it('createOffer sends offer and enters signaling', async () => {\r\n const sm = new SignalManager(channel, mock, fsm, true);\r\n await sm.createOffer();\r\n\r\n expect(channel.send).toHaveBeenCalledWith(\r\n expect.objectContaining({ type: 'offer', sdp: 'mock-offer' }),\r\n );\r\n expect(fsm.state).toBe('signaling');\r\n });\r\n\r\n it('createOffer does not repeat signaling transition', async () => {\r\n fsm.transition('signaling');\r\n const sm = new SignalManager(channel, mock, fsm, true);\r\n\r\n await expect(sm.createOffer()).resolves.toBeUndefined();\r\n expect(fsm.state).toBe('signaling');\r\n });\r\n\r\n it('handleOffer sends answer', async () => {\r\n const sm = new SignalManager(channel, mock, fsm, true);\r\n await sm.handleOffer('remote-offer-sdp');\r\n\r\n expect(mock.setRemoteDescription).toHaveBeenCalled();\r\n expect(mock.createAnswer).toHaveBeenCalled();\r\n expect(channel.send).toHaveBeenCalledWith(\r\n expect.objectContaining({ type: 'answer' }),\r\n );\r\n });\r\n\r\n it('setPeerConnection rebinds without throwing', () => {\r\n const sm = new SignalManager(channel, mock, fsm, true);\r\n const newPc = createMockPc();\r\n expect(() => sm.setPeerConnection(newPc)).not.toThrow();\r\n });\r\n\r\n it('impolite peer ignores colliding offer', async () => {\r\n mock = createMockPc({ signalingState: 'have-local-offer' as RTCSignalingState });\r\n const sm = new SignalManager(channel, mock, fsm, false);\r\n (sm as any).makingOffer = true;\r\n\r\n await sm.handleOffer('colliding-offer');\r\n\r\n expect(mock.setRemoteDescription).not.toHaveBeenCalled();\r\n });\r\n\r\n it('polite peer rolls back colliding offer', async () => {\r\n mock = createMockPc({ signalingState: 'have-local-offer' as RTCSignalingState });\r\n const sm = new SignalManager(channel, mock, fsm, true);\r\n (sm as any).makingOffer = true;\r\n\r\n await sm.handleOffer('colliding-offer');\r\n\r\n expect(mock.setLocalDescription).toHaveBeenCalledWith({ type: 'rollback' });\r\n expect(mock.setRemoteDescription).toHaveBeenCalledWith({ type: 'offer', sdp: 'colliding-offer' });\r\n });\r\n});\r\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJtYXBwaW5ncyI6IiIsIm5hbWVzIjpbXSwiaWdub3JlTGlzdCI6W10sInNvdXJjZXMiOltdLCJzb3VyY2VzQ29udGVudCI6W119"]}
@@ -0,0 +1,153 @@
1
+ "use strict";
2
+ var import_vitest = require("vitest");
3
+ var import_wetrtc = require("../wetrtc");
4
+ const pcs = [];
5
+ class MockPeerConnection {
6
+ constructor() {
7
+ this.iceConnectionState = "new";
8
+ this.connectionState = "new";
9
+ this.signalingState = "stable";
10
+ this.localDescription = null;
11
+ this.remoteDescription = null;
12
+ this.senders = [];
13
+ this.transceivers = [];
14
+ this.closed = false;
15
+ this.addEventListener = import_vitest.vi.fn();
16
+ this.removeEventListener = import_vitest.vi.fn();
17
+ this.createDataChannel = import_vitest.vi.fn(() => ({ close: import_vitest.vi.fn(), readyState: "open" }));
18
+ this.createOffer = import_vitest.vi.fn().mockResolvedValue({ type: "offer", sdp: "offer" });
19
+ this.createAnswer = import_vitest.vi.fn().mockResolvedValue({ type: "answer", sdp: "answer" });
20
+ this.setLocalDescription = import_vitest.vi.fn().mockImplementation(async (desc) => {
21
+ this.localDescription = desc;
22
+ });
23
+ this.setRemoteDescription = import_vitest.vi.fn().mockImplementation(async (desc) => {
24
+ this.remoteDescription = desc;
25
+ });
26
+ this.addIceCandidate = import_vitest.vi.fn().mockResolvedValue(void 0);
27
+ this.getStats = import_vitest.vi.fn().mockResolvedValue({ forEach: import_vitest.vi.fn() });
28
+ this.close = import_vitest.vi.fn(() => {
29
+ this.closed = true;
30
+ });
31
+ this.getSenders = import_vitest.vi.fn(() => this.senders);
32
+ this.getTransceivers = import_vitest.vi.fn(() => this.transceivers);
33
+ this.addTransceiver = import_vitest.vi.fn((kind, init) => {
34
+ const transceiver = {
35
+ direction: init?.direction ?? "sendrecv",
36
+ sender: {},
37
+ receiver: {},
38
+ mid: null,
39
+ currentDirection: null,
40
+ stop: import_vitest.vi.fn()
41
+ };
42
+ this.transceivers.push(transceiver);
43
+ return transceiver;
44
+ });
45
+ this.addTrack = import_vitest.vi.fn((track) => {
46
+ const sender = {
47
+ track,
48
+ replaceTrack: import_vitest.vi.fn().mockResolvedValue(void 0)
49
+ };
50
+ const transceiver = {
51
+ direction: "sendrecv",
52
+ sender,
53
+ receiver: {},
54
+ mid: null,
55
+ currentDirection: null,
56
+ stop: import_vitest.vi.fn()
57
+ };
58
+ this.senders.push(sender);
59
+ this.transceivers.push(transceiver);
60
+ return sender;
61
+ });
62
+ this.removeTrack = import_vitest.vi.fn();
63
+ pcs.push(this);
64
+ }
65
+ }
66
+ function signal() {
67
+ return {
68
+ send: import_vitest.vi.fn().mockResolvedValue(void 0),
69
+ onMessage: import_vitest.vi.fn(() => () => {
70
+ })
71
+ };
72
+ }
73
+ function streamWithTrack(kind = "video") {
74
+ const track = {
75
+ id: `${kind}-1`,
76
+ kind,
77
+ stop: import_vitest.vi.fn(),
78
+ addEventListener: import_vitest.vi.fn()
79
+ };
80
+ const tracks = [track];
81
+ const stream = {
82
+ getTracks: () => tracks,
83
+ getVideoTracks: () => kind === "video" ? tracks : [],
84
+ addTrack: import_vitest.vi.fn((next) => {
85
+ if (!tracks.includes(next))
86
+ tracks.push(next);
87
+ }),
88
+ removeTrack: import_vitest.vi.fn((next) => {
89
+ const idx = tracks.indexOf(next);
90
+ if (idx >= 0)
91
+ tracks.splice(idx, 1);
92
+ })
93
+ };
94
+ return { stream, track };
95
+ }
96
+ (0, import_vitest.describe)("WetRTC lifecycle", () => {
97
+ (0, import_vitest.beforeEach)(() => {
98
+ pcs.length = 0;
99
+ import_vitest.vi.stubGlobal("RTCPeerConnection", MockPeerConnection);
100
+ });
101
+ (0, import_vitest.afterEach)(() => {
102
+ import_vitest.vi.unstubAllGlobals();
103
+ });
104
+ (0, import_vitest.it)("disconnect rebinds peer connection and restores local tracks", async () => {
105
+ const channel = signal();
106
+ const rtc = new import_wetrtc.WetRTC({ signal: channel, reconnect: false });
107
+ const { stream, track } = streamWithTrack();
108
+ const firstPc = rtc.peerConnection;
109
+ rtc.addTrack(track, stream);
110
+ await rtc.disconnect();
111
+ (0, import_vitest.expect)(channel.send).toHaveBeenCalledWith({ type: "bye" });
112
+ const nextPc = rtc.peerConnection;
113
+ (0, import_vitest.expect)(nextPc).not.toBe(firstPc);
114
+ (0, import_vitest.expect)(firstPc.close).toHaveBeenCalled();
115
+ (0, import_vitest.expect)(nextPc.addTrack).toHaveBeenCalledWith(track, stream);
116
+ (0, import_vitest.expect)(rtc.state).toBe("idle");
117
+ });
118
+ (0, import_vitest.it)("recvonly connect creates recvonly transceivers", async () => {
119
+ const rtc = new import_wetrtc.WetRTC({ signal: signal(), direction: "recvonly", reconnect: false });
120
+ await rtc.connect();
121
+ const pc = rtc.peerConnection;
122
+ (0, import_vitest.expect)(pc.addTransceiver).toHaveBeenCalledWith("video", { direction: "recvonly" });
123
+ (0, import_vitest.expect)(pc.addTransceiver).toHaveBeenCalledWith("audio", { direction: "recvonly" });
124
+ });
125
+ (0, import_vitest.it)("recvonly initiator sends offer", async () => {
126
+ const ch = signal();
127
+ const rtc = new import_wetrtc.WetRTC({
128
+ signal: ch,
129
+ direction: "recvonly",
130
+ initiator: true,
131
+ reconnect: false
132
+ });
133
+ await rtc.connect();
134
+ (0, import_vitest.expect)(ch.send).toHaveBeenCalledWith(
135
+ import_vitest.expect.objectContaining({ type: "offer" })
136
+ );
137
+ });
138
+ (0, import_vitest.it)("sendonly passive does not send offer", async () => {
139
+ const ch = signal();
140
+ const rtc = new import_wetrtc.WetRTC({
141
+ signal: ch,
142
+ direction: "sendonly",
143
+ initiator: false,
144
+ reconnect: false
145
+ });
146
+ await rtc.connect();
147
+ (0, import_vitest.expect)(ch.send).not.toHaveBeenCalledWith(
148
+ import_vitest.expect.objectContaining({ type: "offer" })
149
+ );
150
+ });
151
+ });
152
+
153
+ //# sourceMappingURL=wetrtc-lifecycle.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"mappings":";AAAA,oBAAgE;AAChE,oBAAuB;AAGvB,MAAM,MAA4B,CAAC;AAEnC,MAAM,mBAAmB;AAAA,EAUvB,cAAc;AATd,8BAA4C;AAC5C,2BAA0C;AAC1C,0BAAoC;AACpC,4BAAiD;AACjD,6BAAkD;AAClD,mBAA0B,CAAC;AAC3B,wBAAoC,CAAC;AACrC,kBAAS;AAMT,4BAAmB,iBAAG,GAAG;AACzB,+BAAsB,iBAAG,GAAG;AAC5B,6BAAoB,iBAAG,GAAG,OAAO,EAAE,OAAO,iBAAG,GAAG,GAAG,YAAY,OAAO,EAAE;AACxE,uBAAc,iBAAG,GAAG,EAAE,kBAAkB,EAAE,MAAM,SAAS,KAAK,QAAQ,CAAC;AACvE,wBAAe,iBAAG,GAAG,EAAE,kBAAkB,EAAE,MAAM,UAAU,KAAK,SAAS,CAAC;AAC1E,+BAAsB,iBAAG,GAAG,EAAE,mBAAmB,OAAO,SAAS;AAC/D,WAAK,mBAAmB;AAAA,IAC1B,CAAC;AACD,gCAAuB,iBAAG,GAAG,EAAE,mBAAmB,OAAO,SAAS;AAChE,WAAK,oBAAoB;AAAA,IAC3B,CAAC;AACD,2BAAkB,iBAAG,GAAG,EAAE,kBAAkB,MAAS;AACrD,oBAAW,iBAAG,GAAG,EAAE,kBAAkB,EAAE,SAAS,iBAAG,GAAG,EAAE,CAAC;AACzD,iBAAQ,iBAAG,GAAG,MAAM;AAClB,WAAK,SAAS;AAAA,IAChB,CAAC;AACD,sBAAa,iBAAG,GAAG,MAAM,KAAK,OAAO;AACrC,2BAAkB,iBAAG,GAAG,MAAM,KAAK,YAAY;AAC/C,0BAAiB,iBAAG,GAAG,CAAC,MAAc,SAAiC;AACrE,YAAM,cAAc;AAAA,QAClB,WAAW,MAAM,aAAa;AAAA,QAC9B,QAAQ,CAAC;AAAA,QACT,UAAU,CAAC;AAAA,QACX,KAAK;AAAA,QACL,kBAAkB;AAAA,QAClB,MAAM,iBAAG,GAAG;AAAA,MACd;AACA,WAAK,aAAa,KAAK,WAAW;AAClC,aAAO;AAAA,IACT,CAAC;AACD,oBAAW,iBAAG,GAAG,CAAC,UAA4B;AAC5C,YAAM,SAAS;AAAA,QACb;AAAA,QACA,cAAc,iBAAG,GAAG,EAAE,kBAAkB,MAAS;AAAA,MACnD;AACA,YAAM,cAAc;AAAA,QAClB,WAAW;AAAA,QACX;AAAA,QACA,UAAU,CAAC;AAAA,QACX,KAAK;AAAA,QACL,kBAAkB;AAAA,QAClB,MAAM,iBAAG,GAAG;AAAA,MACd;AACA,WAAK,QAAQ,KAAK,MAAM;AACxB,WAAK,aAAa,KAAK,WAAW;AAClC,aAAO;AAAA,IACT,CAAC;AACD,uBAAc,iBAAG,GAAG;AAlDlB,QAAI,KAAK,IAAI;AAAA,EACf;AAkDF;AAEA,SAAS,SAAwB;AAC/B,SAAO;AAAA,IACL,MAAM,iBAAG,GAAG,EAAE,kBAAkB,MAAS;AAAA,IACzC,WAAW,iBAAG,GAAG,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EACjC;AACF;AAEA,SAAS,gBAAgB,OAA0B,SAA2D;AAC5G,QAAM,QAAQ;AAAA,IACZ,IAAI,GAAG,IAAI;AAAA,IACX;AAAA,IACA,MAAM,iBAAG,GAAG;AAAA,IACZ,kBAAkB,iBAAG,GAAG;AAAA,EAC1B;AACA,QAAM,SAA6B,CAAC,KAAK;AACzC,QAAM,SAAS;AAAA,IACb,WAAW,MAAM;AAAA,IACjB,gBAAgB,MAAM,SAAS,UAAU,SAAS,CAAC;AAAA,IACnD,UAAU,iBAAG,GAAG,CAAC,SAA2B;AAC1C,UAAI,CAAC,OAAO,SAAS,IAAI;AAAG,eAAO,KAAK,IAAI;AAAA,IAC9C,CAAC;AAAA,IACD,aAAa,iBAAG,GAAG,CAAC,SAA2B;AAC7C,YAAM,MAAM,OAAO,QAAQ,IAAI;AAC/B,UAAI,OAAO;AAAG,eAAO,OAAO,KAAK,CAAC;AAAA,IACpC,CAAC;AAAA,EACH;AACA,SAAO,EAAE,QAAQ,MAAM;AACzB;AAAA,IAEA,wBAAS,oBAAoB,MAAM;AACjC,gCAAW,MAAM;AACf,QAAI,SAAS;AACb,qBAAG,WAAW,qBAAqB,kBAAkB;AAAA,EACvD,CAAC;AAED,+BAAU,MAAM;AACd,qBAAG,iBAAiB;AAAA,EACtB,CAAC;AAED,wBAAG,gEAAgE,YAAY;AAC7E,UAAM,UAAU,OAAO;AACvB,UAAM,MAAM,IAAI,qBAAO,EAAE,QAAQ,SAAS,WAAW,MAAM,CAAC;AAC5D,UAAM,EAAE,QAAQ,MAAM,IAAI,gBAAgB;AAC1C,UAAM,UAAU,IAAI;AAEpB,QAAI,SAAS,OAAO,MAAM;AAC1B,UAAM,IAAI,WAAW;AAErB,8BAAO,QAAQ,IAAI,EAAE,qBAAqB,EAAE,MAAM,MAAM,CAAC;AACzD,UAAM,SAAS,IAAI;AACnB,8BAAO,MAAM,EAAE,IAAI,KAAK,OAAO;AAC/B,8BAAO,QAAQ,KAAK,EAAE,iBAAiB;AACvC,8BAAO,OAAO,QAAQ,EAAE,qBAAqB,OAAO,MAAM;AAC1D,8BAAO,IAAI,KAAK,EAAE,KAAK,MAAM;AAAA,EAC/B,CAAC;AAED,wBAAG,kDAAkD,YAAY;AAC/D,UAAM,MAAM,IAAI,qBAAO,EAAE,QAAQ,OAAO,GAAG,WAAW,YAAY,WAAW,MAAM,CAAC;AAEpF,UAAM,IAAI,QAAQ;AAElB,UAAM,KAAK,IAAI;AACf,8BAAO,GAAG,cAAc,EAAE,qBAAqB,SAAS,EAAE,WAAW,WAAW,CAAC;AACjF,8BAAO,GAAG,cAAc,EAAE,qBAAqB,SAAS,EAAE,WAAW,WAAW,CAAC;AAAA,EACnF,CAAC;AAED,wBAAG,kCAAkC,YAAY;AAC/C,UAAM,KAAK,OAAO;AAClB,UAAM,MAAM,IAAI,qBAAO;AAAA,MACrB,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,WAAW;AAAA,MACX,WAAW;AAAA,IACb,CAAC;AAED,UAAM,IAAI,QAAQ;AAElB,8BAAO,GAAG,IAAI,EAAE;AAAA,MACd,qBAAO,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAAA,IAC3C;AAAA,EACF,CAAC;AAED,wBAAG,wCAAwC,YAAY;AACrD,UAAM,KAAK,OAAO;AAClB,UAAM,MAAM,IAAI,qBAAO;AAAA,MACrB,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,WAAW;AAAA,MACX,WAAW;AAAA,IACb,CAAC;AAED,UAAM,IAAI,QAAQ;AAElB,8BAAO,GAAG,IAAI,EAAE,IAAI;AAAA,MAClB,qBAAO,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAAA,IAC3C;AAAA,EACF,CAAC;AACH,CAAC","names":[],"ignoreList":[],"sources":["../../../src/__test__/wetrtc-lifecycle.test.ts"],"sourcesContent":["import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';\r\nimport { WetRTC } from '../wetrtc';\r\nimport type { SignalChannel } from '../signal/types';\r\n\r\nconst pcs: MockPeerConnection[] = [];\r\n\r\nclass MockPeerConnection {\r\n iceConnectionState: RTCIceConnectionState = 'new';\r\n connectionState: RTCPeerConnectionState = 'new';\r\n signalingState: RTCSignalingState = 'stable';\r\n localDescription: RTCSessionDescription | null = null;\r\n remoteDescription: RTCSessionDescription | null = null;\r\n senders: RTCRtpSender[] = [];\r\n transceivers: RTCRtpTransceiver[] = [];\r\n closed = false;\r\n\r\n constructor() {\r\n pcs.push(this);\r\n }\r\n\r\n addEventListener = vi.fn();\r\n removeEventListener = vi.fn();\r\n createDataChannel = vi.fn(() => ({ close: vi.fn(), readyState: 'open' }));\r\n createOffer = vi.fn().mockResolvedValue({ type: 'offer', sdp: 'offer' });\r\n createAnswer = vi.fn().mockResolvedValue({ type: 'answer', sdp: 'answer' });\r\n setLocalDescription = vi.fn().mockImplementation(async (desc) => {\r\n this.localDescription = desc as RTCSessionDescription;\r\n });\r\n setRemoteDescription = vi.fn().mockImplementation(async (desc) => {\r\n this.remoteDescription = desc as RTCSessionDescription;\r\n });\r\n addIceCandidate = vi.fn().mockResolvedValue(undefined);\r\n getStats = vi.fn().mockResolvedValue({ forEach: vi.fn() });\r\n close = vi.fn(() => {\r\n this.closed = true;\r\n });\r\n getSenders = vi.fn(() => this.senders);\r\n getTransceivers = vi.fn(() => this.transceivers);\r\n addTransceiver = vi.fn((kind: string, init?: RTCRtpTransceiverInit) => {\r\n const transceiver = {\r\n direction: init?.direction ?? 'sendrecv',\r\n sender: {},\r\n receiver: {},\r\n mid: null,\r\n currentDirection: null,\r\n stop: vi.fn(),\r\n } as unknown as RTCRtpTransceiver;\r\n this.transceivers.push(transceiver);\r\n return transceiver;\r\n });\r\n addTrack = vi.fn((track: MediaStreamTrack) => {\r\n const sender = {\r\n track,\r\n replaceTrack: vi.fn().mockResolvedValue(undefined),\r\n } as unknown as RTCRtpSender;\r\n const transceiver = {\r\n direction: 'sendrecv',\r\n sender,\r\n receiver: {},\r\n mid: null,\r\n currentDirection: null,\r\n stop: vi.fn(),\r\n } as unknown as RTCRtpTransceiver;\r\n this.senders.push(sender);\r\n this.transceivers.push(transceiver);\r\n return sender;\r\n });\r\n removeTrack = vi.fn();\r\n}\r\n\r\nfunction signal(): SignalChannel {\r\n return {\r\n send: vi.fn().mockResolvedValue(undefined),\r\n onMessage: vi.fn(() => () => {}),\r\n };\r\n}\r\n\r\nfunction streamWithTrack(kind: 'audio' | 'video' = 'video'): { stream: MediaStream; track: MediaStreamTrack } {\r\n const track = {\r\n id: `${kind}-1`,\r\n kind,\r\n stop: vi.fn(),\r\n addEventListener: vi.fn(),\r\n } as unknown as MediaStreamTrack;\r\n const tracks: MediaStreamTrack[] = [track];\r\n const stream = {\r\n getTracks: () => tracks,\r\n getVideoTracks: () => kind === 'video' ? tracks : [],\r\n addTrack: vi.fn((next: MediaStreamTrack) => {\r\n if (!tracks.includes(next)) tracks.push(next);\r\n }),\r\n removeTrack: vi.fn((next: MediaStreamTrack) => {\r\n const idx = tracks.indexOf(next);\r\n if (idx >= 0) tracks.splice(idx, 1);\r\n }),\r\n } as unknown as MediaStream;\r\n return { stream, track };\r\n}\r\n\r\ndescribe('WetRTC lifecycle', () => {\r\n beforeEach(() => {\r\n pcs.length = 0;\r\n vi.stubGlobal('RTCPeerConnection', MockPeerConnection);\r\n });\r\n\r\n afterEach(() => {\r\n vi.unstubAllGlobals();\r\n });\r\n\r\n it('disconnect rebinds peer connection and restores local tracks', async () => {\r\n const channel = signal();\r\n const rtc = new WetRTC({ signal: channel, reconnect: false });\r\n const { stream, track } = streamWithTrack();\r\n const firstPc = rtc.peerConnection as unknown as MockPeerConnection;\r\n\r\n rtc.addTrack(track, stream);\r\n await rtc.disconnect();\r\n\r\n expect(channel.send).toHaveBeenCalledWith({ type: 'bye' });\r\n const nextPc = rtc.peerConnection as unknown as MockPeerConnection;\r\n expect(nextPc).not.toBe(firstPc);\r\n expect(firstPc.close).toHaveBeenCalled();\r\n expect(nextPc.addTrack).toHaveBeenCalledWith(track, stream);\r\n expect(rtc.state).toBe('idle');\r\n });\r\n\r\n it('recvonly connect creates recvonly transceivers', async () => {\r\n const rtc = new WetRTC({ signal: signal(), direction: 'recvonly', reconnect: false });\r\n\r\n await rtc.connect();\r\n\r\n const pc = rtc.peerConnection as unknown as MockPeerConnection;\r\n expect(pc.addTransceiver).toHaveBeenCalledWith('video', { direction: 'recvonly' });\r\n expect(pc.addTransceiver).toHaveBeenCalledWith('audio', { direction: 'recvonly' });\r\n });\r\n\r\n it('recvonly initiator sends offer', async () => {\r\n const ch = signal();\r\n const rtc = new WetRTC({\r\n signal: ch,\r\n direction: 'recvonly',\r\n initiator: true,\r\n reconnect: false,\r\n });\r\n\r\n await rtc.connect();\r\n\r\n expect(ch.send).toHaveBeenCalledWith(\r\n expect.objectContaining({ type: 'offer' }),\r\n );\r\n });\r\n\r\n it('sendonly passive does not send offer', async () => {\r\n const ch = signal();\r\n const rtc = new WetRTC({\r\n signal: ch,\r\n direction: 'sendonly',\r\n initiator: false,\r\n reconnect: false,\r\n });\r\n\r\n await rtc.connect();\r\n\r\n expect(ch.send).not.toHaveBeenCalledWith(\r\n expect.objectContaining({ type: 'offer' }),\r\n );\r\n });\r\n});\r\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJtYXBwaW5ncyI6IiIsIm5hbWVzIjpbXSwiaWdub3JlTGlzdCI6W10sInNvdXJjZXMiOltdLCJzb3VyY2VzQ29udGVudCI6W119"]}