npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mertushka/trpc-webrtc-link

v0.1.1

Published

A tRPC v11 terminating client link and server adapter for established RTCDataChannels.

Downloads

284

Readme

@mertushka/trpc-webrtc-link

A tRPC v11 terminating client link and server adapter that transport queries, mutations, and subscriptions over an already established RTCDataChannel.

This package handles tRPC framing, execution, cancellation, error shaping, and backpressure. It does not perform SDP/ICE signaling, peer discovery, reconnection, or authentication.

Runtime requirements

  • Node.js 20.19 or newer;
  • TypeScript 5.7.2 or newer;
  • matching tRPC v11 @trpc/client and @trpc/server versions;
  • an ordered, reliable RTCDataChannel.

Building this repository requires Node.js 22.18 or newer because that is the minimum supported version for the current tsdown build tool. Published output is compiled on Node.js 24 and its packed runtime is tested on Node.js 20.19, 22.18, and 24.

Installation

npm install @mertushka/trpc-webrtc-link @trpc/client @trpc/server

For Node WebRTC peers:

npm install @mertushka/webrtc-node

@mertushka/webrtc-node is not imported or bundled by this package. Browser applications use the browser's native RTCPeerConnection and RTCDataChannel.

Client

Pass an open channel or an async factory. The factory is evaluated lazily on the first tRPC operation.

import { createTRPCClient } from '@trpc/client';
import { createWebRTCLink } from '@mertushka/trpc-webrtc-link';
import type { AppRouter } from './server';

const link = createWebRTCLink<AppRouter>({
  channel: () => connectedDataChannel,
  backpressure: {
    highWatermark: 1024 * 1024,
    lowWatermark: 256 * 1024,
    queueLimit: 1024,
  },
});

const client = createTRPCClient<AppRouter>({
  links: [link],
});

const greeting = await client.hello.query();
const count = await client.counter.increment.mutate();

const subscription = client.clock.subscribe(undefined, {
  onData(value) {
    console.log(value);
  },
});

subscription.unsubscribe(); // sends cancellation to the server
link.close(); // rejects pending operations and removes listeners

When the router uses a transformer, pass the same transformer to the link:

const link = createWebRTCLink<AppRouter>({
  channel,
  transformer: superjson,
});

The link returns remote tRPC errors and transport failures as TRPCClientError. Transport failures include meta.transport === "webrtc" and may include a meta.transportCode.

Server

The server adapter creates context once per attached channel. Context receives the channel, typed application peer metadata, and a signal that aborts when the handler or channel closes.

import { createWebRTCHandler } from '@mertushka/trpc-webrtc-link';
import { RTCPeerConnection } from '@mertushka/webrtc-node';
import { appRouter } from './router';

const peerConnection = new RTCPeerConnection();

peerConnection.addEventListener('datachannel', (event) => {
  const handler = createWebRTCHandler({
    router: appRouter,
    channel: event.channel,
    peer: {
      peerConnection,
      userId: 'user-123',
    },
    createContext({ channel, peer, signal }) {
      return {
        channel,
        userId: peer.userId,
        signal,
      };
    },
    onError({ error, path }) {
      console.error(path, error);
    },
  });

  void handler.ready;

  // Later:
  // handler.close({ closeChannel: true });
});

close() aborts active procedures, closes active subscription iterators, rejects queued writes, and removes listeners. It leaves the underlying channel open unless closeChannel: true is passed.

Signaling

Signaling is an application responsibility. Exchange SDP descriptions and ICE candidates using WebSocket, HTTP, QR codes, or another authenticated signaling system. After the data channel opens, pass it to createWebRTCLink or createWebRTCHandler.

The runnable examples/basic application uses a WebSocket only for SDP/ICE. tRPC messages never pass through the signaling server.

Protocol

The exported protocol identifier is:

TRPC_WEBRTC_PROTOCOL === 'trpc-webrtc/1';

Version 1 uses JSON text frames:

| Frame | Direction | Purpose | | --------------- | ---------------- | ------------------------------------- | | handshake | client to server | Negotiate trpc-webrtc/1 | | ready | server to client | Context exists and requests may start | | request | client to server | Query, mutation, or subscription | | result | server to client | Unary value or subscription start | | data | server to client | Subscription value | | error | server to client | Transformed tRPC error shape | | complete | server to client | Normal operation completion | | cancel | client to server | Abort a server operation | | ping / pong | either direction | Application heartbeat primitives |

Every operation has an opaque collision-resistant string ID. The runtime validates inbound frame shape, protocol version, IDs, and operation transitions. Malformed or version-mismatched connection frames close the channel. Unknown operation IDs and other correlatable state errors are reported through onProtocolError without throwing from event listeners.

The protocol requires an ordered, reliable channel. Version 1 does not add packet reordering or retransmission above SCTP.

Cancellation

  • An AbortSignal passed to a query or mutation sends cancel.
  • Subscription unsubscribe() sends cancel.
  • The server passes an operation-specific signal to tRPC procedures.
  • Channel closure aborts all server operations and rejects all client operations.

Cancellation is best effort if the data channel closes before the cancel frame is delivered. The package does not claim exactly-once execution.

Backpressure

Both endpoints respect RTCDataChannel.bufferedAmount.

  • Writes pause above highWatermark.
  • The writer sets bufferedAmountLowThreshold and resumes after bufferedamountlow.
  • Per-operation queues are drained round-robin so one subscription cannot indefinitely starve other calls.
  • queueLimit bounds queued frame count.
  • An enqueue above the limit fails with WebRTCQueueOverflowError.
  • Frames are never silently dropped.

Defaults:

{
  highWatermark: 1024 * 1024,
  lowWatermark: 256 * 1024,
  queueLimit: 1024,
  maxMessageBytes: 1024 * 1024,
}

tRPC compatibility

The package uses public tRPC v11 APIs for links, observables, procedure calls, error shaping, and transformed responses.

One internal detail is unavoidable: the adapter reads router._def._config to access the router's configured transformer and error formatter. That access is isolated in src/trpc-internals.ts, matches tRPC's own WebSocket adapter, and is covered by the ~11.17.0 peer range. New tRPC minor versions must be reviewed and tested before widening that range.

Security

  • WebRTC encrypts the peer connection, but applications must authenticate and authorize signaling participants.
  • Protect signaling messages against tampering and peer substitution.
  • Validate all procedure input with tRPC validators.
  • Treat createContext peer metadata as trusted only if the signaling layer established it securely.
  • Configure queue and frame limits for the expected workload.
  • Do not expose unrestricted procedures merely because the channel is peer-to-peer.

Limitations

The first release intentionally does not include:

  • SDP or ICE signaling;
  • reconnection or subscription resumption;
  • request batching;
  • binary codecs;
  • React bindings;
  • peer discovery;
  • multiplexing unrelated protocols;
  • exactly-once delivery guarantees.

These are future-work candidates rather than implicit behavior.