Initial Commit
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* 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<RTCSessionDescriptionInit> {
|
||||
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<RTCSessionDescriptionInit> {
|
||||
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<void> {
|
||||
throwIfAborted(signal, "handleAnswer");
|
||||
try {
|
||||
await pc.setRemoteDescription(answer);
|
||||
} catch (cause) {
|
||||
throw wrap(
|
||||
cause,
|
||||
"RTCPeerConnection.setRemoteDescription(answer) failed"
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user