@xtr-dev/zod-rpc
Version:
Simple, type-safe RPC library with Zod validation and automatic TypeScript inference
172 lines • 5.83 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.createWebRTCTransport = exports.WebRTCPeerConnection = exports.WebRTCTransport = void 0;
const errors_1 = require("../errors");
// WebRTC types are now included in modern TypeScript/DOM libs
/**
* @group Transport Layer
*/
class WebRTCTransport {
constructor(dataChannel) {
this.dataChannel = dataChannel;
this.setupEventHandlers();
}
async send(message) {
if (!this.isConnected()) {
throw new errors_1.TransportError('WebRTC DataChannel is not connected');
}
try {
this.dataChannel.send(JSON.stringify(message));
}
catch (error) {
throw new errors_1.TransportError(`Failed to send message: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
onMessage(handler) {
this.messageHandler = handler;
}
async connect() {
return new Promise((resolve, reject) => {
if (this.isConnected()) {
resolve();
return;
}
const onOpen = () => {
this.dataChannel.removeEventListener('open', onOpen);
this.dataChannel.removeEventListener('error', onError);
resolve();
};
const onError = (_event) => {
this.dataChannel.removeEventListener('open', onOpen);
this.dataChannel.removeEventListener('error', onError);
reject(new errors_1.TransportError('Failed to connect WebRTC DataChannel'));
};
if (this.dataChannel.readyState === 'open') {
resolve();
}
else {
this.dataChannel.addEventListener('open', onOpen);
this.dataChannel.addEventListener('error', onError);
}
});
}
async disconnect() {
return new Promise((resolve) => {
if (!this.isConnected()) {
resolve();
return;
}
const onClose = () => {
this.dataChannel.removeEventListener('close', onClose);
resolve();
};
this.dataChannel.addEventListener('close', onClose);
this.dataChannel.close();
});
}
isConnected() {
return this.dataChannel.readyState === 'open';
}
setupEventHandlers() {
this.dataChannel.addEventListener('message', (event) => {
try {
const message = JSON.parse(event.data);
this.messageHandler?.(message);
}
catch (error) {
console.error('Failed to parse WebRTC message:', error);
}
});
this.dataChannel.addEventListener('error', (event) => {
console.error('WebRTC DataChannel error:', event);
});
}
}
exports.WebRTCTransport = WebRTCTransport;
/**
* @group Transport Layer
*/
class WebRTCPeerConnection {
constructor(config) {
this.peerConnection = new RTCPeerConnection(config);
this.setupPeerConnectionHandlers();
}
async createOffer(channelLabel = 'rpc-channel') {
this.dataChannel = this.peerConnection.createDataChannel(channelLabel, {
ordered: true,
});
const offer = await this.peerConnection.createOffer();
await this.peerConnection.setLocalDescription(offer);
// Ensure type field is present
return {
type: 'offer',
sdp: offer.sdp,
};
}
async createAnswer() {
const answer = await this.peerConnection.createAnswer();
await this.peerConnection.setLocalDescription(answer);
// Ensure type field is present
return {
type: 'answer',
sdp: answer.sdp,
};
}
async handleAnswer(answer) {
await this.peerConnection.setRemoteDescription(answer);
}
async setRemoteDescription(description) {
// Ensure the description has the required type field
if (!description.type) {
throw new Error('RTCSessionDescriptionInit must have a type field');
}
await this.peerConnection.setRemoteDescription(description);
}
async addIceCandidate(candidate) {
await this.peerConnection.addIceCandidate(candidate);
}
onIceCandidate(handler) {
this.peerConnection.addEventListener('icecandidate', (event) => {
handler(event.candidate);
});
}
onDataChannel(handler) {
this.peerConnection.addEventListener('datachannel', (event) => {
handler(event.channel);
});
}
onConnectionStateChange(handler) {
this.peerConnection.addEventListener('connectionstatechange', () => {
handler(this.peerConnection.connectionState);
});
}
getDataChannel() {
return this.dataChannel;
}
getConnectionState() {
return this.peerConnection.connectionState;
}
close() {
this.dataChannel?.close();
this.peerConnection.close();
}
setupPeerConnectionHandlers() {
this.peerConnection.addEventListener('connectionstatechange', () => {
console.log('WebRTC connection state:', this.peerConnection.connectionState);
});
this.peerConnection.addEventListener('datachannel', (event) => {
if (!this.dataChannel) {
this.dataChannel = event.channel;
}
});
}
}
exports.WebRTCPeerConnection = WebRTCPeerConnection;
/**
* @group Transport Layer
*/
function createWebRTCTransport(dataChannel) {
return new WebRTCTransport(dataChannel);
}
exports.createWebRTCTransport = createWebRTCTransport;
//# sourceMappingURL=webrtc.js.map