trellis
Version:
Agentic State Engine — event-sourced causal graph with branching, decision traces, and realtime sync for AI-native applications
96 lines • 3.8 kB
TypeScript
/**
* Browser transport for a hosted relay room — the paid-tier accelerator.
*
* Functionally a {@link WebSocketRelayTransport} with three additions a hosted
* deployment needs: a room-aware URL convention, an auth token, and automatic
* reconnect with backoff. The server it talks to is the same dumb fan-out +
* disposable replay contract as {@link createRealtimeRelay} — whether that
* server is a Cloudflare Durable Object, a self-hosted relay, or PartyKit is
* irrelevant to this code. The name reflects the *recommended* hosted target
* (DO: single-writer-per-object matches Trellis's per-graph causality), not a
* hard dependency on it.
*
* Telos alignment: this is one swappable {@link RealtimeTransport} behind the
* stable seam. Local/free presence uses {@link BroadcastChannelTransport}; this
* is what you inject when "sync" is enabled. An Iroh gossip transport slots in
* the same way later with zero client change.
*
* new DurableObjectRelayTransport({
* id: peerId,
* url: 'wss://relay.example.com',
* room: 'doc:42',
* auth: sessionToken,
* });
*
* Reconnect self-heals presence for free: the room's heartbeat re-announces
* local presence after the socket comes back, and this transport re-sends a
* `hello` on every (re)open so the relay replays peers/chat tail immediately.
*/
import type { RealtimeMessage, RealtimeTransport } from './types.js';
interface SocketLike {
readyState: number;
send(data: string): void;
close(): void;
addEventListener(type: string, listener: (event: unknown) => void): void;
}
type SocketCtor = new (url: string) => SocketLike;
export interface DurableObjectRelayReconnect {
/** Max reconnect attempts after an unexpected close. 0 = unlimited. Default 0. */
maxAttempts?: number;
/** Initial backoff (ms). Default 500. */
baseDelayMs?: number;
/** Max backoff (ms). Default 10_000. */
maxDelayMs?: number;
}
export interface DurableObjectRelayTransportOptions {
/** Local peer identity. */
id: string;
/**
* Base relay URL, e.g. `wss://relay.example.com` or
* `wss://x.partykit.dev/parties/main`. The room segment and auth token are
* appended per {@link buildUrl} unless you pass a fully-formed URL and omit
* `room`/`auth`.
*/
url: string;
/** Logical room id, appended as a path segment (`/{room}`) when set. */
room?: string;
/** Auth token, appended as `?token=` (or `&token=`) when set. */
auth?: string;
/**
* Override URL assembly entirely (e.g. a different DO routing convention).
* Receives the constructor options; returns the final `ws(s)://…` URL.
*/
buildUrl?: (opts: {
url: string;
room?: string;
auth?: string;
}) => string;
/** Reconnect policy. Pass `false` to disable reconnect. */
reconnect?: DurableObjectRelayReconnect | false;
/** Cap on frames buffered while disconnected (oldest dropped). Default 128. */
maxPending?: number;
/** Injectable WebSocket for tests / non-browser hosts. */
WebSocketImpl?: SocketCtor;
}
export declare class DurableObjectRelayTransport implements RealtimeTransport {
readonly id: string;
private readonly url;
private readonly WS;
private readonly reconnect;
private readonly maxPending;
private ws;
private handlers;
private pending;
private closed;
private attempts;
private reconnectTimer;
constructor(opts: DurableObjectRelayTransportOptions);
send(message: RealtimeMessage): void;
onMessage(handler: (message: RealtimeMessage) => void): () => void;
close(): void;
private open;
private scheduleReconnect;
private flushPending;
}
export {};
//# sourceMappingURL=durable-object-relay-transport.d.ts.map