# @radist-tech/client

> Browser SDK for connecting with participant tokens, managing the WebRTC session lifecycle, and exchanging encrypted media or data between participants.

```sh
npm install @radist-tech/client
```

Browser only. Never give this package a secret key — it takes a public key plus a token your backend issued.

## Constructor

```ts
import { RadistClient } from '@radist-tech/client';

const radist = new RadistClient({ publicKey: 'rad_pk_...' });
```

| Option             | Type                         | Description                                                                                                                      |
| ------------------ | ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `publicKey`        | `string`                     | Required project public key used for API and WebSocket authentication.                                                           |
| `apiBaseUrl`       | `string`                     | Optional Radist API origin. Defaults to `https://radist.tech`; a relative value such as `/api` resolves against the page origin. |
| `rtcConfiguration` | `RTCConfiguration`           | Optional `RTCPeerConnection` config, such as custom STUN or TURN servers.                                                        |
| `fetch`            | `FetchLike`                  | Optional fetch override used by space metadata and admission requests.                                                           |
| `webSocketFactory` | `(url: string) => WebSocket` | Optional WebSocket factory for tests, wrappers, or custom integrations.                                                          |

## API surface

| Symbol                                                | Returns                         | Description                                                                                  |
| ----------------------------------------------------- | ------------------------------- | -------------------------------------------------------------------------------------------- |
| `call(callToken).connect({ channels })`               | `Promise<RadistConnection>`     | Connects to a P2P call using a server-issued participant token.                              |
| `room(roomToken).connect({ channels? })`              | `Promise<RadistRoomConnection>` | Connects to a multi-party SFU room using a server-issued room token.                         |
| `space(slug).info()`                                  | `Promise<SpaceInfo>`            | Reads safe space metadata including its name and admission type.                             |
| `space(slug).connect({ name, password?, channels? })` | `Promise<RadistRoomConnection>` | Runs persistent-space admission, then connects through the ordinary room API.                |
| `connection.on(event, listener)`                      | `() => void`                    | Subscribes to a connection event and returns an unsubscribe function.                        |
| `connection.off(event, listener)`                     | `void`                          | Removes a listener registered with `on()`.                                                   |
| `connection.reconnect()`                              | `Promise<void>`                 | Reconnects an interrupted session using the stored `callId`, `peerId`, and `reconnectToken`. |
| `connection.disconnect()`                             | `void`                          | Sends a leave message when possible and closes local session state.                          |
| `RadistApiError`                                      | `Error` with `status`           | Thrown when an API request or the initial signaling handshake is unsuccessful.               |

## Channels

Pass the channels you want to `connect()`. `audio` and `video` trigger `getUserMedia`; `data` creates a managed `RTCDataChannel` labeled `radist-data`.

| Channel | Use case                                                    |
| ------- | ----------------------------------------------------------- |
| `audio` | Microphone streams. Emits `localstream` and `remotestream`. |
| `video` | Camera streams.                                             |
| `data`  | Messages and app state, as text, JSON, or binary payloads.  |

## Connection events

| Event          | Payload             | Description                                                             |
| -------------- | ------------------- | ----------------------------------------------------------------------- |
| `statechange`  | `{ state, status }` | Lifecycle updates such as connecting, waiting-for-peer, or connected.   |
| `localstream`  | `{ stream }`        | Emitted after `getUserMedia` succeeds for audio or video channels.      |
| `remotestream` | `{ stream }`        | Emitted when the remote `MediaStream` becomes available.                |
| `datachannel`  | `{ channel }`       | Emitted when the managed `RTCDataChannel` has been created or received. |
| `error`        | `{ error, code? }`  | Raised for signaling, transport, or protocol errors.                    |

## Connection properties

| Property                                             | Type                                                                                     | Description                                                                   |
| ---------------------------------------------------- | ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| `state`                                              | `idle \| connecting \| waiting-for-peer \| connected \| disconnected \| closed \| error` | Current lifecycle state.                                                      |
| `status`                                             | `string`                                                                                 | Human-readable status text for logs and UI.                                   |
| `role`                                               | `host \| guest \| null`                                                                  | Peer role, assigned by arrival order: the first peer in the call is the host. |
| `callId`, `peerId`, `remotePeerId`, `reconnectToken` | `string \| null`                                                                         | Session identity values populated after the handshake succeeds.               |
| `canReconnect`                                       | `boolean`                                                                                | True when an interrupted session still has reconnect metadata.                |
| `peerConnection`                                     | `RTCPeerConnection \| null`                                                              | Underlying WebRTC peer connection.                                            |
| `localStream`, `remoteStream`                        | `MediaStream \| null`                                                                    | Current local and remote media streams.                                       |
| `dataChannel`                                        | `RTCDataChannel \| null`                                                                 | Managed Radist data channel labeled `radist-data`.                            |

## P2P example

```ts
const radist = new RadistClient({ publicKey: 'rad_pk_...' });

const { callToken } = await fetch('/api/radist/session').then((r) => r.json());

const connection = await radist.call(callToken).connect({
	channels: ['audio', 'video', 'data']
});

connection.on('statechange', ({ state, status }) => console.log(state, status));
connection.on('remotestream', ({ stream }) => {
	document.querySelector('video#remote').srcObject = stream;
});
connection.on('datachannel', ({ channel }) => {
	channel.addEventListener('message', (event) => console.log(event.data));
});
```

`connect()` resolves as soon as the session exists. For the peer who created the call that happens _before_ anyone else joins, so the connection is live but idle — read `connection.role` and the current `state` right after it resolves rather than assuming a listener already fired.

## Room example

```ts
const room = await radist.room(roomToken).connect({ channels: ['audio', 'video'] });

room.on('participantstream', ({ peerId, stream }) => console.log(peerId, stream));
room.on('participantleft', ({ peerId }) => console.log('left', peerId));
```

## Space example

```ts
const space = radist.space('daily-standup');
const { accessType } = await space.info();

const room = await space.connect({
	name: 'Alice',
	password: accessType === 'password' ? password : undefined,
	channels: ['audio', 'video']
});
```
