node-peer reference
Provide Node-hosted relay games with data-channel WebRTC using a node-datachannel native binding. Import from @controller-plus/sdk/node-peer. The SDK does not install or import that optional binding for you.
Exports
nativeIceServers(list = []): object[] converts W3C { urls: string | string[], username?, credential? } entries to native records. Non-array lists become empty. It accepts STUN and UDP TURN, strips IPv6 brackets, removes duplicates and skips malformed URLs over 512 characters. Default port is 3478. TURN requires string credentials. TCP/TLS TURN is skipped; native UDP support cannot promise connectivity on TCP-only networks.
STUN result: { hostname, port }. TURN result: { hostname, port, username, password, relayType: 'TurnUdp' }. Ports must be 1–65535. A stun:...?...transport=tcp URL still becomes the ordinary STUN record; no TCP transport field is retained.
nodePeerConnection(binding, { log = 'Warning' } = {}): RTCPeerConnectionClass requires binding.PeerConnection to be a function or throws TypeError. Initializes the binding logger once per binding if available; falsy log disables logger setup. Pass the returned class as createRoom({ RTCPeerConnectionImpl }). Close all rooms/links before calling its static cleanup(): void, which invokes optional binding.cleanup() and ignores cleanup exceptions.
Returned peer class
new RTCPeerConnection(config = {}) uses config.iceServers and disables automatic native negotiation. This is a small adapter for the SDK's peer link, not the complete browser WebRTC API: no audio/video, no ICE restart, and setConfiguration does nothing.
| Signature | Behavior and return |
|---|---|
createDataChannel(label, init = {}): Channel |
String label; supports negotiated, integer id, ordered (default true), integer maxRetransmits or maxPacketLifeTime (retransmits wins), string protocol. |
createOffer(): Promise<{ type: 'offer', sdp: '' }> |
Placeholder description; native SDP is produced by setting it. |
createAnswer(): Promise<{ type: 'answer', sdp: '' }> |
Same for answer. |
setLocalDescription(description = {}): Promise<void> |
Type defaults to answer if remote is offer, otherwise offer. Waits up to five seconds for native description. Rejects when closed, on native failure or timeout. Read localDescription after awaiting. |
setRemoteDescription(description): Promise<void> |
Require type offer/answer and string SDP; reject invalid/closed input or native failure. Set remoteDescription. |
addIceCandidate(candidate): Promise<void> |
String candidate or { candidate, sdpMid? }; empty/end marker or closed peer is ignored. Default mid '0'. Native errors reject. |
setConfiguration(): void |
No-op. A fresh link is needed to change ICE servers. |
getStats(): Promise<Map> |
Empty without a selected pair; otherwise transport T, pair CP, local L, remote R. Pair has byte counters and optional currentRoundTripTime in seconds. |
close(): void |
Idempotently close native peer; set connection/ICE/signaling states to closed. |
Properties: localDescription, remoteDescription start null; connectionState, iceConnectionState, iceGatheringState start 'new'; signalingState starts 'stable'. native exposes the supplied binding's peer. Native state strings are forwarded.
| Callback property | Payload |
|---|---|
onicecandidate |
{ type: 'icecandidate', candidate: { candidate, sdpMid, sdpMLineIndex: 0 } }; candidate null at gathering completion. |
onconnectionstatechange, oniceconnectionstatechange, onicegatheringstatechange, onsignalingstatechange |
{ type: '<matching event name>' }; read current state on peer. Signaling callback requires binding support. |
ondatachannel |
{ type: 'datachannel', channel } when supported by binding. |
Returned channel
Properties: label, id (default null), negotiated, ordered, binaryType = 'arraybuffer', readyState (connecting/open/closing/closed), bufferedAmount (number; zero if native query fails), native.
send(data): void accepts string, Buffer, ArrayBuffer or typed array. Throws if not open or binary data has another shape. close(): void is idempotent; native close errors are ignored. This adapter uses callback properties, not a full EventTarget interface.
| Callback | Payload |
|---|---|
onopen |
{ type: 'open' } |
onclose |
{ type: 'close' } |
onerror |
{ type: 'error', error: Error } |
onmessage |
{ type: 'message', data }; text stays text, binary becomes ArrayBuffer by default. |
The adapter adds no media channel, payload-size limit or game protocol. Session/PeerLink apply protocol budgets. Native binding exceptions can propagate from construction and channel operations.
Filter ICE servers and connect a game
The exported function below accepts a binding already loaded by your application. Keep the WebSocket fallback available when a peer route cannot connect.
import { createRoom } from '@controller-plus/sdk/relay-host';
import { nativeIceServers, nodePeerConnection } from '@controller-plus/sdk/node-peer';
console.log(nativeIceServers([{ urls: ['stun:stun.example:3478'] }]));
export async function startNativeGame(binding, options = {}) {
const Peer = nodePeerConnection(binding, { log: 'Warning' });
let room;
try {
room = await createRoom({
...options, game: { id: 'native-peer-example', name: 'Native peer game' },
RTCPeerConnectionImpl: Peer, localControllers: false,
});
} catch (error) { Peer.cleanup(); throw error; }
return { room, stop() { room.close(); Peer.cleanup(); } };
}
Your application calls startNativeGame(binding) with its compatible native module. Packaging and validating that binding on each target OS is separate from JavaScript integration.