import type {ForwardServerOptions, RelayLimits} from '../types.ts'; export const DEFAULT_LIMITS: Readonly = Object.freeze({ auth_timeout_ms: 10_000, close_timeout_ms: 5_000, rpc_timeout_ms: 5_000, unused_timeout_ms: 60_000, idle_timeout_ms: 30_000, max_token_age_ms: 300_000, join_window_ms: 60_000, clock_skew_ms: 5_000, max_lease_ms: 300_000, max_token_bytes: 8_192, max_payload_bytes: 8 * 1024 * 1024, max_chat_length: 256, max_connections: 2_000, max_sessions: 500, max_grants_per_session: 32, max_pending_per_web: 16, max_pending_total: 4_096, requests_per_second: 30, events_per_second: 200, max_dedupe_entries: 256, dedupe_ttl_ms: 30_000, max_buffered_packets: 128, }); export function resolve_options(options: ForwardServerOptions) { if (!options || !Number.isSafeInteger(options.forward_id) || options.forward_id < 0) { throw new Error('forward_id must be a nonnegative safe integer'); } if (typeof options.key !== 'string' || options.key.trim().length < 16) { throw new Error('A relay secret of at least 16 characters is required'); } if (!Array.isArray(options.allowed_origins) || options.allowed_origins.some(origin => { try { const url = new URL(origin); return !['http:', 'https:'].includes(url.protocol) || url.origin !== origin; } catch { return true; } })) throw new Error('allowed_origins must contain explicit HTTP(S) origins'); if (options.allow_no_origin !== undefined && typeof options.allow_no_origin !== 'boolean') { throw new Error('allow_no_origin must be a boolean'); } const path = options.path ?? '/api/forward_server'; const namespace = options.namespace ?? '/'; if (!/^\/[A-Za-z0-9_/-]+$/.test(path) || path === '/' || path.endsWith('/')) { throw new Error('path must be an absolute Socket.IO path without a trailing slash'); } if (!/^\/[A-Za-z0-9_/-]*$/.test(namespace) || (namespace !== '/' && namespace.endsWith('/'))) { throw new Error('namespace must be an absolute Socket.IO namespace'); } const port = options.port ?? 38858; if (!Number.isInteger(port) || port < 0 || port > 65535) throw new Error('Invalid relay port'); if (options.http_server && (options.port !== undefined || options.host !== undefined)) { throw new Error('Borrowed HTTP server cannot specify a relay host or port'); } const limits = {...DEFAULT_LIMITS, ...options.limits}; for (const [name, value] of Object.entries(limits)) { if (!Object.hasOwn(DEFAULT_LIMITS, name) || !Number.isSafeInteger(value) || value < (name === 'clock_skew_ms' ? 0 : 1) || value > 2_147_483_647) throw new Error(`Invalid relay limit: ${name}`); } if (limits.join_window_ms > limits.max_token_age_ms) throw new Error('Join window exceeds token lifetime'); return {...options, allowed_origins: [...options.allowed_origins], path, namespace, port, limits, allow_no_origin: options.allow_no_origin ?? true}; }