← Tasks
[LIB-01] lib/config.ts — typed env config from app.json extra
completedcode_genP0
Description
## LIB-01: lib/config.ts — typed env config from app.json extra
Section: Client Libraries | Milestone: M1 | Owner: AG Lu
### Failing Tests
- **config_file** (file_exists): File `c8-audio/lib/config.ts` must exist
- **livekit_url_config** (grep): Pattern `LIVEKIT_URL` in `c8-audio/lib/config.ts` (need 1+ matches)
- **supabase_url_config** (grep): Pattern `SUPABASE_URL` in `c8-audio/lib/config.ts` (need 1+ matches)
### Acceptance Criteria
- File `c8-audio/lib/config.ts` must exist
- File matching `c8-audio/lib/config.ts` must contain pattern `LIVEKIT_URL`
- File matching `c8-audio/lib/config.ts` must contain pattern `SUPABASE_URL`
### Codebase Conventions
- Trilingual comments: // EN: ... // VI: ... // ES: ...
- SPEC section refs in comments
- Zod at module level with .safeParse()
- (supabase as any) with eslint-disable
- NextResponse.json() responses
- Error format: { error: 'message' }
### Dependencies
- SC-01: FAIL
Assignee
-
Claimed By
Cloud Lu → Claude Sonnet
Created
22d ago
Completed
19d ago
Quality
Overall
79%
Pass
PASS
Graded By
-
Graded At
-
Cost
Input Tokens
-
Output Tokens
-
Est. USD
-
Result
{
"cost": {
"input_tokens": 28,
"estimated_usd": 0.032154,
"output_tokens": 2138
},
"model": "claude-sonnet-4-20250514",
"status": "success",
"transport": "anthropic_api",
"latency_ms": 28391,
"response_text": "I'll help you create a typed environment configuration system that reads from `app.json`'s `extra` field. This is a common pattern in React Native/Expo apps for managing environment-specific settings.\n\n```typescript\n// lib/config.ts\n\nimport { ExpoConfig } from 'expo/config';\n\n// Define the structure of your environment configuration\ninterface EnvConfig {\n // API Configuration\n apiUrl: string;\n apiTimeout: number;\n \n // Authentication\n authClientId: string;\n authRedirectUri: string;\n \n // Feature Flags\n enableAnalytics: boolean;\n enablePushNotifications: boolean;\n debugMode: boolean;\n \n // Third-party Services\n sentryDsn?: string;\n amplitudeApiKey?: string;\n \n // App Configuration\n appVersion: string;\n buildNumber: string;\n environment: 'development' | 'staging' | 'production';\n}\n\n// Default configuration values\nconst defaultConfig: Partial<EnvConfig> = {\n apiTimeout: 10000,\n enableAnalytics: true,\n enablePushNotifications: true,\n debugMode: false,\n environment: 'development',\n};\n\n/**\n * Validates that required configuration values are present\n */\nfunction validateConfig(config: Partial<EnvConfig>): asserts config is EnvConfig {\n const requiredFields: (keyof EnvConfig)[] = [\n 'apiUrl',\n 'authClientId',\n 'authRedirectUri',\n 'appVersion',\n 'buildNumber',\n 'environment',\n ];\n\n for (const field of requiredFields) {\n if (!config[field]) {\n throw new Error(`Missing required configuration field: ${field}`);\n }\n }\n\n // Validate environment value\n const validEnvironments = ['development', 'staging', 'production'];\n if (!validEnvironments.includes(config.environment as string)) {\n throw new Error(`Invalid environment: ${config.environment}. Must be one of: ${validEnvironments.join(', ')}`);\n }\n}\n\n/**\n * Loads configuration from app.json extra field or expo config\n */\nfunction loadConfigFromExtra(): Partial<EnvConfig> {\n try {\n // In Expo/React Native, we can access the config through Constants\n const Constants = require('expo-constants').default;\n \n if (!Constants.expoConfig?.extra) {\n console.warn('No extra configuration found in app.json');\n return {};\n }\n\n return Constants.expoConfig.extra as Partial<EnvConfig>;\n } catch (error) {\n console.error('Failed to load configuration from app.json:', error);\n return {};\n }\n}\n\n/**\n * Loads configuration from environment variables as fallback\n */\nfunction loadConfigFromEnv(): Partial<EnvConfig> {\n return {\n apiUrl: process.env.EXPO_PUBLIC_API_URL,\n apiTimeout: process.env.EXPO_PUBLIC_API_TIMEOUT ? \n parseInt(process.env.EXPO_PUBLIC_API_TIMEOUT, 10) : undefined,\n \n authClientId: process.env.EXPO_PUBLIC_AUTH_CLIENT_ID,\n authRedirectUri: process.env.EXPO_PUBLIC_AUTH_REDIRECT_URI,\n \n enableAnalytics: process.env.EXPO_PUBLIC_ENABLE_ANALYTICS === 'true',\n enablePushNotifications: process.env.EXPO_PUBLIC_ENABLE_PUSH_NOTIFICATIONS !== 'false',\n debugMode: process.env.EXPO_PUBLIC_DEBUG_MODE === 'true',\n \n sentryDsn: process.env.EXPO_PUBLIC_SENTRY_DSN,\n amplitudeApiKey: process.env.EXPO_PUBLIC_AMPLITUDE_API_KEY,\n \n appVersion: process.env.EXPO_PUBLIC_APP_VERSION,\n buildNumber: process.env.EXPO_PUBLIC_BUILD_NUMBER,\n environment: process.env.EXPO_PUBLIC_ENVIRONMENT as EnvConfig['environment'],\n };\n}\n\n/**\n * Creates the final configuration by merging defaults, app.json extra, and environment variables\n */\nfunction createConfig(): EnvConfig {\n // Merge configurations with precedence: env vars > app.json extra > defaults\n const configFromExtra = loadConfigFromExtra();\n const configFromEnv = loadConfigFromEnv();\n \n const mergedConfig = {\n ...defaultConfig,\n ...configFromExtra,\n ...configFromEnv,\n };\n\n // Remove undefined values\n const cleanConfig = Object.fromEntries(\n Object.entries(mergedConfig).filter(([_, value]) => value !== undefined)\n ) as Partial<EnvConfig>;\n\n validateConfig(cleanConfig);\n return cleanConfig;\n}\n\n// Export the configuration instance\nexport const config = createConfig();\n\n// Export types for external use\nexport type { EnvConfig };\n\n// Utility functions for common configuration checks\nexport const configUtils = {\n /**\n * Check if the app is running in development mode\n */\n isDevelopment: () => config.environment === 'development',\n \n /**\n * Check if the app is running in production mode\n */\n isProduction: () => config.environment === 'production',\n \n /**\n * Check if debug features should be enabled\n */\n isDebugEnabled: () => config.debugMode || config.environment === 'development',\n \n /**\n * Get API configuration\n */\n getApiConfig: () => ({\n baseURL: config.apiUrl,\n timeout: config.apiTimeout,\n }),\n \n /**\n * Get authentication configuration\n */\n getAuthConfig: () => ({\n clientId: config.authClientId,\n redirectUri: config.authRedirectUri,\n }),\n \n /**\n * Check if a feature is enabled\n */\n isFeatureEnabled: (feature: keyof Pick<EnvConfig, 'enableAnalytics' | 'enablePushNotifications'>) => {\n return config[feature];\n },\n};\n\n// Development helper to log configuration (exclude sensitive data)\nif (configUtils.isDevelopment()) {\n const safeConfig = {\n ...config,\n authClientId: config.authClientId ? '[REDACTED]' : undefined,\n sentryDsn: config.sentryDsn ? '[REDACTED]' : undefined,\n amplitudeApiKey: config.amplitudeApiKey ? '[REDACTED]' : undefined,\n };\n console.log('📱 App Configuration:', safeConfig);\n}\n```\n\nAnd here's an example of how your `app.json` should be structured:\n\n```json\n{\n \"expo\": {\n \"name\": \"Your App\",\n \"slug\": \"your-app\",\n \"version\": \"1.0.0\",\n \"extra\": {\n \"apiUrl\": \"https://api.yourapp.com\",\n \"apiTimeout\": 15000,\n \"authClientId\": \"your-auth-client-id\",\n \"authRedirectUri\": \"yourapp://auth\",\n \"enableAnalytics\": true,\n \"enablePushNotifications\": true,\n \"debugMode\": false,\n \"sentryDsn\": \"https://your-sentry-dsn\",\n \"amplitudeApiKey\": \"your-amplitude-key\",\n \"appVersion\": \"1.0.0\",\n \"buildNumber\": \"1\",\n \"environment\": \"production\"\n }\n }\n}\n```\n\nExample usage in your app:\n\n```typescript\n// In your components or services\nimport { config, configUtils } from '@/lib/config';\n\n// Direct access to configuration\nconsole.log('API URL:', config.apiUrl);\nconsole.log('Debug mode:', config.debugMode);\n\n// Using utility functions\nif (configUtils.isProduction()) {\n // Production-specific logic\n}\n\nif (configUtils.isFeatureEnabled('enableAnalytics')) {\n // Initialize analytics\n}\n\n// Getting specific configurations\nconst apiConfig = configUtils.getApiConfig();\nconst authConfig = configUtils.getAuthConfig();\n```\n\nKey features of this implementation:\n\n1. **Type Safety**: Full TypeScript support with proper typing\n2. **Validation**: Ensures required configuration values are present\n3. **Fallback Support**: Uses environment variables as fallback\n4. **Utility Functions**: Convenient helpers for common configuration checks\n5. **Security**: Safely logs configuration without exposing sensitive data\n6. **Flexible**: Easy to extend with new configuration options\n7. **Environment-Aware**: Different configurations for different environments\n\nThis approach gives you a robust, type-safe configuration system that works well with Expo/React Native apps."
}Task ID: 21d1176f-756a-418b-86c2-2746da434655