@sovereignbase/peer2peer
v1.0.0
Published
Simple WebRTC wrapper for peer to peer connection setup in browsers.
Downloads
216
Maintainers
Readme
peer2peer
Simple WebRTC wrapper for peer to peer connection setup in browsers.
It creates portable offer and contract objects that you move through your own signaling channel.
Compatibility
- Runtimes: modern browsers with WebRTC support;
- Module format: ESM and CJS.
- Required globals / APIs:
RTCPeerConnection,RTCDataChannel,MediaStream,EventTarget,CustomEvent,crypto.randomUUID; media sharing also needsnavigator.mediaDevicesanddocument. - TypeScript: bundled types.
Installation
npm install @sovereignbase/peer2peer
# or
pnpm add @sovereignbase/peer2peer
# or
yarn add @sovereignbase/peer2peer
# or
bun add @sovereignbase/peer2peer
# or
deno add jsr:@sovereignbase/peer2peer
# or
vlt install jsr:@sovereignbase/peer2peerUsage
Create a connection
import { P2PConnection } from '@sovereignbase/peer2peer'
type Message = {
type: 'chat'
text: string
}
/**
* Package uses 4 Google STUN Servers by default,
* you can add additional turn or stun servers.
*
* For example `https://developers.cloudflare.com/realtime/turn/`.
*
* WebRTC is end-to-end encrypted by default.
* As long as you stick to standard STUN or TURN servers and
* do not use something like a SFU, all data is e2ee via DTLS
*/
// Peer A
const offer = await P2PConnection.makeOffer()
// send `offer` to peer B using your own transport
// Peer B
const copies = await P2PConnection.acceptOffer(offer)
// send `copies.offeror` back to peer A
// keep `copies.offeree` on peer B
const peerA = new P2PConnection<Message>(copies.offeror)
const peerB = new P2PConnection<Message>(copies.offeree)
peerA.addEventListener('message', (event) => {
console.log('peer A received', event.detail)
})
peerB.addEventListener('message', (event) => {
console.log('peer B received', event.detail)
})
await peerA.ready()
await peerB.ready()
peerA.sendMessage({
type: 'chat',
text: 'hello from peer A',
})Share media
connection.addEventListener('camera', (event) => {
document.body.append(event.detail) // HTMLVideoElement
})
connection.addEventListener('screen', (event) => {
document.body.append(event.detail) // HTMLVideoElement
})
// Stream media
await connection.shareMicrophone()
await connection.shareCamera()
await connection.shareScreen()
// Stop streaming media
connection.stopSharingMicrophone()
connection.stopSharingCamera()
connection.stopSharingScreen()Local preview elements are exposed as:
P2PConnection.localCameraVideoElementP2PConnection.localScreenVideoElement
Runtime behaviour
- Package does not provide signaling transport. You move
OfferandContractobjects through your own channel, for example WebSocket, HTTP, QR, copy-paste or any other out-of-band transport. P2PConnection.makeOffer()andP2PConnection.acceptOffer()always include 4 public Google STUN servers by default and append any additional ICE servers you pass in.ready()resolves only after the underlyingRTCDataChannelreaches the"open"state. If the peer connection fails, closes, or the channel errors first, it rejects with a typedP2PConnectionError.sendMessage()uses MessagePack encoding via@msgpack/msgpack. The"message"event receives the decoded payload asevent.detail.- Media sharing is lazy and shared.
shareMicrophone()andshareCamera()reuse one cachedgetUserMedia()stream, andshareScreen()reuses one cachedgetDisplayMedia()stream. - Local preview elements are also shared through the static
P2PConnection.localCameraVideoElementandP2PConnection.localScreenVideoElementproperties. - Track additions trigger automatic in-band renegotiation over the existing data channel. The offeree side behaves as the polite peer during glare handling.
Errors
Failures throw P2PConnectionError. The code is stable and the message
describes the specific failure site.
Supported error codes:
CHANNEL_ERRORCHANNEL_CLOSEDCHANNEL_NOT_AVAILABLECONNECTION_NOT_READYUNKNOWN_PEER_CONTRACTMISSING_LOCAL_DESCRIPTION
Tests
npm test- Unit tests cover typed errors, contract validation, ICE setup, local description failures and readiness guards.
- Integration tests cover connection setup, message exchange, renegotiation, media sharing, fallback media behavior and failure paths.
- Browser E2E tests use Playwright with separate browser contexts and a WebSocket signaling server to verify real WebRTC connection establishment and isolated simultaneous peer pairs.
- Current automated coverage is
100%for statements, branches, functions and lines on the published runtime bundle.
Benchmarks
npm run benchLast measured on Node v22.14.0 (win32 x64):
| Benchmark | Average | Min | Max |
| -------------------------------- | -----------: | --------: | --------: |
| websocket-signaled ready() | 242.39 ms | 229.80 ms | 284.30 ms |
| one-way sendMessage() delivery | 5.36 ms | 3.21 ms | 8.50 ms |
| message throughput | 186.72 msg/s | - | - |
License
Apache-2.0
