3.8 KiB
DataChannelManager
(no submodules)
Purpose
DataChannelManager opens, multiplexes, and gates writes on
RTCDataChannels for the drive-webrtc mesh. It composes a
WebRTCClient — pulling out the underlying RTCPeerConnection per
peer — and exposes a uniform send/close/event surface so callers
(chunk-source layer, plugin glue, debug tools) never touch raw
RTCDataChannel event listeners. Backpressure-aware send waits for
bufferedamountlow when the channel is buffering above the
high-water mark, preventing the WebRTC stack from being saturated
during burst transfers.
Features
- Per-peer + per-label channel multiplexing.
- Default channel label
drive-chunksand ordered delivery (configurable). - High-water-mark backpressure (default 1 MiB) with AbortSignal cancellation.
- Idempotent
closeandcloseAllsemantics. - Typed event surface:
channel:open,channel:close,channel:message,channel:error. - All errors raised at the API boundary are
DriveWebRtcError(PEER_NOT_FOUND | DATA_CHANNEL_CLOSED).
Usage: Basic Example
import { DataChannelManager, WebRTCClient } from "@suaveplan/drive-webrtc";
const manager = new DataChannelManager({ client });
const channel = manager.open("bob");
manager.on("channel:message", ({ peerId, data }) => {
// route inbound payload
});
await manager.send("bob", "drive-chunks", "hello");
Usage: Advanced Example
import { DataChannelManager } from "@suaveplan/drive-webrtc";
const manager = new DataChannelManager({
client,
defaultLabel: "drive-chunks",
defaultOrdered: true,
highWaterMarkBytes: 256_000,
});
const ctrl = new AbortController();
setTimeout(() => ctrl.abort(), 5_000);
try {
for (const part of parts) {
await manager.send("bob", "drive-chunks", part, ctrl.signal);
}
} catch (err) {
if (err instanceof DriveWebRtcError && err.code === "DRIVE_WEBRTC.DATA_CHANNEL_CLOSED") {
// either peer dropped or signal aborted
}
}
manager.close("bob");
API Reference
new DataChannelManager(options)
client: WebRTCClient.defaultLabel?: string(default"drive-chunks").defaultOrdered?: boolean(defaulttrue).highWaterMarkBytes?: number(default1_048_576).
open(peerId, label?, init?): RTCDataChannel
Reuse an open channel for the label, or create a new one via
pc.createDataChannel. Throws PEER_NOT_FOUND if the client has no
peer connection for peerId.
send(peerId, label, payload, signal?): Promise<void>
Throws DATA_CHANNEL_CLOSED when the channel is missing or its
readyState is not "open". When bufferedAmount is above the
high-water mark, waits for one bufferedamountlow event before
calling channel.send. AbortSignal aborts the wait and the resulting
error has cause set to the signal's reason.
close(peerId, label?, reason?) / closeAll(reason?)
Idempotent. close(peerId) with no label closes every channel for
the peer.
get(peerId, label?): RTCDataChannel | undefined
Lookup. Defaults to the manager's defaultLabel.
Implementation Notes
- The manager listens to
open,close,error, andmessageevents on every channel it creates. It removes those listeners on detach to avoid leaking afterclose. - On
register, if the channel is already"open"(an unusual race the test doubles can reproduce),channel:openis dispatched viaqueueMicrotaskso callers awaiting the event still receive it. errorevents surface aschannel:errorcarrying aDriveWebRtcError(DATA_CHANNEL_CLOSED)with the underlyingevent.errorascause.- The internal storage is
Map<peerId, Map<label, ChannelEntry>>; the outer map is pruned when its inner map becomes empty so acloseAllreturns the manager to a fully-clean state.