first commit
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
# DataChannelManager
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
`DataChannelManager` opens, multiplexes, and gates writes on
|
||||
`RTCDataChannel`s 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-chunks` and ordered delivery
|
||||
(configurable).
|
||||
- High-water-mark backpressure (default 1 MiB) with AbortSignal
|
||||
cancellation.
|
||||
- Idempotent `close` and `closeAll` semantics.
|
||||
- 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
|
||||
|
||||
```ts
|
||||
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
|
||||
|
||||
```ts
|
||||
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` (default `true`).
|
||||
- `highWaterMarkBytes?: number` (default `1_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`, and `message`
|
||||
events on every channel it creates. It removes those listeners on
|
||||
detach to avoid leaking after `close`.
|
||||
- On `register`, if the channel is already `"open"` (an unusual race
|
||||
the test doubles can reproduce), `channel:open` is dispatched via
|
||||
`queueMicrotask` so callers awaiting the event still receive it.
|
||||
- `error` events surface as `channel:error` carrying a
|
||||
`DriveWebRtcError(DATA_CHANNEL_CLOSED)` with the underlying
|
||||
`event.error` as `cause`.
|
||||
- The internal storage is `Map<peerId, Map<label, ChannelEntry>>`;
|
||||
the outer map is pruned when its inner map becomes empty so a
|
||||
`closeAll` returns the manager to a fully-clean state.
|
||||
Reference in New Issue
Block a user