From 9df9966381ee1a444f8f0756af065db779d600fb Mon Sep 17 00:00:00 2001 From: Bas van den Aakster Date: Fri, 7 Nov 2025 23:45:52 +0100 Subject: [PATCH] Replace origin override with global option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove origin parameter from connect() method - Add ConnectOptions interface with global flag - When global: true, sends X-Rondevu-Global header instead of trying to override Origin - Update client methods to accept customHeaders parameter - Pass custom headers through connection polling and ICE candidate exchange - Bump version to 0.1.0 This change works around browser restriction on Origin header modification. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- package.json | 2 +- src/client.ts | 15 ++++++++++----- src/connection.ts | 8 +++++--- src/rondevu.ts | 31 ++++++++++++++----------------- src/types.ts | 8 ++++++++ 5 files changed, 38 insertions(+), 26 deletions(-) diff --git a/package.json b/package.json index 92733db..aee7ed3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@xtr-dev/rondevu-client", - "version": "0.0.6", + "version": "0.1.0", "description": "TypeScript client for Rondevu peer signaling and discovery server", "type": "module", "main": "dist/index.js", diff --git a/src/client.ts b/src/client.ts index ee5aed1..956cc12 100644 --- a/src/client.ts +++ b/src/client.ts @@ -37,13 +37,15 @@ export class RondevuClient { */ private async request( endpoint: string, - options: RequestInit = {} + options: RequestInit = {}, + customHeaders?: Record ): Promise { const url = `${this.baseUrl}${endpoint}`; const headers: Record = { 'Origin': this.origin, ...(options.headers as Record), + ...(customHeaders || {}), }; if (options.body) { @@ -142,6 +144,7 @@ export class RondevuClient { * Sends an answer or candidate to an existing session * * @param request - Answer details including session code and signaling data + * @param customHeaders - Optional custom headers to send with the request * @returns Success confirmation * * @example @@ -163,11 +166,11 @@ export class RondevuClient { * }); * ``` */ - async sendAnswer(request: AnswerRequest): Promise { + async sendAnswer(request: AnswerRequest, customHeaders?: Record): Promise { return this.request('/answer', { method: 'POST', body: JSON.stringify(request), - }); + }, customHeaders); } /** @@ -175,6 +178,7 @@ export class RondevuClient { * * @param code - Session UUID * @param side - Which side is polling ('offerer' or 'answerer') + * @param customHeaders - Optional custom headers to send with the request * @returns Session data including offers, answers, and candidates * * @example @@ -194,13 +198,14 @@ export class RondevuClient { */ async poll( code: string, - side: Side + side: Side, + customHeaders?: Record ): Promise { const request: PollRequest = { code, side }; return this.request('/poll', { method: 'POST', body: JSON.stringify(request), - }); + }, customHeaders); } /** diff --git a/src/connection.ts b/src/connection.ts index 6c1175e..964677e 100644 --- a/src/connection.ts +++ b/src/connection.ts @@ -21,8 +21,9 @@ export class RondevuConnection extends EventEmitter { private connectionTimer?: ReturnType; private isPolling: boolean = false; private isClosed: boolean = false; + private customHeaders?: Record; - constructor(params: RondevuConnectionParams, client: RondevuClient) { + constructor(params: RondevuConnectionParams, client: RondevuClient, customHeaders?: Record) { super(); this.id = params.id; this.topic = params.topic; @@ -34,6 +35,7 @@ export class RondevuConnection extends EventEmitter { this.dataChannels = new Map(); this.pollingIntervalMs = params.pollingInterval; this.connectionTimeoutMs = params.connectionTimeout; + this.customHeaders = customHeaders; this.setupEventHandlers(); this.startConnectionTimeout(); @@ -119,7 +121,7 @@ export class RondevuConnection extends EventEmitter { code: this.id, candidate: JSON.stringify(candidate.toJSON()), side: this.role, - }); + }, this.customHeaders); } catch (err: any) { throw new Error(`Failed to send ICE candidate: ${err.message}`); } @@ -169,7 +171,7 @@ export class RondevuConnection extends EventEmitter { } try { - const response = await this.client.poll(this.id, this.role); + const response = await this.client.poll(this.id, this.role, this.customHeaders); if (this.role === 'offerer') { const offererResponse = response as { answer: string | null; answerCandidates: string[] }; diff --git a/src/rondevu.ts b/src/rondevu.ts index 059fcd2..fbff01c 100644 --- a/src/rondevu.ts +++ b/src/rondevu.ts @@ -1,6 +1,6 @@ import { RondevuClient } from './client'; import { RondevuConnection } from './connection'; -import { RondevuOptions, JoinOptions, RondevuConnectionParams } from './types'; +import { RondevuOptions, JoinOptions, ConnectOptions, RondevuConnectionParams } from './types'; /** * Main Rondevu WebRTC client with automatic connection management @@ -100,21 +100,17 @@ export class Rondevu { /** * Connect to an existing connection by ID (answerer role) * @param id - Connection identifier - * @param origin - Optional origin header override for this connection + * @param options - Optional connection options (e.g., { global: true } for global origin) * @returns Promise that resolves to RondevuConnection */ - async connect(id: string, origin?: string): Promise { - // Create a client with overridden origin if specified - const client = origin - ? new RondevuClient({ - baseUrl: this.baseUrl, - origin, - fetch: this.fetchImpl, - }) - : this.client; + async connect(id: string, options?: ConnectOptions): Promise { + // Build custom headers if global option is set + const customHeaders = options?.global + ? { 'X-Rondevu-Global': 'true' } + : undefined; // Poll server to get session by ID - const sessionData = await this.findSessionByIdWithClient(id, client); + const sessionData = await this.findSessionByIdWithClient(id, this.client, customHeaders); if (!sessionData) { throw new Error(`Connection ${id} not found or expired`); @@ -137,11 +133,11 @@ export class Rondevu { await this.waitForIceGathering(pc); // Send answer to server - await client.sendAnswer({ + await this.client.sendAnswer({ code: id, answer: pc.localDescription!.sdp, side: 'answerer', - }); + }, customHeaders); // Create connection object const connectionParams: RondevuConnectionParams = { @@ -155,7 +151,7 @@ export class Rondevu { connectionTimeout: this.connectionTimeout, }; - const connection = new RondevuConnection(connectionParams, client); + const connection = new RondevuConnection(connectionParams, this.client, customHeaders); // Start polling for ICE candidates connection.startPolling(); @@ -254,7 +250,8 @@ export class Rondevu { */ private async findSessionByIdWithClient( id: string, - client: RondevuClient + client: RondevuClient, + customHeaders?: Record ): Promise<{ code: string; peerId: string; @@ -264,7 +261,7 @@ export class Rondevu { try { // Try to poll for the session directly // The poll endpoint should return the session data - const response = await client.poll(id, 'answerer'); + const response = await client.poll(id, 'answerer', customHeaders); const answererResponse = response as { offer: string; offerCandidates: string[] }; if (answererResponse.offer) { diff --git a/src/types.ts b/src/types.ts index 58543a5..e785565 100644 --- a/src/types.ts +++ b/src/types.ts @@ -191,6 +191,14 @@ export interface RondevuOptions { connectionTimeout?: number; } +/** + * Options for connecting to a session + */ +export interface ConnectOptions { + /** Use global origin (https://ronde.vu) instead of request origin for session isolation */ + global?: boolean; +} + /** * Options for joining a topic */