# TypeScript SDK

> The Relay TypeScript SDK. connectRelaySubscriber subscribes to track objects and connectRelayPublisher publishes them, through Relay, Mux's Media over QUIC (MoQ) relay service.

URL: https://www.caprid.com/docs/sdks/typescript/
Source: https://github.com/caprids/relay-sdks/blob/main/ts/README.md

The Relay TypeScript SDK. `connectRelaySubscriber` subscribes to track objects and
`connectRelayPublisher` publishes them, through Relay, Mux's Media over QUIC (MoQ) relay service.
The SDK does not interpret payloads, so you need no player, media catalog, or Video.js.

## Requirements

- Browser WebTransport in a secure context (HTTPS or localhost). This is the default transport.
  It needs WebTransport protocol negotiation, which the live tests verify against the Relay in
  Chromium and in Firefox 155 or later (Playwright's builds). WebKit cannot open a WebTransport
  session to the Relay yet.
- Node 24 or later: import from `@caprid/relay/node`, which connects over native QUIC through
  `@caprid/relay-quic` ([Node](#node)). Other runtimes must pass a compatible `createTransport`
  factory.
- Where UDP is blocked, or the browser has no usable WebTransport (Safari), pass
  [`@caprid/relay-websocket`](https://github.com/caprids/relay-sdks/blob/main/ts/packages/relay-websocket/README.md)'s `withWebSocketFallback()` or
  `createWebSocketTransport()` as `createTransport`. The connection then runs QMux over WebSocket on
  TCP 443 ([networks without UDP](https://github.com/caprids/relay-sdks/blob/main/ts/GUIDE.md#networks-without-udp-qmux-over-websocket)).
- A short-lived Relay JWT authorized for the namespace you use, and the endpoint of any relay other
  than the hosted one. The SDK consumes tokens. It does not issue them.

## Install

```sh
npm install @caprid/relay
```

Add `@caprid/relay-quic` for Node ([Node](#node)), `@caprid/relay-websocket` for networks without
UDP, and `@caprid/moq-media` to publish or play media. To build the packages from a checkout of this
repository instead, see
[Use the local packages in your application](https://github.com/caprids/relay-sdks/blob/main/ts/GUIDE.md#use-the-local-packages-in-your-application).

## Publish and subscribe

```ts
import { connectRelayPublisher, connectRelaySubscriber, readText } from '@caprid/relay';

const getToken = () => token; // a Relay JWT allowed to publish and subscribe under its root
const namespace = ['quickstart', 'unique-run']; // relative to the token's root

const publisher = await connectRelayPublisher({ namespace, getToken });
const ticker = publisher.registerTrack({ track: 'ticker' });
await publisher.announced;

const subscriber = await connectRelaySubscriber({ getToken });
let received: (text: string) => void;
const arrived = new Promise<string>((resolve) => { received = resolve; });
const subscription = subscriber.subscribe(
  { namespace, track: 'ticker' },
  { onObject: (object) => { if (object.status === 'normal') received(readText(object)); } },
);
await subscription.subscribed; // a fresh namespace can briefly reject this with error 16; retry it

await ticker.writeText('hello from @caprid/relay'); // waits for the subscriber's demand
console.log(await arrived);
subscriber.close();
await publisher.close();
```

With no `url`, each connection dials the hosted Relay, `https://relay.mux.global`, with the
token's root as the path, so namespaces are relative to that root. Pass `url` for another relay,
region or room; `roomEndpoint` builds one
([endpoint and credentials](https://github.com/caprids/relay-sdks/blob/main/ts/GUIDE.md#endpoint-and-credentials)).

The [guide's full demo](https://github.com/caprids/relay-sdks/blob/main/ts/GUIDE.md#run-the-full-demo-from-a-checkout) runs the complete version of this, with
credential handling, the subscribe retry, a deadline, and cleanup on failure.

These connections never reconnect. For a live application, `connectSupervisedPublisher` and
`connectSupervisedSubscriber` reconnect with backoff. They keep tracks registered and subscribed
across connections and publisher restarts ([stay connected](https://github.com/caprids/relay-sdks/blob/main/ts/GUIDE.md#stay-connected-supervised-connections)).

## Node

`@caprid/relay/node` exports everything `@caprid/relay` does. Its `connectRelayPublisher`,
`connectRelaySubscriber`, and both `connectSupervised*` functions connect over native QUIC through
[`@caprid/relay-quic`](https://github.com/caprids/relay-sdks/blob/main/ts/packages/relay-quic/README.md), Mux's transport adapter for Node. Native QUIC
means MOQT directly on QUIC, with ALPN `moqt-22` and a fallback to `moqt-20`. Pass the same
`https://` endpoint you pass in a browser.

### Install

```sh
npm install @caprid/relay @caprid/relay-quic
```

`@caprid/relay-quic` is an optional peer dependency that only `@caprid/relay/node` needs. It lists
a prebuilt native binary for each supported platform as an optional dependency, and your package
manager installs the one that matches ([its README](https://github.com/caprids/relay-sdks/blob/main/ts/packages/relay-quic/README.md#prebuilt-binaries)).
Without the package or the binary, a connect call fails with `RelayError` code
`TRANSPORT_UNAVAILABLE`.

### Publish and subscribe

The code is the browser example's, with a different import:

```ts
import { connectRelayPublisher, connectRelaySubscriber, readText } from '@caprid/relay/node';

const namespace = ['quickstart', 'unique-run'];
const getToken = () => process.env.RELAY_TOKEN;

const publisher = await connectRelayPublisher({ namespace, getToken });
const ticker = publisher.registerTrack({ track: 'ticker' });
await publisher.announced;

const subscriber = await connectRelaySubscriber({ getToken });
const subscription = subscriber.subscribe(
  { namespace, track: 'ticker' },
  { onObject: (object) => { if (object.status === 'normal') console.log(readText(object)); } },
);
await subscription.subscribed;

await ticker.writeText('hello from Node');
```

### Trust

By default a connection trusts the platform's root certificates. Two options change that, on
either connect function:

- `ca` adds extra trust anchors to the platform's roots. Pass PEM as a string, as bytes, or as an
  array of either. Use it for a relay behind a private certificate authority.
- `serverCertificateHashes` takes `[{ algorithm: 'sha-256', value }]`, in the W3C WebTransport
  shape. The server's certificate must match one hash, and the adapter does not validate its chain.

```ts
import { readFile } from 'node:fs/promises';

const subscriber = await connectRelaySubscriber({ url, getToken, ca: await readFile('relay-ca.pem') });
```

Connections without trust options share one adapter. Passing `createTransport` replaces the adapter
entirely. Passing it together with `ca` or `serverCertificateHashes` throws a `TypeError`. To combine
them, build the factory yourself with `nativeQuicTransport({ ca })` from `@caprid/relay-quic`.

### Process lifetime

An open connection keeps the Node process alive from the start of the dial until the connection
has closed. After that the adapter holds nothing, so a script exits by itself once it has closed
its publisher and subscriber ([TA-25, TA-26](https://github.com/caprids/relay-sdks/blob/main/spec/transport-adapter.md)). A script that stops
early without closing them keeps running.

### Native WebTransport

`@caprid/relay/node` uses `@caprid/relay-quic` even where a global `WebTransport` exists. If Node gains
a `WebTransport` that can negotiate protocols, the first connect emits a `DeprecationWarning`
(code `CAPRID_RELAY_NODE_NATIVE_WEBTRANSPORT`) through `process.emitWarning`, once per process. At
that point `@caprid/relay` connects on its own, and this entry point will be retired.

### Deno

Deno support is best effort and untested. Deno can load the entry as `npm:@caprid/relay/node` and
the addon through its npm compatibility layer. Deno's own `WebTransport` cannot negotiate protocols,
so plain `@caprid/relay` fails there with `TRANSPORT_UNAVAILABLE`.

## Stability

Before 1.0, a minor version can break the API, and [CHANGELOG.md](https://github.com/caprids/relay-sdks/blob/main/ts/CHANGELOG.md) says what changed.
`@caprid/relay`, `@caprid/moq-core`, `@caprid/moq-media`, `@caprid/moq-mediabunny`,
`@caprid/relay-quic`, and `@caprid/relay-websocket` always release together at the same version, so
install matching versions. A release supports:

- browsers with WebTransport, and Node 24 or later on the platforms `@caprid/relay-quic` ships
  binaries for (Linux x64 and arm64 with glibc or musl, macOS arm64 and x64, Windows x64).
- MOQT draft 22 (`moqt-22`), with draft 20 (`moqt-20`) as an offered fallback, against moq-relay
  0.15.4 or later.

## Documentation

- [TypeScript guide](https://github.com/caprids/relay-sdks/blob/main/ts/GUIDE.md): the quickstart, subscription and publishing lifecycles, async
  consumption, and publishing and playing media.
- API reference: run `mise run ts:docs` from a checkout to build it from the source into
  `ts/docs-api/`.
- [Errors and recovery](https://github.com/caprids/relay-sdks/blob/main/ts/GUIDE.md#errors-and-recovery): stable `RelayError` codes and phases, and
  how to recover without assuming delivery or safe replay.
- [Shared specification](https://github.com/caprids/relay-sdks/blob/main/spec/README.md): the protocol pins, behavior contract, and test vectors
  every Relay SDK implements.
