4.5 KiB
@suaveplan/drive-transport-webrtc — WebRTC DriveTransport
WebRTC datachannel implementation of the DriveTransport interface for
browser-to-browser chunk transfer. Composes WebRTCClient +
DataChannelManager from @suaveplan/drive-webrtc; consumed by
@suaveplan/drive-mesh alongside drive-transport-https as one of the
pluggable wire transports.
Requirements
Requirement: DriveTransport contract
The package SHALL expose WebRTCDriveTransport implementing the
structural DriveTransport interface:
name: "drive-transport-webrtc"requestChunk(peerId, fileId, chunkId, signal?): AsyncIterable<Uint8Array>advertise(peerId, fileId, bitset): Promise<void>onAdvertisement(handler): () => voidpeers(): Iterable<PeerInfo>dispose(): Promise<void>
The local DriveTransport interface MUST remain structurally compatible
with the canonical one defined in @suaveplan/drive-mesh so consumers can
swap to the mesh-canonical type without code changes when mesh lands.
Requirement: Wire framing
Each datachannel message SHALL be a length-prefixed binary frame:
[4 B uint32 BE frameLen]
[1 B uint8 frameType] // 0x01 chunk-request, 0x02 chunk-response, 0x03 advertisement
[frameLen-1 B payload]
MAX_FRAME_BYTES = 16 MiB; over-cap frames MUST be rejected withDriveTransportWebRtcError(FRAME_TOO_LARGE).- Malformed frames (length mismatch, unknown type) MUST raise
FRAME_INVALID. - Chunk request payload:
fileId(32) ‖ chunkId(4 BE uint32). - Chunk response payload: raw chunk bytes (no extra length prefix — frame
already carries
frameLen). - Advertisement payload:
fileId(32) ‖ gzipped-bitset. Compression uses the platformCompressionStream/DecompressionStreamAPI; the stream ctors are injectable viaStreamCtorResolverso tests cover missing- stream branches withoutvi.stubGlobal.
Requirement: requestChunk semantics
requestChunk(peerId, fileId, chunkId, signal?):
- Reuse existing channel via
dataChannels.get(peerId, defaultLabel)oropen(peerId, defaultLabel). Unknown peer →PEER_NOT_CONNECTED. - Encode + send chunk-request frame.
- Wait for the next chunk-response frame from the same
(peerId, label)withinrequestTimeoutMs. AbortSignal cancels the wait + throwsABORTED. Timeout throwsREQUEST_TIMEOUT. - Yield the response payload as a single value of the
AsyncIterable<Uint8Array>. - Chunk-responses FIFO-match per peer — the wire format has no request-id field. Documented in implementation notes as a known design constraint.
Requirement: advertise + onAdvertisement
advertise(peerId, fileId, bitset):
- Gzip the bitset via
CompressionStream. Compression failure throwsBITSET_COMPRESSION_FAILED. - Encode advertisement frame, send via
DataChannelManager.send. - Underlying send failure (channel closed) maps to
DATA_CHANNEL_CLOSED.
onAdvertisement(handler) subscribes to dataChannels.on("channel:message"),
decodes incoming frames, invokes handler only for frameType === 0x03,
returns an unsubscribe function.
Requirement: AbortSignal threading
Every async path (requestChunk, advertise, listener wait) SHALL accept
and honour an optional AbortSignal. Pre-aborted signals throw
DriveTransportWebRtcError(ABORTED) before any side effect.
Requirement: dispose idempotency
dispose() SHALL unsubscribe from DataChannelManager listeners and
release internal state. It MUST be safe to call multiple times. It MUST NOT
close the underlying WebRTCClient — client lifetime is managed by the
parent (typically WebRTCDrivePlugin or DriveMesh).
Requirement: OTel observability
The package SHALL register the following instruments under tracer
@suaveplan/drive-transport-webrtc:
- Counter
drive.transport.webrtc.requests_total{outcome} - Counter
drive.transport.webrtc.advertisements_total{direction} - Histogram
drive.transport.webrtc.request_duration_ms - Histogram
drive.transport.webrtc.frame_size_bytes
Requirement: Tests use injected real fakes
Tests MUST NOT use vi.mock or vi.stubGlobal. The
FakeRTCPeerConnection / FakeRTCDataChannel real-implementation pattern
from @suaveplan/drive-webrtc MUST be reused (structural EventTarget
subclasses with simulate* driver methods). All test imports come from
@suaveplan/testing/runner.
Requirement: Coverage + gate
bunx turbo lint typecheck test:coverage --filter=@suaveplan/drive-transport-webrtc
MUST be green and istanbul coverage MUST be 100% on every touched module
before any release.