function trimTrailingSlashes(value: string): string { return value.replace(/\/+$/, ''); } export type AppVariant = 'development' | 'staging' | 'production'; function resolveAppVariant(): AppVariant { const raw = process.env.EXPO_PUBLIC_APP_VARIANT; if (raw === 'development' || raw === 'staging' || raw === 'production') { return raw; } if (__DEV__) { return 'development'; } return 'production'; } /** Shown on About screen for dev/staging builds only. */ export function shouldShowAboutBackendUrl(variant: AppVariant = appVariant): boolean { return variant === 'development' || variant === 'staging'; } export const appVariant = resolveAppVariant(); export const config = { apiBaseUrl: trimTrailingSlashes( process.env.EXPO_PUBLIC_API_URL ?? 'http://192.168.10.151:8000', ), wsBaseUrl: trimTrailingSlashes( process.env.EXPO_PUBLIC_WS_URL ?? 'ws://192.168.10.151:8000', ), isDebugMode: __DEV__, appVariant, showAboutBackendUrl: shouldShowAboutBackendUrl(), api: { timeoutMs: 30_000, refreshPath: '/api/auth/refresh', }, ws: { reconnectMaxRetries: 10, reconnectBaseDelayMs: 1_000, reconnectMaxDelayMs: 30_000, heartbeatIntervalMs: 30_000, /** * 仅当 App 处于 `background` 连续超过该毫秒数才释放当前会话 WebSocket。 * 短暂切到其它应用再返回时保持连接,避免反复重连。 */ backgroundDisconnectAfterMs: 300_000, }, } as const;