From 85e4e9ddaa5440a2226906b10323f4d6c83a7924 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Tue, 21 Oct 2025 18:15:05 +0200 Subject: [PATCH] Prompt is too long --- app/settings.tsx | 316 ++ .../voice-assistant/src/server/messages.ts | 37 + .../voice-assistant/src/server/session.ts | 75 +- .../src/ui/hooks/useWebSocket.ts | 2 +- packages/voice-mobile/EAS_SETUP.md | 120 + .../PHASE_2_AUDIO_IMPLEMENTATION.md | 427 ++ packages/voice-mobile/app.json | 32 +- packages/voice-mobile/app/(tabs)/_layout.tsx | 35 - packages/voice-mobile/app/(tabs)/explore.tsx | 112 - packages/voice-mobile/app/(tabs)/index.tsx | 98 - packages/voice-mobile/app/_layout.tsx | 24 +- packages/voice-mobile/app/audio-test.tsx | 299 ++ packages/voice-mobile/app/index.tsx | 939 ++++ packages/voice-mobile/app/modal.tsx | 29 - packages/voice-mobile/app/settings.tsx | 367 ++ .../components/active-processes.tsx | 169 + .../components/agent-stream-view.tsx | 286 ++ .../components/artifact-drawer.tsx | 126 + .../components/connection-status.tsx | 18 + .../components/conversation-selector.tsx | 270 + .../voice-mobile/components/external-link.tsx | 25 - .../voice-mobile/components/haptic-tab.tsx | 18 - .../voice-mobile/components/hello-wave.tsx | 19 - packages/voice-mobile/components/message.tsx | 288 ++ .../components/parallax-scroll-view.tsx | 79 - .../voice-mobile/components/themed-text.tsx | 60 - .../voice-mobile/components/themed-view.tsx | 14 - .../components/ui/collapsible.tsx | 45 - .../components/ui/icon-symbol.ios.tsx | 32 - .../components/ui/icon-symbol.tsx | 41 - .../voice-mobile/components/voice-button.tsx | 109 + packages/voice-mobile/eas.json | 22 + .../voice-mobile/hooks/use-audio-player.ts | 259 + .../voice-mobile/hooks/use-audio-recorder.ts | 277 ++ packages/voice-mobile/hooks/use-settings.ts | 78 + packages/voice-mobile/hooks/use-websocket.ts | 161 + packages/voice-mobile/package-lock.json | 4326 ++++++++++++++++- packages/voice-mobile/package.json | 14 +- packages/voice-mobile/tsconfig.json | 3 + 39 files changed, 8955 insertions(+), 696 deletions(-) create mode 100644 app/settings.tsx create mode 100644 packages/voice-mobile/EAS_SETUP.md create mode 100644 packages/voice-mobile/PHASE_2_AUDIO_IMPLEMENTATION.md delete mode 100644 packages/voice-mobile/app/(tabs)/_layout.tsx delete mode 100644 packages/voice-mobile/app/(tabs)/explore.tsx delete mode 100644 packages/voice-mobile/app/(tabs)/index.tsx create mode 100644 packages/voice-mobile/app/audio-test.tsx create mode 100644 packages/voice-mobile/app/index.tsx delete mode 100644 packages/voice-mobile/app/modal.tsx create mode 100644 packages/voice-mobile/app/settings.tsx create mode 100644 packages/voice-mobile/components/active-processes.tsx create mode 100644 packages/voice-mobile/components/agent-stream-view.tsx create mode 100644 packages/voice-mobile/components/artifact-drawer.tsx create mode 100644 packages/voice-mobile/components/connection-status.tsx create mode 100644 packages/voice-mobile/components/conversation-selector.tsx delete mode 100644 packages/voice-mobile/components/external-link.tsx delete mode 100644 packages/voice-mobile/components/haptic-tab.tsx delete mode 100644 packages/voice-mobile/components/hello-wave.tsx create mode 100644 packages/voice-mobile/components/message.tsx delete mode 100644 packages/voice-mobile/components/parallax-scroll-view.tsx delete mode 100644 packages/voice-mobile/components/themed-text.tsx delete mode 100644 packages/voice-mobile/components/themed-view.tsx delete mode 100644 packages/voice-mobile/components/ui/collapsible.tsx delete mode 100644 packages/voice-mobile/components/ui/icon-symbol.ios.tsx delete mode 100644 packages/voice-mobile/components/ui/icon-symbol.tsx create mode 100644 packages/voice-mobile/components/voice-button.tsx create mode 100644 packages/voice-mobile/eas.json create mode 100644 packages/voice-mobile/hooks/use-audio-player.ts create mode 100644 packages/voice-mobile/hooks/use-audio-recorder.ts create mode 100644 packages/voice-mobile/hooks/use-settings.ts create mode 100644 packages/voice-mobile/hooks/use-websocket.ts diff --git a/app/settings.tsx b/app/settings.tsx new file mode 100644 index 000000000..d99a3662c --- /dev/null +++ b/app/settings.tsx @@ -0,0 +1,316 @@ +import { useState, useEffect } from 'react'; +import { View, Text, TextInput, Switch, Pressable, ScrollView, Alert, ActivityIndicator } from 'react-native'; +import { router } from 'expo-router'; +import { StatusBar } from 'expo-status-bar'; +import { + AppSettings, + getSettings, + saveSettings, + resetSettings, + validateServerUrl, +} from '../lib/settings-storage'; + +export default function SettingsScreen() { + const [settings, setSettings] = useState(null); + const [serverUrl, setServerUrl] = useState(''); + const [useSpeaker, setUseSpeaker] = useState(false); + const [keepScreenOn, setKeepScreenOn] = useState(true); + const [theme, setTheme] = useState<'dark' | 'light' | 'auto'>('dark'); + const [urlError, setUrlError] = useState(null); + const [isSaving, setIsSaving] = useState(false); + const [isTesting, setIsTesting] = useState(false); + const [testResult, setTestResult] = useState<{ success: boolean; message: string } | null>(null); + + useEffect(() => { + loadSettings(); + }, []); + + async function loadSettings() { + const loaded = await getSettings(); + setSettings(loaded); + setServerUrl(loaded.serverUrl); + setUseSpeaker(loaded.useSpeaker); + setKeepScreenOn(loaded.keepScreenOn); + setTheme(loaded.theme); + } + + function validateUrl(url: string) { + const validation = validateServerUrl(url); + setUrlError(validation.valid ? null : validation.error || null); + return validation.valid; + } + + async function handleSaveSettings() { + if (!validateUrl(serverUrl)) { + return; + } + + setIsSaving(true); + try { + const newSettings: AppSettings = { + serverUrl, + useSpeaker, + keepScreenOn, + theme, + }; + await saveSettings(newSettings); + setSettings(newSettings); + Alert.alert('Success', 'Settings saved successfully'); + } catch (error) { + Alert.alert('Error', 'Failed to save settings'); + console.error('Save settings error:', error); + } finally { + setIsSaving(false); + } + } + + async function handleResetSettings() { + Alert.alert( + 'Reset Settings', + 'Are you sure you want to reset all settings to defaults?', + [ + { text: 'Cancel', style: 'cancel' }, + { + text: 'Reset', + style: 'destructive', + onPress: async () => { + const defaults = await resetSettings(); + setServerUrl(defaults.serverUrl); + setUseSpeaker(defaults.useSpeaker); + setKeepScreenOn(defaults.keepScreenOn); + setTheme(defaults.theme); + setSettings(defaults); + Alert.alert('Success', 'Settings reset to defaults'); + }, + }, + ] + ); + } + + async function handleTestConnection() { + if (!validateUrl(serverUrl)) { + return; + } + + setIsTesting(true); + setTestResult(null); + + try { + const ws = new WebSocket(serverUrl); + + const timeout = setTimeout(() => { + ws.close(); + setTestResult({ + success: false, + message: 'Connection timeout - server did not respond', + }); + setIsTesting(false); + }, 5000); + + ws.onopen = () => { + clearTimeout(timeout); + ws.close(); + setTestResult({ + success: true, + message: 'Connection successful', + }); + setIsTesting(false); + }; + + ws.onerror = () => { + clearTimeout(timeout); + setTestResult({ + success: false, + message: 'Connection failed - check URL and network', + }); + setIsTesting(false); + }; + } catch (error) { + setTestResult({ + success: false, + message: 'Failed to create connection', + }); + setIsTesting(false); + } + } + + if (!settings) { + return ( + + + + ); + } + + return ( + + + + {/* Header */} + + + Settings + router.back()} + className="px-4 py-2 rounded-lg bg-zinc-800 active:bg-zinc-700" + > + Done + + + + + + {/* Server Configuration */} + + + Server Configuration + + + + WebSocket URL + { + setServerUrl(text); + validateUrl(text); + setTestResult(null); + }} + placeholder="wss://your-server.com/ws" + placeholderTextColor="#71717a" + autoCapitalize="none" + autoCorrect={false} + keyboardType="url" + /> + {urlError && ( + {urlError} + )} + + {testResult && ( + + + {testResult.message} + + + )} + + + {isTesting ? ( + <> + + Testing... + + ) : ( + Test Connection + )} + + + + + {/* Audio Settings */} + + + Audio Settings + + + + + + Use Speaker + + Use phone speaker instead of earpiece + + + + + + + + Keep Screen On + + Prevent screen from sleeping during use + + + + + + + + {/* App Information */} + + + App Information + + + + + App Version + 1.0.0 + + + Audio Format + PCM 16kHz Mono + + + Current Server + + {settings.serverUrl.replace('wss://', '').replace('ws://', '')} + + + + + + {/* Actions */} + + + {isSaving ? ( + + ) : ( + + Save Settings + + )} + + + + + Reset to Defaults + + + + + {/* Bottom spacing */} + + + + ); +} diff --git a/packages/voice-assistant/src/server/messages.ts b/packages/voice-assistant/src/server/messages.ts index 77fc89d7e..9ca035039 100644 --- a/packages/voice-assistant/src/server/messages.ts +++ b/packages/voice-assistant/src/server/messages.ts @@ -30,12 +30,23 @@ export const LoadConversationRequestMessageSchema = z.object({ conversationId: z.string(), }); +export const ListConversationsRequestMessageSchema = z.object({ + type: z.literal("list_conversations_request"), +}); + +export const DeleteConversationRequestMessageSchema = z.object({ + type: z.literal("delete_conversation_request"), + conversationId: z.string(), +}); + export const SessionInboundMessageSchema = z.discriminatedUnion("type", [ UserTextMessageSchema, AudioChunkMessageSchema, AbortRequestMessageSchema, AudioPlayedMessageSchema, LoadConversationRequestMessageSchema, + ListConversationsRequestMessageSchema, + DeleteConversationRequestMessageSchema, ]); export type SessionInboundMessage = z.infer; @@ -189,6 +200,28 @@ export const SessionStateMessageSchema = z.object({ }), }); +export const ListConversationsResponseMessageSchema = z.object({ + type: z.literal("list_conversations_response"), + payload: z.object({ + conversations: z.array( + z.object({ + id: z.string(), + lastUpdated: z.string(), + messageCount: z.number(), + }) + ), + }), +}); + +export const DeleteConversationResponseMessageSchema = z.object({ + type: z.literal("delete_conversation_response"), + payload: z.object({ + conversationId: z.string(), + success: z.boolean(), + error: z.string().optional(), + }), +}); + export const SessionOutboundMessageSchema = z.discriminatedUnion("type", [ ActivityLogMessageSchema, AssistantChunkMessageSchema, @@ -201,6 +234,8 @@ export const SessionOutboundMessageSchema = z.discriminatedUnion("type", [ AgentUpdateMessageSchema, AgentStatusMessageSchema, SessionStateMessageSchema, + ListConversationsResponseMessageSchema, + DeleteConversationResponseMessageSchema, ]); export type SessionOutboundMessage = z.infer< @@ -219,6 +254,8 @@ export type AgentCreatedMessage = z.infer; export type AgentUpdateMessage = z.infer; export type AgentStatusMessage = z.infer; export type SessionStateMessage = z.infer; +export type ListConversationsResponseMessage = z.infer; +export type DeleteConversationResponseMessage = z.infer; // Type exports for payload types export type ActivityLogPayload = z.infer; diff --git a/packages/voice-assistant/src/server/session.ts b/packages/voice-assistant/src/server/session.ts index 387442850..3cf857726 100644 --- a/packages/voice-assistant/src/server/session.ts +++ b/packages/voice-assistant/src/server/session.ts @@ -15,7 +15,7 @@ import { getSystemPrompt } from "./agent/system-prompt.js"; import { getAllTools } from "./agent/llm-openai.js"; import { TTSManager } from "./agent/tts-manager.js"; import { STTManager } from "./agent/stt-manager.js"; -import { saveConversation } from "./persistence.js"; +import { saveConversation, listConversations, deleteConversation } from "./persistence.js"; import { experimental_createMCPClient } from "ai"; import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js"; import { createTerminalMcpServer } from "./terminal-mcp/index.js"; @@ -257,6 +257,14 @@ export class Session { case "load_conversation_request": await this.handleLoadConversation(); break; + + case "list_conversations_request": + await this.handleListConversations(); + break; + + case "delete_conversation_request": + await this.handleDeleteConversation(msg.conversationId); + break; } } catch (error: any) { console.error( @@ -292,6 +300,71 @@ export class Session { await this.sendSessionState(); } + /** + * List all conversations + */ + public async handleListConversations(): Promise { + try { + const conversations = await listConversations(); + this.emit({ + type: "list_conversations_response", + payload: { + conversations: conversations.map(conv => ({ + id: conv.id, + lastUpdated: conv.lastUpdated.toISOString(), + messageCount: conv.messageCount, + })), + }, + }); + } catch (error: any) { + console.error( + `[Session ${this.clientId}] Failed to list conversations:`, + error + ); + this.emit({ + type: "activity_log", + payload: { + id: uuidv4(), + timestamp: new Date(), + type: "error", + content: `Failed to list conversations: ${error.message}`, + }, + }); + } + } + + /** + * Delete a conversation + */ + public async handleDeleteConversation(conversationId: string): Promise { + try { + await deleteConversation(conversationId); + this.emit({ + type: "delete_conversation_response", + payload: { + conversationId, + success: true, + }, + }); + console.log( + `[Session ${this.clientId}] Deleted conversation ${conversationId}` + ); + } catch (error: any) { + console.error( + `[Session ${this.clientId}] Failed to delete conversation ${conversationId}:`, + error + ); + this.emit({ + type: "delete_conversation_response", + payload: { + conversationId, + success: false, + error: error.message, + }, + }); + } + } + /** * Send current session state (live agents and commands) to client */ diff --git a/packages/voice-assistant/src/ui/hooks/useWebSocket.ts b/packages/voice-assistant/src/ui/hooks/useWebSocket.ts index 6ecc8ac05..42e9f8779 100644 --- a/packages/voice-assistant/src/ui/hooks/useWebSocket.ts +++ b/packages/voice-assistant/src/ui/hooks/useWebSocket.ts @@ -19,7 +19,7 @@ export function useWebSocket(url: string, conversationId?: string | null): UseWe const [currentConversationId, setCurrentConversationId] = useState(null); const wsRef = useRef(null); const handlersRef = useRef void>>>(new Map()); - const reconnectTimeoutRef = useRef(); + const reconnectTimeoutRef = useRef(undefined); const connect = useCallback(() => { if (wsRef.current?.readyState === WebSocket.OPEN) { diff --git a/packages/voice-mobile/EAS_SETUP.md b/packages/voice-mobile/EAS_SETUP.md new file mode 100644 index 000000000..54a7c5630 --- /dev/null +++ b/packages/voice-mobile/EAS_SETUP.md @@ -0,0 +1,120 @@ +# EAS Update Setup Guide + +## Current Status + +✅ **Completed:** +- Installed `eas-cli` as dev dependency +- Created `eas.json` with update channels (production, preview, development) +- Configured `app.json` with runtime version policy + +⏳ **Manual Steps Required:** + +### 1. Initialize EAS Project + +Run the following command and follow the prompts: + +```bash +npx eas init +``` + +This will: +- Prompt you to log in to your Expo account (create one if needed at expo.dev) +- Create a new project in your Expo account +- Add a `projectId` to your `app.json` under `expo.extra.eas.projectId` + +### 2. Update the Updates URL + +After running `eas init`, you'll receive a project ID. Update the `updates.url` in `app.json`: + +```json +"updates": { + "url": "https://u.expo.dev/YOUR_PROJECT_ID_HERE" +} +``` + +Replace `YOUR_PROJECT_ID` with the actual project ID from the previous step. + +### 3. Build a New Development Build + +Since you're using a custom development client (`expo-dev-client`), you need to create a new build with EAS Update configured: + +```bash +# For Android +npx eas build --profile development --platform android + +# For iOS (requires Apple Developer account) +npx eas build --profile development --platform ios +``` + +Install this new build on your device. Your existing development build won't receive updates. + +## Publishing Updates + +Once setup is complete, publish updates on the go: + +### Production Update + +```bash +npx eas update --branch production --message "Fix critical bug" +``` + +Users on the production channel will receive this update. + +### Preview Update (for testing) + +```bash +npx eas update --branch preview --message "Test new feature" +``` + +### Development Update + +```bash +npx eas update --branch development --message "WIP changes" +``` + +## Update Channels Explained + +- **production**: For released apps in app stores +- **preview**: For internal testing before production +- **development**: For active development testing + +Configure which channel your app uses in `eas.json` or at build time. + +## Runtime Version Policy + +We're using `"policy": "appVersion"` which means: +- Updates only work within the same app version (1.0.0) +- If you change native code or dependencies, increment the version in `app.json` +- Users need a new build for native changes, but JS/React changes update via EAS + +## Quick Reference + +```bash +# Check update status +npx eas update:list --branch production + +# Roll back to previous update +npx eas update:rollback --branch production + +# View update details +npx eas update:view [update-id] +``` + +## Troubleshooting + +**Update not appearing?** +- Ensure device is on the same channel as the update +- Force close and reopen the app +- Check the update was published: `npx eas update:list` + +**"Runtime version mismatch" error?** +- Your app version doesn't match the update +- Rebuild the app with `eas build` + +## Next Steps + +1. Run `npx eas init` now +2. Update the `updates.url` in app.json with your project ID +3. Create a new development build with `npx eas build --profile development --platform android` +4. Install the new build on your device +5. Test publishing an update! diff --git a/packages/voice-mobile/PHASE_2_AUDIO_IMPLEMENTATION.md b/packages/voice-mobile/PHASE_2_AUDIO_IMPLEMENTATION.md new file mode 100644 index 000000000..0382d0350 --- /dev/null +++ b/packages/voice-mobile/PHASE_2_AUDIO_IMPLEMENTATION.md @@ -0,0 +1,427 @@ +# Phase 2: Audio Capture & Playback Implementation + +## Overview + +This document describes the implementation of audio recording and playback functionality for the voice-mobile React Native app using expo-audio. + +## Dependencies Installed + +### expo-audio (~1.0.13) +- Cross-platform audio recording and playback library +- Provides hooks: `useAudioRecorder` and `useAudioPlayer` +- Supports iOS, Android, and Web platforms +- Auto-configured via config plugin in app.json + +### expo-file-system (~19.0.17) +- File system operations for reading/writing audio files +- New API using `File` and `Paths` classes +- Required for converting between audio URIs and Blobs + +## Permissions Configuration + +### app.json Updates + +**iOS**: +```json +{ + "ios": { + "infoPlist": { + "NSMicrophoneUsageDescription": "This app needs access to the microphone for voice commands." + } + } +} +``` + +**Android**: +```json +{ + "android": { + "permissions": [ + "RECORD_AUDIO" + ] + } +} +``` + +**Plugins**: +```json +{ + "plugins": [ + "expo-audio" + ] +} +``` + +## File Structure + +``` +packages/voice-mobile/ +├── hooks/ +│ ├── use-audio-recorder.ts # Audio recording hook (NEW) +│ └── use-audio-player.ts # Audio playback hook (NEW) +├── lib/ +│ ├── audio-capture.ts # Legacy/factory version (NOT USED) +│ └── audio-playback.ts # Legacy/factory version (NOT USED) +├── app/ +│ ├── index.tsx # Updated with link to audio test +│ └── audio-test.tsx # Audio test screen (NEW) +└── app.json # Updated with permissions +``` + +## Implementation Details + +### Audio Recording (`use-audio-recorder.ts`) + +**Key Features**: +- Matches web app audio constraints: + - Sample rate: 16000 Hz (optimal for Whisper STT) + - Channels: 1 (mono) + - Format: M4A/AAC on native, WebM/Opus on web + - Audio source: `voice_communication` (enables echo cancellation, noise suppression, auto gain control on Android) + +**Platform-Specific Configuration**: + +Android: +```typescript +android: { + extension: '.m4a', + outputFormat: 'mpeg4', + audioEncoder: 'aac', + sampleRate: 16000, + audioSource: 'voice_communication', // Key for voice optimization +} +``` + +iOS: +```typescript +ios: { + extension: '.m4a', + audioQuality: 127, // High quality + sampleRate: 16000, + linearPCMBitDepth: 16, + linearPCMIsBigEndian: false, + linearPCMIsFloat: false, +} +``` + +Web: +```typescript +web: { + mimeType: 'audio/webm;codecs=opus', + bitsPerSecond: 128000, +} +``` + +**Usage**: +```typescript +const audioRecorder = useAudioRecorder({ + sampleRate: 16000, + numberOfChannels: 1, +}); + +// Start recording +await audioRecorder.start(); + +// Stop and get Blob for WebSocket transmission +const audioBlob = await audioRecorder.stop(); +``` + +**File Handling**: +1. Recording creates temporary file (managed by expo-audio) +2. On stop, reads file URI from `audioRecorder.uri` +3. Converts to Blob using expo-file-system's `File` class +4. Cleans up temporary file after conversion + +### Audio Playback (`use-audio-player.ts`) + +**Key Features**: +- Queue system matching web version +- Supports playing Blobs from server (base64-encoded MP3) +- Automatic cleanup of temporary files +- Play/pause/resume/stop controls + +**Queue System**: +```typescript +interface QueuedAudio { + audioData: Blob; + resolve: (duration: number) => void; + reject: (error: Error) => void; +} +``` + +**Usage**: +```typescript +const audioPlayer = useAudioPlayer(); + +// Play audio (queued automatically) +const duration = await audioPlayer.play(audioBlob); + +// Controls +audioPlayer.pause(); +audioPlayer.resume(); +audioPlayer.stop(); +audioPlayer.clearQueue(); +``` + +**File Handling**: +1. Converts Blob to ArrayBuffer +2. Creates temporary file in cache directory +3. Writes ArrayBuffer to file +4. Plays from file URI +5. Cleans up after playback + +### Audio Test Screen (`audio-test.tsx`) + +**Features**: +- Permission request/check on mount +- Visual permission status indicator +- Recording controls (Start/Stop) +- Recording status display +- Last recording size/type display +- Playback controls (Play Last Recording, Stop) +- Audio configuration info display +- Styled with NativeWind (Tailwind CSS) + +**Navigation**: +- Added link button on main screen (index.tsx) +- Route: `/audio-test` + +## Key Differences from Web Implementation + +### 1. API Differences + +**Web (MediaRecorder)**: +- `new MediaRecorder(stream, options)` +- Events: `ondataavailable`, `onstop` +- Returns Blob directly + +**React Native (expo-audio)**: +- Hook-based: `useAudioRecorder(options)` +- Returns Promise from `stop()` +- URI property for file location +- Requires manual Blob conversion + +### 2. File System + +**Web**: +- Direct Blob creation from chunks +- No file system access needed + +**React Native**: +- Must use file system (expo-file-system) +- Temporary files in cache directory +- Manual cleanup required + +### 3. Permissions + +**Web**: +- Runtime prompt via `getUserMedia()` +- Browser-managed + +**React Native**: +- Platform-specific: Info.plist (iOS), AndroidManifest.xml (Android) +- Requires explicit `requestRecordingPermissionsAsync()` +- Must be configured in app.json + +### 4. Audio Processing + +**Web**: +- WebRTC constraints: `echoCancellation`, `noiseSuppression`, `autoGainControl` + +**React Native**: +- Android: `audioSource: 'voice_communication'` enables similar features +- iOS: Built-in audio quality settings +- Platform-specific implementations + +## Platform-Specific Considerations + +### iOS +- M4A/AAC format (better compatibility) +- Microphone permission required (Info.plist) +- Audio session management handled by expo-audio +- No manual echo cancellation needed (handled by system) + +### Android +- M4A/AAC format +- RECORD_AUDIO permission required +- `voice_communication` audio source crucial for voice quality +- Enables: echo cancellation, noise suppression, auto gain control +- Works well with VoIP and voice commands + +### Web +- WebM/Opus format (better compression) +- MediaRecorder API used under the hood +- Permissions via browser prompt +- WebRTC audio processing + +## Testing + +### Manual Testing Steps + +1. **Start Expo Development Server**: + ```bash + cd packages/voice-mobile + npm start + ``` + +2. **Test on Web**: + - Press 'w' in terminal or visit http://localhost:8081 + - Click "Test Audio Capture & Playback" button + - Grant microphone permission + - Test recording and playback + +3. **Test on iOS Simulator**: + - Press 'i' in terminal + - Note: iOS Simulator doesn't have microphone access + - Test on physical device for full functionality + +4. **Test on Android Emulator/Device**: + - Press 'a' in terminal + - Grant microphone permission + - Test recording and playback + +### Expected Behavior + +**Recording**: +- Status shows "Recording..." with red indicator +- Stop button becomes enabled +- After stop, shows recording size and type + +**Playback**: +- "Play Last Recording" enabled after recording +- Plays back the recorded audio +- Shows duration after playback completes + +**Permissions**: +- Green indicator when granted +- Red indicator when denied +- Automatic request on mount + +## Integration with WebSocket + +The audio hooks are designed to work seamlessly with the WebSocket client: + +```typescript +// In your component with WebSocket +const audioRecorder = useAudioRecorder(); +const { send } = useWebSocket(WS_URL); + +// Record and send to server +async function handleRecord() { + await audioRecorder.start(); + // ... wait for user to stop + const audioBlob = await audioRecorder.stop(); + + // Convert to base64 for WebSocket + const reader = new FileReader(); + reader.onload = () => { + const base64 = reader.result.split(',')[1]; + send({ + type: 'audio_chunk', + payload: { + audio: base64, + format: 'audio/m4a', + isLast: true, + }, + }); + }; + reader.readAsDataURL(audioBlob); +} + +// Play audio from server +const audioPlayer = useAudioPlayer(); + +// When receiving audio from server +function handleAudioOutput(message) { + const audioBlob = base64ToBlob(message.payload.audio, 'audio/mp3'); + audioPlayer.play(audioBlob); +} +``` + +## Future Enhancements + +1. **Streaming Audio**: + - Implement chunk-based recording for real-time streaming + - Send audio chunks as they're recorded (not just on stop) + - Requires updating expo-audio usage + +2. **Audio Visualization**: + - Add waveform display during recording + - Use expo-audio's metering feature + - Display audio levels in real-time + +3. **Error Handling**: + - Add retry logic for failed recordings + - Better error messages for users + - Fallback for unsupported formats + +4. **Performance**: + - Optimize Blob conversion + - Reduce memory usage for long recordings + - Implement audio compression if needed + +## Common Issues & Solutions + +### Issue: "No recording URI returned" +**Cause**: Recording stopped before it started or expo-audio initialization failed +**Solution**: Ensure `audioRecorder.record()` completes before stopping + +### Issue: Playback fails with "Failed to load audio" +**Cause**: File was deleted before playback or invalid Blob +**Solution**: Ensure Blob is valid and file exists before playing + +### Issue: Permission denied +**Cause**: User denied microphone permission or app.json not configured +**Solution**: +1. Check app.json has correct permission entries +2. Rebuild app after changing permissions +3. Check device settings + +### Issue: Poor audio quality +**Cause**: Incorrect audio source or low sample rate +**Solution**: Ensure `audioSource: 'voice_communication'` on Android + +## TypeScript Support + +All audio hooks are fully typed: + +```typescript +// Hook return types +interface AudioRecorder { + start(): Promise; + stop(): Promise; + isRecording(): boolean; + getSupportedMimeType(): string | null; +} + +interface AudioPlayer { + play(audioData: Blob): Promise; + stop(): void; + pause(): void; + resume(): void; + isPlaying(): boolean; + isPaused(): boolean; + clearQueue(): void; +} + +// Configuration +interface AudioCaptureConfig { + sampleRate?: number; + numberOfChannels?: number; + bitRate?: number; +} +``` + +## Summary + +Phase 2 successfully implements audio capture and playback for the voice-mobile app using expo-audio. The implementation: + +✅ Matches web app audio constraints (16kHz, mono, optimized for speech) +✅ Supports all platforms (iOS, Android, Web) +✅ Provides Blob output compatible with WebSocket transmission +✅ Includes test screen for verification +✅ Fully typed with TypeScript +✅ Follows React Native best practices +✅ Properly manages permissions and temporary files + +The implementation is ready for integration with Phase 1 (WebSocket) to create the complete voice assistant mobile experience. diff --git a/packages/voice-mobile/app.json b/packages/voice-mobile/app.json index 590d2542a..cf236f355 100644 --- a/packages/voice-mobile/app.json +++ b/packages/voice-mobile/app.json @@ -8,8 +8,17 @@ "scheme": "voicemobile", "userInterfaceStyle": "automatic", "newArchEnabled": true, + "runtimeVersion": { + "policy": "appVersion" + }, + "updates": { + "url": "https://u.expo.dev/0e7f65ce-0367-46c8-a238-2b65963d235a" + }, "ios": { - "supportsTablet": true + "supportsTablet": true, + "infoPlist": { + "NSMicrophoneUsageDescription": "This app needs access to the microphone for voice commands." + } }, "android": { "adaptiveIcon": { @@ -19,7 +28,14 @@ "monochromeImage": "./assets/images/android-icon-monochrome.png" }, "edgeToEdgeEnabled": true, - "predictiveBackGestureEnabled": false + "predictiveBackGestureEnabled": false, + "softwareKeyboardLayoutMode": "pan", + "permissions": [ + "RECORD_AUDIO", + "android.permission.RECORD_AUDIO", + "android.permission.MODIFY_AUDIO_SETTINGS" + ], + "package": "com.moboudra.voicemobile" }, "web": { "output": "static", @@ -38,11 +54,19 @@ "backgroundColor": "#000000" } } - ] + ], + "expo-audio" ], "experiments": { "typedRoutes": true, "reactCompiler": false - } + }, + "extra": { + "router": {}, + "eas": { + "projectId": "0e7f65ce-0367-46c8-a238-2b65963d235a" + } + }, + "owner": "moboudra" } } diff --git a/packages/voice-mobile/app/(tabs)/_layout.tsx b/packages/voice-mobile/app/(tabs)/_layout.tsx deleted file mode 100644 index 54e11d00f..000000000 --- a/packages/voice-mobile/app/(tabs)/_layout.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { Tabs } from 'expo-router'; -import React from 'react'; - -import { HapticTab } from '@/components/haptic-tab'; -import { IconSymbol } from '@/components/ui/icon-symbol'; -import { Colors } from '@/constants/theme'; -import { useColorScheme } from '@/hooks/use-color-scheme'; - -export default function TabLayout() { - const colorScheme = useColorScheme(); - - return ( - - , - }} - /> - , - }} - /> - - ); -} diff --git a/packages/voice-mobile/app/(tabs)/explore.tsx b/packages/voice-mobile/app/(tabs)/explore.tsx deleted file mode 100644 index 71518f9a7..000000000 --- a/packages/voice-mobile/app/(tabs)/explore.tsx +++ /dev/null @@ -1,112 +0,0 @@ -import { Image } from 'expo-image'; -import { Platform, StyleSheet } from 'react-native'; - -import { Collapsible } from '@/components/ui/collapsible'; -import { ExternalLink } from '@/components/external-link'; -import ParallaxScrollView from '@/components/parallax-scroll-view'; -import { ThemedText } from '@/components/themed-text'; -import { ThemedView } from '@/components/themed-view'; -import { IconSymbol } from '@/components/ui/icon-symbol'; -import { Fonts } from '@/constants/theme'; - -export default function TabTwoScreen() { - return ( - - }> - - - Explore - - - This app includes example code to help you get started. - - - This app has two screens:{' '} - app/(tabs)/index.tsx and{' '} - app/(tabs)/explore.tsx - - - The layout file in app/(tabs)/_layout.tsx{' '} - sets up the tab navigator. - - - Learn more - - - - - You can open this project on Android, iOS, and the web. To open the web version, press{' '} - w in the terminal running this project. - - - - - For static images, you can use the @2x and{' '} - @3x suffixes to provide files for - different screen densities - - - - Learn more - - - - - This template has light and dark mode support. The{' '} - useColorScheme() hook lets you inspect - what the user's current color scheme is, and so you can adjust UI colors accordingly. - - - Learn more - - - - - This template includes an example of an animated component. The{' '} - components/HelloWave.tsx component uses - the powerful{' '} - - react-native-reanimated - {' '} - library to create a waving hand animation. - - {Platform.select({ - ios: ( - - The components/ParallaxScrollView.tsx{' '} - component provides a parallax effect for the header image. - - ), - })} - - - ); -} - -const styles = StyleSheet.create({ - headerImage: { - color: '#808080', - bottom: -90, - left: -35, - position: 'absolute', - }, - titleContainer: { - flexDirection: 'row', - gap: 8, - }, -}); diff --git a/packages/voice-mobile/app/(tabs)/index.tsx b/packages/voice-mobile/app/(tabs)/index.tsx deleted file mode 100644 index 786b7366c..000000000 --- a/packages/voice-mobile/app/(tabs)/index.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import { Image } from 'expo-image'; -import { Platform, StyleSheet } from 'react-native'; - -import { HelloWave } from '@/components/hello-wave'; -import ParallaxScrollView from '@/components/parallax-scroll-view'; -import { ThemedText } from '@/components/themed-text'; -import { ThemedView } from '@/components/themed-view'; -import { Link } from 'expo-router'; - -export default function HomeScreen() { - return ( - - }> - - Welcome! - - - - Step 1: Try it - - Edit app/(tabs)/index.tsx to see changes. - Press{' '} - - {Platform.select({ - ios: 'cmd + d', - android: 'cmd + m', - web: 'F12', - })} - {' '} - to open developer tools. - - - - - - Step 2: Explore - - - - alert('Action pressed')} /> - alert('Share pressed')} - /> - - alert('Delete pressed')} - /> - - - - - - {`Tap the Explore tab to learn more about what's included in this starter app.`} - - - - Step 3: Get a fresh start - - {`When you're ready, run `} - npm run reset-project to get a fresh{' '} - app directory. This will move the current{' '} - app to{' '} - app-example. - - - - ); -} - -const styles = StyleSheet.create({ - titleContainer: { - flexDirection: 'row', - alignItems: 'center', - gap: 8, - }, - stepContainer: { - gap: 8, - marginBottom: 8, - }, - reactLogo: { - height: 178, - width: 290, - bottom: 0, - left: 0, - position: 'absolute', - }, -}); diff --git a/packages/voice-mobile/app/_layout.tsx b/packages/voice-mobile/app/_layout.tsx index 7e3773d3d..f898ddb70 100644 --- a/packages/voice-mobile/app/_layout.tsx +++ b/packages/voice-mobile/app/_layout.tsx @@ -1,25 +1,17 @@ import '../global.css'; -import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native'; import { Stack } from 'expo-router'; import { StatusBar } from 'expo-status-bar'; -import 'react-native-reanimated'; - -import { useColorScheme } from '@/hooks/use-color-scheme'; - -export const unstable_settings = { - anchor: '(tabs)', -}; +import { SafeAreaProvider } from 'react-native-safe-area-context'; export default function RootLayout() { - const colorScheme = useColorScheme(); - return ( - - - - + + + + + - - + + ); } diff --git a/packages/voice-mobile/app/audio-test.tsx b/packages/voice-mobile/app/audio-test.tsx new file mode 100644 index 000000000..18de2ae93 --- /dev/null +++ b/packages/voice-mobile/app/audio-test.tsx @@ -0,0 +1,299 @@ +import { View, Text, Pressable, ScrollView, Alert } from 'react-native'; +import { useState, useEffect } from 'react'; +import { useAudioRecorder } from '../hooks/use-audio-recorder'; +import { useAudioPlayer } from '../hooks/use-audio-player'; +import { getRecordingPermissionsAsync, requestRecordingPermissionsAsync } from 'expo-audio'; + +export default function AudioTestScreen() { + const [permissionGranted, setPermissionGranted] = useState(false); + const [recordingStatus, setRecordingStatus] = useState<'idle' | 'recording' | 'processing'>('idle'); + const [lastRecordingSize, setLastRecordingSize] = useState(null); + const [playbackStatus, setPlaybackStatus] = useState('No audio playing'); + const [lastRecordedBlob, setLastRecordedBlob] = useState(null); + + const audioRecorder = useAudioRecorder({ + sampleRate: 16000, + numberOfChannels: 1, + }); + + const audioPlayer = useAudioPlayer(); + + useEffect(() => { + checkAndRequestPermissions(); + }, []); + + async function checkAndRequestPermissions() { + try { + const { status } = await getRecordingPermissionsAsync(); + + if (status === 'granted') { + setPermissionGranted(true); + } else { + const { granted } = await requestRecordingPermissionsAsync(); + setPermissionGranted(granted); + + if (!granted) { + Alert.alert( + 'Permission Required', + 'Microphone permission is required to test audio recording.' + ); + } + } + } catch (error) { + console.error('[AudioTest] Permission error:', error); + Alert.alert('Error', 'Failed to check/request microphone permissions'); + } + } + + async function handleStartRecording() { + if (!permissionGranted) { + Alert.alert('Permission Required', 'Please grant microphone permission first'); + return; + } + + try { + setRecordingStatus('recording'); + setLastRecordingSize(null); + await audioRecorder.start(); + } catch (error: any) { + console.error('[AudioTest] Start recording error:', error); + Alert.alert('Recording Error', error.message); + setRecordingStatus('idle'); + } + } + + async function handleStopRecording() { + try { + setRecordingStatus('processing'); + const audioBlob = await audioRecorder.stop(); + + setLastRecordingSize(audioBlob.size); + setLastRecordedBlob(audioBlob); + setRecordingStatus('idle'); + + Alert.alert( + 'Recording Complete', + `Audio recorded successfully!\nSize: ${audioBlob.size} bytes\nType: ${audioBlob.type}` + ); + } catch (error: any) { + console.error('[AudioTest] Stop recording error:', error); + Alert.alert('Recording Error', error.message); + setRecordingStatus('idle'); + } + } + + async function handlePlayLastRecording() { + if (!lastRecordedBlob) { + Alert.alert('No Recording', 'Please record audio first'); + return; + } + + try { + setPlaybackStatus('Playing last recording...'); + const duration = await audioPlayer.play(lastRecordedBlob); + setPlaybackStatus(`Playback complete (${duration.toFixed(2)}s)`); + } catch (error: any) { + console.error('[AudioTest] Playback error:', error); + Alert.alert('Playback Error', error.message); + setPlaybackStatus('Playback failed'); + } + } + + async function handlePlayTestAudio() { + // Create a test audio blob (silent audio for testing) + // In a real scenario, this would be audio from the server + try { + setPlaybackStatus('Playing test audio...'); + + // For now, we'll show an alert since we don't have a test audio file + Alert.alert( + 'Test Audio', + 'This would play a test audio file. Try recording and playing back instead.' + ); + setPlaybackStatus('Test audio not implemented'); + } catch (error: any) { + console.error('[AudioTest] Test playback error:', error); + Alert.alert('Playback Error', error.message); + setPlaybackStatus('Test playback failed'); + } + } + + function handleStopPlayback() { + audioPlayer.stop(); + setPlaybackStatus('Playback stopped'); + } + + return ( + + + {/* Header */} + + Audio Test + + + Test audio recording and playback functionality + + + {/* Permission Status */} + + + Permission Status + + + + {permissionGranted ? '✓ Microphone permission granted' : '✗ Microphone permission denied'} + + + + + {/* Recording Section */} + + + Audio Recording + + + {/* Recording Status */} + + + Status + + + {recordingStatus === 'idle' && 'Ready to record'} + {recordingStatus === 'recording' && '🔴 Recording...'} + {recordingStatus === 'processing' && 'Processing...'} + + + + {/* Recording Controls */} + + + + Start Recording + + + + + + Stop Recording + + + + + {/* Last Recording Info */} + {lastRecordingSize !== null && ( + + + Last Recording + + + Size: {lastRecordingSize} bytes + + + Type: audio/m4a + + + )} + + + {/* Playback Section */} + + + Audio Playback + + + {/* Playback Status */} + + + Status + + + {playbackStatus} + + + + {/* Playback Controls */} + + + + Play Last Recording + + + + + + Play Test Audio + + + + + + Stop Playback + + + + + + {/* Audio Settings Info */} + + + Audio Configuration + + + • Sample Rate: 16000 Hz + + + • Channels: 1 (Mono) + + + • Format: M4A/AAC + + + • Optimized for voice/speech + + + + + ); +} diff --git a/packages/voice-mobile/app/index.tsx b/packages/voice-mobile/app/index.tsx new file mode 100644 index 000000000..a26f8c753 --- /dev/null +++ b/packages/voice-mobile/app/index.tsx @@ -0,0 +1,939 @@ +import { useEffect, useState, useRef } from 'react'; +import { View, ScrollView, KeyboardAvoidingView, Platform, Pressable, Text, TextInput, Keyboard } from 'react-native'; +import { router } from 'expo-router'; +import { activateKeepAwakeAsync, deactivateKeepAwake } from 'expo-keep-awake'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; + +// Simple unique ID generator +let messageIdCounter = 0; +function generateMessageId(): string { + return `msg_${Date.now()}_${messageIdCounter++}`; +} +import { useWebSocket } from '@/hooks/use-websocket'; +import { useAudioRecorder } from '@/hooks/use-audio-recorder'; +import { useAudioPlayer } from '@/hooks/use-audio-player'; +import { useSettings } from '@/hooks/use-settings'; +import { ConnectionStatus } from '@/components/connection-status'; +import { UserMessage, AssistantMessage, ActivityLog, ToolCall } from '@/components/message'; +import { ArtifactDrawer, type Artifact } from '@/components/artifact-drawer'; +import { ActiveProcesses } from '@/components/active-processes'; +import { AgentStreamView } from '@/components/agent-stream-view'; +import { ConversationSelector } from '@/components/conversation-selector'; +import { MaterialIcons } from '@expo/vector-icons'; +import type { + ActivityLogPayload, + SessionInboundMessage, + WSInboundMessage, +} from '@voice-assistant/server/messages'; +import type { AgentStatus } from '@voice-assistant/server/acp/types'; +import type { SessionNotification } from '@agentclientprotocol/sdk'; + +type MessageEntry = + | { + type: 'user'; + id: string; + timestamp: number; + message: string; + } + | { + type: 'assistant'; + id: string; + timestamp: number; + message: string; + } + | { + type: 'activity'; + id: string; + timestamp: number; + activityType: 'system' | 'info' | 'success' | 'error'; + message: string; + metadata?: Record; + } + | { + type: 'tool_call'; + id: string; + timestamp: number; + toolName: string; + args: any; + result?: any; + error?: any; + status: 'executing' | 'completed' | 'failed'; + }; + +type ViewMode = 'orchestrator' | 'agent'; + +interface Agent { + id: string; + status: AgentStatus; + createdAt: Date; + type: 'claude'; + sessionId?: string; + error?: string; + currentModeId?: string; + availableModes?: Array<{ id: string; name: string; description?: string | null }>; +} + +interface Command { + id: string; + name: string; + workingDirectory: string; + currentCommand: string; + isDead: boolean; + exitCode: number | null; +} + +interface AgentUpdate { + timestamp: Date; + notification: SessionNotification; +} + +export default function VoiceAssistantScreen() { + const { settings, isLoading: settingsLoading } = useSettings(); + const [conversationId, setConversationId] = useState(null); + const ws = useWebSocket(settings.serverUrl, conversationId); + const audioRecorder = useAudioRecorder(); + const audioPlayer = useAudioPlayer({ useSpeaker: settings.useSpeaker }); + const insets = useSafeAreaInsets(); + + const [messages, setMessages] = useState([]); + const [currentAssistantMessage, setCurrentAssistantMessage] = useState(''); + const [isProcessingAudio, setIsProcessingAudio] = useState(false); + const [isPlayingAudio, setIsPlayingAudio] = useState(false); + const [isRecording, setIsRecording] = useState(false); + const [userInput, setUserInput] = useState(''); + + // Artifact state + const [artifacts, setArtifacts] = useState>(new Map()); + const [currentArtifact, setCurrentArtifact] = useState(null); + + // Multi-view navigation state + const [viewMode, setViewMode] = useState('orchestrator'); + const [activeAgentId, setActiveAgentId] = useState(null); + + // Agent and command state + const [agents, setAgents] = useState>(new Map()); + const [commands, setCommands] = useState>(new Map()); + const [agentUpdates, setAgentUpdates] = useState>(new Map()); + + const scrollViewRef = useRef(null); + + // Keep screen awake if setting is enabled + useEffect(() => { + if (settings.keepScreenOn) { + activateKeepAwakeAsync('voice-assistant'); + } else { + deactivateKeepAwake('voice-assistant'); + } + }, [settings.keepScreenOn]); + + // Auto-scroll to bottom when messages update + useEffect(() => { + scrollViewRef.current?.scrollToEnd({ animated: true }); + }, [messages, currentAssistantMessage]); + + // WebSocket message handlers + useEffect(() => { + // Session state handler - initial agents/commands + const unsubSessionState = ws.on('session_state', (message) => { + if (message.type !== 'session_state') return; + const { agents: agentsList, commands: commandsList } = message.payload; + + console.log('[App] Session state received:', agentsList.length, 'agents,', commandsList.length, 'commands'); + + // Update agents + setAgents(new Map(agentsList.map((a) => [a.id, a as Agent]))); + + // Update commands + setCommands(new Map(commandsList.map((c) => [c.id, c as Command]))); + }); + + // Agent created handler + const unsubAgentCreated = ws.on('agent_created', (message) => { + if (message.type !== 'agent_created') return; + const { agentId, status, type, currentModeId, availableModes } = message.payload; + + console.log('[App] Agent created:', agentId); + + const agent: Agent = { + id: agentId, + status: status as AgentStatus, + type, + createdAt: new Date(), + currentModeId, + availableModes, + }; + + setAgents((prev) => new Map(prev).set(agentId, agent)); + setAgentUpdates((prev) => new Map(prev).set(agentId, [])); + }); + + // Agent update handler + const unsubAgentUpdate = ws.on('agent_update', (message) => { + if (message.type !== 'agent_update') return; + const { agentId, timestamp, notification } = message.payload; + + console.log('[App] Agent update:', agentId); + + setAgentUpdates((prev) => { + const updated = new Map(prev); + const updates = updated.get(agentId) || []; + updated.set(agentId, [ + ...updates, + { + timestamp: new Date(timestamp), + notification, + }, + ]); + return updated; + }); + }); + + // Agent status handler + const unsubAgentStatus = ws.on('agent_status', (message) => { + if (message.type !== 'agent_status') return; + const { agentId, status, info } = message.payload; + + console.log('[App] Agent status changed:', agentId, status); + + setAgents((prev) => { + const updated = new Map(prev); + const agent = updated.get(agentId); + if (agent) { + updated.set(agentId, { + ...agent, + status: status as AgentStatus, + sessionId: info.sessionId, + error: info.error, + }); + } + return updated; + }); + }); + + // Activity log handler + const unsubActivity = ws.on('activity_log', (message) => { + if (message.type !== 'activity_log') return; + const data = message.payload; + + // Handle tool calls + if (data.type === 'tool_call' && data.metadata) { + const { + toolCallId, + toolName, + arguments: args, + } = data.metadata as { + toolCallId: string; + toolName: string; + arguments: unknown; + }; + + setMessages((prev) => [ + ...prev, + { + type: 'tool_call', + id: toolCallId, + timestamp: Date.now(), + toolName, + args, + status: 'executing', + }, + ]); + return; + } + + // Handle tool results + if (data.type === 'tool_result' && data.metadata) { + const { toolCallId, result } = data.metadata as { + toolCallId: string; + result: unknown; + }; + + setMessages((prev) => + prev.map((msg) => + msg.type === 'tool_call' && msg.id === toolCallId + ? { ...msg, result, status: 'completed' as const } + : msg + ) + ); + return; + } + + // Handle tool errors + if ( + data.type === 'error' && + data.metadata && + 'toolCallId' in data.metadata + ) { + const { toolCallId, error } = data.metadata as { + toolCallId: string; + error: unknown; + }; + + setMessages((prev) => + prev.map((msg) => + msg.type === 'tool_call' && msg.id === toolCallId + ? { ...msg, error, status: 'failed' as const } + : msg + ) + ); + } + + // Map activity types to message types + let activityType: 'system' | 'info' | 'success' | 'error' = 'info'; + if (data.type === 'error') activityType = 'error'; + + // Add user transcripts as user messages + if (data.type === 'transcript') { + setMessages((prev) => [ + ...prev, + { + type: 'user', + id: generateMessageId(), + timestamp: Date.now(), + message: data.content, + }, + ]); + return; + } + + // Add assistant messages + if (data.type === 'assistant') { + setMessages((prev) => [ + ...prev, + { + type: 'assistant', + id: generateMessageId(), + timestamp: Date.now(), + message: data.content, + }, + ]); + setCurrentAssistantMessage(''); + return; + } + + // Add activity log for other types + setMessages((prev) => [ + ...prev, + { + type: 'activity', + id: generateMessageId(), + timestamp: Date.now(), + activityType, + message: data.content, + metadata: data.metadata, + }, + ]); + }); + + // Assistant chunk handler (streaming) + const unsubChunk = ws.on('assistant_chunk', (message) => { + if (message.type !== 'assistant_chunk') return; + setCurrentAssistantMessage((prev) => prev + message.payload.chunk); + }); + + // Transcription result handler + const unsubTranscription = ws.on('transcription_result', (message) => { + if (message.type !== 'transcription_result') return; + + setIsProcessingAudio(false); + + const transcriptText = message.payload.text.trim(); + + if (!transcriptText) { + // Empty transcription - false positive, resume playback + console.log('[App] Empty transcription (false positive) - resuming playback'); + audioPlayer.resume(); + } else { + // Has content - real speech detected, stop playback + console.log('[App] Transcription received - stopping playback'); + audioPlayer.stop(); + setIsPlayingAudio(false); + setCurrentAssistantMessage(''); + } + }); + + // Audio output handler (TTS) + const unsubAudioOutput = ws.on('audio_output', async (message) => { + if (message.type !== 'audio_output') return; + const data = message.payload; + + try { + setIsPlayingAudio(true); + + // Create blob-like object with correct mime type (React Native compatible) + const mimeType = + data.format === 'mp3' ? 'audio/mpeg' : `audio/${data.format}`; + const base64Audio = data.audio; + + // Create a Blob-like object that works in React Native + const audioBlob = { + type: mimeType, + size: Math.ceil(base64Audio.length * 3 / 4), // Approximate size from base64 + arrayBuffer: async () => { + // Convert base64 to ArrayBuffer + const binaryString = atob(base64Audio); + const bytes = new Uint8Array(binaryString.length); + for (let i = 0; i < binaryString.length; i++) { + bytes[i] = binaryString.charCodeAt(i); + } + return bytes.buffer; + }, + } as Blob; + + // Play audio + await audioPlayer.play(audioBlob); + + // Send confirmation back to server (properly typed) + const confirmMessage: WSInboundMessage = { + type: 'session', + message: { + type: 'audio_played', + id: data.id, + }, + }; + ws.send(confirmMessage); + + setIsPlayingAudio(false); + } catch (error: any) { + console.error('[App] Audio playback error:', error); + setMessages((prev) => [ + ...prev, + { + type: 'activity', + id: generateMessageId(), + timestamp: Date.now(), + activityType: 'error', + message: `Audio playback failed: ${error.message}`, + }, + ]); + setIsPlayingAudio(false); + } + }); + + // Status handler + const unsubStatus = ws.on('status', (message) => { + if (message.type !== 'status') return; + const msg = + 'message' in message.payload + ? String(message.payload.message) + : `Status: ${message.payload.status}`; + + setMessages((prev) => [ + ...prev, + { + type: 'activity', + id: generateMessageId(), + timestamp: Date.now(), + activityType: 'info', + message: msg, + }, + ]); + }); + + // Conversation loaded handler + const unsubConversationLoaded = ws.on('conversation_loaded', (message) => { + if (message.type !== 'conversation_loaded') return; + const { conversationId, messageCount } = message.payload; + + setMessages((prev) => [ + ...prev, + { + type: 'activity', + id: generateMessageId(), + timestamp: Date.now(), + activityType: 'success', + message: `Loaded conversation with ${messageCount} messages`, + }, + ]); + }); + + // Artifact handler + const unsubArtifact = ws.on('artifact', (message) => { + if (message.type !== 'artifact') return; + const artifactData = message.payload; + + console.log('[App] Received artifact:', artifactData.id, artifactData.type, artifactData.title); + + // Store artifact + setArtifacts((prev) => { + const updated = new Map(prev); + updated.set(artifactData.id, artifactData); + return updated; + }); + + // Show drawer immediately + setCurrentArtifact(artifactData); + }); + + return () => { + unsubSessionState(); + unsubAgentCreated(); + unsubAgentUpdate(); + unsubAgentStatus(); + unsubActivity(); + unsubChunk(); + unsubTranscription(); + unsubAudioOutput(); + unsubStatus(); + unsubConversationLoaded(); + unsubArtifact(); + }; + }, [ws, audioPlayer]); + + // Connection status handler + useEffect(() => { + if (ws.isConnected) { + setMessages((prev) => [ + ...prev, + { + type: 'activity', + id: generateMessageId(), + timestamp: Date.now(), + activityType: 'success', + message: 'WebSocket connected', + }, + ]); + } else if (messages.length > 0) { + setMessages((prev) => [ + ...prev, + { + type: 'activity', + id: generateMessageId(), + timestamp: Date.now(), + activityType: 'error', + message: 'WebSocket disconnected', + }, + ]); + } + }, [ws.isConnected]); + + // Voice button handler + async function handleVoicePress() { + if (!ws.isConnected) return; + + // If recording, stop and send + if (isRecording) { + try { + console.log('[App] Stopping recording...'); + const audioBlob = await audioRecorder.stop(); + setIsRecording(false); + + const format = audioBlob.type || 'audio/m4a'; + console.log( + `[App] Recording complete: ${audioBlob.size} bytes, format: ${format}` + ); + + setIsProcessingAudio(true); + setMessages((prev) => [ + ...prev, + { + type: 'activity', + id: generateMessageId(), + timestamp: Date.now(), + activityType: 'info', + message: 'Sending audio to server...', + }, + ]); + + // Convert to base64 + const arrayBuffer = await audioBlob.arrayBuffer(); + const base64Audio = btoa( + new Uint8Array(arrayBuffer).reduce( + (data, byte) => data + String.fromCharCode(byte), + '' + ) + ); + + // Send to server (properly typed) + const audioMessage: WSInboundMessage = { + type: 'session', + message: { + type: 'audio_chunk', + audio: base64Audio, + format: format, + isLast: true, + }, + }; + ws.send(audioMessage); + + console.log( + `[App] Sent audio: ${audioBlob.size} bytes, format: ${format}` + ); + } catch (error: any) { + console.error('[App] Recording error:', error); + setMessages((prev) => [ + ...prev, + { + type: 'activity', + id: generateMessageId(), + timestamp: Date.now(), + activityType: 'error', + message: `Failed to record audio: ${error.message}`, + }, + ]); + setIsRecording(false); + } + } else { + // Start recording + try { + console.log('[App] Starting recording...'); + + // Stop any currently playing audio + audioPlayer.stop(); + setIsPlayingAudio(false); + + await audioRecorder.start(); + setIsRecording(true); + } catch (error: any) { + console.error('[App] Failed to start recording:', error); + setMessages((prev) => [ + ...prev, + { + type: 'activity', + id: generateMessageId(), + timestamp: Date.now(), + activityType: 'error', + message: `Failed to start recording: ${error.message}`, + }, + ]); + } + } + } + + // Handle artifact click from activity log + function handleArtifactClick(artifactId: string) { + const artifact = artifacts.get(artifactId); + if (artifact) { + console.log('[App] Opening artifact:', artifactId); + setCurrentArtifact(artifact); + } else { + console.warn('[App] Artifact not found:', artifactId); + } + } + + // Close artifact drawer + function handleCloseArtifact() { + setCurrentArtifact(null); + } + + // Handle agent selection + function handleSelectAgent(agentId: string) { + setActiveAgentId(agentId); + setViewMode('agent'); + } + + // Handle back to orchestrator + function handleBackToOrchestrator() { + setActiveAgentId(null); + setViewMode('orchestrator'); + } + + // Agent control handlers + function handleKillAgent(agentId: string) { + console.log('[App] Kill agent:', agentId); + // TODO: Implement kill agent API call + } + + function handleCancelAgent(agentId: string) { + console.log('[App] Cancel agent:', agentId); + // TODO: Implement cancel agent API call + } + + // Text message handlers + function handleSendMessage() { + if (!userInput.trim() || !ws.isConnected) return; + + // Stop any currently playing audio + audioPlayer.stop(); + setIsPlayingAudio(false); + + // Send message to server using the hook's method + ws.sendUserMessage(userInput); + + // Clear input and reset streaming state + setUserInput(''); + setCurrentAssistantMessage(''); + Keyboard.dismiss(); + } + + function handleCancel() { + console.log('[App] Cancelling operations...'); + + // Stop audio playback + audioPlayer.stop(); + setIsPlayingAudio(false); + + // Clear streaming state + setCurrentAssistantMessage(''); + + // Reset processing state + setIsProcessingAudio(false); + + // Send abort request to server (properly typed) + const abortMessage: WSInboundMessage = { + type: 'session', + message: { + type: 'abort_request', + }, + }; + ws.send(abortMessage); + + setMessages((prev) => [ + ...prev, + { + type: 'activity', + id: generateMessageId(), + timestamp: Date.now(), + activityType: 'info', + message: 'Operations cancelled', + }, + ]); + } + + // Compute if we're processing + const isInProgress = + isProcessingAudio || isPlayingAudio || currentAssistantMessage.length > 0; + + function handleButtonClick() { + if (isRecording) { + // Stop recording and send the audio + handleVoicePress(); + } else if (isInProgress) { + // Cancel processing/playback + handleCancel(); + } else if (userInput.trim()) { + // Send text message + handleSendMessage(); + } else { + // Start recording + handleVoicePress(); + } + } + + // Conversation selection handler + function handleSelectConversation(newConversationId: string | null) { + // Clear all state + setMessages([]); + setCurrentAssistantMessage(''); + setUserInput(''); + setArtifacts(new Map()); + setCurrentArtifact(null); + setAgents(new Map()); + setCommands(new Map()); + setAgentUpdates(new Map()); + setViewMode('orchestrator'); + setActiveAgentId(null); + + // Stop any ongoing operations + if (isRecording) { + audioRecorder.stop().catch(console.error); + setIsRecording(false); + } + audioPlayer.stop(); + setIsPlayingAudio(false); + setIsProcessingAudio(false); + + // Update conversation ID (will trigger WebSocket reconnection) + setConversationId(newConversationId); + } + + // Render agent stream view + if (viewMode === 'agent' && activeAgentId) { + const agent = agents.get(activeAgentId); + const updates = agentUpdates.get(activeAgentId) || []; + + if (!agent) { + return ( + + Agent not found + + ); + } + + return ( + + ); + } + + // Render orchestrator view (main chat) + return ( + + + {/* Connection status header with buttons */} + + + + + + + + router.push('/settings')} + className="bg-zinc-800 p-3 rounded-lg" + > + + + + + + + {/* Active processes bar */} + + + {/* Messages */} + + {messages.map((msg) => { + if (msg.type === 'user') { + return ( + + ); + } + + if (msg.type === 'assistant') { + return ( + + ); + } + + if (msg.type === 'activity') { + return ( + + ); + } + + if (msg.type === 'tool_call') { + return ( + + ); + } + + return null; + })} + + {/* Streaming assistant message */} + {currentAssistantMessage && ( + + )} + + + {/* Input area */} + + {/* Text input */} + + + {/* Buttons */} + + {/* Realtime mode button (placeholder) */} + + + + + + + + {/* Main action button */} + + {isInProgress ? ( + + + + + ) : isRecording ? ( + + ) : userInput.trim() ? ( + ▶ + ) : ( + + + + + )} + + + + + {/* Artifact drawer */} + + + + ); +} diff --git a/packages/voice-mobile/app/modal.tsx b/packages/voice-mobile/app/modal.tsx deleted file mode 100644 index 6dfbc1ab9..000000000 --- a/packages/voice-mobile/app/modal.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { Link } from 'expo-router'; -import { StyleSheet } from 'react-native'; - -import { ThemedText } from '@/components/themed-text'; -import { ThemedView } from '@/components/themed-view'; - -export default function ModalScreen() { - return ( - - This is a modal - - Go to home screen - - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - alignItems: 'center', - justifyContent: 'center', - padding: 20, - }, - link: { - marginTop: 15, - paddingVertical: 15, - }, -}); diff --git a/packages/voice-mobile/app/settings.tsx b/packages/voice-mobile/app/settings.tsx new file mode 100644 index 000000000..e47c61ba1 --- /dev/null +++ b/packages/voice-mobile/app/settings.tsx @@ -0,0 +1,367 @@ +import { useState, useEffect } from 'react'; +import { View, Text, ScrollView, TextInput, Switch, Pressable, Alert, ActivityIndicator } from 'react-native'; +import { router } from 'expo-router'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; +import { useSettings } from '@/hooks/use-settings'; + +export default function SettingsScreen() { + const { settings, isLoading, updateSettings, resetSettings } = useSettings(); + const insets = useSafeAreaInsets(); + + const [serverUrl, setServerUrl] = useState(settings.serverUrl); + const [useSpeaker, setUseSpeaker] = useState(settings.useSpeaker); + const [keepScreenOn, setKeepScreenOn] = useState(settings.keepScreenOn); + const [theme, setTheme] = useState<'dark' | 'light' | 'auto'>(settings.theme); + const [hasChanges, setHasChanges] = useState(false); + const [isTesting, setIsTesting] = useState(false); + const [testResult, setTestResult] = useState<{ success: boolean; message: string } | null>(null); + + // Update local state when settings load + useEffect(() => { + setServerUrl(settings.serverUrl); + setUseSpeaker(settings.useSpeaker); + setKeepScreenOn(settings.keepScreenOn); + setTheme(settings.theme); + }, [settings]); + + // Track changes + useEffect(() => { + const changed = + serverUrl !== settings.serverUrl || + useSpeaker !== settings.useSpeaker || + keepScreenOn !== settings.keepScreenOn || + theme !== settings.theme; + setHasChanges(changed); + }, [serverUrl, useSpeaker, keepScreenOn, theme, settings]); + + function validateServerUrl(url: string): boolean { + try { + const urlObj = new URL(url); + return urlObj.protocol === 'ws:' || urlObj.protocol === 'wss:'; + } catch { + return false; + } + } + + async function handleSave() { + // Validate server URL + if (!validateServerUrl(serverUrl)) { + Alert.alert( + 'Invalid URL', + 'Server URL must be a valid WebSocket URL (ws:// or wss://)', + [{ text: 'OK' }] + ); + return; + } + + try { + await updateSettings({ + serverUrl, + useSpeaker, + keepScreenOn, + theme, + }); + + Alert.alert( + 'Settings Saved', + 'Your settings have been saved successfully.', + [ + { + text: 'OK', + onPress: () => router.back(), + }, + ] + ); + } catch (error) { + Alert.alert( + 'Error', + 'Failed to save settings. Please try again.', + [{ text: 'OK' }] + ); + } + } + + async function handleReset() { + Alert.alert( + 'Reset Settings', + 'Are you sure you want to reset all settings to defaults?', + [ + { text: 'Cancel', style: 'cancel' }, + { + text: 'Reset', + style: 'destructive', + onPress: async () => { + try { + await resetSettings(); + Alert.alert('Settings Reset', 'All settings have been reset to defaults.'); + } catch (error) { + Alert.alert('Error', 'Failed to reset settings. Please try again.'); + } + }, + }, + ] + ); + } + + function handleCancel() { + if (hasChanges) { + Alert.alert( + 'Discard Changes', + 'You have unsaved changes. Are you sure you want to go back?', + [ + { text: 'Stay', style: 'cancel' }, + { + text: 'Discard', + style: 'destructive', + onPress: () => router.back(), + }, + ] + ); + } else { + router.back(); + } + } + + async function handleTestConnection() { + if (!validateServerUrl(serverUrl)) { + Alert.alert( + 'Invalid URL', + 'Server URL must be a valid WebSocket URL (ws:// or wss://)', + [{ text: 'OK' }] + ); + return; + } + + setIsTesting(true); + setTestResult(null); + + try { + const ws = new WebSocket(serverUrl); + + const timeout = setTimeout(() => { + ws.close(); + setTestResult({ + success: false, + message: 'Connection timeout - server did not respond', + }); + setIsTesting(false); + }, 5000); + + ws.onopen = () => { + clearTimeout(timeout); + ws.close(); + setTestResult({ + success: true, + message: 'Connection successful', + }); + setIsTesting(false); + }; + + ws.onerror = () => { + clearTimeout(timeout); + setTestResult({ + success: false, + message: 'Connection failed - check URL and network', + }); + setIsTesting(false); + }; + } catch (error) { + setTestResult({ + success: false, + message: 'Failed to create connection', + }); + setIsTesting(false); + } + } + + if (isLoading) { + return ( + + Loading settings... + + ); + } + + return ( + + {/* Header */} + + + Settings + + Cancel + + + + + + + {/* Server Configuration */} + + + Server Configuration + + + WebSocket URL + { + setServerUrl(text); + setTestResult(null); + }} + autoCapitalize="none" + autoCorrect={false} + keyboardType="url" + /> + + Must be a valid WebSocket URL (ws:// or wss://) + + + {/* Test Connection Button */} + + {isTesting ? ( + <> + + Testing... + + ) : ( + Test Connection + )} + + + {/* Test Result */} + {testResult && ( + + + {testResult.message} + + + )} + + + {/* Audio Settings */} + + + Audio + + + + + + Use Speaker + + Play audio through speaker instead of earpiece + + + + + + + + + + Keep Screen On + + Prevent screen from sleeping during voice sessions + + + + + + + + {/* Theme Settings */} + + + Theme + + + + + Theme selection (coming soon) + + + {(['dark', 'light', 'auto'] as const).map((themeOption) => ( + + + {theme === themeOption && ( + + )} + + + {themeOption} + + + ))} + + + + {/* Action Buttons */} + + + + Save Settings + + + + + + Reset to Defaults + + + + + {/* App Info */} + + + Voice Assistant Mobile + + + Version 1.0.0 + + + + + + ); +} diff --git a/packages/voice-mobile/components/active-processes.tsx b/packages/voice-mobile/components/active-processes.tsx new file mode 100644 index 000000000..2125d874a --- /dev/null +++ b/packages/voice-mobile/components/active-processes.tsx @@ -0,0 +1,169 @@ +import { View, Text, Pressable, ScrollView } from 'react-native'; +import type { AgentStatus } from '@voice-assistant/server/acp/types'; + +export interface ActiveProcessesProps { + agents: Array<{ + id: string; + status: AgentStatus; + type: 'claude'; + currentModeId?: string; + availableModes?: Array<{ id: string; name: string; description?: string | null }>; + }>; + commands: Array<{ + id: string; + name: string; + workingDirectory: string; + currentCommand: string; + isDead: boolean; + exitCode: number | null; + }>; + activeProcessId: string | null; + activeProcessType: 'agent' | null; + onSelectAgent: (id: string) => void; + onBackToOrchestrator: () => void; +} + +function getAgentStatusColor(status: AgentStatus): string { + switch (status) { + case 'initializing': + return '#f59e0b'; // orange + case 'ready': + return '#3b82f6'; // blue + case 'processing': + return '#fbbf24'; // yellow + case 'completed': + return '#22c55e'; // green + case 'failed': + return '#ef4444'; // red + case 'killed': + return '#9ca3af'; // gray + default: + return '#9ca3af'; + } +} + +function getModeName(modeId?: string, availableModes?: Array<{ id: string; name: string }>): string { + if (!modeId) return 'unknown'; + const mode = availableModes?.find((m) => m.id === modeId); + return mode?.name || modeId; +} + +function getModeColor(modeId?: string): string { + if (!modeId) return '#9ca3af'; // gray + + // Color based on common mode types + if (modeId.includes('ask')) return '#f59e0b'; // orange - asks permission + if (modeId.includes('code')) return '#22c55e'; // green - writes code + if (modeId.includes('architect') || modeId.includes('plan')) return '#3b82f6'; // blue - plans + + return '#9ca3af'; // gray - unknown +} + +export function ActiveProcesses({ + agents, + commands, + activeProcessId, + activeProcessType, + onSelectAgent, + onBackToOrchestrator, +}: ActiveProcessesProps) { + const hasActiveProcess = activeProcessId !== null && activeProcessType !== null; + + if (agents.length === 0 && commands.length === 0) { + return null; + } + + return ( + + {/* Header with Back button */} + {hasActiveProcess && ( + + + + Back to Chat + + + + )} + + {/* Scrollable process list */} + + {/* Agents */} + {agents.map((agent) => { + const modeName = getModeName(agent.currentModeId, agent.availableModes); + const isActive = activeProcessType === 'agent' && activeProcessId === agent.id; + + return ( + onSelectAgent(agent.id)} + className={`flex-row items-center gap-2 px-3 py-2 rounded-lg ${ + isActive ? 'bg-blue-600' : 'bg-zinc-800' + } active:opacity-70`} + > + {/* Agent icon */} + + + {/* Agent ID (shortened) */} + + {agent.id.substring(0, 8)} + + + {/* Status indicator */} + + + {/* Mode indicator */} + {agent.currentModeId && ( + + )} + + ); + })} + + {/* Commands */} + {commands.map((command) => { + const statusColor = command.isDead + ? command.exitCode === 0 + ? '#22c55e' + : '#ef4444' + : '#3b82f6'; + + return ( + + {/* Command icon */} + + + {/* Command name */} + + {command.name || command.currentCommand.substring(0, 20)} + + + {/* Status indicator */} + + + ); + })} + + + ); +} diff --git a/packages/voice-mobile/components/agent-stream-view.tsx b/packages/voice-mobile/components/agent-stream-view.tsx new file mode 100644 index 000000000..c0953fa70 --- /dev/null +++ b/packages/voice-mobile/components/agent-stream-view.tsx @@ -0,0 +1,286 @@ +import { useState, useEffect, useRef } from 'react'; +import { View, Text, ScrollView, Pressable } from 'react-native'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; +import type { AgentStatus } from '@voice-assistant/server/acp/types'; +import type { SessionNotification } from '@agentclientprotocol/sdk'; + +export interface AgentStreamViewProps { + agentId: string; + agent: { + id: string; + status: AgentStatus; + createdAt: Date; + type: 'claude'; + }; + updates: Array<{ + timestamp: Date; + notification: SessionNotification; + }>; + onBack: () => void; + onKillAgent: (agentId: string) => void; + onCancelAgent: (agentId: string) => void; +} + +function getStatusColor(status: AgentStatus): string { + switch (status) { + case 'initializing': + return '#f59e0b'; + case 'ready': + return '#3b82f6'; + case 'processing': + return '#fbbf24'; + case 'completed': + return '#22c55e'; + case 'failed': + return '#ef4444'; + case 'killed': + return '#9ca3af'; + default: + return '#9ca3af'; + } +} + +function getStatusIcon(status: AgentStatus): string { + switch (status) { + case 'initializing': + case 'processing': + return '⏳'; + case 'ready': + case 'completed': + return '✓'; + case 'failed': + case 'killed': + return '✗'; + default: + return '•'; + } +} + +function formatTimestamp(date: Date): string { + return new Intl.DateTimeFormat('en-US', { + hour: 'numeric', + minute: '2-digit', + second: '2-digit', + hour12: true, + }).format(date); +} + +function AgentUpdate({ + update, +}: { + update: { timestamp: Date; notification: SessionNotification }; +}) { + const [isExpanded, setIsExpanded] = useState(true); + const notification = update.notification as any; + + // Parse different notification types + if (notification.type === 'sessionUpdate') { + const sessionUpdate = notification.sessionUpdate || {}; + + // Handle message chunks + if (sessionUpdate.kind === 'agent_message_chunk') { + return ( + + + {formatTimestamp(update.timestamp)} + + {sessionUpdate.chunk || ''} + + ); + } + + // Handle tool calls + if (sessionUpdate.kind === 'tool_call') { + return ( + + setIsExpanded(!isExpanded)} + className="px-3 py-2 active:bg-zinc-700" + > + + {formatTimestamp(update.timestamp)} + + + Tool Call: {sessionUpdate.toolName || 'unknown'} + + + {isExpanded && sessionUpdate.arguments && ( + + + {JSON.stringify(sessionUpdate.arguments, null, 2)} + + + )} + + ); + } + + // Handle tool call updates + if (sessionUpdate.kind === 'tool_call_update') { + return ( + + + {formatTimestamp(update.timestamp)} + + + {sessionUpdate.status || 'updating'} + + + ); + } + + // Handle available commands + if (sessionUpdate.kind === 'available_commands_update') { + const commands = sessionUpdate.commands || []; + return ( + + setIsExpanded(!isExpanded)} + className="px-3 py-2 active:bg-zinc-700" + > + + {formatTimestamp(update.timestamp)} + + + Available Commands ({commands.length}) + + + {isExpanded && ( + + {commands.map((cmd: any, idx: number) => ( + + • {cmd.name || cmd} + + ))} + + )} + + ); + } + + // Generic session update + return ( + + + {formatTimestamp(update.timestamp)} + + + {JSON.stringify(sessionUpdate, null, 2)} + + + ); + } + + // Fallback for unknown notification types + return ( + + + {formatTimestamp(update.timestamp)} + + + {JSON.stringify(notification, null, 2)} + + + ); +} + +export function AgentStreamView({ + agentId, + agent, + updates, + onBack, + onKillAgent, + onCancelAgent, +}: AgentStreamViewProps) { + const scrollViewRef = useRef(null); + const insets = useSafeAreaInsets(); + + // Auto-scroll to bottom when new updates arrive + useEffect(() => { + scrollViewRef.current?.scrollToEnd({ animated: true }); + }, [updates]); + + const canCancel = agent.status === 'processing'; + const canKill = agent.status !== 'killed' && agent.status !== 'completed'; + + return ( + + {/* Header */} + + {/* Back button */} + + + ← Back to Chat + + + + {/* Agent info */} + + + Agent: + + {agentId.substring(0, 8)} + + + + {getStatusIcon(agent.status)} + {agent.status} + + + + + Created: {formatTimestamp(agent.createdAt)} + + + {/* Control buttons */} + {(canCancel || canKill) && ( + + {canCancel && ( + onCancelAgent(agentId)} + className="flex-1 bg-orange-600 px-4 py-2 rounded-lg active:bg-orange-700" + > + + Cancel + + + )} + {canKill && ( + onKillAgent(agentId)} + className="flex-1 bg-red-600 px-4 py-2 rounded-lg active:bg-red-700" + > + + Kill + + + )} + + )} + + + {/* Updates list */} + + {updates.length === 0 ? ( + + + No updates yet.{'\n'}Waiting for agent activity... + + + ) : ( + updates.map((update, idx) => ) + )} + + + ); +} diff --git a/packages/voice-mobile/components/artifact-drawer.tsx b/packages/voice-mobile/components/artifact-drawer.tsx new file mode 100644 index 000000000..26ac84a25 --- /dev/null +++ b/packages/voice-mobile/components/artifact-drawer.tsx @@ -0,0 +1,126 @@ +import { View, Text, ScrollView, Pressable, Modal } from 'react-native'; +import { useEffect } from 'react'; + +export interface Artifact { + id: string; + type: 'markdown' | 'diff' | 'image' | 'code'; + title: string; + content: string; + isBase64: boolean; +} + +interface ArtifactDrawerProps { + artifact: Artifact | null; + onClose: () => void; +} + +export function ArtifactDrawer({ artifact, onClose }: ArtifactDrawerProps) { + useEffect(() => { + if (!artifact) return; + console.log('[ArtifactDrawer] Showing artifact:', artifact.id, artifact.type, artifact.title); + }, [artifact]); + + if (!artifact) { + return null; + } + + // Decode content if base64 + const content = artifact.isBase64 ? atob(artifact.content) : artifact.content; + + // Type badge colors + const typeColors = { + markdown: 'bg-blue-600', + diff: 'bg-purple-600', + image: 'bg-green-600', + code: 'bg-orange-600', + }; + + return ( + + + {/* Header */} + + + + + {artifact.title} + + + + + + {artifact.type} + + + + × + + + + + + {/* Content */} + + {artifact.type === 'image' ? ( + + + Image viewing not yet implemented + + + Base64 image data received + + + ) : ( + + + + {content} + + + + )} + + {/* Metadata */} + + METADATA + + + ID: + + {artifact.id} + + + + Type: + {artifact.type} + + + Encoding: + + {artifact.isBase64 ? 'Base64' : 'Plain text'} + + + + Size: + + {content.length.toLocaleString()} characters + + + + + + + + ); +} diff --git a/packages/voice-mobile/components/connection-status.tsx b/packages/voice-mobile/components/connection-status.tsx new file mode 100644 index 000000000..dad139af7 --- /dev/null +++ b/packages/voice-mobile/components/connection-status.tsx @@ -0,0 +1,18 @@ +import { View, Text } from 'react-native'; + +interface ConnectionStatusProps { + isConnected: boolean; +} + +export function ConnectionStatus({ isConnected }: ConnectionStatusProps) { + return ( + + + + + {isConnected ? 'Connected' : 'Disconnected'} + + + + ); +} diff --git a/packages/voice-mobile/components/conversation-selector.tsx b/packages/voice-mobile/components/conversation-selector.tsx new file mode 100644 index 000000000..9cc7f9d2d --- /dev/null +++ b/packages/voice-mobile/components/conversation-selector.tsx @@ -0,0 +1,270 @@ +import { useState, useEffect } from 'react'; +import { Modal, View, Text, Pressable, ScrollView, Alert } from 'react-native'; +import { MaterialIcons } from '@expo/vector-icons'; +import AsyncStorage from '@react-native-async-storage/async-storage'; +import type { UseWebSocketReturn } from '../hooks/use-websocket'; + +const STORAGE_KEY = '@voice-assistant:conversation-id'; + +interface Conversation { + id: string; + lastUpdated: string; + messageCount: number; +} + +interface ConversationSelectorProps { + currentConversationId: string | null; + onSelectConversation: (conversationId: string | null) => void; + websocket: UseWebSocketReturn; +} + +export function ConversationSelector({ + currentConversationId, + onSelectConversation, + websocket, +}: ConversationSelectorProps) { + const [conversations, setConversations] = useState([]); + const [isOpen, setIsOpen] = useState(false); + const [isLoading, setIsLoading] = useState(false); + + // Listen for conversation list responses + useEffect(() => { + const unsubscribe = websocket.on('list_conversations_response', (message) => { + console.log('[ConversationSelector] Received conversations:', message.payload.conversations.length); + setConversations(message.payload.conversations); + setIsLoading(false); + }); + + return unsubscribe; + }, [websocket]); + + // Listen for delete conversation responses + useEffect(() => { + const unsubscribe = websocket.on('delete_conversation_response', (message) => { + console.log('[ConversationSelector] Delete response:', message.payload); + if (message.payload.success) { + // Refresh conversations list + fetchConversations(); + + // If we deleted the current conversation, start a new one + if (message.payload.conversationId === currentConversationId) { + handleNewConversation(); + } + } else { + Alert.alert('Error', `Failed to delete conversation: ${message.payload.error}`); + } + }); + + return unsubscribe; + }, [websocket, currentConversationId]); + + // Fetch conversations when modal opens + useEffect(() => { + if (isOpen) { + fetchConversations(); + } + }, [isOpen]); + + function fetchConversations() { + setIsLoading(true); + console.log('[ConversationSelector] Requesting conversations via WebSocket'); + websocket.send({ + type: 'session', + message: { + type: 'list_conversations_request', + }, + }); + } + + function handleDeleteConversation(id: string) { + Alert.alert( + 'Delete Conversation', + 'Are you sure you want to delete this conversation?', + [ + { text: 'Cancel', style: 'cancel' }, + { + text: 'Delete', + style: 'destructive', + onPress: () => { + console.log('[ConversationSelector] Deleting conversation via WebSocket:', id); + websocket.send({ + type: 'session', + message: { + type: 'delete_conversation_request', + conversationId: id, + }, + }); + }, + }, + ] + ); + } + + function handleClearAll() { + Alert.alert( + 'Clear All Conversations', + 'Are you sure you want to delete all conversations?', + [ + { text: 'Cancel', style: 'cancel' }, + { + text: 'Clear All', + style: 'destructive', + onPress: () => { + console.log('[ConversationSelector] Clearing all conversations via WebSocket'); + // Delete all conversations + conversations.forEach((conv) => { + websocket.send({ + type: 'session', + message: { + type: 'delete_conversation_request', + conversationId: conv.id, + }, + }); + }); + + // Clear local state + setConversations([]); + + // Start new conversation + handleNewConversation(); + setIsOpen(false); + }, + }, + ] + ); + } + + async function handleSelectConversation(id: string) { + try { + // Save to AsyncStorage for persistence + await AsyncStorage.setItem(STORAGE_KEY, id); + onSelectConversation(id); + setIsOpen(false); + } catch (error) { + console.error('[ConversationSelector] Failed to save conversation ID:', error); + } + } + + async function handleNewConversation() { + try { + // Clear saved conversation ID + await AsyncStorage.removeItem(STORAGE_KEY); + onSelectConversation(null); + setIsOpen(false); + } catch (error) { + console.error('[ConversationSelector] Failed to clear conversation ID:', error); + } + } + + function formatDate(dateString: string): string { + const date = new Date(dateString); + const now = new Date(); + const diffMs = now.getTime() - date.getTime(); + const diffMins = Math.floor(diffMs / 60000); + const diffHours = Math.floor(diffMs / 3600000); + const diffDays = Math.floor(diffMs / 86400000); + + if (diffMins < 1) return 'Just now'; + if (diffMins < 60) return `${diffMins}m ago`; + if (diffHours < 24) return `${diffHours}h ago`; + if (diffDays < 7) return `${diffDays}d ago`; + return date.toLocaleDateString(); + } + + return ( + + setIsOpen(true)} + className="bg-zinc-800 px-4 py-2 rounded-lg" + > + + + + setIsOpen(false)} + > + + setIsOpen(false)} + /> + + {/* Header */} + + Conversations + setIsOpen(false)}> + + + + + {/* New Conversation Button */} + + + + New Conversation + + + + {/* Conversations List */} + + {isLoading ? ( + + Loading... + + ) : conversations.length > 0 ? ( + <> + {conversations.map((conversation) => ( + handleSelectConversation(conversation.id)} + className={`flex-row items-center justify-between p-4 border-b border-zinc-800 ${ + conversation.id === currentConversationId ? 'bg-zinc-800' : '' + }`} + > + + + {conversation.messageCount} messages + + + {formatDate(conversation.lastUpdated)} + + + handleDeleteConversation(conversation.id)} + className="p-2" + hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }} + > + + + + ))} + + {/* Clear All Button */} + + + + Clear All + + + + ) : ( + + No saved conversations + + )} + + + + + + ); +} diff --git a/packages/voice-mobile/components/external-link.tsx b/packages/voice-mobile/components/external-link.tsx deleted file mode 100644 index 883e515a4..000000000 --- a/packages/voice-mobile/components/external-link.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { Href, Link } from 'expo-router'; -import { openBrowserAsync, WebBrowserPresentationStyle } from 'expo-web-browser'; -import { type ComponentProps } from 'react'; - -type Props = Omit, 'href'> & { href: Href & string }; - -export function ExternalLink({ href, ...rest }: Props) { - return ( - { - if (process.env.EXPO_OS !== 'web') { - // Prevent the default behavior of linking to the default browser on native. - event.preventDefault(); - // Open the link in an in-app browser. - await openBrowserAsync(href, { - presentationStyle: WebBrowserPresentationStyle.AUTOMATIC, - }); - } - }} - /> - ); -} diff --git a/packages/voice-mobile/components/haptic-tab.tsx b/packages/voice-mobile/components/haptic-tab.tsx deleted file mode 100644 index 7f3981cb9..000000000 --- a/packages/voice-mobile/components/haptic-tab.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { BottomTabBarButtonProps } from '@react-navigation/bottom-tabs'; -import { PlatformPressable } from '@react-navigation/elements'; -import * as Haptics from 'expo-haptics'; - -export function HapticTab(props: BottomTabBarButtonProps) { - return ( - { - if (process.env.EXPO_OS === 'ios') { - // Add a soft haptic feedback when pressing down on the tabs. - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); - } - props.onPressIn?.(ev); - }} - /> - ); -} diff --git a/packages/voice-mobile/components/hello-wave.tsx b/packages/voice-mobile/components/hello-wave.tsx deleted file mode 100644 index 5def547c2..000000000 --- a/packages/voice-mobile/components/hello-wave.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import Animated from 'react-native-reanimated'; - -export function HelloWave() { - return ( - - 👋 - - ); -} diff --git a/packages/voice-mobile/components/message.tsx b/packages/voice-mobile/components/message.tsx new file mode 100644 index 000000000..cae7a7514 --- /dev/null +++ b/packages/voice-mobile/components/message.tsx @@ -0,0 +1,288 @@ +import { View, Text, Pressable, Animated } from 'react-native'; +import { useState, useEffect, useRef } from 'react'; + +interface UserMessageProps { + message: string; + timestamp: number; +} + +export function UserMessage({ message, timestamp }: UserMessageProps) { + const time = new Date(timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); + + return ( + + + {message} + {time} + + + ); +} + +interface AssistantMessageProps { + message: string; + timestamp: number; + isStreaming?: boolean; +} + +export function AssistantMessage({ message, timestamp, isStreaming = false }: AssistantMessageProps) { + const time = new Date(timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); + const fadeAnim = useRef(new Animated.Value(0.3)).current; + + useEffect(() => { + if (isStreaming) { + Animated.loop( + Animated.sequence([ + Animated.timing(fadeAnim, { + toValue: 1, + duration: 600, + useNativeDriver: true, + }), + Animated.timing(fadeAnim, { + toValue: 0.3, + duration: 600, + useNativeDriver: true, + }), + ]) + ).start(); + } else { + fadeAnim.stopAnimation(); + fadeAnim.setValue(1); + } + }, [isStreaming, fadeAnim]); + + return ( + + + {message} + + {time} + {isStreaming && ( + + ... + + )} + + + + ); +} + +interface ActivityLogProps { + type: 'system' | 'info' | 'success' | 'error' | 'artifact'; + message: string; + timestamp: number; + metadata?: Record; + artifactId?: string; + artifactType?: string; + title?: string; + onArtifactClick?: (artifactId: string) => void; +} + +export function ActivityLog({ + type, + message, + timestamp, + metadata, + artifactId, + artifactType, + title, + onArtifactClick +}: ActivityLogProps) { + const [isExpanded, setIsExpanded] = useState(false); + + const typeConfig = { + system: { bg: 'bg-zinc-800/50', text: 'text-zinc-400', icon: '●' }, + info: { bg: 'bg-blue-900/30', text: 'text-blue-400', icon: 'i' }, + success: { bg: 'bg-green-900/30', text: 'text-green-400', icon: '✓' }, + error: { bg: 'bg-red-900/30', text: 'text-red-400', icon: '✗' }, + artifact: { bg: 'bg-blue-900/40', text: 'text-blue-300', icon: '📋' }, + }; + + const config = typeConfig[type]; + + const handlePress = () => { + if (type === 'artifact' && artifactId && onArtifactClick) { + onArtifactClick(artifactId); + } else if (metadata) { + setIsExpanded(!isExpanded); + } + }; + + const displayMessage = type === 'artifact' && artifactType && title + ? `${artifactType}: ${title}` + : message; + + return ( + + + + + {config.icon} + + + + {displayMessage} + + {metadata && ( + + Details + + {isExpanded ? '▼' : '▶'} + + + )} + + + {isExpanded && metadata && ( + + + {JSON.stringify(metadata, null, 2)} + + + )} + + + ); +} + +interface ToolCallProps { + toolName: string; + args: any; + result?: any; + error?: any; + status: 'executing' | 'completed' | 'failed'; +} + +export function ToolCall({ toolName, args, result, error, status }: ToolCallProps) { + const [isExpanded, setIsExpanded] = useState(false); + const spinAnim = useRef(new Animated.Value(0)).current; + + useEffect(() => { + if (status === 'executing') { + Animated.loop( + Animated.timing(spinAnim, { + toValue: 1, + duration: 1000, + useNativeDriver: true, + }) + ).start(); + } else { + spinAnim.stopAnimation(); + spinAnim.setValue(0); + } + }, [status, spinAnim]); + + const spin = spinAnim.interpolate({ + inputRange: [0, 1], + outputRange: ['0deg', '360deg'], + }); + + const statusConfig = { + executing: { + border: 'border-amber-500', + bg: 'bg-amber-500/20', + text: 'text-amber-300', + icon: '⟳', + label: 'executing', + }, + completed: { + border: 'border-green-500', + bg: 'bg-green-500/20', + text: 'text-green-300', + icon: '✓', + label: 'completed', + }, + failed: { + border: 'border-red-500', + bg: 'bg-red-500/20', + text: 'text-red-300', + icon: '✗', + label: 'failed', + }, + }; + + const config = statusConfig[status]; + + return ( + setIsExpanded(!isExpanded)} + className={`mx-2 mb-2 bg-zinc-900 rounded-lg border ${config.border} overflow-hidden active:opacity-80`} + > + + + + {isExpanded ? '▼' : '▶'} + + + {toolName} + + + {status === 'executing' ? ( + + {config.icon} + + ) : ( + + {config.icon} + + )} + + {config.label} + + + + + {isExpanded && ( + + + + Arguments + + + + {JSON.stringify(args, null, 2)} + + + + + {result !== undefined && ( + + + Result + + + + {JSON.stringify(result, null, 2)} + + + + )} + + {error !== undefined && ( + + + Error + + + + {JSON.stringify(error, null, 2)} + + + + )} + + )} + + + ); +} diff --git a/packages/voice-mobile/components/parallax-scroll-view.tsx b/packages/voice-mobile/components/parallax-scroll-view.tsx deleted file mode 100644 index 6f674a762..000000000 --- a/packages/voice-mobile/components/parallax-scroll-view.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import type { PropsWithChildren, ReactElement } from 'react'; -import { StyleSheet } from 'react-native'; -import Animated, { - interpolate, - useAnimatedRef, - useAnimatedStyle, - useScrollOffset, -} from 'react-native-reanimated'; - -import { ThemedView } from '@/components/themed-view'; -import { useColorScheme } from '@/hooks/use-color-scheme'; -import { useThemeColor } from '@/hooks/use-theme-color'; - -const HEADER_HEIGHT = 250; - -type Props = PropsWithChildren<{ - headerImage: ReactElement; - headerBackgroundColor: { dark: string; light: string }; -}>; - -export default function ParallaxScrollView({ - children, - headerImage, - headerBackgroundColor, -}: Props) { - const backgroundColor = useThemeColor({}, 'background'); - const colorScheme = useColorScheme() ?? 'light'; - const scrollRef = useAnimatedRef(); - const scrollOffset = useScrollOffset(scrollRef); - const headerAnimatedStyle = useAnimatedStyle(() => { - return { - transform: [ - { - translateY: interpolate( - scrollOffset.value, - [-HEADER_HEIGHT, 0, HEADER_HEIGHT], - [-HEADER_HEIGHT / 2, 0, HEADER_HEIGHT * 0.75] - ), - }, - { - scale: interpolate(scrollOffset.value, [-HEADER_HEIGHT, 0, HEADER_HEIGHT], [2, 1, 1]), - }, - ], - }; - }); - - return ( - - - {headerImage} - - {children} - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, - header: { - height: HEADER_HEIGHT, - overflow: 'hidden', - }, - content: { - flex: 1, - padding: 32, - gap: 16, - overflow: 'hidden', - }, -}); diff --git a/packages/voice-mobile/components/themed-text.tsx b/packages/voice-mobile/components/themed-text.tsx deleted file mode 100644 index d79d0a1c5..000000000 --- a/packages/voice-mobile/components/themed-text.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { StyleSheet, Text, type TextProps } from 'react-native'; - -import { useThemeColor } from '@/hooks/use-theme-color'; - -export type ThemedTextProps = TextProps & { - lightColor?: string; - darkColor?: string; - type?: 'default' | 'title' | 'defaultSemiBold' | 'subtitle' | 'link'; -}; - -export function ThemedText({ - style, - lightColor, - darkColor, - type = 'default', - ...rest -}: ThemedTextProps) { - const color = useThemeColor({ light: lightColor, dark: darkColor }, 'text'); - - return ( - - ); -} - -const styles = StyleSheet.create({ - default: { - fontSize: 16, - lineHeight: 24, - }, - defaultSemiBold: { - fontSize: 16, - lineHeight: 24, - fontWeight: '600', - }, - title: { - fontSize: 32, - fontWeight: 'bold', - lineHeight: 32, - }, - subtitle: { - fontSize: 20, - fontWeight: 'bold', - }, - link: { - lineHeight: 30, - fontSize: 16, - color: '#0a7ea4', - }, -}); diff --git a/packages/voice-mobile/components/themed-view.tsx b/packages/voice-mobile/components/themed-view.tsx deleted file mode 100644 index 6f181d826..000000000 --- a/packages/voice-mobile/components/themed-view.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { View, type ViewProps } from 'react-native'; - -import { useThemeColor } from '@/hooks/use-theme-color'; - -export type ThemedViewProps = ViewProps & { - lightColor?: string; - darkColor?: string; -}; - -export function ThemedView({ style, lightColor, darkColor, ...otherProps }: ThemedViewProps) { - const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, 'background'); - - return ; -} diff --git a/packages/voice-mobile/components/ui/collapsible.tsx b/packages/voice-mobile/components/ui/collapsible.tsx deleted file mode 100644 index 6345fdebe..000000000 --- a/packages/voice-mobile/components/ui/collapsible.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { PropsWithChildren, useState } from 'react'; -import { StyleSheet, TouchableOpacity } from 'react-native'; - -import { ThemedText } from '@/components/themed-text'; -import { ThemedView } from '@/components/themed-view'; -import { IconSymbol } from '@/components/ui/icon-symbol'; -import { Colors } from '@/constants/theme'; -import { useColorScheme } from '@/hooks/use-color-scheme'; - -export function Collapsible({ children, title }: PropsWithChildren & { title: string }) { - const [isOpen, setIsOpen] = useState(false); - const theme = useColorScheme() ?? 'light'; - - return ( - - setIsOpen((value) => !value)} - activeOpacity={0.8}> - - - {title} - - {isOpen && {children}} - - ); -} - -const styles = StyleSheet.create({ - heading: { - flexDirection: 'row', - alignItems: 'center', - gap: 6, - }, - content: { - marginTop: 6, - marginLeft: 24, - }, -}); diff --git a/packages/voice-mobile/components/ui/icon-symbol.ios.tsx b/packages/voice-mobile/components/ui/icon-symbol.ios.tsx deleted file mode 100644 index 9177f4daf..000000000 --- a/packages/voice-mobile/components/ui/icon-symbol.ios.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { SymbolView, SymbolViewProps, SymbolWeight } from 'expo-symbols'; -import { StyleProp, ViewStyle } from 'react-native'; - -export function IconSymbol({ - name, - size = 24, - color, - style, - weight = 'regular', -}: { - name: SymbolViewProps['name']; - size?: number; - color: string; - style?: StyleProp; - weight?: SymbolWeight; -}) { - return ( - - ); -} diff --git a/packages/voice-mobile/components/ui/icon-symbol.tsx b/packages/voice-mobile/components/ui/icon-symbol.tsx deleted file mode 100644 index b7ece6b35..000000000 --- a/packages/voice-mobile/components/ui/icon-symbol.tsx +++ /dev/null @@ -1,41 +0,0 @@ -// Fallback for using MaterialIcons on Android and web. - -import MaterialIcons from '@expo/vector-icons/MaterialIcons'; -import { SymbolWeight, SymbolViewProps } from 'expo-symbols'; -import { ComponentProps } from 'react'; -import { OpaqueColorValue, type StyleProp, type TextStyle } from 'react-native'; - -type IconMapping = Record['name']>; -type IconSymbolName = keyof typeof MAPPING; - -/** - * Add your SF Symbols to Material Icons mappings here. - * - see Material Icons in the [Icons Directory](https://icons.expo.fyi). - * - see SF Symbols in the [SF Symbols](https://developer.apple.com/sf-symbols/) app. - */ -const MAPPING = { - 'house.fill': 'home', - 'paperplane.fill': 'send', - 'chevron.left.forwardslash.chevron.right': 'code', - 'chevron.right': 'chevron-right', -} as IconMapping; - -/** - * An icon component that uses native SF Symbols on iOS, and Material Icons on Android and web. - * This ensures a consistent look across platforms, and optimal resource usage. - * Icon `name`s are based on SF Symbols and require manual mapping to Material Icons. - */ -export function IconSymbol({ - name, - size = 24, - color, - style, -}: { - name: IconSymbolName; - size?: number; - color: string | OpaqueColorValue; - style?: StyleProp; - weight?: SymbolWeight; -}) { - return ; -} diff --git a/packages/voice-mobile/components/voice-button.tsx b/packages/voice-mobile/components/voice-button.tsx new file mode 100644 index 000000000..18e8423a7 --- /dev/null +++ b/packages/voice-mobile/components/voice-button.tsx @@ -0,0 +1,109 @@ +import { Pressable, View, Text, Animated } from 'react-native'; +import { useEffect, useRef } from 'react'; + +interface VoiceButtonProps { + state: 'idle' | 'recording' | 'processing' | 'playing'; + onPress: () => void; + disabled?: boolean; +} + +export function VoiceButton({ state, onPress, disabled = false }: VoiceButtonProps) { + const pulseAnim = useRef(new Animated.Value(1)).current; + + useEffect(() => { + if (state === 'recording') { + Animated.loop( + Animated.sequence([ + Animated.timing(pulseAnim, { + toValue: 1.2, + duration: 800, + useNativeDriver: true, + }), + Animated.timing(pulseAnim, { + toValue: 1, + duration: 800, + useNativeDriver: true, + }), + ]) + ).start(); + } else { + pulseAnim.setValue(1); + } + }, [state, pulseAnim]); + + const getButtonStyle = () => { + switch (state) { + case 'recording': + return 'bg-red-500'; + case 'processing': + return 'bg-blue-500'; + case 'playing': + return 'bg-green-500'; + default: + return 'bg-zinc-700'; + } + }; + + const getIcon = () => { + switch (state) { + case 'recording': + return ( + + ); + case 'processing': + return ( + + + + + + ); + case 'playing': + return ( + + + + + + ); + default: + return ( + + + + + ); + } + }; + + const getLabel = () => { + switch (state) { + case 'recording': + return 'Recording...'; + case 'processing': + return 'Processing...'; + case 'playing': + return 'Playing...'; + default: + return 'Tap to speak'; + } + }; + + return ( + + + + {getIcon()} + + + {getLabel()} + + ); +} diff --git a/packages/voice-mobile/eas.json b/packages/voice-mobile/eas.json new file mode 100644 index 000000000..57d519620 --- /dev/null +++ b/packages/voice-mobile/eas.json @@ -0,0 +1,22 @@ +{ + "cli": { + "version": ">= 14.0.0" + }, + "build": { + "development": { + "developmentClient": true, + "distribution": "internal", + "channel": "development" + }, + "preview": { + "distribution": "internal", + "channel": "preview" + }, + "production": { + "channel": "production" + } + }, + "submit": { + "production": {} + } +} diff --git a/packages/voice-mobile/hooks/use-audio-player.ts b/packages/voice-mobile/hooks/use-audio-player.ts new file mode 100644 index 000000000..bb6201f8c --- /dev/null +++ b/packages/voice-mobile/hooks/use-audio-player.ts @@ -0,0 +1,259 @@ +import { useAudioPlayer as useExpoAudioPlayer, setAudioModeAsync } from 'expo-audio'; +import { Paths, File } from 'expo-file-system'; +import { useState, useRef, useEffect } from 'react'; +import { Platform } from 'react-native'; + +interface QueuedAudio { + audioData: Blob; + resolve: (duration: number) => void; + reject: (error: Error) => void; +} + +interface AudioPlayerOptions { + useSpeaker?: boolean; +} + +/** + * Convert Blob to file URI for expo-audio playback + */ +async function blobToFile(blob: Blob): Promise { + // Read blob as array buffer + const arrayBuffer = await blob.arrayBuffer(); + + // Create temporary file in cache directory + const fileName = `audio_${Date.now()}.mp3`; + const file = new File(Paths.cache, fileName); + + // Write array buffer to file + const uint8Array = new Uint8Array(arrayBuffer); + file.create(); + const stream = file.writableStream(); + const writer = stream.getWriter(); + await writer.write(uint8Array); + await writer.close(); + + return file; +} + +/** + * Hook for audio playback with queue system matching web version functionality + */ +export function useAudioPlayer(options?: AudioPlayerOptions) { + const player = useExpoAudioPlayer(); + const [isPlaying, setIsPlaying] = useState(false); + const [isPaused, setIsPaused] = useState(false); + const queueRef = useRef([]); + const isProcessingQueueRef = useRef(false); + const currentRejectRef = useRef<((error: Error) => void) | null>(null); + const currentFileRef = useRef(null); + + // Configure audio mode based on speaker/earpiece preference + useEffect(() => { + async function configureAudioMode() { + try { + if (Platform.OS === 'android') { + // On Android, use shouldRouteThroughEarpiece + await setAudioModeAsync({ + shouldRouteThroughEarpiece: !options?.useSpeaker, + }); + } else if (Platform.OS === 'ios') { + // On iOS, the audio routing is controlled by the AVAudioSession category + // The default category should work, but we can configure it if needed + // For now, we rely on the iOS default behavior which routes to the receiver + // unless the user has changed the route (e.g., via speaker button in Control Center) + } + } catch (error) { + console.warn('[AudioPlayer] Failed to configure audio mode:', error); + } + } + + configureAudioMode(); + }, [options?.useSpeaker]); + + // Monitor playback status + useEffect(() => { + if (!player.playing && isPlaying && !isPaused) { + // Playback finished + setIsPlaying(false); + processNextInQueue(); + } + }, [player.playing, isPlaying, isPaused]); + + async function play(audioData: Blob): Promise { + return new Promise((resolve, reject) => { + // Add to queue with its promise handlers + queueRef.current.push({ audioData, resolve, reject }); + + // Start processing queue if not already processing + if (!isProcessingQueueRef.current) { + processQueue(); + } + }); + } + + async function processQueue(): Promise { + if (isProcessingQueueRef.current || queueRef.current.length === 0) { + return; + } + + isProcessingQueueRef.current = true; + + while (queueRef.current.length > 0) { + const item = queueRef.current.shift()!; + try { + const duration = await playAudio(item.audioData); + item.resolve(duration); + } catch (error) { + item.reject(error as Error); + } + } + + isProcessingQueueRef.current = false; + } + + async function processNextInQueue(): Promise { + if (queueRef.current.length > 0) { + const item = queueRef.current.shift()!; + try { + const duration = await playAudio(item.audioData); + item.resolve(duration); + } catch (error) { + item.reject(error as Error); + } + } else { + isProcessingQueueRef.current = false; + } + } + + async function playAudio(audioData: Blob): Promise { + return new Promise(async (resolve, reject) => { + currentRejectRef.current = reject; + + try { + console.log( + `[AudioPlayer] Playing audio (${audioData.size} bytes, type: ${audioData.type})` + ); + + // Convert blob to file + const file = await blobToFile(audioData); + currentFileRef.current = file; + + setIsPlaying(true); + setIsPaused(false); + + // Load and play audio + await player.replace({ uri: file.uri }); + player.play(); + + // Wait for playback to finish + const checkInterval = setInterval(() => { + if (!player.playing && player.duration > 0) { + clearInterval(checkInterval); + + const duration = player.duration; + console.log(`[AudioPlayer] Playback finished (duration: ${duration}s)`); + + setIsPlaying(false); + currentRejectRef.current = null; + + // Clean up temporary file + if (currentFileRef.current) { + try { + currentFileRef.current.delete(); + } catch (err: any) { + console.warn('[AudioPlayer] Failed to delete temp file:', err); + } + currentFileRef.current = null; + } + + resolve(duration); + } + }, 100); + + } catch (error) { + console.error('[AudioPlayer] Error playing audio:', error); + setIsPlaying(false); + currentRejectRef.current = null; + + // Clean up temporary file + if (currentFileRef.current) { + try { + currentFileRef.current.delete(); + } catch (err: any) { + console.warn('[AudioPlayer] Failed to delete temp file:', err); + } + currentFileRef.current = null; + } + + reject(error); + } + }); + } + + function stop(): void { + // Stop currently playing audio + if (player) { + player.pause(); + } + setIsPlaying(false); + setIsPaused(false); + + // Clean up temporary file + if (currentFileRef.current) { + try { + currentFileRef.current.delete(); + } catch (err: any) { + console.warn('[AudioPlayer] Failed to delete temp file:', err); + } + currentFileRef.current = null; + } + + // Reject the current playing promise if it exists + if (currentRejectRef.current) { + currentRejectRef.current(new Error('Playback stopped')); + currentRejectRef.current = null; + } + + // Reject all pending promises in the queue + while (queueRef.current.length > 0) { + const item = queueRef.current.shift()!; + item.reject(new Error('Playback stopped')); + } + + isProcessingQueueRef.current = false; + } + + function pause(): void { + if (player && isPlaying && !isPaused) { + player.pause(); + setIsPaused(true); + console.log('[AudioPlayer] Playback paused'); + } + } + + function resume(): void { + if (player && isPaused) { + player.play(); + setIsPaused(false); + console.log('[AudioPlayer] Playback resumed'); + } + } + + function clearQueue(): void { + // Reject all pending promises in the queue + while (queueRef.current.length > 0) { + const item = queueRef.current.shift()!; + item.reject(new Error('Queue cleared')); + } + } + + return { + play, + stop, + pause, + resume, + isPlaying: () => isPlaying, + isPaused: () => isPaused, + clearQueue, + }; +} diff --git a/packages/voice-mobile/hooks/use-audio-recorder.ts b/packages/voice-mobile/hooks/use-audio-recorder.ts new file mode 100644 index 000000000..cd591d8b4 --- /dev/null +++ b/packages/voice-mobile/hooks/use-audio-recorder.ts @@ -0,0 +1,277 @@ +import { useAudioRecorder as useExpoAudioRecorder, RecordingOptions, RecordingPresets, requestRecordingPermissionsAsync, setAudioModeAsync } from 'expo-audio'; +import { Paths, File, Directory, FileInfo } from 'expo-file-system'; +import { useState } from 'react'; + +export interface AudioCaptureConfig { + sampleRate?: number; + numberOfChannels?: number; + bitRate?: number; +} + +/** + * Workaround for Expo SDK 54 Android bug where audioRecorder.uri returns empty/zero-byte file + * https://github.com/expo/expo/issues/39646 + */ +async function getActualRecordingUri(createdAt: Date): Promise { + try { + console.log('[AudioRecorder] Searching for recording file created at:', createdAt.toISOString()); + + const audioDir = new Directory(Paths.cache, 'Audio'); + console.log('[AudioRecorder] Audio cache directory URI:', audioDir.uri); + console.log('[AudioRecorder] Directory exists:', audioDir.exists); + + if (!audioDir.exists) { + console.log('[AudioRecorder] Audio cache directory does not exist'); + return null; + } + + const files = audioDir.list(); + console.log('[AudioRecorder] Found files in Audio cache:', files.length); + + if (!files.length) { + console.log('[AudioRecorder] No files found in Audio cache directory'); + return null; + } + + const validFiles = files + .map(file => { + const info = file.info(); + console.log('[AudioRecorder] File info:', { + uri: info.uri, + size: info.size, + creationTime: info.creationTime ? new Date(info.creationTime).toISOString() : null, + }); + return info; + }) + .filter(f => f.size && f.size > 0); + + console.log('[AudioRecorder] Valid files (size > 0):', validFiles.length); + + if (validFiles.length === 0) { + console.log('[AudioRecorder] No valid files found (all are zero-byte)'); + return null; + } + + let closest: FileInfo | null = null; + let minDiff = Infinity; + + for (const file of validFiles) { + if (!file.creationTime || !file.uri) continue; + const diff = Math.abs(file.creationTime - createdAt.getTime()); + console.log('[AudioRecorder] Time diff for file:', { + uri: file.uri, + diffMs: diff, + }); + if (diff < minDiff) { + closest = file; + minDiff = diff; + } + } + + if (closest) { + const resultUri = closest.uri?.slice(0, -1) ?? null; + console.log('[AudioRecorder] Found closest file:', { + uri: resultUri, + size: closest.size, + timeDiffMs: minDiff, + }); + return resultUri; + } + + console.log('[AudioRecorder] No closest file found'); + return null; + } catch (e) { + console.error('[AudioRecorder] Error finding actual recording file:', e); + return null; + } +} + +/** + * Convert audio file URI to Blob for WebSocket transmission + * Returns a Blob-like object that works in React Native + */ +async function uriToBlob(uri: string): Promise { + // Use expo-file-system to read the file + const file = new File(uri); + const base64 = await file.base64(); + const size = file.size; + + // React Native doesn't support creating Blobs from binary data + // Create a Blob-like object that has the methods we need + const blobLike = { + type: 'audio/m4a', + size: size, + arrayBuffer: async () => { + // Convert base64 to ArrayBuffer + const binaryString = atob(base64); + const bytes = new Uint8Array(binaryString.length); + for (let i = 0; i < binaryString.length; i++) { + bytes[i] = binaryString.charCodeAt(i); + } + return bytes.buffer; + }, + } as Blob; + + return blobLike; +} + +/** + * Hook for audio recording with configuration matching web version + * Matches the web app's audio constraints: + * - 16000 sample rate (optimal for speech/Whisper) + * - 1 channel (mono) + * - Echo cancellation, noise suppression, auto gain control (voice_communication on Android) + */ +export function useAudioRecorder(config?: AudioCaptureConfig) { + const [isRecording, setIsRecording] = useState(false); + const [recordingStartTime, setRecordingStartTime] = useState(null); + + // Create recording options matching web app constraints + const recordingOptions: RecordingOptions = { + ...RecordingPresets.HIGH_QUALITY, + sampleRate: config?.sampleRate || 16000, + numberOfChannels: config?.numberOfChannels || 1, + bitRate: config?.bitRate || 128000, + extension: '.m4a', + android: { + extension: '.m4a', + outputFormat: 'mpeg4', + audioEncoder: 'aac', + sampleRate: config?.sampleRate || 16000, + audioSource: 'voice_communication', // Enables echo cancellation, noise suppression, auto gain control + }, + ios: { + extension: '.m4a', + audioQuality: 127, // High quality + sampleRate: config?.sampleRate || 16000, + linearPCMBitDepth: 16, + linearPCMIsBigEndian: false, + linearPCMIsFloat: false, + }, + web: { + mimeType: 'audio/webm;codecs=opus', + bitsPerSecond: config?.bitRate || 128000, + }, + }; + + const audioRecorder = useExpoAudioRecorder(recordingOptions); + + async function start(): Promise { + if (isRecording) { + throw new Error('Already recording'); + } + + try { + // Request microphone permissions + console.log('[AudioRecorder] Requesting recording permissions...'); + const permissionResponse = await requestRecordingPermissionsAsync(); + + if (!permissionResponse.granted) { + throw new Error('Microphone permission denied. Please enable microphone access in your device settings.'); + } + + // Configure audio mode for recording + console.log('[AudioRecorder] Configuring audio mode...'); + await setAudioModeAsync({ + playsInSilentMode: true, + allowsRecording: true, + }); + + console.log('[AudioRecorder] Starting recording with options:', { + sampleRate: recordingOptions.sampleRate, + numberOfChannels: recordingOptions.numberOfChannels, + bitRate: recordingOptions.bitRate, + }); + + const startTime = new Date(); + setRecordingStartTime(startTime); + + // Prepare the recorder before recording (required step) + console.log('[AudioRecorder] Preparing recorder...'); + await audioRecorder.prepareToRecordAsync(); + + console.log('[AudioRecorder] Starting recording...'); + await audioRecorder.record(); + setIsRecording(true); + + console.log('[AudioRecorder] Recording started at:', startTime.toISOString()); + console.log('[AudioRecorder] Recorder isRecording:', audioRecorder.isRecording); + } catch (error: any) { + console.error('[AudioRecorder] Failed to start recording:', error); + throw new Error(`Failed to start audio recording: ${error.message}`); + } + } + + async function stop(): Promise { + if (!isRecording) { + throw new Error('Not recording'); + } + + try { + // Stop recording + await audioRecorder.stop(); + setIsRecording(false); + + // Get URI from recorder + let uri = audioRecorder.uri; + console.log('[AudioRecorder] Initial URI from recorder:', uri); + + // Workaround for Expo SDK 54 Android bug - find actual recording file + if (recordingStartTime && (!uri || uri === '')) { + console.log('[AudioRecorder] Using workaround to find actual recording file...'); + const actualUri = await getActualRecordingUri(recordingStartTime); + if (actualUri) { + uri = actualUri; + console.log('[AudioRecorder] Found actual recording URI:', uri); + } + } + + if (!uri || uri === '') { + throw new Error('No recording URI found'); + } + + // Get file info + const file = new File(uri); + const exists = file.exists; + console.log('[AudioRecorder] File exists:', exists); + + if (!exists) { + throw new Error('Recording file does not exist'); + } + + // Convert URI to Blob + const audioBlob = await uriToBlob(uri); + + console.log('[AudioRecorder] Recording converted to blob:', { + size: audioBlob.size, + type: audioBlob.type, + }); + + // Clean up the temporary file + file.delete(); + + // Reset start time + setRecordingStartTime(null); + + return audioBlob; + } catch (error: any) { + setIsRecording(false); + setRecordingStartTime(null); + console.error('[AudioRecorder] Failed to stop recording:', error); + throw new Error(`Failed to stop audio recording: ${error.message}`); + } + } + + function getSupportedMimeType(): string | null { + // On native platforms, expo-audio uses m4a/AAC + // On web, it can use webm/opus + return 'audio/m4a'; + } + + return { + start, + stop, + isRecording: () => isRecording, + getSupportedMimeType, + }; +} diff --git a/packages/voice-mobile/hooks/use-settings.ts b/packages/voice-mobile/hooks/use-settings.ts new file mode 100644 index 000000000..1bd87d119 --- /dev/null +++ b/packages/voice-mobile/hooks/use-settings.ts @@ -0,0 +1,78 @@ +import { useState, useEffect, useCallback } from 'react'; +import AsyncStorage from '@react-native-async-storage/async-storage'; + +const STORAGE_KEY = '@voice-assistant:settings'; + +export interface Settings { + serverUrl: string; + useSpeaker: boolean; + keepScreenOn: boolean; + theme: 'dark' | 'light' | 'auto'; +} + +const DEFAULT_SETTINGS: Settings = { + serverUrl: 'wss://mohameds-macbook-pro.tail8fe838.ts.net/ws', + useSpeaker: true, + keepScreenOn: true, + theme: 'dark', +}; + +export interface UseSettingsReturn { + settings: Settings; + isLoading: boolean; + updateSettings: (updates: Partial) => Promise; + resetSettings: () => Promise; +} + +export function useSettings(): UseSettingsReturn { + const [settings, setSettings] = useState(DEFAULT_SETTINGS); + const [isLoading, setIsLoading] = useState(true); + + // Load settings from AsyncStorage on mount + useEffect(() => { + loadSettings(); + }, []); + + async function loadSettings() { + try { + const stored = await AsyncStorage.getItem(STORAGE_KEY); + if (stored) { + const parsed = JSON.parse(stored) as Partial; + setSettings({ ...DEFAULT_SETTINGS, ...parsed }); + } + } catch (error) { + console.error('[Settings] Failed to load settings:', error); + // Continue with default settings + } finally { + setIsLoading(false); + } + } + + const updateSettings = useCallback(async (updates: Partial) => { + try { + const newSettings = { ...settings, ...updates }; + setSettings(newSettings); + await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(newSettings)); + } catch (error) { + console.error('[Settings] Failed to save settings:', error); + throw error; + } + }, [settings]); + + const resetSettings = useCallback(async () => { + try { + setSettings(DEFAULT_SETTINGS); + await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(DEFAULT_SETTINGS)); + } catch (error) { + console.error('[Settings] Failed to reset settings:', error); + throw error; + } + }, []); + + return { + settings, + isLoading, + updateSettings, + resetSettings, + }; +} diff --git a/packages/voice-mobile/hooks/use-websocket.ts b/packages/voice-mobile/hooks/use-websocket.ts new file mode 100644 index 000000000..1e4f21eaf --- /dev/null +++ b/packages/voice-mobile/hooks/use-websocket.ts @@ -0,0 +1,161 @@ +import { useEffect, useRef, useState, useCallback } from 'react'; +import type { + WSInboundMessage, + WSOutboundMessage, + SessionOutboundMessage +} from '@voice-assistant/server/messages'; + +export interface UseWebSocketReturn { + isConnected: boolean; + conversationId: string | null; + send: (message: WSInboundMessage) => void; + on: (type: SessionOutboundMessage['type'], handler: (message: SessionOutboundMessage) => void) => () => void; + sendPing: () => void; + sendUserMessage: (message: string) => void; +} + +export function useWebSocket(url: string, conversationId?: string | null): UseWebSocketReturn { + const [isConnected, setIsConnected] = useState(false); + const [currentConversationId, setCurrentConversationId] = useState(null); + const wsRef = useRef(null); + const handlersRef = useRef void>>>(new Map()); + const reconnectTimeoutRef = useRef | undefined>(undefined); + + const connect = useCallback(() => { + if (wsRef.current?.readyState === WebSocket.OPEN) { + return; + } + + try { + // Add conversation ID to URL if provided + const wsUrl = conversationId ? `${url}?conversationId=${conversationId}` : url; + const ws = new WebSocket(wsUrl); + + ws.onopen = () => { + console.log('[WS] Connected to server'); + setIsConnected(true); + }; + + ws.onclose = () => { + console.log('[WS] Disconnected from server'); + setIsConnected(false); + + // Attempt to reconnect after 3 seconds + reconnectTimeoutRef.current = setTimeout(() => { + console.log('[WS] Attempting to reconnect...'); + connect(); + }, 3000); + }; + + ws.onerror = (error) => { + console.error('[WS] Error:', error); + }; + + ws.onmessage = (event) => { + try { + const wsMessage: WSOutboundMessage = JSON.parse(event.data); + + // Only session messages trigger handlers + if (wsMessage.type === 'session') { + const sessionMessage = wsMessage.message; + console.log(`[WS] Received session message type: ${sessionMessage.type}`); + + // Track conversation ID when loaded + if (sessionMessage.type === 'conversation_loaded') { + setCurrentConversationId(sessionMessage.payload.conversationId); + } + + // Call all registered handlers for this message type + const handlers = handlersRef.current.get(sessionMessage.type); + if (handlers) { + handlers.forEach((handler) => { + try { + handler(sessionMessage); + } catch (err) { + console.error(`[WS] Error in handler for ${sessionMessage.type}:`, err); + } + }); + } + } else { + // pong - just log + console.log(`[WS] Received ${wsMessage.type}`); + } + } catch (err) { + console.error('[WS] Failed to parse message:', err); + } + }; + + wsRef.current = ws; + } catch (err) { + console.error('[WS] Failed to create WebSocket:', err); + } + }, [url, conversationId]); + + useEffect(() => { + connect(); + + return () => { + if (reconnectTimeoutRef.current) { + clearTimeout(reconnectTimeoutRef.current); + } + if (wsRef.current) { + wsRef.current.close(); + } + }; + }, [connect]); + + const send = useCallback((message: WSInboundMessage) => { + if (wsRef.current?.readyState === WebSocket.OPEN) { + wsRef.current.send(JSON.stringify(message)); + } else { + console.warn('[WS] Cannot send message - not connected'); + } + }, []); + + const on = useCallback(( + type: SessionOutboundMessage['type'], + handler: (message: SessionOutboundMessage) => void + ) => { + if (!handlersRef.current.has(type)) { + handlersRef.current.set(type, new Set()); + } + handlersRef.current.get(type)!.add(handler); + + // Return cleanup function + return () => { + const handlers = handlersRef.current.get(type); + if (handlers) { + handlers.delete(handler); + if (handlers.size === 0) { + handlersRef.current.delete(type); + } + } + }; + }, []); + + const sendPing = useCallback(() => { + send({ type: 'ping' }); + }, [send]); + + const sendUserMessage = useCallback( + (message: string) => { + send({ + type: 'session', + message: { + type: 'user_text', + text: message, + }, + }); + }, + [send] + ); + + return { + isConnected, + conversationId: currentConversationId, + send, + on, + sendPing, + sendUserMessage, + }; +} diff --git a/packages/voice-mobile/package-lock.json b/packages/voice-mobile/package-lock.json index fc71401f9..c9caf3292 100644 --- a/packages/voice-mobile/package-lock.json +++ b/packages/voice-mobile/package-lock.json @@ -9,20 +9,27 @@ "version": "1.0.0", "dependencies": { "@expo/vector-icons": "^15.0.2", + "@react-native-async-storage/async-storage": "2.2.0", "@react-navigation/bottom-tabs": "^7.4.0", "@react-navigation/elements": "^2.6.3", "@react-navigation/native": "^7.1.8", "expo": "~54.0.15", + "expo-audio": "~1.0.13", + "expo-av": "^16.0.7", "expo-constants": "~18.0.9", + "expo-dev-client": "^6.0.15", + "expo-file-system": "~19.0.17", "expo-font": "~14.0.9", "expo-haptics": "~15.0.7", "expo-image": "~3.0.10", + "expo-keep-awake": "^15.0.7", "expo-linking": "~8.0.8", "expo-router": "~6.0.13", "expo-splash-screen": "~31.0.10", "expo-status-bar": "~3.0.8", "expo-symbols": "~1.0.7", "expo-system-ui": "~6.0.7", + "expo-updates": "~29.0.12", "expo-web-browser": "~15.0.8", "nativewind": "^5.0.0-preview.2", "react": "19.1.0", @@ -39,6 +46,7 @@ "devDependencies": { "@tailwindcss/postcss": "^4.1.15", "@types/react": "~19.1.0", + "eas-cli": "^16.24.1", "eslint": "^9.25.0", "eslint-config-expo": "~10.0.0", "postcss": "^8.4.49", @@ -1550,6 +1558,30 @@ "node": ">=6.9.0" } }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@egjs/hammerjs": { "version": "2.0.17", "resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz", @@ -1788,6 +1820,39 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@expo/apple-utils": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@expo/apple-utils/-/apple-utils-2.1.12.tgz", + "integrity": "sha512-ugpL2URxNFxIRw943AIpX3dcvL5rhNCumpL8XTosYqZPyQQ7JRLVNd1m8FNyPg3pmFrz8M4tSucY2pt2ATuKOA==", + "dev": true, + "license": "MIT", + "bin": { + "apple-utils": "bin.js" + } + }, + "node_modules/@expo/bunyan": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@expo/bunyan/-/bunyan-4.0.1.tgz", + "integrity": "sha512-+Lla7nYSiHZirgK+U/uYzsLv/X+HaJienbD5AKX1UQZHYfWaP+9uuQluRB4GrEVWF0GZ7vEVp/jzaOT9k/SQlg==", + "dev": true, + "license": "MIT", + "dependencies": { + "uuid": "^8.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@expo/bunyan/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@expo/code-signing-certificates": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/@expo/code-signing-certificates/-/code-signing-certificates-0.0.5.tgz", @@ -1921,6 +1986,281 @@ } } }, + "node_modules/@expo/eas-build-job": { + "version": "1.0.239", + "resolved": "https://registry.npmjs.org/@expo/eas-build-job/-/eas-build-job-1.0.239.tgz", + "integrity": "sha512-5P/dQL2S2iEyTvGKOPN95r7cNFW3HgnA4PNqXU0s5ctb10837sMX6/A5XgIg/Ow0SFGZRiP4cK9ZFAiK3m2Ong==", + "dev": true, + "license": "MIT", + "dependencies": { + "@expo/logger": "1.0.221", + "joi": "^17.13.1", + "semver": "^7.6.2", + "zod": "^4.1.3" + } + }, + "node_modules/@expo/eas-build-job/node_modules/joi": { + "version": "17.13.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", + "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, + "node_modules/@expo/eas-build-job/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@expo/eas-build-job/node_modules/zod": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz", + "integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@expo/eas-json": { + "version": "16.23.0", + "resolved": "https://registry.npmjs.org/@expo/eas-json/-/eas-json-16.23.0.tgz", + "integrity": "sha512-HgG3P5OzCp7TpC9ZqBglkamq6N4M4gdigqmCijegYMgLnJpc/Sn1zOYuLAvUdsBAZFvJBs5aeUaFbmkrF2/ivw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "7.23.5", + "@expo/eas-build-job": "1.0.231", + "chalk": "4.1.2", + "env-string": "1.0.1", + "fs-extra": "11.2.0", + "golden-fleece": "1.0.9", + "joi": "17.11.0", + "log-symbols": "4.1.0", + "semver": "7.5.2", + "terminal-link": "2.1.1", + "tslib": "2.4.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@expo/eas-json/node_modules/@babel/code-frame": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@expo/eas-json/node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@expo/eas-json/node_modules/@expo/eas-build-job": { + "version": "1.0.231", + "resolved": "https://registry.npmjs.org/@expo/eas-build-job/-/eas-build-job-1.0.231.tgz", + "integrity": "sha512-jktsZ7IJYcC9lbgUWftbFCpfrhcGwJX4q0qSubUrqvFTMuUyLjCzJqyIZ4znbXLXfJSUPDJNAd4H0wBqKJA9kA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@expo/logger": "1.0.221", + "joi": "^17.13.1", + "semver": "^7.6.2", + "zod": "^4.1.3" + } + }, + "node_modules/@expo/eas-json/node_modules/@expo/eas-build-job/node_modules/joi": { + "version": "17.13.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", + "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, + "node_modules/@expo/eas-json/node_modules/@expo/eas-build-job/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@expo/eas-json/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@expo/eas-json/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@expo/eas-json/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@expo/eas-json/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@expo/eas-json/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@expo/eas-json/node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@expo/eas-json/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@expo/eas-json/node_modules/semver": { + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.2.tgz", + "integrity": "sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@expo/eas-json/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@expo/eas-json/node_modules/tslib": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@expo/eas-json/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/@expo/eas-json/node_modules/zod": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz", + "integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "node_modules/@expo/env": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/@expo/env/-/env-2.0.7.tgz", @@ -2017,6 +2357,17 @@ "@babel/highlight": "^7.10.4" } }, + "node_modules/@expo/logger": { + "version": "1.0.221", + "resolved": "https://registry.npmjs.org/@expo/logger/-/logger-1.0.221.tgz", + "integrity": "sha512-arNkhzq4Ludz9J7PIYWTVYXuT1bO/A8prR8HYAviKuF6fEZfitemNVriGSetQASOE8Vnv3lmBZv6YIwLiFuv/Q==", + "dev": true, + "license": "BUSL-1.1", + "dependencies": { + "@types/bunyan": "^1.8.11", + "bunyan": "^1.8.15" + } + }, "node_modules/@expo/mcp-tunnel": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/@expo/mcp-tunnel/-/mcp-tunnel-0.0.8.tgz", @@ -2165,6 +2516,16 @@ } } }, + "node_modules/@expo/multipart-body-parser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@expo/multipart-body-parser/-/multipart-body-parser-2.0.0.tgz", + "integrity": "sha512-yS/wsqlj0d8ZKETEN7ro3dZtjdMhpte8wp+xUzjUQC3jizxcE0E62xgvGquJObiYUMGoCF5qRYr2t78STPEaSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "multipasta": "^0.2.5" + } + }, "node_modules/@expo/osascript": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/@expo/osascript/-/osascript-2.3.7.tgz", @@ -2192,6 +2553,16 @@ "resolve-workspace-root": "^2.0.0" } }, + "node_modules/@expo/pkcs12": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@expo/pkcs12/-/pkcs12-0.1.3.tgz", + "integrity": "sha512-96MePEGppKi08vawrTPw8kMCRdsbrDbV900MlI8rrP9F57DfDl/y1P52bwIDBYCEHE3XtPMo7s1xkG0BKOLCVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "node-forge": "^1.2.1" + } + }, "node_modules/@expo/plist": { "version": "0.4.7", "resolved": "https://registry.npmjs.org/@expo/plist/-/plist-0.4.7.tgz", @@ -2203,6 +2574,333 @@ "xmlbuilder": "^15.1.1" } }, + "node_modules/@expo/plugin-help": { + "version": "5.1.23", + "resolved": "https://registry.npmjs.org/@expo/plugin-help/-/plugin-help-5.1.23.tgz", + "integrity": "sha512-s0uH6cPplLj73ZVie40EYUhl7X7q9kRR+8IfZWDod3wUtVGOFInxuCPX9Jpv1UwwBgbRu2cLisqr8m45LrFgxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oclif/core": "^2.11.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@expo/plugin-help/node_modules/@oclif/core": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/@oclif/core/-/core-2.16.0.tgz", + "integrity": "sha512-dL6atBH0zCZl1A1IXCKJgLPrM/wR7K+Wi401E/IvqsK8m2iCHW+0TEOGrans/cuN3oTW+uxIyJFHJ8Im0k4qBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/cli-progress": "^3.11.0", + "ansi-escapes": "^4.3.2", + "ansi-styles": "^4.3.0", + "cardinal": "^2.1.1", + "chalk": "^4.1.2", + "clean-stack": "^3.0.1", + "cli-progress": "^3.12.0", + "debug": "^4.3.4", + "ejs": "^3.1.8", + "get-package-type": "^0.1.0", + "globby": "^11.1.0", + "hyperlinker": "^1.0.0", + "indent-string": "^4.0.0", + "is-wsl": "^2.2.0", + "js-yaml": "^3.14.1", + "natural-orderby": "^2.0.3", + "object-treeify": "^1.1.33", + "password-prompt": "^1.1.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "supports-color": "^8.1.1", + "supports-hyperlinks": "^2.2.0", + "ts-node": "^10.9.1", + "tslib": "^2.5.0", + "widest-line": "^3.1.0", + "wordwrap": "^1.0.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@expo/plugin-help/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@expo/plugin-help/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@expo/plugin-help/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@expo/plugin-help/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@expo/plugin-help/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@expo/plugin-help/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@expo/plugin-help/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@expo/plugin-warn-if-update-available": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@expo/plugin-warn-if-update-available/-/plugin-warn-if-update-available-2.5.1.tgz", + "integrity": "sha512-B65QSIZ+TgFHnVXsTw+1Q6djsJByWwnIjYfoG8ZV9wizOC01gbAw1cOZ/YtrJ2BrDnzFQtM8qecjlmZ7C3MPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oclif/core": "^2.11.1", + "chalk": "^4.1.0", + "debug": "^4.3.4", + "ejs": "^3.1.7", + "fs-extra": "^10.1.0", + "http-call": "^5.2.2", + "semver": "^7.3.7", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@expo/plugin-warn-if-update-available/node_modules/@oclif/core": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/@oclif/core/-/core-2.16.0.tgz", + "integrity": "sha512-dL6atBH0zCZl1A1IXCKJgLPrM/wR7K+Wi401E/IvqsK8m2iCHW+0TEOGrans/cuN3oTW+uxIyJFHJ8Im0k4qBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/cli-progress": "^3.11.0", + "ansi-escapes": "^4.3.2", + "ansi-styles": "^4.3.0", + "cardinal": "^2.1.1", + "chalk": "^4.1.2", + "clean-stack": "^3.0.1", + "cli-progress": "^3.12.0", + "debug": "^4.3.4", + "ejs": "^3.1.8", + "get-package-type": "^0.1.0", + "globby": "^11.1.0", + "hyperlinker": "^1.0.0", + "indent-string": "^4.0.0", + "is-wsl": "^2.2.0", + "js-yaml": "^3.14.1", + "natural-orderby": "^2.0.3", + "object-treeify": "^1.1.33", + "password-prompt": "^1.1.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "supports-color": "^8.1.1", + "supports-hyperlinks": "^2.2.0", + "ts-node": "^10.9.1", + "tslib": "^2.5.0", + "widest-line": "^3.1.0", + "wordwrap": "^1.0.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@expo/plugin-warn-if-update-available/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@expo/plugin-warn-if-update-available/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@expo/plugin-warn-if-update-available/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@expo/plugin-warn-if-update-available/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@expo/plugin-warn-if-update-available/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@expo/plugin-warn-if-update-available/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@expo/plugin-warn-if-update-available/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@expo/plugin-warn-if-update-available/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@expo/plugin-warn-if-update-available/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@expo/prebuild-config": { "version": "54.0.5", "resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-54.0.5.tgz", @@ -2236,6 +2934,45 @@ "node": ">=10" } }, + "node_modules/@expo/results": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@expo/results/-/results-1.0.0.tgz", + "integrity": "sha512-qECzzXX5oJot3m2Gu9pfRDz50USdBieQVwYAzeAtQRUTD3PVeTK1tlRUoDcrK8PSruDLuVYdKkLebX4w/o55VA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/@expo/rudder-sdk-node": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@expo/rudder-sdk-node/-/rudder-sdk-node-1.1.1.tgz", + "integrity": "sha512-uy/hS/awclDJ1S88w9UGpc6Nm9XnNUjzOAAib1A3PVAnGQIwebg8DpFqOthFBTlZxeuV/BKbZ5jmTbtNZkp1WQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@expo/bunyan": "^4.0.0", + "@segment/loosely-validate-event": "^2.0.0", + "fetch-retry": "^4.1.1", + "md5": "^2.2.1", + "node-fetch": "^2.6.1", + "remove-trailing-slash": "^0.1.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@expo/rudder-sdk-node/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@expo/schema-utils": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/@expo/schema-utils/-/schema-utils-0.1.7.tgz", @@ -2260,12 +2997,107 @@ "node": ">=12" } }, + "node_modules/@expo/steps": { + "version": "1.0.231", + "resolved": "https://registry.npmjs.org/@expo/steps/-/steps-1.0.231.tgz", + "integrity": "sha512-o7Wj6W32S4H4geWC0b3RL5finHmiPT+e8SMP4/4Il2R0oc9er8jLXaj1tQ3fTZvpnlqoMWcFn5fAvaGnxfYDPA==", + "dev": true, + "license": "BUSL-1.1", + "dependencies": { + "@expo/eas-build-job": "1.0.231", + "@expo/logger": "1.0.221", + "@expo/spawn-async": "^1.7.2", + "arg": "^5.0.2", + "fs-extra": "^11.2.0", + "joi": "^17.13.1", + "jsep": "^1.3.8", + "lodash.clonedeep": "^4.5.0", + "lodash.get": "^4.4.2", + "this-file": "^2.0.3", + "uuid": "^9.0.1", + "yaml": "^2.4.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@expo/steps/node_modules/@expo/eas-build-job": { + "version": "1.0.231", + "resolved": "https://registry.npmjs.org/@expo/eas-build-job/-/eas-build-job-1.0.231.tgz", + "integrity": "sha512-jktsZ7IJYcC9lbgUWftbFCpfrhcGwJX4q0qSubUrqvFTMuUyLjCzJqyIZ4znbXLXfJSUPDJNAd4H0wBqKJA9kA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@expo/logger": "1.0.221", + "joi": "^17.13.1", + "semver": "^7.6.2", + "zod": "^4.1.3" + } + }, + "node_modules/@expo/steps/node_modules/joi": { + "version": "17.13.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", + "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, + "node_modules/@expo/steps/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@expo/steps/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@expo/steps/node_modules/zod": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz", + "integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "node_modules/@expo/sudo-prompt": { "version": "9.3.2", "resolved": "https://registry.npmjs.org/@expo/sudo-prompt/-/sudo-prompt-9.3.2.tgz", "integrity": "sha512-HHQigo3rQWKMDzYDLkubN5WQOYXJJE2eNqIQC2axC2iO3mHdwnIR7FgZVvHWtBwAdzBgAP0ECp8KqS8TiMKvgw==", "license": "MIT" }, + "node_modules/@expo/timeago.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@expo/timeago.js/-/timeago.js-1.0.0.tgz", + "integrity": "sha512-PD45CGlCL8kG0U3YcH1NvYxQThw5XAS7qE9bgP4L7dakm8lsMz+p8BQ1IjBFMmImawVWsV3py6JZINaEebXLnw==", + "dev": true, + "license": "MIT" + }, "node_modules/@expo/vector-icons": { "version": "15.0.2", "resolved": "https://registry.npmjs.org/@expo/vector-icons/-/vector-icons-15.0.2.tgz", @@ -2307,6 +3139,23 @@ "@babel/highlight": "^7.10.4" } }, + "node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -2720,6 +3569,334 @@ "node": ">=12.4.0" } }, + "node_modules/@oclif/core": { + "version": "1.26.2", + "resolved": "https://registry.npmjs.org/@oclif/core/-/core-1.26.2.tgz", + "integrity": "sha512-6jYuZgXvHfOIc9GIaS4T3CIKGTjPmfAxuMcbCbMRKJJl4aq/4xeRlEz0E8/hz8HxvxZBGvN2GwAUHlrGWQVrVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oclif/linewrap": "^1.0.0", + "@oclif/screen": "^3.0.4", + "ansi-escapes": "^4.3.2", + "ansi-styles": "^4.3.0", + "cardinal": "^2.1.1", + "chalk": "^4.1.2", + "clean-stack": "^3.0.1", + "cli-progress": "^3.10.0", + "debug": "^4.3.4", + "ejs": "^3.1.6", + "fs-extra": "^9.1.0", + "get-package-type": "^0.1.0", + "globby": "^11.1.0", + "hyperlinker": "^1.0.0", + "indent-string": "^4.0.0", + "is-wsl": "^2.2.0", + "js-yaml": "^3.14.1", + "natural-orderby": "^2.0.3", + "object-treeify": "^1.1.33", + "password-prompt": "^1.1.2", + "semver": "^7.3.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "supports-color": "^8.1.1", + "supports-hyperlinks": "^2.2.0", + "tslib": "^2.4.1", + "widest-line": "^3.1.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oclif/core/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@oclif/core/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@oclif/core/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@oclif/core/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@oclif/core/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@oclif/core/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@oclif/core/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@oclif/core/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@oclif/core/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@oclif/linewrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@oclif/linewrap/-/linewrap-1.0.0.tgz", + "integrity": "sha512-Ups2dShK52xXa8w6iBWLgcjPJWjais6KPJQq3gQ/88AY6BXoTX+MIGFPrWQO1KLMiQfoTpcLnUwloN4brrVUHw==", + "dev": true, + "license": "ISC" + }, + "node_modules/@oclif/plugin-autocomplete": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@oclif/plugin-autocomplete/-/plugin-autocomplete-2.3.10.tgz", + "integrity": "sha512-Ow1AR8WtjzlyCtiWWPgzMyT8SbcDJFr47009riLioHa+MHX2BCDtVn2DVnN/E6b9JlPV5ptQpjefoRSNWBesmg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oclif/core": "^2.15.0", + "chalk": "^4.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@oclif/plugin-autocomplete/node_modules/@oclif/core": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/@oclif/core/-/core-2.16.0.tgz", + "integrity": "sha512-dL6atBH0zCZl1A1IXCKJgLPrM/wR7K+Wi401E/IvqsK8m2iCHW+0TEOGrans/cuN3oTW+uxIyJFHJ8Im0k4qBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/cli-progress": "^3.11.0", + "ansi-escapes": "^4.3.2", + "ansi-styles": "^4.3.0", + "cardinal": "^2.1.1", + "chalk": "^4.1.2", + "clean-stack": "^3.0.1", + "cli-progress": "^3.12.0", + "debug": "^4.3.4", + "ejs": "^3.1.8", + "get-package-type": "^0.1.0", + "globby": "^11.1.0", + "hyperlinker": "^1.0.0", + "indent-string": "^4.0.0", + "is-wsl": "^2.2.0", + "js-yaml": "^3.14.1", + "natural-orderby": "^2.0.3", + "object-treeify": "^1.1.33", + "password-prompt": "^1.1.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "supports-color": "^8.1.1", + "supports-hyperlinks": "^2.2.0", + "ts-node": "^10.9.1", + "tslib": "^2.5.0", + "widest-line": "^3.1.0", + "wordwrap": "^1.0.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oclif/plugin-autocomplete/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@oclif/plugin-autocomplete/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@oclif/plugin-autocomplete/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@oclif/plugin-autocomplete/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@oclif/plugin-autocomplete/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@oclif/plugin-autocomplete/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@oclif/plugin-autocomplete/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@oclif/screen": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@oclif/screen/-/screen-3.0.8.tgz", + "integrity": "sha512-yx6KAqlt3TAHBduS2fMQtJDL2ufIHnDRArrJEOoTTuizxqmjLT+psGYOHpmMl3gvQpFJ11Hs76guUUktzAF9Bg==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -2917,6 +4094,18 @@ } } }, + "node_modules/@react-native-async-storage/async-storage": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-2.2.0.tgz", + "integrity": "sha512-gvRvjR5JAaUZF8tv2Kcq/Gbt3JHwbKFYfmb445rhOj6NUMx3qPLixmDx5pZAyb9at1bYvJ4/eTUipU5aki45xw==", + "license": "MIT", + "dependencies": { + "merge-options": "^3.0.4" + }, + "peerDependencies": { + "react-native": "^0.0.0-0 || >=0.65 <1.0" + } + }, "node_modules/@react-native/assets-registry": { "version": "0.81.4", "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.81.4.tgz", @@ -3276,6 +4465,40 @@ "dev": true, "license": "MIT" }, + "node_modules/@segment/loosely-validate-event": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@segment/loosely-validate-event/-/loosely-validate-event-2.0.0.tgz", + "integrity": "sha512-ZMCSfztDBqwotkl848ODgVcAmN4OItEWDCkshcKz0/W6gGSQayuuCtWV/MlodFivAZD793d6UgANd6wCXUfrIw==", + "dev": true, + "dependencies": { + "component-type": "^1.2.1", + "join-component": "^1.1.0" + } + }, + "node_modules/@sideway/address": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", + "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -3571,6 +4794,34 @@ "tailwindcss": "4.1.15" } }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, "node_modules/@tybys/wasm-util": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", @@ -3623,6 +4874,26 @@ "@babel/types": "^7.28.2" } }, + "node_modules/@types/bunyan": { + "version": "1.8.11", + "resolved": "https://registry.npmjs.org/@types/bunyan/-/bunyan-1.8.11.tgz", + "integrity": "sha512-758fRH7umIMk5qt5ELmRMff4mLDlN+xyYzC+dkPTdKwbSkJFvz6xwyScrytPU0QIBbRRwbiE8/BIg8bpajerNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cli-progress": { + "version": "3.11.6", + "resolved": "https://registry.npmjs.org/@types/cli-progress/-/cli-progress-3.11.6.tgz", + "integrity": "sha512-cE3+jb9WRlu+uOSAugewNpITJDt1VF8dHOopPO4IABFc3SXYL5WE/+PTz/FCdZRRfIujiWW3n3aMbv1eIGVRWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -4322,6 +5593,19 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", @@ -4348,6 +5632,48 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, "node_modules/anser": { "version": "1.4.10", "resolved": "https://registry.npmjs.org/anser/-/anser-1.4.10.tgz", @@ -4405,6 +5731,13 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/ansicolors": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", + "integrity": "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==", + "dev": true, + "license": "MIT" + }, "node_modules/any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", @@ -4494,6 +5827,16 @@ "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==", "license": "MIT" }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/array.prototype.findlast": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", @@ -4620,6 +5963,33 @@ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "license": "MIT" }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, "node_modules/async-function": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", @@ -4636,6 +6006,23 @@ "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", "license": "MIT" }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -4652,6 +6039,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/b4a": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.3.tgz", + "integrity": "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, "node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", @@ -4867,6 +6269,21 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, + "node_modules/bare-events": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.0.tgz", + "integrity": "sha512-AOhh6Bg5QmFIXdViHbMc2tLDsBIRxdkIaIddPslJF9Z5De3APBScuqGP2uThXnIpqFrgoxMNC6km7uXNIMLHXA==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -5042,12 +6459,38 @@ "ieee754": "^1.1.13" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "license": "MIT" }, + "node_modules/bunyan": { + "version": "1.8.15", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.15.tgz", + "integrity": "sha512-0tECWShh6wUysgucJcBAoYegf3JJoZWibxdqhTm7OHPeT42qdjkZ29QCMcKwbgU1kiH+auSIasNRXMLWXafXig==", + "dev": true, + "engines": [ + "node >=0.10.0" + ], + "license": "MIT", + "bin": { + "bunyan": "bin/bunyan" + }, + "optionalDependencies": { + "dtrace-provider": "~0.8", + "moment": "^2.19.3", + "mv": "~2", + "safe-json-stringify": "~1" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -5149,6 +6592,20 @@ ], "license": "CC-BY-4.0" }, + "node_modules/cardinal": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz", + "integrity": "sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansicolors": "~0.3.2", + "redeyed": "~2.1.0" + }, + "bin": { + "cdl": "bin/cdl.js" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -5165,6 +6622,16 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, "node_modules/chownr": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", @@ -5212,6 +6679,22 @@ "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", "license": "MIT" }, + "node_modules/clean-stack": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-3.0.1.tgz", + "integrity": "sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cli-cursor": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", @@ -5224,6 +6707,54 @@ "node": ">=4" } }, + "node_modules/cli-progress": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.12.0.tgz", + "integrity": "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.3" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cli-progress/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cli-progress/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-progress/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/cli-spinners": { "version": "2.9.2", "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", @@ -5365,6 +6896,19 @@ "url": "https://opencollective.com/color" } }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", @@ -5388,6 +6932,16 @@ "node": ">= 6" } }, + "node_modules/component-type": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/component-type/-/component-type-1.2.2.tgz", + "integrity": "sha512-99VUHREHiN5cLeHm3YLq312p6v+HUEcwtLCAtelvUDI6+SH5g5Cr85oNR2S1o6ywzL0ykMbuwLzM2ANocjEOIA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/compressible": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", @@ -5478,6 +7032,16 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -5503,6 +7067,13 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "license": "MIT" }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, "node_modules/cross-fetch": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz", @@ -5526,6 +7097,16 @@ "node": ">= 8" } }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, "node_modules/crypto-random-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", @@ -5605,6 +7186,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/dateformat": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", + "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -5713,6 +7304,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -5747,6 +7348,29 @@ "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", "license": "MIT" }, + "node_modules/diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -5760,6 +7384,13 @@ "node": ">=0.10.0" } }, + "node_modules/domino": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/domino/-/domino-2.1.6.tgz", + "integrity": "sha512-3VdM/SXBZX2omc9JF9nOPCtDaYQ67BGp5CoLpIQlO2KCAPETs8TcDHacF26jXadGbvUteZzRTeos2fhID5+ucQ==", + "dev": true, + "license": "BSD-2-Clause" + }, "node_modules/dotenv": { "version": "16.4.7", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", @@ -5787,6 +7418,21 @@ "url": "https://dotenvx.com" } }, + "node_modules/dtrace-provider": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz", + "integrity": "sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==", + "dev": true, + "hasInstallScript": true, + "license": "BSD-2-Clause", + "optional": true, + "dependencies": { + "nan": "^2.14.0" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -5802,18 +7448,1237 @@ "node": ">= 0.4" } }, + "node_modules/eas-cli": { + "version": "16.24.1", + "resolved": "https://registry.npmjs.org/eas-cli/-/eas-cli-16.24.1.tgz", + "integrity": "sha512-jhNIn5gptXoO/JGzPR/eAHtJxa5Ms3P6X5oV6k57J5AKT1qn9EwTW/WxPYtN3TzyWStFREhGiN11GfjHr8Gr+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@expo/apple-utils": "2.1.12", + "@expo/code-signing-certificates": "0.0.5", + "@expo/config": "10.0.6", + "@expo/config-plugins": "9.0.12", + "@expo/eas-build-job": "1.0.239", + "@expo/eas-json": "16.23.0", + "@expo/env": "^1.0.0", + "@expo/json-file": "8.3.3", + "@expo/logger": "1.0.221", + "@expo/multipart-body-parser": "2.0.0", + "@expo/osascript": "2.1.4", + "@expo/package-manager": "1.7.0", + "@expo/pkcs12": "0.1.3", + "@expo/plist": "0.2.0", + "@expo/plugin-help": "5.1.23", + "@expo/plugin-warn-if-update-available": "2.5.1", + "@expo/prebuild-config": "8.0.17", + "@expo/results": "1.0.0", + "@expo/rudder-sdk-node": "1.1.1", + "@expo/spawn-async": "1.7.2", + "@expo/steps": "1.0.231", + "@expo/timeago.js": "1.0.0", + "@oclif/core": "^1.26.2", + "@oclif/plugin-autocomplete": "^2.3.10", + "@segment/ajv-human-errors": "^2.1.2", + "@urql/core": "4.0.11", + "@urql/exchange-retry": "1.2.0", + "ajv": "8.11.0", + "ajv-formats": "2.1.1", + "better-opn": "3.0.2", + "bplist-parser": "^0.3.0", + "chalk": "4.1.2", + "cli-progress": "3.12.0", + "dateformat": "4.6.3", + "diff": "7.0.0", + "dotenv": "16.3.1", + "env-paths": "2.2.0", + "envinfo": "7.11.0", + "fast-deep-equal": "3.1.3", + "fast-glob": "3.3.2", + "figures": "3.2.0", + "form-data": "^4.0.4", + "fs-extra": "11.2.0", + "getenv": "1.0.0", + "gradle-to-js": "2.0.1", + "graphql": "16.8.1", + "graphql-tag": "2.12.6", + "https-proxy-agent": "5.0.1", + "ignore": "5.3.0", + "indent-string": "4.0.0", + "jks-js": "1.1.0", + "joi": "17.11.0", + "jsonwebtoken": "9.0.0", + "keychain": "1.5.0", + "log-symbols": "4.1.0", + "mime": "3.0.0", + "minimatch": "5.1.2", + "minizlib": "3.0.1", + "nanoid": "3.3.8", + "node-fetch": "2.6.7", + "node-forge": "1.3.1", + "node-stream-zip": "1.15.0", + "nullthrows": "1.1.1", + "ora": "5.1.0", + "pkg-dir": "4.2.0", + "pngjs": "7.0.0", + "promise-limit": "2.7.0", + "promise-retry": "2.0.1", + "prompts": "2.4.2", + "qrcode-terminal": "0.12.0", + "resolve-from": "5.0.0", + "semver": "7.5.4", + "set-interval-async": "3.0.3", + "slash": "3.0.0", + "tar": "6.2.1", + "tar-stream": "3.1.7", + "terminal-link": "2.1.1", + "ts-deepmerge": "6.2.0", + "tslib": "2.6.2", + "turndown": "7.1.2", + "untildify": "4.0.0", + "uuid": "9.0.1", + "wrap-ansi": "7.0.0", + "yaml": "2.6.0", + "zod": "^4.1.3" + }, + "bin": { + "eas": "bin/run" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eas-cli/node_modules/@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, + "node_modules/eas-cli/node_modules/@expo/config": { + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/@expo/config/-/config-10.0.6.tgz", + "integrity": "sha512-xXkfPElrtxznkOZxFASJ7OPa6E9IHSjcZwj5BQ6XUF2dz5M7AFa2h5sXM8AalSaDU5tEBSgoUOjTh5957TlR8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "~7.10.4", + "@expo/config-plugins": "~9.0.10", + "@expo/config-types": "^52.0.0", + "@expo/json-file": "^9.0.0", + "deepmerge": "^4.3.1", + "getenv": "^1.0.0", + "glob": "^10.4.2", + "require-from-string": "^2.0.2", + "resolve-from": "^5.0.0", + "resolve-workspace-root": "^2.0.0", + "semver": "^7.6.0", + "slugify": "^1.3.4", + "sucrase": "3.35.0" + } + }, + "node_modules/eas-cli/node_modules/@expo/config-plugins": { + "version": "9.0.12", + "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-9.0.12.tgz", + "integrity": "sha512-/Ko/NM+GzvJyRkq8PITm8ms0KY5v0wmN1OQFYRMkcJqOi3PjlhndW+G6bHpJI9mkQXBaUnHwAiGLqIC3+MQ5Wg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@expo/config-types": "^52.0.0", + "@expo/json-file": "~9.0.0", + "@expo/plist": "^0.2.0", + "@expo/sdk-runtime-versions": "^1.0.0", + "chalk": "^4.1.2", + "debug": "^4.3.5", + "getenv": "^1.0.0", + "glob": "^10.4.2", + "resolve-from": "^5.0.0", + "semver": "^7.5.4", + "slash": "^3.0.0", + "slugify": "^1.6.6", + "xcode": "^3.0.1", + "xml2js": "0.6.0" + } + }, + "node_modules/eas-cli/node_modules/@expo/config-plugins/node_modules/@expo/json-file": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-9.0.2.tgz", + "integrity": "sha512-yAznIUrybOIWp3Uax7yRflB0xsEpvIwIEqIjao9SGi2Gaa+N0OamWfe0fnXBSWF+2zzF4VvqwT4W5zwelchfgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "~7.10.4", + "json5": "^2.2.3", + "write-file-atomic": "^2.3.0" + } + }, + "node_modules/eas-cli/node_modules/@expo/config-types": { + "version": "52.0.5", + "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-52.0.5.tgz", + "integrity": "sha512-AMDeuDLHXXqd8W+0zSjIt7f37vUd/BP8p43k68NHpyAvQO+z8mbQZm3cNQVAMySeayK2XoPigAFB1JF2NFajaA==", + "dev": true, + "license": "MIT" + }, + "node_modules/eas-cli/node_modules/@expo/config/node_modules/@expo/json-file": { + "version": "9.1.5", + "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-9.1.5.tgz", + "integrity": "sha512-prWBhLUlmcQtvN6Y7BpW2k9zXGd3ySa3R6rAguMJkp1z22nunLN64KYTUWfijFlprFoxm9r2VNnGkcbndAlgKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "~7.10.4", + "json5": "^2.2.3" + } + }, + "node_modules/eas-cli/node_modules/@expo/config/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eas-cli/node_modules/@expo/env": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@expo/env/-/env-1.0.7.tgz", + "integrity": "sha512-qSTEnwvuYJ3umapO9XJtrb1fAqiPlmUUg78N0IZXXGwQRt+bkp0OBls+Y5Mxw/Owj8waAM0Z3huKKskRADR5ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "debug": "^4.3.4", + "dotenv": "~16.4.5", + "dotenv-expand": "~11.0.6", + "getenv": "^2.0.0" + } + }, + "node_modules/eas-cli/node_modules/@expo/env/node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/eas-cli/node_modules/@expo/env/node_modules/getenv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/getenv/-/getenv-2.0.0.tgz", + "integrity": "sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/eas-cli/node_modules/@expo/image-utils": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@expo/image-utils/-/image-utils-0.6.5.tgz", + "integrity": "sha512-RsS/1CwJYzccvlprYktD42KjyfWZECH6PPIEowvoSmXfGLfdViwcUEI4RvBfKX5Jli6P67H+6YmHvPTbGOboew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@expo/spawn-async": "^1.7.2", + "chalk": "^4.0.0", + "fs-extra": "9.0.0", + "getenv": "^1.0.0", + "jimp-compact": "0.16.1", + "parse-png": "^2.1.0", + "resolve-from": "^5.0.0", + "semver": "^7.6.0", + "temp-dir": "~2.0.0", + "unique-string": "~2.0.0" + } + }, + "node_modules/eas-cli/node_modules/@expo/image-utils/node_modules/fs-extra": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.0.tgz", + "integrity": "sha512-pmEYSk3vYsG/bF651KPUXZ+hvjpgWYw/Gc7W9NFUe3ZVLczKKWIij3IKpOrQcdw4TILtibFslZ0UmR8Vvzig4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eas-cli/node_modules/@expo/image-utils/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eas-cli/node_modules/@expo/image-utils/node_modules/universalify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", + "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/eas-cli/node_modules/@expo/json-file": { + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-8.3.3.tgz", + "integrity": "sha512-eZ5dld9AD0PrVRiIWpRkm5aIoWBw3kAyd8VkuWEy92sEthBKDDDHAnK2a0dw0Eil6j7rK7lS/Qaq/Zzngv2h5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "~7.10.4", + "json5": "^2.2.2", + "write-file-atomic": "^2.3.0" + } + }, + "node_modules/eas-cli/node_modules/@expo/osascript": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@expo/osascript/-/osascript-2.1.4.tgz", + "integrity": "sha512-LcPjxJ5FOFpqPORm+5MRLV0CuYWMthJYV6eerF+lQVXKlvgSn3EOqaHC3Vf3H+vmB0f6G4kdvvFtg40vG4bIhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@expo/spawn-async": "^1.7.2", + "exec-async": "^2.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/eas-cli/node_modules/@expo/package-manager": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@expo/package-manager/-/package-manager-1.7.0.tgz", + "integrity": "sha512-yWn5TIjd42wLHZjNtdZkvCkcxqUGxlI4YHb+bQmgm3tWZ8aBHnLhPb0rgU8+hVHCofmRvVUXfVZv8Uh+kkLXgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@expo/json-file": "^9.0.0", + "@expo/spawn-async": "^1.7.2", + "ansi-regex": "^5.0.0", + "chalk": "^4.0.0", + "find-up": "^5.0.0", + "js-yaml": "^3.13.1", + "micromatch": "^4.0.8", + "npm-package-arg": "^11.0.0", + "ora": "^3.4.0", + "resolve-workspace-root": "^2.0.0", + "split": "^1.0.1", + "sudo-prompt": "9.1.1" + } + }, + "node_modules/eas-cli/node_modules/@expo/package-manager/node_modules/@expo/json-file": { + "version": "9.1.5", + "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-9.1.5.tgz", + "integrity": "sha512-prWBhLUlmcQtvN6Y7BpW2k9zXGd3ySa3R6rAguMJkp1z22nunLN64KYTUWfijFlprFoxm9r2VNnGkcbndAlgKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "~7.10.4", + "json5": "^2.2.3" + } + }, + "node_modules/eas-cli/node_modules/@expo/package-manager/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eas-cli/node_modules/@expo/package-manager/node_modules/log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eas-cli/node_modules/@expo/package-manager/node_modules/log-symbols/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eas-cli/node_modules/@expo/package-manager/node_modules/ora": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-3.4.0.tgz", + "integrity": "sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-spinners": "^2.0.0", + "log-symbols": "^2.2.0", + "strip-ansi": "^5.2.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/eas-cli/node_modules/@expo/package-manager/node_modules/ora/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eas-cli/node_modules/@expo/package-manager/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/eas-cli/node_modules/@expo/package-manager/node_modules/strip-ansi/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/eas-cli/node_modules/@expo/plist": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@expo/plist/-/plist-0.2.0.tgz", + "integrity": "sha512-F/IZJQaf8OIVnVA6XWUeMPC3OH6MV00Wxf0WC0JhTQht2QgjyHUa3U5Gs3vRtDq8tXNsZneOQRDVwpaOnd4zTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@xmldom/xmldom": "~0.7.7", + "base64-js": "^1.2.3", + "xmlbuilder": "^14.0.0" + } + }, + "node_modules/eas-cli/node_modules/@expo/prebuild-config": { + "version": "8.0.17", + "resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-8.0.17.tgz", + "integrity": "sha512-HM+XpDox3fAZuXZXvy55VRcBbsZSDijGf8jI8i/pexgWvtsnt1ouelPXRuE1pXDicMX+lZO83QV+XkyLmBEXYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@expo/config": "~10.0.4", + "@expo/config-plugins": "~9.0.0", + "@expo/config-types": "^52.0.0", + "@expo/image-utils": "^0.6.0", + "@expo/json-file": "^9.0.0", + "@react-native/normalize-colors": "0.76.2", + "debug": "^4.3.1", + "fs-extra": "^9.0.0", + "resolve-from": "^5.0.0", + "semver": "^7.6.0", + "xml2js": "0.6.0" + } + }, + "node_modules/eas-cli/node_modules/@expo/prebuild-config/node_modules/@expo/json-file": { + "version": "9.1.5", + "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-9.1.5.tgz", + "integrity": "sha512-prWBhLUlmcQtvN6Y7BpW2k9zXGd3ySa3R6rAguMJkp1z22nunLN64KYTUWfijFlprFoxm9r2VNnGkcbndAlgKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "~7.10.4", + "json5": "^2.2.3" + } + }, + "node_modules/eas-cli/node_modules/@expo/prebuild-config/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eas-cli/node_modules/@expo/prebuild-config/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eas-cli/node_modules/@react-native/normalize-colors": { + "version": "0.76.2", + "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.76.2.tgz", + "integrity": "sha512-ICoOpaTLPsFQjNLSM00NgQr6wal300cZZonHVSDXKntX+BfkLeuCHRtr/Mn+klTtW+/1v2/2FRm9dXjvyGf9Dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/eas-cli/node_modules/@segment/ajv-human-errors": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/@segment/ajv-human-errors/-/ajv-human-errors-2.15.0.tgz", + "integrity": "sha512-tgeMMuYYJt3Aar5IIk3kyfL9zMvGsv5d7KsVT/2auri+hEH/L2M1i8X67ne4JjMWZqENYIGY1WuI4oPEL1H/xA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^8.0.0" + } + }, + "node_modules/eas-cli/node_modules/@urql/core": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/@urql/core/-/core-4.0.11.tgz", + "integrity": "sha512-FFdY97vF5xnUrElcGw9erOLvtu+KGMLfwrLNDfv4IPgdp2IBsiGe+Kb7Aypfd3kH//BETewVSLm3+y2sSzjX6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@0no-co/graphql.web": "^1.0.1", + "wonka": "^6.3.2" + } + }, + "node_modules/eas-cli/node_modules/@urql/exchange-retry": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@urql/exchange-retry/-/exchange-retry-1.2.0.tgz", + "integrity": "sha512-1O/biKiVhhn0EtvDF4UOvz325K4RrLupfL8rHcmqD2TBLv4qVDWQuzx4JGa1FfqjjRb+C9TNZ6w19f32Mq85Ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@urql/core": ">=4.0.0", + "wonka": "^6.3.2" + } + }, + "node_modules/eas-cli/node_modules/@xmldom/xmldom": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.13.tgz", + "integrity": "sha512-lm2GW5PkosIzccsaZIz7tp8cPADSIlIHWDFTR1N0SzfinhhYgeIQjFMz4rYzanCScr3DqQLeomUDArp6MWKm+g==", + "deprecated": "this version is no longer supported, please update to at least 0.8.*", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/eas-cli/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/eas-cli/node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eas-cli/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/eas-cli/node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/eas-cli/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/eas-cli/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/eas-cli/node_modules/dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" + } + }, + "node_modules/eas-cli/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/eas-cli/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eas-cli/node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/eas-cli/node_modules/getenv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/getenv/-/getenv-1.0.0.tgz", + "integrity": "sha512-7yetJWqbS9sbn0vIfliPsFgoXMKn/YMF+Wuiog97x+urnSRRRZ7xB+uVkwGKzRgq9CDFfMQnE9ruL5DHv9c6Xg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/eas-cli/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/eas-cli/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/eas-cli/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/eas-cli/node_modules/ignore": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/eas-cli/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/eas-cli/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/eas-cli/node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eas-cli/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eas-cli/node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/eas-cli/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/eas-cli/node_modules/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eas-cli/node_modules/minizlib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.1.tgz", + "integrity": "sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/eas-cli/node_modules/nanoid": { + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/eas-cli/node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/eas-cli/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eas-cli/node_modules/ora": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.1.0.tgz", + "integrity": "sha512-9tXIMPvjZ7hPTbk8DFq1f7Kow/HU/pQYB60JbNq+QnGwcyhWVZaQ4hM9zQDEsPxw/muLpgiHSaumUZxCAmod/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.4.0", + "is-interactive": "^1.0.0", + "log-symbols": "^4.0.0", + "mute-stream": "0.0.8", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eas-cli/node_modules/ora/node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eas-cli/node_modules/pngjs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", + "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.19.0" + } + }, + "node_modules/eas-cli/node_modules/qrcode-terminal": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz", + "integrity": "sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==", + "dev": true, + "bin": { + "qrcode-terminal": "bin/qrcode-terminal.js" + } + }, + "node_modules/eas-cli/node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eas-cli/node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/eas-cli/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eas-cli/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/eas-cli/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eas-cli/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eas-cli/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eas-cli/node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "dev": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eas-cli/node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/eas-cli/node_modules/tar/node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/eas-cli/node_modules/tar/node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eas-cli/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true, + "license": "0BSD" + }, + "node_modules/eas-cli/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/eas-cli/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/eas-cli/node_modules/write-file-atomic": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", + "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "node_modules/eas-cli/node_modules/xmlbuilder": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-14.0.0.tgz", + "integrity": "sha512-ts+B2rSe4fIckR6iquDjsKbQFK2NlUk6iG5nf14mDEyldgoc2nEKZ3jZWMPTxGQwVgToSjt6VGIho1H8/fNFTg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0" + } + }, + "node_modules/eas-cli/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/eas-cli/node_modules/yaml": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz", + "integrity": "sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/eas-cli/node_modules/zod": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz", + "integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "license": "MIT" }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.237", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.237.tgz", @@ -5858,6 +8723,60 @@ "node": ">=8" } }, + "node_modules/env-paths": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz", + "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/env-string": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/env-string/-/env-string-1.0.1.tgz", + "integrity": "sha512-/DhCJDf5DSFK32joQiWRpWrT0h7p3hVQfMKxiBb7Nt8C8IF8BYyPtclDnuGGLOoj16d/8udKeiE7JbkotDmorQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/envinfo": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.11.0.tgz", + "integrity": "sha512-G9/6xF1FPbIw0TtalAMaVPpiq2aDEuKLXM314jPVAO9r2fo2a4BLqMNkmRS7O/xPPZ+COAhGIz3ETvHEV3eUcg==", + "dev": true, + "license": "MIT", + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/error-ex/node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, "node_modules/error-stack-parser": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", @@ -6572,6 +9491,16 @@ "node": ">=6" } }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, "node_modules/exec-async": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/exec-async/-/exec-async-2.2.0.tgz", @@ -6645,6 +9574,34 @@ "react-native": "*" } }, + "node_modules/expo-audio": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/expo-audio/-/expo-audio-1.0.13.tgz", + "integrity": "sha512-Z91jMYv/BMpn5Dkyu204RyNl2EpXRx8HgScTYlwRRYewtfTH8NN+bwZQAApn33NwGhW1cIlcuG/XCAeJBr9l4Q==", + "license": "MIT", + "peerDependencies": { + "expo": "*", + "react": "*", + "react-native": "*" + } + }, + "node_modules/expo-av": { + "version": "16.0.7", + "resolved": "https://registry.npmjs.org/expo-av/-/expo-av-16.0.7.tgz", + "integrity": "sha512-QReef6/RYuZ4GekTcZZw5zY26pcPOmHqK6LMgFlPhnsT0ga97HJrgMc63pvIiojDP+q4oIv24g+QPD8ljZu4XQ==", + "license": "MIT", + "peerDependencies": { + "expo": "*", + "react": "*", + "react-native": "*", + "react-native-web": "*" + }, + "peerDependenciesMeta": { + "react-native-web": { + "optional": true + } + } + }, "node_modules/expo-constants": { "version": "18.0.9", "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-18.0.9.tgz", @@ -6659,6 +9616,62 @@ "react-native": "*" } }, + "node_modules/expo-dev-client": { + "version": "6.0.15", + "resolved": "https://registry.npmjs.org/expo-dev-client/-/expo-dev-client-6.0.15.tgz", + "integrity": "sha512-zdpuK7wPp7q01qE90EDQunL49cumGPEKCGDykB86K0myCPZt1lvkpyy4OHTh3urm3pkikWOb9biuVgLdq7oq/g==", + "license": "MIT", + "dependencies": { + "expo-dev-launcher": "6.0.15", + "expo-dev-menu": "7.0.14", + "expo-dev-menu-interface": "2.0.0", + "expo-manifests": "~1.0.8", + "expo-updates-interface": "~2.0.0" + }, + "peerDependencies": { + "expo": "*" + } + }, + "node_modules/expo-dev-launcher": { + "version": "6.0.15", + "resolved": "https://registry.npmjs.org/expo-dev-launcher/-/expo-dev-launcher-6.0.15.tgz", + "integrity": "sha512-aFRKg9qcq47Y/1UGTPhtOWrbI5jOFgttOfhdBu9knLKl72jGXpDTdBHsHPkCfIezzDrSoZccl482Hv7RvKfrqA==", + "license": "MIT", + "dependencies": { + "expo-dev-menu": "7.0.14", + "expo-manifests": "~1.0.8" + }, + "peerDependencies": { + "expo": "*" + } + }, + "node_modules/expo-dev-menu": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/expo-dev-menu/-/expo-dev-menu-7.0.14.tgz", + "integrity": "sha512-nWyzSztFWfnhDOiKJ6DuZLjIbq+tG9e5y4TDmW6wYlSKKPBXbdOn2UdxaknhSqZrw6NwWMcjyhS+QG4MoDGD8w==", + "license": "MIT", + "dependencies": { + "expo-dev-menu-interface": "2.0.0" + }, + "peerDependencies": { + "expo": "*" + } + }, + "node_modules/expo-dev-menu-interface": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/expo-dev-menu-interface/-/expo-dev-menu-interface-2.0.0.tgz", + "integrity": "sha512-BvAMPt6x+vyXpThsyjjOYyjwfjREV4OOpQkZ0tNl+nGpsPfcY9mc6DRACoWnH9KpLzyIt3BOgh3cuy/h/OxQjw==", + "license": "MIT", + "peerDependencies": { + "expo": "*" + } + }, + "node_modules/expo-eas-client": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/expo-eas-client/-/expo-eas-client-1.0.7.tgz", + "integrity": "sha512-Q/b1X0fM+3beqqvffok14pjxMF600NxopdSr9WJY61fF4xllcVnALS0kEudffp9ihMOfcb5xWYqzKj6jMqYDIw==", + "license": "MIT" + }, "node_modules/expo-file-system": { "version": "19.0.17", "resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-19.0.17.tgz", @@ -6709,6 +9722,12 @@ } } }, + "node_modules/expo-json-utils": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/expo-json-utils/-/expo-json-utils-0.15.0.tgz", + "integrity": "sha512-duRT6oGl80IDzH2LD2yEFWNwGIC2WkozsB6HF3cDYNoNNdUvFk6uN3YiwsTsqVM/D0z6LEAQ01/SlYvN+Fw0JQ==", + "license": "MIT" + }, "node_modules/expo-keep-awake": { "version": "15.0.7", "resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-15.0.7.tgz", @@ -6733,6 +9752,19 @@ "react-native": "*" } }, + "node_modules/expo-manifests": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/expo-manifests/-/expo-manifests-1.0.8.tgz", + "integrity": "sha512-nA5PwU2uiUd+2nkDWf9e71AuFAtbrb330g/ecvuu52bmaXtN8J8oiilc9BDvAX0gg2fbtOaZdEdjBYopt1jdlQ==", + "license": "MIT", + "dependencies": { + "@expo/config": "~12.0.8", + "expo-json-utils": "~0.15.0" + }, + "peerDependencies": { + "expo": "*" + } + }, "node_modules/expo-modules-autolinking": { "version": "3.0.17", "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-3.0.17.tgz", @@ -7050,6 +10082,12 @@ "react-native": "*" } }, + "node_modules/expo-structured-headers": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/expo-structured-headers/-/expo-structured-headers-5.0.0.tgz", + "integrity": "sha512-RmrBtnSphk5REmZGV+lcdgdpxyzio5rJw8CXviHE6qH5pKQQ83fhMEcigvrkBdsn2Efw2EODp4Yxl1/fqMvOZw==", + "license": "MIT" + }, "node_modules/expo-symbols": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/expo-symbols/-/expo-symbols-1.0.7.tgz", @@ -7083,6 +10121,51 @@ } } }, + "node_modules/expo-updates": { + "version": "29.0.12", + "resolved": "https://registry.npmjs.org/expo-updates/-/expo-updates-29.0.12.tgz", + "integrity": "sha512-gE3bU6qi5g8Y1TtBzoeHac3utR0i1Wj1ufThh+zpDyFjFbegFm+gwvNLVCBagZUClYKk/4CKxh5ytnwZmPzH+g==", + "license": "MIT", + "dependencies": { + "@expo/code-signing-certificates": "0.0.5", + "@expo/plist": "^0.4.7", + "@expo/spawn-async": "^1.7.2", + "arg": "4.1.0", + "chalk": "^4.1.2", + "debug": "^4.3.4", + "expo-eas-client": "~1.0.7", + "expo-manifests": "~1.0.8", + "expo-structured-headers": "~5.0.0", + "expo-updates-interface": "~2.0.0", + "getenv": "^2.0.0", + "glob": "^10.4.2", + "ignore": "^5.3.1", + "resolve-from": "^5.0.0" + }, + "bin": { + "expo-updates": "bin/cli.js" + }, + "peerDependencies": { + "expo": "*", + "react": "*", + "react-native": "*" + } + }, + "node_modules/expo-updates-interface": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/expo-updates-interface/-/expo-updates-interface-2.0.0.tgz", + "integrity": "sha512-pTzAIufEZdVPKql6iMi5ylVSPqV1qbEopz9G6TSECQmnNde2nwq42PxdFBaUEd8IZJ/fdJLQnOT3m6+XJ5s7jg==", + "license": "MIT", + "peerDependencies": { + "expo": "*" + } + }, + "node_modules/expo-updates/node_modules/arg": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.0.tgz", + "integrity": "sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg==", + "license": "MIT" + }, "node_modules/expo-web-browser": { "version": "15.0.8", "resolved": "https://registry.npmjs.org/expo-web-browser/-/expo-web-browser-15.0.8.tgz", @@ -7302,6 +10385,13 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "license": "MIT" }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "dev": true, + "license": "MIT" + }, "node_modules/fast-glob": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", @@ -7345,6 +10435,23 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fastq": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", @@ -7394,6 +10501,39 @@ "asap": "~2.0.3" } }, + "node_modules/fetch-retry": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/fetch-retry/-/fetch-retry-4.1.1.tgz", + "integrity": "sha512-e6eB7zN6UBSwGVwrbWVH+gdLnkW9WwHhmq2YDK1Sh30pzx1onRVGBvogTlUeWxwTa+L86NYdo4hFkh7O8ZjSnA==", + "dev": true, + "license": "MIT" + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -7407,6 +10547,29 @@ "node": ">=16.0.0" } }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -7542,6 +10705,23 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/freeport-async": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/freeport-async/-/freeport-async-2.0.0.tgz", @@ -7560,6 +10740,54 @@ "node": ">= 0.6" } }, + "node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs-minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -7820,6 +11048,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/golden-fleece": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/golden-fleece/-/golden-fleece-1.0.9.tgz", + "integrity": "sha512-YSwLaGMOgSBx9roJlNLL12c+FRiw7VECphinc6mGucphc/ZxTHgdEz6gmJqH6NOzYEd/yr64hwjom5pZ+tJVpg==", + "dev": true + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -7839,6 +11094,19 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, + "node_modules/gradle-to-js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/gradle-to-js/-/gradle-to-js-2.0.1.tgz", + "integrity": "sha512-is3hDn9zb8XXnjbEeAEIqxTpLHUiGBqjegLmXPuyMBfKAggpadWFku4/AP8iYAGBX6qR9/5UIUIp47V0XI3aMw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "lodash.merge": "^4.6.2" + }, + "bin": { + "gradle-to-js": "cli.js" + } + }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -7846,6 +11114,32 @@ "dev": true, "license": "MIT" }, + "node_modules/graphql": { + "version": "16.8.1", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.1.tgz", + "integrity": "sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, + "node_modules/graphql-tag": { + "version": "2.12.6", + "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz", + "integrity": "sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" + } + }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", @@ -7986,6 +11280,24 @@ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "license": "ISC" }, + "node_modules/http-call": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/http-call/-/http-call-5.3.0.tgz", + "integrity": "sha512-ahwimsC23ICE4kPl9xTBjKB4inbRaeLyZeRunC/1Jy/Z6X8tv22MEAjK+KBOMSVLaqXPTTmd8638waVIKLGx2w==", + "dev": true, + "license": "ISC", + "dependencies": { + "content-type": "^1.0.4", + "debug": "^4.1.1", + "is-retry-allowed": "^1.1.0", + "is-stream": "^2.0.0", + "parse-json": "^4.0.0", + "tunnel-agent": "^0.6.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -8024,6 +11336,16 @@ "node": ">= 14" } }, + "node_modules/hyperlinker": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hyperlinker/-/hyperlinker-1.0.0.tgz", + "integrity": "sha512-Ty8UblRWFEcfSuIaajM34LdPXIhbs1ajEX/BBPv24J+enSVaEVY63xQ6lTO9VRYS5LAoghIG0IDJ+p+IPzKUQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/hyphenate-style-name": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.1.0.tgz", @@ -8110,6 +11432,16 @@ "node": ">=0.8.19" } }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -8243,6 +11575,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true, + "license": "MIT" + }, "node_modules/is-bun-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", @@ -8412,6 +11751,16 @@ "node": ">=0.10.0" } }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", @@ -8464,6 +11813,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-regex": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", @@ -8483,6 +11841,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-retry-allowed": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", + "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-set": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", @@ -8512,6 +11880,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-string": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", @@ -8563,6 +11944,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-weakmap": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", @@ -8692,6 +12086,24 @@ "@pkgjs/parseargs": "^0.11.0" } }, + "node_modules/jake": { + "version": "10.9.4", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", + "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.6", + "filelist": "^1.0.4", + "picocolors": "^1.1.1" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/jest-environment-node": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", @@ -8881,6 +12293,39 @@ "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/jks-js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jks-js/-/jks-js-1.1.0.tgz", + "integrity": "sha512-irWi8S2V029Vic63w0/TYa8NIZwXu9oeMtHQsX51JDIVBo0lrEaOoyM8ALEEh5PVKD6TrA26FixQK6TzT7dHqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "node-forge": "^1.3.1", + "node-int64": "^0.4.0", + "node-rsa": "^1.1.1" + } + }, + "node_modules/joi": { + "version": "17.11.0", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.11.0.tgz", + "integrity": "sha512-NgB+lZLNoqISVy1rZocE9PZI36bL/77ie924Ri43yEvi9GUUMPeyVIr8KdFTMUlby1p0PBYMk9spIxEUQYqrJQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0", + "@hapi/topo": "^5.0.0", + "@sideway/address": "^4.1.3", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, + "node_modules/join-component": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/join-component/-/join-component-1.1.0.tgz", + "integrity": "sha512-bF7vcQxbODoGK1imE2P9GS9aw4zD0Sd+Hni68IMZLj7zRnquH7dXUmMw9hDI5S/Jzt7q+IyTXN0rSg2GI0IKhQ==", + "dev": true, + "license": "MIT" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -8905,6 +12350,16 @@ "integrity": "sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q==", "license": "0BSD" }, + "node_modules/jsep": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.4.0.tgz", + "integrity": "sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.16.0" + } + }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -8924,6 +12379,13 @@ "dev": true, "license": "MIT" }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true, + "license": "MIT" + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -8950,6 +12412,49 @@ "node": ">=6" } }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", + "integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash": "^4.17.21", + "ms": "^2.1.1", + "semver": "^7.3.8" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -8966,6 +12471,36 @@ "node": ">=4.0" } }, + "node_modules/jwa": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/keychain": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/keychain/-/keychain-1.5.0.tgz", + "integrity": "sha512-liyp4r+93RI7EB2jhwaRd4MWfdgHH6shuldkaPMkELCJjMFvOOVXuTvw1pGqFfhsrgA6OqfykWWPQgBjQakVag==", + "dev": true, + "license": "MIT" + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -9043,9 +12578,9 @@ "license": "MIT" }, "node_modules/lightningcss": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", - "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", + "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", "license": "MPL-2.0", "dependencies": { "detect-libc": "^2.0.3" @@ -9058,43 +12593,22 @@ "url": "https://opencollective.com/parcel" }, "optionalDependencies": { - "lightningcss-android-arm64": "1.30.2", - "lightningcss-darwin-arm64": "1.30.2", - "lightningcss-darwin-x64": "1.30.2", - "lightningcss-freebsd-x64": "1.30.2", - "lightningcss-linux-arm-gnueabihf": "1.30.2", - "lightningcss-linux-arm64-gnu": "1.30.2", - "lightningcss-linux-arm64-musl": "1.30.2", - "lightningcss-linux-x64-gnu": "1.30.2", - "lightningcss-linux-x64-musl": "1.30.2", - "lightningcss-win32-arm64-msvc": "1.30.2", - "lightningcss-win32-x64-msvc": "1.30.2" - } - }, - "node_modules/lightningcss-android-arm64": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz", - "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "lightningcss-darwin-arm64": "1.30.1", + "lightningcss-darwin-x64": "1.30.1", + "lightningcss-freebsd-x64": "1.30.1", + "lightningcss-linux-arm-gnueabihf": "1.30.1", + "lightningcss-linux-arm64-gnu": "1.30.1", + "lightningcss-linux-arm64-musl": "1.30.1", + "lightningcss-linux-x64-gnu": "1.30.1", + "lightningcss-linux-x64-musl": "1.30.1", + "lightningcss-win32-arm64-msvc": "1.30.1", + "lightningcss-win32-x64-msvc": "1.30.1" } }, "node_modules/lightningcss-darwin-arm64": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz", - "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", + "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", "cpu": [ "arm64" ], @@ -9112,9 +12626,9 @@ } }, "node_modules/lightningcss-darwin-x64": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz", - "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", + "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", "cpu": [ "x64" ], @@ -9132,9 +12646,9 @@ } }, "node_modules/lightningcss-freebsd-x64": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz", - "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", + "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", "cpu": [ "x64" ], @@ -9152,9 +12666,9 @@ } }, "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz", - "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", + "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", "cpu": [ "arm" ], @@ -9172,9 +12686,9 @@ } }, "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz", - "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", + "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", "cpu": [ "arm64" ], @@ -9192,9 +12706,9 @@ } }, "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz", - "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", + "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", "cpu": [ "arm64" ], @@ -9212,9 +12726,9 @@ } }, "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz", - "integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", + "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", "cpu": [ "x64" ], @@ -9232,9 +12746,9 @@ } }, "node_modules/lightningcss-linux-x64-musl": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz", - "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", + "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", "cpu": [ "x64" ], @@ -9252,9 +12766,9 @@ } }, "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz", - "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", + "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", "cpu": [ "arm64" ], @@ -9272,9 +12786,9 @@ } }, "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz", - "integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", + "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", "cpu": [ "x64" ], @@ -9312,12 +12826,34 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "license": "MIT" }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -9445,6 +12981,13 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, "node_modules/makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", @@ -9470,12 +13013,36 @@ "node": ">= 0.4" } }, + "node_modules/md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, "node_modules/memoize-one": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==", "license": "MIT" }, + "node_modules/merge-options": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/merge-options/-/merge-options-3.0.4.tgz", + "integrity": "sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==", + "license": "MIT", + "dependencies": { + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -9903,12 +13470,127 @@ "node": ">=10" } }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": "*" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/multipasta": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/multipasta/-/multipasta-0.2.7.tgz", + "integrity": "sha512-KPA58d68KgGil15oDqXjkUBEBYc00XvbPj5/X+dyzeo/lWm9Nc25pQRlf1D+gv4OpK7NM0J1odrbu9JNNGvynA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true, + "license": "ISC" + }, + "node_modules/mv": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", + "integrity": "sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "mkdirp": "~0.5.1", + "ncp": "~2.0.0", + "rimraf": "~2.4.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/mv/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/mv/node_modules/glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "optional": true, + "dependencies": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mv/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "optional": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mv/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mv/node_modules/rimraf": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "integrity": "sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "optional": true, + "dependencies": { + "glob": "^6.0.1" + }, + "bin": { + "rimraf": "bin.js" + } + }, "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", @@ -9920,6 +13602,14 @@ "thenify-all": "^1.0.0" } }, + "node_modules/nan": { + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.23.0.tgz", + "integrity": "sha512-1UxuyYGdoQHcGg87Lkqm3FzefucTa0NAiOcuRsDmysep3c1LVCRK2krrUDafMWtjSG04htvAmvg96+SDknOmgQ==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", @@ -9980,6 +13670,27 @@ "dev": true, "license": "MIT" }, + "node_modules/natural-orderby": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/natural-orderby/-/natural-orderby-2.0.3.tgz", + "integrity": "sha512-p7KTHxU0CUrcOXe62Zfrb5Z13nLvPhSWR/so3kFulUQU0sgUll2Z0LwpsLN351eOOD+hRGu/F1g+6xDfPeD++Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==", + "dev": true, + "license": "MIT", + "optional": true, + "bin": { + "ncp": "bin/ncp" + } + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -10036,6 +13747,30 @@ "integrity": "sha512-S2M9YimhSjBSvYnlr5/+umAnPHE++ODwt5e2Ij6FoX45HA/s4vHdkDx1eax2pAPeAOqu4s9b7ppahsyEFdVqQA==", "license": "MIT" }, + "node_modules/node-rsa": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/node-rsa/-/node-rsa-1.1.1.tgz", + "integrity": "sha512-Jd4cvbJMryN21r5HgxQOpMEqv+ooke/korixNNK3mGqfGJmy0M77WDDzo/05969+OkMy3XW1UuZsSmW9KQm7Fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "asn1": "^0.2.4" + } + }, + "node_modules/node-stream-zip": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/node-stream-zip/-/node-stream-zip-1.15.0.tgz", + "integrity": "sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/antelle" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -10122,6 +13857,16 @@ "node": ">= 0.4" } }, + "node_modules/object-treeify": { + "version": "1.1.33", + "resolved": "https://registry.npmjs.org/object-treeify/-/object-treeify-1.1.33.tgz", + "integrity": "sha512-EFVjAYfzWqWsBMRHPMAXLCDIJnpMhdWAqR7xG6M6a2cs6PMFpl/+Z20w9zDW4vkxOFfddegBKq9Rehd0bxWE7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, "node_modules/object.assign": { "version": "4.1.7", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", @@ -10473,6 +14218,20 @@ "node": ">=6" } }, + "node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/parse-png": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/parse-png/-/parse-png-2.1.0.tgz", @@ -10494,6 +14253,17 @@ "node": ">= 0.8" } }, + "node_modules/password-prompt": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/password-prompt/-/password-prompt-1.1.3.tgz", + "integrity": "sha512-HkrjG2aJlvF0t2BMH0e2LB/EHf3Lcq3fNMzy4GYHcQblAvOl+QQji1Lx7WRBMqpVK8p+KR7bCg7oqAMXtdgqyw==", + "dev": true, + "license": "0BSD", + "dependencies": { + "ansi-escapes": "^4.3.2", + "cross-spawn": "^7.0.3" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -10549,6 +14319,16 @@ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "license": "ISC" }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -10576,6 +14356,75 @@ "node": ">= 6" } }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/plist": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", @@ -10725,6 +14574,27 @@ "asap": "~2.0.6" } }, + "node_modules/promise-limit": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/promise-limit/-/promise-limit-2.7.0.tgz", + "integrity": "sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw==", + "dev": true, + "license": "ISC" + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -11319,6 +15189,16 @@ } } }, + "node_modules/redeyed": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz", + "integrity": "sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esprima": "~4.0.0" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -11422,6 +15302,13 @@ "regjsparser": "bin/parser" } }, + "node_modules/remove-trailing-slash": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/remove-trailing-slash/-/remove-trailing-slash-0.1.1.tgz", + "integrity": "sha512-o4S4Qh6L2jpnCy83ysZDau+VORNvnFw07CKSAymkd6ICNVEPisMyzlc00KlvvicsxKck94SEwhDnMNdICzO+tA==", + "dev": true, + "license": "MIT" + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -11547,6 +15434,16 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "license": "ISC" }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", @@ -11681,6 +15578,14 @@ ], "license": "MIT" }, + "node_modules/safe-json-stringify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", + "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/safe-push-apply": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", @@ -11716,6 +15621,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, "node_modules/sax": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", @@ -11870,6 +15782,16 @@ "node": ">= 0.4" } }, + "node_modules/set-interval-async": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/set-interval-async/-/set-interval-async-3.0.3.tgz", + "integrity": "sha512-o4DyBv6mko+A9cH3QKek4SAAT5UyJRkfdTi6JHii6ZCKUYFun8SwgBmQrOXd158JOwBQzA+BnO8BvT64xuCaSw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/set-proto": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", @@ -12068,6 +15990,24 @@ "node": ">=8" } }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, "node_modules/slugify": { "version": "1.6.6", "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz", @@ -12114,6 +16054,19 @@ "node": ">=0.10.0" } }, + "node_modules/split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, "node_modules/split-on-first": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", @@ -12207,6 +16160,18 @@ "node": ">= 0.10.0" } }, + "node_modules/streamx": { + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz", + "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, "node_modules/strict-uri-encode": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", @@ -12470,6 +16435,14 @@ "node": ">= 6" } }, + "node_modules/sudo-prompt": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-9.1.1.tgz", + "integrity": "sha512-es33J1g2HjMpyAhz8lOR+ICmXXAqTuKbuXuUWLhOLew20oN9oUCgCJx615U/v7aioZg7IX5lIh9x34vwneu4pA==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT" + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -12558,6 +16531,18 @@ "node": ">=18" } }, + "node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, "node_modules/tar/node_modules/yallist": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", @@ -12673,6 +16658,16 @@ "node": "*" } }, + "node_modules/text-decoder": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", + "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, "node_modules/thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", @@ -12694,12 +16689,29 @@ "node": ">=0.8" } }, + "node_modules/this-file": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/this-file/-/this-file-2.0.3.tgz", + "integrity": "sha512-IdMH1bUkVJdJjM7o8v83Mv4QvVPdkAofur20STl2Bbw9uMuuS/bT/PZURkEdZsy9XC/1ZXWgZ1wIL9nvouGaEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.15.0" + } + }, "node_modules/throat": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", "license": "MIT" }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true, + "license": "MIT" + }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -12794,12 +16806,83 @@ "typescript": ">=4.8.4" } }, + "node_modules/ts-deepmerge": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ts-deepmerge/-/ts-deepmerge-6.2.0.tgz", + "integrity": "sha512-2qxI/FZVDPbzh63GwWIZYE7daWKtwXZYuyc8YNq0iTmMUwn4mL0jRLsp6hfFlgbdRSR4x2ppe+E86FnvEpN7Nw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14.13.1" + } + }, "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", "license": "Apache-2.0" }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", @@ -12832,6 +16915,29 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/turndown": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/turndown/-/turndown-7.1.2.tgz", + "integrity": "sha512-ntI9R7fcUKjqBP6QU8rBK2Ehyt8LAzt3UBT9JR9tgo6GtuKvyUzpayWmeMKJw1DPdXzktvtIT8m2mVXz+bL/Qg==", + "dev": true, + "license": "MIT", + "dependencies": { + "domino": "^2.1.6" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -13067,6 +17173,16 @@ "node": ">=8" } }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -13111,6 +17227,16 @@ "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" } }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/update-browserslist-db": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", @@ -13230,6 +17356,13 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, "node_modules/validate-npm-package-name": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", @@ -13617,6 +17750,54 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/widest-line/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/widest-line/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/widest-line/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/wonka": { "version": "6.3.5", "resolved": "https://registry.npmjs.org/wonka/-/wonka-6.3.5.tgz", @@ -13633,6 +17814,13 @@ "node": ">=0.10.0" } }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, "node_modules/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", @@ -13888,6 +18076,16 @@ "node": ">=8" } }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/packages/voice-mobile/package.json b/packages/voice-mobile/package.json index 28ded522a..b170f2ff8 100644 --- a/packages/voice-mobile/package.json +++ b/packages/voice-mobile/package.json @@ -5,27 +5,34 @@ "scripts": { "start": "expo start", "reset-project": "node ./scripts/reset-project.js", - "android": "expo start --android", - "ios": "expo start --ios", + "android": "expo run:android", + "ios": "expo run:ios", "web": "expo start --web", "lint": "expo lint" }, "dependencies": { "@expo/vector-icons": "^15.0.2", + "@react-native-async-storage/async-storage": "2.2.0", "@react-navigation/bottom-tabs": "^7.4.0", "@react-navigation/elements": "^2.6.3", "@react-navigation/native": "^7.1.8", "expo": "~54.0.15", + "expo-audio": "~1.0.13", + "expo-av": "^16.0.7", "expo-constants": "~18.0.9", + "expo-dev-client": "^6.0.15", + "expo-file-system": "~19.0.17", "expo-font": "~14.0.9", "expo-haptics": "~15.0.7", "expo-image": "~3.0.10", + "expo-keep-awake": "^15.0.7", "expo-linking": "~8.0.8", "expo-router": "~6.0.13", "expo-splash-screen": "~31.0.10", "expo-status-bar": "~3.0.8", "expo-symbols": "~1.0.7", "expo-system-ui": "~6.0.7", + "expo-updates": "~29.0.12", "expo-web-browser": "~15.0.8", "nativewind": "^5.0.0-preview.2", "react": "19.1.0", @@ -42,13 +49,14 @@ "devDependencies": { "@tailwindcss/postcss": "^4.1.15", "@types/react": "~19.1.0", + "eas-cli": "^16.24.1", "eslint": "^9.25.0", "eslint-config-expo": "~10.0.0", "postcss": "^8.4.49", "tailwindcss": "^4.1.15", "typescript": "~5.9.2" }, - "resolutions": { + "overrides": { "lightningcss": "1.30.1" }, "private": true diff --git a/packages/voice-mobile/tsconfig.json b/packages/voice-mobile/tsconfig.json index 2f35dd457..50d0f7253 100644 --- a/packages/voice-mobile/tsconfig.json +++ b/packages/voice-mobile/tsconfig.json @@ -5,6 +5,9 @@ "paths": { "@/*": [ "./*" + ], + "@voice-assistant/*": [ + "../voice-assistant/src/*" ] } },