/** * config.js — Connection configuration for The Matrix. * * Override at deploy time via URL query params: * ?ws=ws://tower:8080/ws/world-state — WebSocket endpoint * ?token=my-secret — Auth token (Phase 1 shared secret) * ?mock=true — Force mock mode (no real WS) * * Or via Vite env vars: * VITE_WS_URL — WebSocket endpoint * VITE_WS_TOKEN — Auth token * VITE_MOCK_MODE — 'true' to force mock mode * * Priority: URL params > env vars > defaults. * * Resolves Issue #7 — js/config.js * Resolves Issue #11 — WS authentication strategy (Phase 1: shared secret) */ const params = new URLSearchParams(window.location.search); function param(name, envKey, fallback) { return params.get(name) ?? (import.meta.env[envKey] || null) ?? fallback; } export const Config = Object.freeze({ /** WebSocket endpoint. Empty string = no live connection (mock mode). */ wsUrl: param('ws', 'VITE_WS_URL', ''), /** Auth token appended as ?token= query param on WS connect (Issue #11). */ wsToken: param('token', 'VITE_WS_TOKEN', ''), /** Force mock mode even if wsUrl is set. Useful for local dev. */ mockMode: param('mock', 'VITE_MOCK_MODE', 'false') === 'true', /** Reconnection timing */ reconnectBaseMs: 2000, reconnectMaxMs: 30000, /** Heartbeat / zombie detection */ heartbeatIntervalMs: 30000, heartbeatTimeoutMs: 5000, /** * Computed: should we use the real WebSocket client? * True when wsUrl is non-empty AND mockMode is false. */ get isLive() { return this.wsUrl !== '' && !this.mockMode; }, /** * Build the final WS URL with auth token appended as a query param. * Returns null if not in live mode. * * Result: ws://tower:8080/ws/world-state?token=my-secret */ get wsUrlWithAuth() { if (!this.isLive) return null; const url = new URL(this.wsUrl); if (this.wsToken) { url.searchParams.set('token', this.wsToken); } return url.toString(); }, });