Skip to main content

TypeScript

Freehop includes declarations for all public package entry points starting with 0.1.0-alpha.4. Your editor can complete options, infer event payloads and catch invalid calls. The runtime is still JavaScript, so existing JavaScript integrations keep working.

Install the typed release​

The typed alpha is available on npm and GitHub Releases. Install the current alpha:

npm install freehop@alpha

To pin the current release, use npm install freehop@0.1.0-alpha.6.

No separate @types/freehop package is needed. Use a current TypeScript compiler and moduleResolution: "Bundler" in a browser build or "NodeNext" in an ESM Node project. Browser code needs the DOM library. Backend entry points work without it.

Connect with inferred types​

call.ts
import { connect, type ConnectOptions, type Session, type Ticket } from 'freehop';

const options = {
media: { audio: true, video: true },
adaptiveVideo: true,
} satisfies ConnectOptions;

export async function joinCall(ticket: Ticket, video: HTMLVideoElement): Promise<Session> {
const session = await connect(ticket, options);

session.on('track', ({ track }) => {
// track is a MediaStreamTrack, inferred from the event name.
session.attach(track, video);
});

await session.send({ type: 'ready', ready: true });
return session;
}

Explicit annotations are optional. connect() already returns Promise<Session>; type Ticket, ConnectOptions, RoomEvents and the stats types are available when you need them for your application's own interfaces.

Opt-in routes are typed too​

import { connect, type ConnectOptions, type NatInfo, type TurnServer } from 'freehop';

const turn: TurnServer[] = [{ urls: ['turn:turn.example.com:3478'], username: 'u', credential: 'c' }];
const session = await connect(ticket, { portPrediction: true, turn } satisfies ConnectOptions);
const nat: NatInfo | undefined = (await session.stats()).nat; // { type: 'eim' | 'sequential' | 'random' | 'unknown', delta }

turn and portPrediction are off unless you pass them. On the backend, createAuthority({ turn }) accepts the same TurnServer[] or a function that mints them per ticket.

Validate data at the boundary​

Types describe your code; they do not authenticate incoming data. Message payloads are unknown until your application validates them:

session.on('message', ({ from, data }) => {
if (typeof data === 'string') {
console.log(from, data);
}
});

Outgoing send() payloads must be JSON-compatible. Plain application interfaces are supported; functions and bigint values are rejected by the compiler. Your app should still validate message shape and enforce its own permissions.

Use validTicket(value) to narrow an unknown value, or decodeTicket(text) to decode and validate a ticket string. Ticket handling is separate from authenticating the user.

Backend and Electron​

All the backend entry points are typed: freehop/authority, /gate, /host, /relay, /member, /turn, /port-mapper, /stun and /tokens. The /ticket entry point is also usable without browser APIs. /electron describes the main-process helper; /electron/preload is a side-effect-only preload.

hostSession() returns a union. Check hosted.available before accessing hosted.gateway or calling hosted.drop(). Gate authorization callbacks receive untrusted hello fields; narrow hello.auth before using it.

The optional DesktopGateway type describes the preload bridge. Freehop discovers window.freehopGateway at runtime; importing the SDK does not add global declarations to your application.

Checks in this repository​

npm run typesgate compiles strict browser consumers with both Bundler and NodeNext resolution, then a Node-only consumer without DOM types. It also compares declared value exports with the actual runtime exports. Invalid-use assertions ensure that unknown events, invalid media options and incompatible messages remain compiler errors. These checks run in CI and in npm run ship.