/** * SDP offer / answer primitives. * * Pure async wrappers over the spec-standard * `pc.createOffer() → pc.setLocalDescription()` pattern (and inverse). * Every failure mode is translated into `DriveWebRtcError(PEER_HANDSHAKE_FAILED)` * with the original DOMException attached as `cause` so callers never see * raw browser errors leak past the package boundary. */ import { DRIVE_WEBRTC_CODES, DriveWebRtcError, } from "../../error-codes/error-codes.js"; function throwIfAborted(signal: AbortSignal | undefined, where: string): void { if (signal?.aborted === true) { throw new DriveWebRtcError( DRIVE_WEBRTC_CODES.PEER_HANDSHAKE_FAILED, `${where} aborted` ); } } function wrap(cause: unknown, message: string): DriveWebRtcError { return new DriveWebRtcError( DRIVE_WEBRTC_CODES.PEER_HANDSHAKE_FAILED, message, { cause: cause as Error } ); } /** * Create an SDP offer on `pc`, apply it as local description, and return it. */ export async function createOffer( pc: RTCPeerConnection, signal?: AbortSignal ): Promise { throwIfAborted(signal, "createOffer"); let offer: RTCSessionDescriptionInit; try { offer = await pc.createOffer(); } catch (cause) { throw wrap(cause, "RTCPeerConnection.createOffer failed"); } throwIfAborted(signal, "createOffer"); try { await pc.setLocalDescription(offer); } catch (cause) { throw wrap( cause, "RTCPeerConnection.setLocalDescription(offer) failed" ); } return offer; } /** * Apply a remote offer to `pc`, create an answer, apply it as local * description, and return the answer. */ export async function handleOffer( pc: RTCPeerConnection, offer: RTCSessionDescriptionInit, signal?: AbortSignal ): Promise { throwIfAborted(signal, "handleOffer"); try { await pc.setRemoteDescription(offer); } catch (cause) { throw wrap( cause, "RTCPeerConnection.setRemoteDescription(offer) failed" ); } throwIfAborted(signal, "handleOffer"); let answer: RTCSessionDescriptionInit; try { answer = await pc.createAnswer(); } catch (cause) { throw wrap(cause, "RTCPeerConnection.createAnswer failed"); } throwIfAborted(signal, "handleOffer"); try { await pc.setLocalDescription(answer); } catch (cause) { throw wrap( cause, "RTCPeerConnection.setLocalDescription(answer) failed" ); } return answer; } /** * Apply a remote answer to an outbound offer we already sent. */ export async function handleAnswer( pc: RTCPeerConnection, answer: RTCSessionDescriptionInit, signal?: AbortSignal ): Promise { throwIfAborted(signal, "handleAnswer"); try { await pc.setRemoteDescription(answer); } catch (cause) { throw wrap( cause, "RTCPeerConnection.setRemoteDescription(answer) failed" ); } }