diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cf36d21..1088e7b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,16 +11,56 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add Program section replacing Steps tab with 5 sub-sections: Steps, Daily, Prayers, Literature, Meetings - Add horizontal top tabs navigation within Program section +- Add sponsor/sponsee connection system with Intent & Ownership pattern +- Add `ConnectionIntent` type with `not_looking`, `seeking_sponsor`, `open_to_sponsoring`, `open_to_both` options +- Add `ConnectionIntentSelector` component for users to set their connection preferences on profile +- Add `PersistentInviteCard` component showing active invite codes with expiration timer, copy, share, regenerate, and revoke actions +- Add `SymmetricRevealSection` component for mutual consent contact sharing within relationships +- Add `ExternalHandlesSection` in Settings for storing private contact info (Discord, Telegram, WhatsApp, Signal, Phone) +- Add `external_handles` JSONB field to profiles for storing contact info privately +- Add `sponsor_reveal_consent` and `sponsee_reveal_consent` columns to relationships for symmetric reveal +- Add `revoked_at` and `intent` columns to invite_codes for better invite management +- Add `FindSupportSection` component for opt-in matching to find sponsors/sponsees based on complementary intents +- Add `connection_matches` table with bilateral acceptance pattern (both parties must accept to connect) +- Add `find_potential_matches`, `accept_match`, and `reject_match` database functions for matching workflow +- Add database migration with RLS policies for connection intent, external handles, symmetric reveal, and opt-in matching ### Changed - Move Steps screens from `/steps` to `/program/steps` - Update Settings toggle label from "Include 12-Step Content" to "Show 12-Step Program" with expanded description +- Extract `getTimeRemaining` and `formatTimeRemaining` to shared `lib/time-utils.ts` (DRY refactor) +- Extract `getPlatformIcon` and `getPlatformLabel` to shared `lib/platform-icons.tsx` (DRY refactor) +- Extract `SheetInputComponent` to shared `lib/sheet-input.tsx` for platform-specific bottom sheet inputs +- Improve `lib/platform-icons.tsx` organization with canonical section dividers +- Enhance platform-icons test suite with size verification tests +- Switch invite code generation to cryptographically secure `expo-crypto` (replacing `Math.random()`) +- Memoize `handleConnectionIntentChange` with `useCallback` for performance optimization +- Export `IconTheme` interface from `lib/platform-icons.tsx` for type safety +- Strengthen `platformLabels` typing with `Record` constraint +- Rename boolean props for consistency (myConsent → hasMyConsent, theirConsent → hasTheirConsent, disabled → isDisabled, loadingInviteCode → isLoadingInviteCode) ### Removed ### Fixed +- Fix Steps tab still showing in native tab bar when 12-step content toggle is disabled +- Fix bottom sheet text inputs not working on web by using platform-specific InputComponent pattern +- Fix UPDATE policy missing `revoked_at` check in invite_codes RLS migration +- Fix overly broad RLS policy for profile viewing via invite code, replaced with SECURITY DEFINER RPC +- Fix UNIQUE constraint preventing sponsor/sponsee rematches after disconnect, converted to partial index +- Fix external handles exposure in profile queries before consent check +- Fix RLS violation when providers create connection matches directly +- Fix external handles DB write on every keystroke, added debouncing with draft state +- Fix timer showing stale values on mount, now updates immediately when expires_at changes +- Fix clipboard copy errors not handled in PersistentInviteCard +- Fix togglePlatform UI/data state mixing in ExternalHandlesSection +- Fix flaky time-utils test by using deterministic fake timers +- Fix import aliases not using @/ prefix in RelationshipCard and SettingsContent +- Fix empty external handle values shown in SymmetricRevealSection +- Fix timer display not showing minutes when hours = 0 in FindSupportSection +- Fix potential memory leak in SymmetricRevealSection using ref-based mount guard pattern + ## [1.3.0] - 2026-01-27 ### Added diff --git a/__mocks__/lucide-react-native.js b/__mocks__/lucide-react-native.js index 094c43e8..17cc48e4 100644 --- a/__mocks__/lucide-react-native.js +++ b/__mocks__/lucide-react-native.js @@ -145,4 +145,5 @@ module.exports = { Layout: createIconMock('Layout'), Sparkles: createIconMock('Sparkles'), Flame: createIconMock('Flame'), + QrCode: createIconMock('QrCode'), }; diff --git a/__tests__/app/profile.keyboard-avoidance.test.tsx b/__tests__/app/profile.keyboard-avoidance.test.tsx index bc9d5c54..ec992910 100644 --- a/__tests__/app/profile.keyboard-avoidance.test.tsx +++ b/__tests__/app/profile.keyboard-avoidance.test.tsx @@ -204,6 +204,20 @@ jest.mock('lucide-react-native', () => ({ ChevronLeft: () => null, Layout: () => null, Sparkles: () => null, + // ConnectionIntentSelector icons + Search: () => null, + Users: () => null, + UserPlus: () => null, + // PersistentInviteCard icons + Clock: () => null, + Plus: () => null, + // SymmetricRevealSection icons + Eye: () => null, + EyeOff: () => null, + MessageCircle: () => null, + Phone: () => null, + Send: () => null, + Check: () => null, })); // Mock useWhatsNew hook diff --git a/__tests__/app/profile.test.tsx b/__tests__/app/profile.test.tsx index eb8fdf0d..01a431b4 100644 --- a/__tests__/app/profile.test.tsx +++ b/__tests__/app/profile.test.tsx @@ -68,12 +68,36 @@ jest.mock('@/lib/supabase', () => ({ } if (table === 'invite_codes') { return { - insert: jest.fn().mockResolvedValue({ error: null }), + insert: jest.fn().mockReturnValue({ + select: jest.fn().mockReturnValue({ + single: jest.fn().mockResolvedValue({ + data: { + id: 'invite-123', + code: 'TESTCODE', + sponsor_id: 'user-123', + expires_at: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(), + created_at: new Date().toISOString(), + }, + error: null, + }), + }), + }), + update: jest.fn().mockReturnValue({ + eq: jest.fn().mockReturnValue({ + eq: jest.fn().mockResolvedValue({ error: null }), + }), + }), select: jest.fn().mockReturnValue({ eq: jest.fn().mockReturnValue({ - gt: jest.fn().mockReturnValue({ + is: jest.fn().mockReturnValue({ is: jest.fn().mockReturnValue({ - single: jest.fn().mockResolvedValue({ data: null, error: null }), + gt: jest.fn().mockReturnValue({ + order: jest.fn().mockReturnValue({ + limit: jest.fn().mockReturnValue({ + maybeSingle: jest.fn().mockResolvedValue({ data: null, error: null }), + }), + }), + }), }), }), maybeSingle: jest.fn().mockResolvedValue({ data: null, error: null }), @@ -208,6 +232,19 @@ jest.mock('lucide-react-native', () => ({ ChevronLeft: () => null, Layout: () => null, Sparkles: () => null, + // Icons used by ConnectionIntentSelector + Search: () => null, + Users: () => null, + UserPlus: () => null, + // Icons used by PersistentInviteCard + Clock: () => null, + // Icons used by SymmetricRevealSection + Eye: () => null, + EyeOff: () => null, + MessageCircle: () => null, + Phone: () => null, + Send: () => null, + Check: () => null, })); // Mock useWhatsNew hook @@ -1332,7 +1369,6 @@ describe('ProfileScreen', () => { }); it('generates invite code when pressed', async () => { - const { Alert } = jest.requireMock('react-native'); render(); await waitFor(() => { @@ -1342,10 +1378,8 @@ describe('ProfileScreen', () => { fireEvent.press(screen.getByText('Generate Invite Code')); await waitFor(() => { - expect(Alert.alert).toHaveBeenCalledWith( - 'Invite Code Generated', - expect.stringContaining('Your invite code is:'), - expect.any(Array) + expect(Toast.show).toHaveBeenCalledWith( + expect.objectContaining({ type: 'success', text1: 'New invite code generated' }) ); }); }); @@ -1385,7 +1419,29 @@ describe('ProfileScreen', () => { supabase.from.mockImplementation((table: string) => { if (table === 'invite_codes') { return { - insert: jest.fn().mockResolvedValue({ error: new Error('Database error') }), + insert: jest.fn().mockReturnValue({ + select: jest.fn().mockReturnValue({ + single: jest.fn().mockResolvedValue({ + data: null, + error: new Error('Database error'), + }), + }), + }), + select: jest.fn().mockReturnValue({ + eq: jest.fn().mockReturnValue({ + is: jest.fn().mockReturnValue({ + is: jest.fn().mockReturnValue({ + gt: jest.fn().mockReturnValue({ + order: jest.fn().mockReturnValue({ + limit: jest.fn().mockReturnValue({ + maybeSingle: jest.fn().mockResolvedValue({ data: null, error: null }), + }), + }), + }), + }), + }), + }), + }), }; } if (table === 'sponsor_sponsee_relationships') { diff --git a/__tests__/app/profile.web.test.tsx b/__tests__/app/profile.web.test.tsx index f7e33bbd..b7c28979 100644 --- a/__tests__/app/profile.web.test.tsx +++ b/__tests__/app/profile.web.test.tsx @@ -68,6 +68,25 @@ jest.mock('lucide-react-native', () => ({ UserMinus: () => null, CheckCircle: () => null, Settings: () => null, + // ConnectionIntentSelector icons + Search: () => null, + Users: () => null, + UserPlus: () => null, + // PersistentInviteCard icons + Copy: () => null, + RefreshCw: () => null, + Trash2: () => null, + Clock: () => null, + Plus: () => null, + // SymmetricRevealSection icons + Eye: () => null, + EyeOff: () => null, + MessageCircle: () => null, + Phone: () => null, + Send: () => null, + Check: () => null, + Shield: () => null, + X: () => null, })); jest.mock('@/lib/logger', () => ({ diff --git a/__tests__/app/settings.test.tsx b/__tests__/app/settings.test.tsx index 16b0c4f4..1effcc1e 100644 --- a/__tests__/app/settings.test.tsx +++ b/__tests__/app/settings.test.tsx @@ -121,6 +121,10 @@ jest.mock('lucide-react-native', () => ({ RotateCcw: () => null, Zap: () => null, BookOpen: () => null, + MessageCircle: () => null, + Phone: () => null, + Send: () => null, + Plus: () => null, })); jest.mock('@react-native-community/datetimepicker', () => { diff --git a/__tests__/components/SettingsSheet.test.tsx b/__tests__/components/SettingsSheet.test.tsx index 3d01b286..91f99bfb 100644 --- a/__tests__/components/SettingsSheet.test.tsx +++ b/__tests__/components/SettingsSheet.test.tsx @@ -209,6 +209,11 @@ jest.mock('lucide-react-native', () => ({ RotateCcw: () => null, Zap: () => null, BookOpen: () => null, + // ExternalHandlesSection icons + MessageCircle: () => null, + Phone: () => null, + Send: () => null, + Plus: () => null, })); jest.mock('@react-native-community/datetimepicker', () => { diff --git a/__tests__/components/settings/SettingsContent.analytics.test.tsx b/__tests__/components/settings/SettingsContent.analytics.test.tsx index 9acab166..dceba00b 100644 --- a/__tests__/components/settings/SettingsContent.analytics.test.tsx +++ b/__tests__/components/settings/SettingsContent.analytics.test.tsx @@ -75,6 +75,10 @@ jest.mock('lucide-react-native', () => ({ ChevronUp: () => null, Calendar: () => null, BookOpen: () => null, + MessageCircle: () => null, + Phone: () => null, + Send: () => null, + Plus: () => null, })); jest.mock('@react-native-community/datetimepicker', () => { diff --git a/__tests__/components/settings/SettingsContent.dev.test.tsx b/__tests__/components/settings/SettingsContent.dev.test.tsx index 30ef798b..94a8739c 100644 --- a/__tests__/components/settings/SettingsContent.dev.test.tsx +++ b/__tests__/components/settings/SettingsContent.dev.test.tsx @@ -51,6 +51,10 @@ jest.mock('lucide-react-native', () => ({ Bell: () => null, Calendar: () => null, BookOpen: () => null, + MessageCircle: () => null, + Phone: () => null, + Send: () => null, + Plus: () => null, })); jest.mock('@react-native-community/datetimepicker', () => { diff --git a/__tests__/components/settings/SettingsContent.sections.test.tsx b/__tests__/components/settings/SettingsContent.sections.test.tsx index 244eab72..1c730577 100644 --- a/__tests__/components/settings/SettingsContent.sections.test.tsx +++ b/__tests__/components/settings/SettingsContent.sections.test.tsx @@ -48,6 +48,10 @@ jest.mock('lucide-react-native', () => ({ Bell: () => null, Calendar: () => null, BookOpen: () => null, + MessageCircle: () => null, + Phone: () => null, + Send: () => null, + Plus: () => null, })); jest.mock('@react-native-community/datetimepicker', () => { diff --git a/__tests__/lib/platform-icons.test.tsx b/__tests__/lib/platform-icons.test.tsx new file mode 100644 index 00000000..41e05bf5 --- /dev/null +++ b/__tests__/lib/platform-icons.test.tsx @@ -0,0 +1,93 @@ +import React from 'react'; +import { render } from '@testing-library/react-native'; +import { View } from 'react-native'; +import { getPlatformIcon, getPlatformLabel, platformLabels } from '@/lib/platform-icons'; + +// Mock theme for testing +const mockTheme = { + primary: '#7c3aed', + info: '#3b82f6', + success: '#22c55e', + warning: '#f59e0b', + textSecondary: '#64748b', +}; + +describe('platform-icons', () => { + describe('platformLabels', () => { + it('contains all expected platforms', () => { + expect(platformLabels.discord).toBe('Discord'); + expect(platformLabels.telegram).toBe('Telegram'); + expect(platformLabels.whatsapp).toBe('WhatsApp'); + expect(platformLabels.signal).toBe('Signal'); + expect(platformLabels.phone).toBe('Phone'); + }); + }); + + describe('getPlatformLabel', () => { + it('returns correct label for known platforms', () => { + expect(getPlatformLabel('discord')).toBe('Discord'); + expect(getPlatformLabel('telegram')).toBe('Telegram'); + expect(getPlatformLabel('whatsapp')).toBe('WhatsApp'); + expect(getPlatformLabel('signal')).toBe('Signal'); + expect(getPlatformLabel('phone')).toBe('Phone'); + }); + + it('returns the key itself for unknown platforms', () => { + expect(getPlatformLabel('unknown')).toBe('unknown'); + expect(getPlatformLabel('custom_platform')).toBe('custom_platform'); + }); + }); + + describe('getPlatformIcon', () => { + it('returns an icon for discord', () => { + const icon = getPlatformIcon('discord', mockTheme); + const { getByTestId } = render({icon}); + expect(getByTestId('icon-container').children).toHaveLength(1); + }); + + it('returns an icon for telegram', () => { + const icon = getPlatformIcon('telegram', mockTheme); + const { getByTestId } = render({icon}); + expect(getByTestId('icon-container').children).toHaveLength(1); + }); + + it('returns an icon for whatsapp', () => { + const icon = getPlatformIcon('whatsapp', mockTheme); + const { getByTestId } = render({icon}); + expect(getByTestId('icon-container').children).toHaveLength(1); + }); + + it('returns an icon for signal', () => { + const icon = getPlatformIcon('signal', mockTheme); + const { getByTestId } = render({icon}); + expect(getByTestId('icon-container').children).toHaveLength(1); + }); + + it('returns an icon for phone', () => { + const icon = getPlatformIcon('phone', mockTheme); + const { getByTestId } = render({icon}); + expect(getByTestId('icon-container').children).toHaveLength(1); + }); + + it('returns a default icon for unknown platforms', () => { + const icon = getPlatformIcon('unknown', mockTheme); + const { getByTestId } = render({icon}); + expect(getByTestId('icon-container').children).toHaveLength(1); + }); + + it('respects custom size parameter', () => { + const icon = getPlatformIcon('discord', mockTheme, 24); + const { root } = render({icon}); + // Find the icon element (Svg component from lucide-react-native) + const iconElement = root.findByProps({ testID: 'icon-container' }).props.children; + expect(iconElement.props.size).toBe(24); + }); + + it('uses default size of 16 when not specified', () => { + const icon = getPlatformIcon('telegram', mockTheme); + const { root } = render({icon}); + const iconElement = root.findByProps({ testID: 'icon-container' }).props.children; + expect(iconElement.props.size).toBe(16); + }); + }); +}); diff --git a/__tests__/lib/time-utils.test.ts b/__tests__/lib/time-utils.test.ts new file mode 100644 index 00000000..4fdc12e5 --- /dev/null +++ b/__tests__/lib/time-utils.test.ts @@ -0,0 +1,169 @@ +import { getTimeRemaining, formatTimeRemaining, TimeRemaining } from '@/lib/time-utils'; + +describe('time-utils', () => { + describe('getTimeRemaining', () => { + it('returns expired state when date is in the past', () => { + const pastDate = new Date(Date.now() - 1000).toISOString(); + const result = getTimeRemaining(pastDate); + + expect(result.isExpired).toBe(true); + expect(result.days).toBe(0); + expect(result.hours).toBe(0); + expect(result.minutes).toBe(0); + expect(result.isExpiringSoon).toBe(false); + }); + + it('calculates days, hours, and minutes correctly', () => { + // 2 days, 3 hours, 30 minutes from now + const futureDate = new Date( + Date.now() + 2 * 24 * 60 * 60 * 1000 + 3 * 60 * 60 * 1000 + 30 * 60 * 1000 + ).toISOString(); + const result = getTimeRemaining(futureDate); + + expect(result.isExpired).toBe(false); + expect(result.days).toBe(2); + expect(result.hours).toBe(3); + expect(result.minutes).toBe(30); + }); + + it('marks as expiring soon when less than 2 days remain (default)', () => { + // 1 day from now + const futureDate = new Date(Date.now() + 1 * 24 * 60 * 60 * 1000).toISOString(); + const result = getTimeRemaining(futureDate); + + expect(result.isExpiringSoon).toBe(true); + }); + + it('does not mark as expiring soon when 2+ days remain', () => { + // 3 days from now + const futureDate = new Date(Date.now() + 3 * 24 * 60 * 60 * 1000).toISOString(); + const result = getTimeRemaining(futureDate); + + expect(result.isExpiringSoon).toBe(false); + }); + + it('uses custom expiringSoonDays threshold', () => { + // 2 days from now, with threshold of 3 days + const futureDate = new Date(Date.now() + 2 * 24 * 60 * 60 * 1000).toISOString(); + const result = getTimeRemaining(futureDate, 3); + + expect(result.isExpiringSoon).toBe(true); + }); + + it('handles edge case of exactly now', () => { + // Use fake timers to avoid race condition between timestamp creation and comparison + const mockTime = 1705589400000; // 2024-01-18T12:00:00Z + jest.useFakeTimers(); + jest.setSystemTime(mockTime); + + const now = new Date(mockTime).toISOString(); + const result = getTimeRemaining(now); + + // Should be expired since we're at or past the time + expect(result.isExpired).toBe(true); + + jest.useRealTimers(); + }); + }); + + describe('formatTimeRemaining', () => { + it('returns "Expired" when expired', () => { + const time: TimeRemaining = { + days: 0, + hours: 0, + minutes: 0, + isExpired: true, + isExpiringSoon: false, + }; + expect(formatTimeRemaining(time)).toBe('Expired'); + }); + + it('formats days and hours in short format', () => { + const time: TimeRemaining = { + days: 5, + hours: 3, + minutes: 45, + isExpired: false, + isExpiringSoon: false, + }; + expect(formatTimeRemaining(time, 'short')).toBe('5d 3h'); + }); + + it('formats hours and minutes when no days in short format', () => { + const time: TimeRemaining = { + days: 0, + hours: 2, + minutes: 30, + isExpired: false, + isExpiringSoon: true, + }; + expect(formatTimeRemaining(time, 'short')).toBe('2h 30m'); + }); + + it('formats minutes only when no hours or days in short format', () => { + const time: TimeRemaining = { + days: 0, + hours: 0, + minutes: 15, + isExpired: false, + isExpiringSoon: true, + }; + expect(formatTimeRemaining(time, 'short')).toBe('15m'); + }); + + it('formats in long format with singular units', () => { + const time: TimeRemaining = { + days: 1, + hours: 1, + minutes: 1, + isExpired: false, + isExpiringSoon: true, + }; + expect(formatTimeRemaining(time, 'long')).toBe('1 day, 1 hour'); + }); + + it('formats in long format with plural units', () => { + const time: TimeRemaining = { + days: 3, + hours: 5, + minutes: 0, + isExpired: false, + isExpiringSoon: false, + }; + expect(formatTimeRemaining(time, 'long')).toBe('3 days, 5 hours'); + }); + + it('includes minutes in long format when no days', () => { + const time: TimeRemaining = { + days: 0, + hours: 2, + minutes: 30, + isExpired: false, + isExpiringSoon: true, + }; + expect(formatTimeRemaining(time, 'long')).toBe('2 hours, 30 minutes'); + }); + + it('returns "Less than a minute" for very short times in long format', () => { + const time: TimeRemaining = { + days: 0, + hours: 0, + minutes: 0, + isExpired: false, + isExpiringSoon: true, + }; + expect(formatTimeRemaining(time, 'long')).toBe('Less than a minute'); + }); + + it('defaults to short format', () => { + const time: TimeRemaining = { + days: 2, + hours: 4, + minutes: 30, + isExpired: false, + isExpiringSoon: false, + }; + expect(formatTimeRemaining(time)).toBe('2d 4h'); + }); + }); +}); diff --git a/app/(app)/(tabs)/profile/index.tsx b/app/(app)/(tabs)/profile/index.tsx index 8aa774a6..67a50101 100644 --- a/app/(app)/(tabs)/profile/index.tsx +++ b/app/(app)/(tabs)/profile/index.tsx @@ -4,32 +4,39 @@ import { Text, StyleSheet, TouchableOpacity, - Share, - Platform, ActivityIndicator, ScrollView, } from 'react-native'; import { KeyboardAwareScrollView } from 'react-native-keyboard-controller'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { useRouter } from 'expo-router'; +import * as Crypto from 'expo-crypto'; import { useAuth } from '@/contexts/AuthContext'; import { useTheme } from '@/contexts/ThemeContext'; import { supabase } from '@/lib/supabase'; import { useDaysSober } from '@/hooks/useDaysSober'; import { Settings } from 'lucide-react-native'; -import type { SponsorSponseeRelationship } from '@/types/database'; +import type { + SponsorSponseeRelationship, + InviteCode, + ConnectionIntent, + ConnectionMatch, +} from '@/types/database'; import { logger, LogCategory } from '@/lib/logger'; import LogSlipUpSheet, { LogSlipUpSheetRef } from '@/components/sheets/LogSlipUpSheet'; import EnterInviteCodeSheet, { EnterInviteCodeSheetRef, } from '@/components/sheets/EnterInviteCodeSheet'; import { useTabBarPadding } from '@/hooks/useTabBarPadding'; -import { showAlert, showConfirm } from '@/lib/alert'; +import { showConfirm } from '@/lib/alert'; import { showToast } from '@/lib/toast'; import ProfileHeader from '@/components/profile/ProfileHeader'; import SobrietyStats from '@/components/profile/SobrietyStats'; import RelationshipCard from '@/components/profile/RelationshipCard'; import InviteCodeSection from '@/components/profile/InviteCodeSection'; +import ConnectionIntentSelector from '@/components/profile/ConnectionIntentSelector'; +import PersistentInviteCard from '@/components/profile/PersistentInviteCard'; +import FindSupportSection from '@/components/profile/FindSupportSection'; /** * Render the authenticated user's profile, sobriety stats, and sponsor/sponsee management interface, @@ -57,15 +64,200 @@ export default function ProfileScreen() { const [sponseeTaskStats, setSponseeTaskStats] = useState<{ [key: string]: { total: number; completed: number }; }>({}); + const [activeInviteCode, setActiveInviteCode] = useState(null); + const [isLoadingInviteCode, setLoadingInviteCode] = useState(false); + const [pendingMatches, setPendingMatches] = useState([]); + + /** + * Fetches the user's active invite code (not used, not revoked, not expired). + */ + const fetchActiveInviteCode = useCallback(async () => { + if (!profile) return; + + setLoadingInviteCode(true); + try { + const { data, error } = await supabase + .from('invite_codes') + .select('*') + .eq('sponsor_id', profile.id) + .is('used_by', null) + .is('revoked_at', null) + .gt('expires_at', new Date().toISOString()) + .order('created_at', { ascending: false }) + .limit(1) + .maybeSingle(); + + if (error) { + logger.error('Failed to fetch active invite code', error, { + category: LogCategory.DATABASE, + }); + return; + } + + setActiveInviteCode(data); + } catch (error) { + logger.error('Fetch active invite code failed', error as Error, { + category: LogCategory.DATABASE, + }); + } finally { + setLoadingInviteCode(false); + } + }, [profile]); + + /** + * Fetches pending connection matches for the current user. + */ + const fetchPendingMatches = useCallback(async () => { + if (!profile) return; + + try { + const { data, error } = await supabase + .from('connection_matches') + .select('*, seeker:seeker_id(id, display_name), provider:provider_id(id, display_name)') + .or(`seeker_id.eq.${profile.id},provider_id.eq.${profile.id}`) + .eq('status', 'pending') + .gt('expires_at', new Date().toISOString()) + .order('created_at', { ascending: false }); + + if (error) { + logger.error('Failed to fetch pending matches', error, { + category: LogCategory.DATABASE, + }); + return; + } + + setPendingMatches(data || []); + } catch (error) { + logger.error('Fetch pending matches failed', error as Error, { + category: LogCategory.DATABASE, + }); + } + }, [profile]); + + /** + * Revokes the current active invite code. + */ + const revokeInviteCode = useCallback(async () => { + if (!activeInviteCode) return; + + const confirmed = await showConfirm( + 'Revoke Invite Code', + 'This will invalidate the current invite code. Anyone with this code will no longer be able to use it.', + 'Revoke', + 'Cancel', + true + ); + + if (!confirmed) return; + + setLoadingInviteCode(true); + try { + const { error } = await supabase + .from('invite_codes') + .update({ revoked_at: new Date().toISOString() }) + .eq('id', activeInviteCode.id); + + if (error) { + throw error; + } + + setActiveInviteCode(null); + showToast.success('Invite code revoked'); + } catch (error) { + logger.error('Failed to revoke invite code', error as Error, { + category: LogCategory.DATABASE, + }); + showToast.error('Failed to revoke invite code'); + } finally { + setLoadingInviteCode(false); + } + }, [activeInviteCode]); + + /** + * Regenerates invite code (revokes old, creates new). + */ + const regenerateInviteCode = useCallback(async () => { + if (!profile) return; + + setLoadingInviteCode(true); + try { + // Revoke existing code if any (silently) + if (activeInviteCode) { + await supabase + .from('invite_codes') + .update({ revoked_at: new Date().toISOString() }) + .eq('id', activeInviteCode.id); + } + + // Generate new code using cryptographically secure random bytes + const randomBytes = await Crypto.getRandomBytesAsync(6); + const code = Array.from(randomBytes) + .map((b) => b.toString(36).padStart(2, '0')) + .join('') + .substring(0, 8) + .toUpperCase(); + const expiresAt = new Date(); + expiresAt.setDate(expiresAt.getDate() + 30); + + const { data, error } = await supabase + .from('invite_codes') + .insert({ + code, + sponsor_id: profile.id, + expires_at: expiresAt.toISOString(), + intent: profile.connection_intent, + }) + .select() + .single(); + + if (error) { + throw error; + } + + setActiveInviteCode(data); + showToast.success('New invite code generated'); + } catch (error) { + logger.error('Failed to regenerate invite code', error as Error, { + category: LogCategory.DATABASE, + }); + showToast.error('Failed to generate invite code'); + } finally { + setLoadingInviteCode(false); + } + }, [profile, activeInviteCode]); + + /** + * Handles connection intent change from the selector. + */ + const handleConnectionIntentChange = useCallback( + async (intent: ConnectionIntent | null) => { + if (!profile) return; + const { error } = await supabase + .from('profiles') + .update({ connection_intent: intent }) + .eq('id', profile.id); + if (error) { + showToast.error('Failed to update connection intent'); + logger.error('Failed to update connection intent', error, { + category: LogCategory.DATABASE, + }); + } else { + await refreshProfile(); + showToast.success('Connection intent updated'); + } + }, + [profile, refreshProfile] + ); const fetchRelationships = useCallback(async () => { if (!profile) return; setLoadingRelationships(true); try { + // Only fetch non-sensitive profile fields (external_handles requires consent) const { data: asSponsee, error: asSponseeError } = await supabase .from('sponsor_sponsee_relationships') - .select('*, sponsor:sponsor_id(*)') + .select('*, sponsor:sponsor_id(id, display_name, sobriety_date, avatar_url)') .eq('sponsee_id', profile.id) .eq('status', 'active'); @@ -80,9 +272,10 @@ export default function ProfileScreen() { return; } + // Only fetch non-sensitive profile fields (external_handles requires consent) const { data: asSponsor, error: asSponsorError } = await supabase .from('sponsor_sponsee_relationships') - .select('*, sponsee:sponsee_id(*)') + .select('*, sponsee:sponsee_id(id, display_name, sobriety_date, avatar_url)') .eq('sponsor_id', profile.id) .eq('status', 'active'); @@ -152,7 +345,9 @@ export default function ProfileScreen() { useEffect(() => { fetchRelationships(); - }, [profile, fetchRelationships]); + fetchActiveInviteCode(); + fetchPendingMatches(); + }, [profile, fetchRelationships, fetchActiveInviteCode, fetchPendingMatches]); // Use hook for current user's days sober const { @@ -163,52 +358,6 @@ export default function ProfileScreen() { loading: loadingDaysSober, } = useDaysSober(); - const generateInviteCode = async () => { - if (!profile) return; - - const code = Math.random().toString(36).substring(2, 10).toUpperCase(); - const expiresAt = new Date(); - expiresAt.setDate(expiresAt.getDate() + 30); - - const { error } = await supabase.from('invite_codes').insert({ - code, - sponsor_id: profile.id, - expires_at: expiresAt.toISOString(), - }); - - if (error) { - showToast.error('Failed to generate invite code'); - } else { - if (Platform.OS === 'web') { - const shouldCopy = await showConfirm( - 'Invite Code Generated', - `Your invite code is: ${code}\n\nShare this with your sponsee to connect.`, - 'Copy to Clipboard', - 'Cancel' - ); - if (shouldCopy) { - navigator.clipboard.writeText(code); - showToast.success('Invite code copied to clipboard!'); - } - } else { - showAlert( - 'Invite Code Generated', - `Your invite code is: ${code}\n\nShare this with your sponsee to connect.`, - [ - { - text: 'Share', - onPress: () => - Share.share({ - message: `Join me on Sobers! Use invite code: ${code}`, - }), - }, - { text: 'OK' }, - ] - ); - } - } - }; - /** * Handles connecting to a sponsor using an invite code. * Called from the EnterInviteCodeSheet component. @@ -462,6 +611,35 @@ export default function ProfileScreen() { inviteCodeSheetRef.current?.present(); }; + /** + * Updates the reveal consent for a relationship. + * @param relationshipId - The relationship ID + * @param isSponsor - Whether current user is the sponsor in this relationship + * @param consent - The new consent value + */ + const updateRevealConsent = async ( + relationshipId: string, + isSponsor: boolean, + consent: boolean + ) => { + const columnToUpdate = isSponsor ? 'sponsor_reveal_consent' : 'sponsee_reveal_consent'; + + const { error } = await supabase + .from('sponsor_sponsee_relationships') + .update({ [columnToUpdate]: consent }) + .eq('id', relationshipId); + + if (error) { + logger.error('Failed to update reveal consent', error, { + category: LogCategory.DATABASE, + }); + showToast.error('Failed to update contact sharing preference'); + } else { + await fetchRelationships(); + showToast.success(consent ? 'Contact sharing enabled' : 'Contact sharing disabled'); + } + }; + const styles = useMemo(() => createStyles(theme, insets), [theme, insets]); return ( @@ -506,6 +684,29 @@ export default function ProfileScreen() { onLogSlipUp={handleLogSlipUp} /> + {/* Connection Intent Selector */} + + + {/* Find Support Section (Opt-in Matching) */} + {profile && ( + + { + fetchPendingMatches(); + fetchRelationships(); + }} + /> + + )} + {loadingRelationships ? ( Your Sponsees @@ -516,16 +717,30 @@ export default function ProfileScreen() { ) : ( 0 ? 'Generate New Invite Code' : 'Generate Invite Code' + activeInviteCode + ? '' // Hide primary button when there's an active code (actions are on the card) + : sponseeRelationships.length > 0 + ? 'Generate New Invite Code' + : 'Generate Invite Code' } - showGenerateNew={sponseeRelationships.length > 0} + showGenerateNew={sponseeRelationships.length > 0 && !activeInviteCode} theme={theme} - onPrimaryAction={generateInviteCode} + onPrimaryAction={regenerateInviteCode} testIDPrefix="sponsor" > + {/* Show active invite code card */} + {activeInviteCode && ( + + )} {sponseeRelationships.map((rel) => ( router.push('/tasks')} + relationship={rel} + myHandles={profile?.external_handles} + onConsentChange={(consent) => updateRevealConsent(rel.id, true, consent)} /> ))} @@ -576,6 +794,9 @@ export default function ProfileScreen() { onDisconnect={() => disconnectRelationship(rel.id, false, rel.sponsor?.display_name || 'Unknown') } + relationship={rel} + myHandles={profile?.external_handles} + onConsentChange={(consent) => updateRevealConsent(rel.id, false, consent)} /> ))} diff --git a/components/TaskCreationSheet.tsx b/components/TaskCreationSheet.tsx index 5ec51b3e..d89c8930 100644 --- a/components/TaskCreationSheet.tsx +++ b/components/TaskCreationSheet.tsx @@ -19,7 +19,8 @@ import { ActivityIndicator, Platform, } from 'react-native'; -import { BottomSheetScrollView, BottomSheetTextInput } from '@gorhom/bottom-sheet'; +import { BottomSheetScrollView } from '@gorhom/bottom-sheet'; +import { SheetInputComponent } from '@/lib/sheet-input'; import { supabase } from '@/lib/supabase'; import { TaskTemplate, Profile } from '@/types/database'; import { ThemeColors } from '@/contexts/ThemeContext'; @@ -510,7 +511,7 @@ const TaskCreationSheet = forwardRef Task Title * - Task Description * - void; + /** Theme object from ThemeContext */ + theme: ThemeColors; + /** Whether the selector is disabled */ + disabled?: boolean; +} + +// ============================================================================= +// Component +// ============================================================================= + +/** + * A selector component for choosing connection intent. + * Users must declare their intent before generating invite codes. + * + * @example + * ```tsx + * updateProfile({ connection_intent: intent })} + * theme={theme} + * /> + * ``` + */ +export default function ConnectionIntentSelector({ + value, + onChange, + theme, + disabled = false, +}: ConnectionIntentSelectorProps): React.JSX.Element { + const styles = useMemo(() => createStyles(theme), [theme]); + + const options: IntentOption[] = useMemo( + () => [ + { + value: 'not_looking', + label: 'Not Looking', + description: 'Not currently seeking connections', + icon: , + }, + { + value: 'seeking_sponsor', + label: 'Seeking a Sponsor', + description: 'Looking for someone to guide my recovery', + icon: , + }, + { + value: 'open_to_sponsoring', + label: 'Open to Sponsoring', + description: 'Willing to help others in their recovery', + icon: , + }, + { + value: 'open_to_both', + label: 'Open to Both', + description: 'Seeking a sponsor and willing to sponsor others', + icon: , + }, + ], + [theme] + ); + + return ( + + Connection Intent + Declare your availability before connecting with others + + {options.map((option) => { + const isSelected = value === option.value; + return ( + !disabled && onChange(option.value)} + disabled={disabled} + accessibilityRole="radio" + accessibilityState={{ selected: isSelected, disabled }} + accessibilityLabel={`${option.label}: ${option.description}`} + > + {option.icon} + + + {option.label} + + {option.description} + + {isSelected && ( + + )} + + ); + })} + + + ); +} + +// ============================================================================= +// Styles +// ============================================================================= + +const createStyles = (theme: ThemeColors) => + StyleSheet.create({ + container: { + padding: 16, + }, + title: { + fontSize: 18, + fontFamily: theme.fontRegular, + fontWeight: '600', + color: theme.text, + marginBottom: 4, + }, + subtitle: { + fontSize: 14, + fontFamily: theme.fontRegular, + color: theme.textSecondary, + marginBottom: 16, + }, + optionsContainer: { + gap: 12, + }, + option: { + flexDirection: 'row', + alignItems: 'center', + backgroundColor: theme.card, + padding: 16, + borderRadius: 12, + borderWidth: 2, + borderColor: 'transparent', + shadowColor: theme.black, + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.1, + shadowRadius: 8, + elevation: 3, + }, + optionSelected: { + borderColor: theme.primary, + backgroundColor: theme.primaryLight, + }, + optionDisabled: { + opacity: 0.5, + }, + optionIcon: { + width: 40, + height: 40, + borderRadius: 20, + backgroundColor: theme.background, + justifyContent: 'center', + alignItems: 'center', + }, + optionContent: { + flex: 1, + marginLeft: 12, + }, + optionLabel: { + fontSize: 16, + fontFamily: theme.fontRegular, + fontWeight: '600', + color: theme.text, + }, + optionLabelSelected: { + color: theme.primary, + }, + optionDescription: { + fontSize: 13, + fontFamily: theme.fontRegular, + color: theme.textSecondary, + marginTop: 2, + }, + selectedIndicator: { + width: 12, + height: 12, + borderRadius: 6, + backgroundColor: theme.primary, + }, + }); diff --git a/components/profile/FindSupportSection.tsx b/components/profile/FindSupportSection.tsx new file mode 100644 index 00000000..8da07482 --- /dev/null +++ b/components/profile/FindSupportSection.tsx @@ -0,0 +1,563 @@ +import React, { useMemo, useState, useCallback } from 'react'; +import { View, Text, StyleSheet, TouchableOpacity, ActivityIndicator } from 'react-native'; +import { Users, Search, Check, X, Clock, UserPlus, Shield } from 'lucide-react-native'; +import type { ThemeColors } from '@/contexts/ThemeContext'; +import type { ConnectionMatch, ConnectionIntent, PotentialMatch } from '@/types/database'; +import { supabase } from '@/lib/supabase'; +import { showToast } from '@/lib/toast'; +import { logger, LogCategory } from '@/lib/logger'; +import { getTimeRemaining } from '@/lib/time-utils'; + +// ============================================================================= +// Types & Interfaces +// ============================================================================= + +/** + * Props for the FindSupportSection component. + */ +interface FindSupportSectionProps { + /** Current user's ID */ + userId: string; + /** Current user's connection intent */ + intent: ConnectionIntent | null | undefined; + /** Theme object from ThemeContext */ + theme: ThemeColors; + /** Pending matches for this user */ + pendingMatches: ConnectionMatch[]; + /** Callback to refresh matches after action */ + onMatchUpdate: () => void; + /** Whether the section is disabled */ + isDisabled?: boolean; +} + +// ============================================================================= +// Helper Functions +// ============================================================================= + +/** + * Format the user's role in this match (seeker or provider). + */ +function getUserRole(match: ConnectionMatch, visibleMatches: string): 'seeker' | 'provider' { + return match.seeker_id === visibleMatches ? 'seeker' : 'provider'; +} + +/** + * Get display text for intent. + */ +function getIntentLabel(intent: ConnectionIntent | null | undefined): string { + switch (intent) { + case 'seeking_sponsor': + return 'Looking for a sponsor'; + case 'open_to_sponsoring': + return 'Open to sponsoring'; + case 'open_to_both': + return 'Open to both'; + default: + return 'Not set'; + } +} + +// ============================================================================= +// Component +// ============================================================================= + +/** + * A section for opt-in matching to find sponsors/sponsees. + * No feeds, no browsing - system proposes matches based on mutual needs. + * Both parties must accept to connect. + * + * @example + * ```tsx + * + * ``` + */ +export default function FindSupportSection({ + userId, + intent, + theme, + pendingMatches, + onMatchUpdate, + isDisabled = false, +}: FindSupportSectionProps): React.JSX.Element | null { + const styles = useMemo(() => createStyles(theme), [theme]); + const [isSearching, setIsSearching] = useState(false); + const [isProcessing, setIsProcessing] = useState(null); + + // Check if section should be shown (computed before hooks that use intent) + const shouldShow = intent && intent !== 'not_looking'; + + const findMatches = useCallback(async () => { + if (!shouldShow) return; + if (isDisabled || isSearching) return; + + setIsSearching(true); + try { + // Call the database function to find potential matches + const { data, error } = await supabase.rpc('find_potential_matches', { + user_id: userId, + max_results: 3, + }); + + if (error) { + logger.error('Failed to find matches', new Error(error.message), { + category: LogCategory.DATABASE, + }); + showToast.error('Failed to find matches'); + return; + } + + const potentialMatches = data as PotentialMatch[]; + + if (!potentialMatches || potentialMatches.length === 0) { + showToast.info('No matches found right now. Check back later!'); + return; + } + + // Create match requests for each potential match + // Only seekers can create matches directly (RLS policy: seeker_id = auth.uid()) + // Providers wait to be matched by seekers + const isSeeking = intent === 'seeking_sponsor' || intent === 'open_to_both'; + + if (!isSeeking) { + // Providers can't directly create matches - they're shown to seekers + showToast.info( + 'Providers are matched by seekers. Update your intent if you want to find a sponsor.' + ); + return; + } + + for (const match of potentialMatches) { + const matchData = { seeker_id: userId, provider_id: match.matched_user_id }; + + const { error: insertError } = await supabase.from('connection_matches').insert(matchData); + + if (insertError && !insertError.message.includes('unique')) { + logger.warn('Failed to create match', { + category: LogCategory.DATABASE, + errorMessage: insertError.message, + }); + } + } + + showToast.success(`Found ${potentialMatches.length} potential match(es)!`); + onMatchUpdate(); + } catch (error) { + logger.error('Error finding matches', error as Error, { + category: LogCategory.DATABASE, + }); + showToast.error('Something went wrong'); + } finally { + setIsSearching(false); + } + }, [userId, intent, isDisabled, isSearching, onMatchUpdate, shouldShow]); + + const acceptMatch = useCallback( + async (matchId: string) => { + if (isDisabled || isProcessing) return; + + setIsProcessing(matchId); + try { + const { data, error } = await supabase.rpc('accept_match', { + match_id: matchId, + }); + + if (error) { + logger.error('Failed to accept match', new Error(error.message), { + category: LogCategory.DATABASE, + }); + showToast.error('Failed to accept match'); + return; + } + + const result = data as ConnectionMatch; + if (result.status === 'accepted') { + showToast.success('Connection established!'); + } else { + showToast.success('Match accepted! Waiting for other party.'); + } + onMatchUpdate(); + } catch (error) { + logger.error('Error accepting match', error as Error, { + category: LogCategory.DATABASE, + }); + showToast.error('Something went wrong'); + } finally { + setIsProcessing(null); + } + }, + [isDisabled, isProcessing, onMatchUpdate] + ); + + const rejectMatch = useCallback( + async (matchId: string) => { + if (isDisabled || isProcessing) return; + + setIsProcessing(matchId); + try { + const { error } = await supabase.rpc('reject_match', { + match_id: matchId, + }); + + if (error) { + logger.error('Failed to reject match', new Error(error.message), { + category: LogCategory.DATABASE, + }); + showToast.error('Failed to decline match'); + return; + } + + showToast.info('Match declined'); + onMatchUpdate(); + } catch (error) { + logger.error('Error rejecting match', error as Error, { + category: LogCategory.DATABASE, + }); + showToast.error('Something went wrong'); + } finally { + setIsProcessing(null); + } + }, + [isDisabled, isProcessing, onMatchUpdate] + ); + + // Separate matches by user's role and acceptance status + const matchesNeedingAction = pendingMatches.filter((m) => { + const role = getUserRole(m, userId); + return role === 'seeker' ? m.seeker_accepted === null : m.provider_accepted === null; + }); + + const matchesWaiting = pendingMatches.filter((m) => { + const role = getUserRole(m, userId); + return role === 'seeker' + ? m.seeker_accepted === true && m.provider_accepted === null + : m.provider_accepted === true && m.seeker_accepted === null; + }); + + // Don't show if not looking or no intent set (after all hooks) + if (!shouldShow) { + return null; + } + + return ( + + {/* Header */} + + + + Find Support + + + + Private + + + + + {getIntentLabel(intent)} • System finds compatible matches for you + + + {/* Pending matches needing action */} + {matchesNeedingAction.length > 0 && ( + + New Matches + {matchesNeedingAction.map((match) => { + const role = getUserRole(match, userId); + const otherUser = role === 'seeker' ? match.provider : match.seeker; + const timeLeft = getTimeRemaining(match.expires_at); + const isProcessingThis = isProcessing === match.id; + + return ( + + + + + + + {otherUser?.display_name || 'Anonymous'} + + + + {timeLeft.days > 0 + ? `${timeLeft.days}d ${timeLeft.hours}h left` + : timeLeft.hours > 0 + ? `${timeLeft.hours}h ${timeLeft.minutes}m left` + : `${timeLeft.minutes}m left`} + + + + + + rejectMatch(match.id)} + disabled={isDisabled || isProcessingThis} + accessibilityRole="button" + accessibilityLabel="Decline match" + > + {isProcessingThis ? ( + + ) : ( + + )} + + acceptMatch(match.id)} + disabled={isDisabled || isProcessingThis} + accessibilityRole="button" + accessibilityLabel="Accept match" + > + {isProcessingThis ? ( + + ) : ( + + )} + + + + ); + })} + + )} + + {/* Matches waiting for other party */} + {matchesWaiting.length > 0 && ( + + Waiting for Response + {matchesWaiting.map((match) => { + const role = getUserRole(match, userId); + const otherUser = role === 'seeker' ? match.provider : match.seeker; + const timeLeft = getTimeRemaining(match.expires_at); + + return ( + + + + + + + {otherUser?.display_name || 'Anonymous'} + Waiting for them to respond + + + + {timeLeft.days > 0 + ? `${timeLeft.days}d` + : timeLeft.hours > 0 + ? `${timeLeft.hours}h` + : `${timeLeft.minutes}m`} + + + ); + })} + + )} + + {/* Find matches button */} + + {isSearching ? ( + + ) : ( + <> + + Find Matches + + )} + + + {/* Privacy note */} + + Matches are private. Only you and your match can see each other. + + + ); +} + +// ============================================================================= +// Styles +// ============================================================================= + +const createStyles = (theme: ThemeColors) => + StyleSheet.create({ + container: { + backgroundColor: theme.card, + padding: 16, + borderRadius: 12, + marginBottom: 12, + borderWidth: 1, + borderColor: theme.border, + }, + header: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + marginBottom: 4, + }, + headerLeft: { + flexDirection: 'row', + alignItems: 'center', + gap: 8, + }, + title: { + fontSize: 18, + fontFamily: theme.fontRegular, + fontWeight: '600', + color: theme.text, + }, + privacyBadge: { + flexDirection: 'row', + alignItems: 'center', + backgroundColor: theme.successLight, + paddingHorizontal: 8, + paddingVertical: 4, + borderRadius: 12, + gap: 4, + }, + privacyText: { + fontSize: 11, + fontFamily: theme.fontRegular, + fontWeight: '600', + color: theme.success, + }, + subtitle: { + fontSize: 14, + fontFamily: theme.fontRegular, + color: theme.textSecondary, + marginBottom: 16, + }, + matchesSection: { + marginBottom: 16, + }, + sectionLabel: { + fontSize: 12, + fontFamily: theme.fontRegular, + fontWeight: '600', + color: theme.textSecondary, + textTransform: 'uppercase', + letterSpacing: 0.5, + marginBottom: 8, + }, + matchCard: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + backgroundColor: theme.background, + padding: 12, + borderRadius: 10, + marginBottom: 8, + borderWidth: 1, + borderColor: theme.border, + }, + matchCardWaiting: { + opacity: 0.7, + }, + matchInfo: { + flexDirection: 'row', + alignItems: 'center', + flex: 1, + }, + matchIcon: { + width: 40, + height: 40, + borderRadius: 20, + backgroundColor: theme.primaryLight, + justifyContent: 'center', + alignItems: 'center', + }, + matchIconWaiting: { + backgroundColor: theme.border, + }, + matchDetails: { + marginLeft: 12, + flex: 1, + }, + matchName: { + fontSize: 16, + fontFamily: theme.fontRegular, + fontWeight: '600', + color: theme.text, + }, + matchMeta: { + flexDirection: 'row', + alignItems: 'center', + gap: 4, + marginTop: 2, + }, + matchExpiry: { + fontSize: 12, + fontFamily: theme.fontRegular, + color: theme.textSecondary, + }, + matchExpiryWarn: { + color: theme.warning, + }, + matchWaitingText: { + fontSize: 12, + fontFamily: theme.fontRegular, + color: theme.textSecondary, + marginTop: 2, + }, + matchActions: { + flexDirection: 'row', + gap: 8, + }, + actionButton: { + width: 40, + height: 40, + borderRadius: 20, + justifyContent: 'center', + alignItems: 'center', + }, + rejectButton: { + backgroundColor: theme.dangerLight, + }, + acceptButton: { + backgroundColor: theme.successLight, + }, + findButton: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + backgroundColor: theme.primary, + paddingVertical: 12, + paddingHorizontal: 20, + borderRadius: 10, + gap: 8, + }, + findButtonDisabled: { + opacity: 0.6, + }, + findButtonText: { + fontSize: 16, + fontFamily: theme.fontRegular, + fontWeight: '600', + color: theme.white, + }, + privacyNote: { + fontSize: 12, + fontFamily: theme.fontRegular, + color: theme.textSecondary, + textAlign: 'center', + marginTop: 12, + }, + }); diff --git a/components/profile/PersistentInviteCard.tsx b/components/profile/PersistentInviteCard.tsx new file mode 100644 index 00000000..40cc6135 --- /dev/null +++ b/components/profile/PersistentInviteCard.tsx @@ -0,0 +1,359 @@ +import React, { useMemo, useState, useEffect } from 'react'; +import { View, Text, StyleSheet, TouchableOpacity, Share, Platform } from 'react-native'; +import { Copy, Share2, RefreshCw, Trash2, Clock } from 'lucide-react-native'; +import type { ThemeColors } from '@/contexts/ThemeContext'; +import type { InviteCode } from '@/types/database'; +import { showToast } from '@/lib/toast'; +import { getTimeRemaining, formatTimeRemaining, TimeRemaining } from '@/lib/time-utils'; + +// ============================================================================= +// Types & Interfaces +// ============================================================================= + +/** + * Props for the PersistentInviteCard component. + */ +interface PersistentInviteCardProps { + /** The invite code data */ + inviteCode: InviteCode; + /** Theme object from ThemeContext */ + theme: ThemeColors; + /** Callback when regenerate is pressed */ + onRegenerate: () => void; + /** Callback when revoke is pressed */ + onRevoke: () => void; + /** Whether actions are disabled (during loading) */ + disabled?: boolean; +} + +// ============================================================================= +// Helpers +// ============================================================================= + +/** + * Format time remaining with "remaining" suffix for invite cards. + */ +function formatInviteTimeRemaining(time: TimeRemaining): string { + if (time.isExpired) return 'Expired'; + const formatted = formatTimeRemaining(time, 'short'); + return `${formatted} remaining`; +} + +// ============================================================================= +// Component +// ============================================================================= + +/** + * Displays a persistent invite code card with expiration timer and actions. + * Part of the Intent & Ownership system - invites are visible, owned objects. + * + * @example + * ```tsx + * + * ``` + */ +export default function PersistentInviteCard({ + inviteCode, + theme, + onRegenerate, + onRevoke, + disabled = false, +}: PersistentInviteCardProps): React.JSX.Element { + const styles = useMemo(() => createStyles(theme), [theme]); + // Use 3 days for "expiring soon" threshold on invite codes + const [timeRemaining, setTimeRemaining] = useState(() => + getTimeRemaining(inviteCode.expires_at, 3) + ); + + // Update timer every minute + useEffect(() => { + // Update immediately when expires_at changes (avoids stale display) + setTimeRemaining(getTimeRemaining(inviteCode.expires_at, 3)); + + const interval = setInterval(() => { + setTimeRemaining(getTimeRemaining(inviteCode.expires_at, 3)); + }, 60000); + + return () => clearInterval(interval); + }, [inviteCode.expires_at]); + + const handleCopy = async () => { + try { + if (Platform.OS === 'web') { + await navigator.clipboard.writeText(inviteCode.code); + } else { + // Use expo-clipboard for React Native + const { setStringAsync } = await import('expo-clipboard'); + await setStringAsync(inviteCode.code); + } + showToast.success('Code copied to clipboard'); + } catch { + showToast.error('Failed to copy code'); + } + }; + + const handleShare = async () => { + try { + await Share.share({ + message: `Join me on Sobers! Use invite code: ${inviteCode.code}`, + }); + } catch { + // User cancelled or share failed silently + } + }; + + const isUsed = !!inviteCode.used_by; + const isRevoked = !!inviteCode.revoked_at; + const isInactive = timeRemaining.isExpired || isUsed || isRevoked; + + return ( + + {/* Code Display */} + + Your Invite Code + + + {inviteCode.code} + + {!isInactive && ( + + + + )} + + + + {/* Status Badge */} + + {isRevoked ? ( + + Revoked + + ) : isUsed ? ( + + Used + + ) : timeRemaining.isExpired ? ( + + Expired + + ) : ( + + + + {formatInviteTimeRemaining(timeRemaining)} + + + )} + + + {/* Actions */} + + {!isInactive && ( + + + Share + + )} + + + Regenerate + + {!isInactive && ( + + + Revoke + + )} + + + ); +} + +// ============================================================================= +// Styles +// ============================================================================= + +const createStyles = (theme: ThemeColors) => + StyleSheet.create({ + container: { + backgroundColor: theme.card, + padding: 16, + borderRadius: 12, + marginBottom: 12, + borderWidth: 1, + borderColor: theme.border, + shadowColor: theme.black, + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.1, + shadowRadius: 8, + elevation: 3, + }, + containerInactive: { + opacity: 0.7, + borderColor: theme.textSecondary, + }, + codeSection: { + marginBottom: 12, + }, + codeLabel: { + fontSize: 12, + fontFamily: theme.fontRegular, + color: theme.textSecondary, + marginBottom: 4, + textTransform: 'uppercase', + letterSpacing: 0.5, + }, + codeRow: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + }, + code: { + fontSize: 28, + fontFamily: theme.fontRegular, + fontWeight: '700', + color: theme.text, + letterSpacing: 4, + }, + codeInactive: { + color: theme.textSecondary, + textDecorationLine: 'line-through', + }, + copyButton: { + padding: 8, + borderRadius: 8, + backgroundColor: theme.primaryLight, + }, + statusSection: { + marginBottom: 12, + }, + statusBadge: { + flexDirection: 'row', + alignItems: 'center', + alignSelf: 'flex-start', + paddingHorizontal: 10, + paddingVertical: 4, + borderRadius: 12, + gap: 6, + }, + statusActive: { + backgroundColor: theme.successLight, + }, + statusExpiringSoon: { + backgroundColor: theme.primaryLight, // Use primaryLight as fallback for warning state + }, + statusExpired: { + backgroundColor: theme.dangerLight, + }, + statusUsed: { + backgroundColor: theme.primaryLight, + }, + statusRevoked: { + backgroundColor: theme.dangerLight, + }, + statusTextActive: { + fontSize: 12, + fontFamily: theme.fontRegular, + fontWeight: '600', + color: theme.success, + }, + statusTextExpiringSoon: { + color: theme.warning, + }, + statusTextExpired: { + fontSize: 12, + fontFamily: theme.fontRegular, + fontWeight: '600', + color: theme.danger, + }, + statusTextUsed: { + fontSize: 12, + fontFamily: theme.fontRegular, + fontWeight: '600', + color: theme.primary, + }, + statusTextRevoked: { + fontSize: 12, + fontFamily: theme.fontRegular, + fontWeight: '600', + color: theme.danger, + }, + actionsSection: { + flexDirection: 'row', + gap: 8, + }, + actionButton: { + flex: 1, + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + paddingVertical: 10, + paddingHorizontal: 12, + borderRadius: 8, + backgroundColor: theme.primaryLight, + gap: 6, + }, + actionButtonDanger: { + backgroundColor: theme.dangerLight, + }, + actionText: { + fontSize: 13, + fontFamily: theme.fontRegular, + fontWeight: '600', + color: theme.primary, + }, + actionTextDanger: { + fontSize: 13, + fontFamily: theme.fontRegular, + fontWeight: '600', + color: theme.danger, + }, + }); diff --git a/components/profile/RelationshipCard.tsx b/components/profile/RelationshipCard.tsx index 52f53cfe..46e5619f 100644 --- a/components/profile/RelationshipCard.tsx +++ b/components/profile/RelationshipCard.tsx @@ -3,7 +3,8 @@ import { View, Text, StyleSheet, TouchableOpacity } from 'react-native'; import { Heart, UserMinus, CheckCircle, Plus } from 'lucide-react-native'; import type { ThemeColors } from '@/contexts/ThemeContext'; import { useDaysSober } from '@/hooks/useDaysSober'; -import type { Profile } from '@/types/database'; +import type { Profile, ExternalHandles, SponsorSponseeRelationship } from '@/types/database'; +import SymmetricRevealSection from '@/components/profile/SymmetricRevealSection'; // ============================================================================= // Types & Interfaces @@ -39,6 +40,12 @@ interface RelationshipCardProps { taskStats?: TaskStats; /** Optional callback when "Assign a task" link is pressed (only shown when no tasks assigned) */ onAssignTask?: () => void; + /** Full relationship object for symmetric reveal consent */ + relationship?: SponsorSponseeRelationship; + /** Current user's external handles */ + myHandles?: ExternalHandles; + /** Callback when consent changes */ + onConsentChange?: (consent: boolean) => void; } // ============================================================================= @@ -76,6 +83,9 @@ export default function RelationshipCard({ onDisconnect, taskStats, onAssignTask, + relationship, + myHandles, + onConsentChange, }: RelationshipCardProps): React.JSX.Element { const styles = useMemo(() => createStyles(theme), [theme]); const { daysSober, loading: loadingDaysSober } = useDaysSober(userId); @@ -139,6 +149,31 @@ export default function RelationshipCard({ )} + + {/* Symmetric Reveal Section */} + {relationship && onConsentChange && ( + + )} + void; + /** Whether actions are disabled */ + isDisabled?: boolean; +} + +// ============================================================================= +// Helpers +// ============================================================================= + +/** + * Get the reveal state based on both parties' consent. + */ +function getRevealState(hasMyConsent: boolean, hasTheirConsent: boolean): RevealState { + if (hasMyConsent && hasTheirConsent) return 'mutual'; + if (hasMyConsent && !hasTheirConsent) return 'you_pending'; + if (!hasMyConsent && hasTheirConsent) return 'them_pending'; + return 'none'; +} + +// ============================================================================= +// Component +// ============================================================================= + +/** + * Displays the symmetric reveal section for a connection. + * Shows consent toggle and revealed handles when mutual consent is achieved. + * + * @example + * ```tsx + * updateConsent(consent)} + * /> + * ``` + */ +export default function SymmetricRevealSection({ + relationshipId, + hasMyConsent, + hasTheirConsent, + otherProfile, + myHandles, + relationshipType, + theme, + onConsentChange, + isDisabled = false, +}: SymmetricRevealSectionProps): React.JSX.Element { + const styles = useMemo(() => createStyles(theme), [theme]); + const revealState = getRevealState(hasMyConsent, hasTheirConsent); + + // State for consent-checked handles fetched via RPC + const [otherHandles, setOtherHandles] = useState({}); + const [isLoadingHandles, setIsLoadingHandles] = useState(false); + + // Ref-based mount guard to prevent state updates after unmount + const isMountedRef = useRef(true); + + // Cleanup ref on unmount + useEffect(() => { + return () => { + isMountedRef.current = false; + }; + }, []); + + const otherName = otherProfile?.display_name || 'Unknown'; + const hasMyHandles = myHandles && Object.keys(myHandles).length > 0; + + // Fetch handles only when there's mutual consent (via secure RPC) + useEffect(() => { + // Per-effect active flag for cancellation + let isActive = true; + + if (revealState !== 'mutual') { + setOtherHandles({}); + setIsLoadingHandles(false); + return; + } + + const fetchHandles = async () => { + setIsLoadingHandles(true); + try { + const { data, error } = await supabase.rpc('get_handles_with_consent', { + relationship_id: relationshipId, + }); + + // Guard: Only update state if effect is still active and component is mounted + if (!isActive || !isMountedRef.current) return; + + if (error) { + logger.error('Failed to fetch handles with consent', new Error(error.message), { + category: LogCategory.DATABASE, + }); + return; + } + + setOtherHandles((data as ExternalHandles) || {}); + } catch (err) { + // Guard: Only update state if effect is still active and component is mounted + if (!isActive || !isMountedRef.current) return; + + logger.error('Unexpected error fetching handles', err as Error, { + category: LogCategory.DATABASE, + }); + } finally { + if (isActive && isMountedRef.current) { + setIsLoadingHandles(false); + } + } + }; + + fetchHandles(); + + return () => { + isActive = false; + }; + }, [revealState, relationshipId]); + + // Filter out empty handle values + const filteredHandles = Object.entries(otherHandles).filter(([_, v]) => Boolean(v)); + const hasOtherHandles = filteredHandles.length > 0; + + return ( + + {/* Consent Toggle */} + + + {hasMyConsent ? ( + + ) : ( + + )} + + Share my contact info + + {hasMyConsent + ? hasMyHandles + ? 'Your handles will be visible when mutual' + : 'Add contact methods in Settings first' + : 'Your contact info is hidden'} + + + + + + + {/* Status Display */} + + {revealState === 'none' && ( + + + Neither party has opted to share contact info + + )} + + {revealState === 'you_pending' && ( + + + + {"You've opted in. Waiting for "} + {otherName} + {' to share.'} + + + )} + + {revealState === 'them_pending' && ( + + + + {otherName} wants to share. Toggle on to reveal mutually. + + + )} + + {revealState === 'mutual' && ( + + + + Mutual consent! Contact info revealed. + + + )} + + + {/* Revealed Handles */} + {revealState === 'mutual' && isLoadingHandles && ( + + + Loading contact info... + + )} + {revealState === 'mutual' && !isLoadingHandles && hasOtherHandles && ( + + {otherName}'s Contact Info + + {filteredHandles.map(([key, value]) => ( + + {getPlatformIcon(key, theme)} + {getPlatformLabel(key)}: + + {value} + + + ))} + + + )} + + {/* No handles warning */} + {revealState === 'mutual' && !isLoadingHandles && !hasOtherHandles && ( + + + {otherName} hasn't added any contact methods yet. + + + )} + + ); +} + +// ============================================================================= +// Styles +// ============================================================================= + +const createStyles = (theme: ThemeColors) => + StyleSheet.create({ + container: { + marginTop: 12, + paddingTop: 12, + borderTopWidth: 1, + borderTopColor: theme.border, + }, + consentRow: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + }, + consentInfo: { + flexDirection: 'row', + alignItems: 'center', + flex: 1, + gap: 10, + }, + consentText: { + flex: 1, + }, + consentLabel: { + fontSize: 14, + fontFamily: theme.fontRegular, + fontWeight: '600', + color: theme.text, + }, + consentDescription: { + fontSize: 12, + fontFamily: theme.fontRegular, + color: theme.textSecondary, + marginTop: 2, + }, + statusContainer: { + marginTop: 12, + }, + statusRow: { + flexDirection: 'row', + alignItems: 'center', + backgroundColor: theme.background, + padding: 10, + borderRadius: 8, + gap: 8, + }, + statusPending: { + backgroundColor: theme.primaryLight, + }, + statusMutual: { + backgroundColor: theme.successLight, + }, + statusText: { + fontSize: 13, + fontFamily: theme.fontRegular, + color: theme.textSecondary, + flex: 1, + }, + statusTextPending: { + color: theme.warning, + }, + statusTextInfo: { + color: theme.info, + }, + statusTextMutual: { + color: theme.success, + }, + handlesContainer: { + marginTop: 12, + backgroundColor: theme.card, + padding: 12, + borderRadius: 8, + borderWidth: 1, + borderColor: theme.border, + }, + handlesTitle: { + fontSize: 13, + fontFamily: theme.fontRegular, + fontWeight: '600', + color: theme.text, + marginBottom: 8, + }, + handlesList: { + gap: 8, + }, + handleRow: { + flexDirection: 'row', + alignItems: 'center', + gap: 8, + }, + handlePlatform: { + fontSize: 13, + fontFamily: theme.fontRegular, + color: theme.textSecondary, + }, + handleValue: { + fontSize: 13, + fontFamily: theme.fontRegular, + color: theme.text, + fontWeight: '500', + }, + noHandlesContainer: { + marginTop: 12, + backgroundColor: theme.primaryLight, + padding: 10, + borderRadius: 8, + }, + noHandlesText: { + fontSize: 13, + fontFamily: theme.fontRegular, + color: theme.warning, + }, + loadingContainer: { + marginTop: 12, + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + padding: 12, + gap: 8, + }, + loadingText: { + fontSize: 13, + fontFamily: theme.fontRegular, + color: theme.textSecondary, + }, + }); diff --git a/components/settings/ExternalHandlesSection.tsx b/components/settings/ExternalHandlesSection.tsx new file mode 100644 index 00000000..8a8c247b --- /dev/null +++ b/components/settings/ExternalHandlesSection.tsx @@ -0,0 +1,352 @@ +import React, { useMemo, useState } from 'react'; +import { + View, + Text, + StyleSheet, + TextInput, + TouchableOpacity, + ActivityIndicator, +} from 'react-native'; +import { MessageCircle, Phone, Send, Shield, Plus, X } from 'lucide-react-native'; +import type { ThemeColors } from '@/contexts/ThemeContext'; +import type { ExternalHandles } from '@/types/database'; + +// ============================================================================= +// Types & Interfaces +// ============================================================================= + +/** + * Known platform keys for external handles. + */ +type PlatformKey = 'discord' | 'telegram' | 'whatsapp' | 'signal' | 'phone'; + +/** + * Supported external platforms with their configuration. + */ +interface PlatformConfig { + key: PlatformKey; + label: string; + placeholder: string; + icon: React.ReactNode; +} + +/** + * Props for the ExternalHandlesSection component. + */ +interface ExternalHandlesSectionProps { + /** Current external handles from profile */ + value: ExternalHandles | undefined; + /** Callback when handles change */ + onChange: (handles: ExternalHandles) => void; + /** Theme object from ThemeContext */ + theme: ThemeColors; + /** Whether changes are being saved (shows indicator, doesn't disable input) */ + isSaving?: boolean; +} + +// ============================================================================= +// Component +// ============================================================================= + +/** + * A section for managing external platform handles (Discord, Telegram, etc.). + * Handles are stored privately and only revealed with mutual consent per-connection. + * + * @example + * ```tsx + * updateProfile({ external_handles: handles })} + * theme={theme} + * /> + * ``` + */ +export default function ExternalHandlesSection({ + value = {}, + onChange, + theme, + isSaving = false, +}: ExternalHandlesSectionProps): React.JSX.Element { + const styles = useMemo(() => createStyles(theme), [theme]); + const [expandedPlatforms, setExpandedPlatforms] = useState>(() => { + // Auto-expand platforms that already have values + const expanded = new Set(); + Object.entries(value).forEach(([key, val]) => { + if (val) expanded.add(key); + }); + return expanded; + }); + + const platforms: PlatformConfig[] = useMemo( + () => [ + { + key: 'discord', + label: 'Discord', + placeholder: 'username#1234 or @username', + icon: , + }, + { + key: 'telegram', + label: 'Telegram', + placeholder: '@username', + icon: , + }, + { + key: 'whatsapp', + label: 'WhatsApp', + placeholder: '+1 234 567 8900', + icon: , + }, + { + key: 'signal', + label: 'Signal', + placeholder: '+1 234 567 8900', + icon: , + }, + { + key: 'phone', + label: 'Phone', + placeholder: '+1 234 567 8900', + icon: , + }, + ], + [theme] + ); + + const handleChange = (key: PlatformKey, newValue: string) => { + const updated = { ...value }; + if (newValue.trim()) { + updated[key] = newValue.trim(); + } else { + delete updated[key]; + } + onChange(updated); + }; + + const togglePlatform = (key: PlatformKey) => { + // Check if collapsing (currently expanded) + const isCollapsing = expandedPlatforms.has(key); + + // Update UI state first + setExpandedPlatforms((prev) => { + const next = new Set(prev); + if (isCollapsing) { + next.delete(key); + } else { + next.add(key); + } + return next; + }); + + // Clear data separately when collapsing (not inside state updater) + if (isCollapsing) { + handleChange(key, ''); + } + }; + + const visiblePlatforms = platforms.filter((p) => expandedPlatforms.has(p.key)); + const hiddenPlatforms = platforms.filter((p) => !expandedPlatforms.has(p.key)); + + return ( + + + External Contacts + + {isSaving && ( + + + Saving... + + )} + + + Private + + + + + Store your contact info privately. Only revealed to connections with mutual consent. + + + {/* Active platforms */} + + {visiblePlatforms.map((platform) => ( + + {platform.icon} + + {platform.label} + handleChange(platform.key, text)} + placeholder={platform.placeholder} + placeholderTextColor={theme.textSecondary} + autoCapitalize="none" + autoCorrect={false} + testID={`handle-input-${platform.key}`} + /> + + togglePlatform(platform.key)} + style={styles.removeButton} + accessibilityRole="button" + accessibilityLabel={`Remove ${platform.label}`} + > + + + + ))} + + + {/* Add platform buttons */} + {hiddenPlatforms.length > 0 && ( + + Add contact method: + + {hiddenPlatforms.map((platform) => ( + togglePlatform(platform.key)} + accessibilityRole="button" + accessibilityLabel={`Add ${platform.label}`} + > + {platform.icon} + {platform.label} + + + ))} + + + )} + + ); +} + +// ============================================================================= +// Styles +// ============================================================================= + +const createStyles = (theme: ThemeColors) => + StyleSheet.create({ + container: { + padding: 16, + }, + header: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + marginBottom: 4, + }, + headerRight: { + flexDirection: 'row', + alignItems: 'center', + gap: 8, + }, + savingIndicator: { + flexDirection: 'row', + alignItems: 'center', + gap: 4, + }, + savingText: { + fontSize: 12, + fontFamily: theme.fontRegular, + color: theme.textSecondary, + }, + title: { + fontSize: 18, + fontFamily: theme.fontRegular, + fontWeight: '600', + color: theme.text, + }, + privacyBadge: { + flexDirection: 'row', + alignItems: 'center', + backgroundColor: theme.successLight, + paddingHorizontal: 8, + paddingVertical: 4, + borderRadius: 12, + gap: 4, + }, + privacyText: { + fontSize: 11, + fontFamily: theme.fontRegular, + fontWeight: '600', + color: theme.success, + }, + subtitle: { + fontSize: 14, + fontFamily: theme.fontRegular, + color: theme.textSecondary, + marginBottom: 16, + }, + platformsContainer: { + gap: 12, + }, + platformRow: { + flexDirection: 'row', + alignItems: 'center', + backgroundColor: theme.card, + padding: 12, + borderRadius: 12, + borderWidth: 1, + borderColor: theme.border, + }, + platformIcon: { + width: 40, + height: 40, + borderRadius: 20, + backgroundColor: theme.background, + justifyContent: 'center', + alignItems: 'center', + }, + platformInput: { + flex: 1, + marginLeft: 12, + }, + platformLabel: { + fontSize: 12, + fontFamily: theme.fontRegular, + color: theme.textSecondary, + marginBottom: 2, + }, + input: { + fontSize: 16, + fontFamily: theme.fontRegular, + color: theme.text, + padding: 0, + }, + removeButton: { + padding: 8, + }, + addSection: { + marginTop: 16, + }, + addLabel: { + fontSize: 13, + fontFamily: theme.fontRegular, + color: theme.textSecondary, + marginBottom: 8, + }, + addButtonsRow: { + flexDirection: 'row', + flexWrap: 'wrap', + gap: 8, + }, + addButton: { + flexDirection: 'row', + alignItems: 'center', + backgroundColor: theme.card, + paddingVertical: 8, + paddingHorizontal: 12, + borderRadius: 20, + borderWidth: 1, + borderColor: theme.border, + gap: 6, + }, + addButtonText: { + fontSize: 13, + fontFamily: theme.fontRegular, + color: theme.text, + }, + }); diff --git a/components/settings/SettingsContent.tsx b/components/settings/SettingsContent.tsx index d8c4e1c5..2c630e9d 100644 --- a/components/settings/SettingsContent.tsx +++ b/components/settings/SettingsContent.tsx @@ -64,9 +64,11 @@ import { showConfirm } from '@/lib/alert'; import { showToast } from '@/lib/toast'; import { useWhatsNew } from '@/lib/whats-new'; import { WhatsNewSheet, type WhatsNewSheetRef } from '@/components/whats-new'; +import ExternalHandlesSection from '@/components/settings/ExternalHandlesSection'; import packageJson from '../../package.json'; import type { SettingsContentProps } from './types'; +import type { ExternalHandles } from '@/types/database'; import { EXTERNAL_LINKS, noop } from './constants'; import { getBuildInfo, formatBuildInfoForCopy } from './utils'; @@ -490,6 +492,10 @@ export function SettingsContent({ onDismiss }: SettingsContentProps) { const [isSavingName, setIsSavingName] = useState(false); const [isSavingDashboard, setIsSavingDashboard] = useState(false); const [isSavingTwelveStep, setIsSavingTwelveStep] = useState(false); + const [isSavingHandles, setIsSavingHandles] = useState(false); + // Draft state for external handles to allow immediate UI updates while debouncing DB writes + const [draftHandles, setDraftHandles] = useState(null); + const handlesDebounceRef = useRef | null>(null); const [showSobrietyDatePicker, setShowSobrietyDatePicker] = useState(false); const [selectedSobrietyDate, setSelectedSobrietyDate] = useState(new Date()); const buildInfo = getBuildInfo(); @@ -766,6 +772,51 @@ export function SettingsContent({ onDismiss }: SettingsContentProps) { } }, [profile?.id, profile?.show_program_content, isSavingTwelveStep, refreshProfile]); + /** + * Updates the user's external handles (Discord, Telegram, etc.). + * These are stored privately and only revealed with mutual consent. + * Uses debouncing to avoid DB writes on every keystroke. + */ + const handleExternalHandlesChange = useCallback( + (handles: ExternalHandles) => { + if (!profile?.id) return; + + // Update draft state immediately for responsive UI + setDraftHandles(handles); + + // Clear existing debounce timeout + if (handlesDebounceRef.current) { + clearTimeout(handlesDebounceRef.current); + } + + // Debounce the actual database write + handlesDebounceRef.current = setTimeout(async () => { + setIsSavingHandles(true); + try { + const { error } = await supabase + .from('profiles') + .update({ external_handles: handles }) + .eq('id', profile.id); + + if (error) throw error; + + await refreshProfile(); + // Clear draft state after successful save + setDraftHandles(null); + // No toast - inline editing feels better without interruptions + } catch (error: unknown) { + logger.error('External handles update failed', error as Error, { + category: LogCategory.DATABASE, + }); + showToast.error('Failed to save contact info'); + } finally { + setIsSavingHandles(false); + } + }, 500); + }, + [profile?.id, refreshProfile] + ); + /** * Opens the sobriety date picker with the current sobriety date pre-selected. */ @@ -1077,6 +1128,14 @@ export function SettingsContent({ onDismiss }: SettingsContentProps) { + {/* External Contacts Section */} + + {/* About Section */} About diff --git a/components/sheets/EditSavingsSheet.tsx b/components/sheets/EditSavingsSheet.tsx index bf31e36a..6ccd5fc7 100644 --- a/components/sheets/EditSavingsSheet.tsx +++ b/components/sheets/EditSavingsSheet.tsx @@ -9,16 +9,9 @@ import React, { useRef, useMemo, } from 'react'; -import { - View, - Text, - TextInput, - TouchableOpacity, - StyleSheet, - ActivityIndicator, - Platform, -} from 'react-native'; -import { BottomSheetScrollView, BottomSheetTextInput } from '@gorhom/bottom-sheet'; +import { View, Text, TouchableOpacity, StyleSheet, ActivityIndicator } from 'react-native'; +import { BottomSheetScrollView } from '@gorhom/bottom-sheet'; +import { SheetInputComponent } from '@/lib/sheet-input'; import { useTheme, type ThemeColors } from '@/contexts/ThemeContext'; import { X, DollarSign } from 'lucide-react-native'; import { supabase } from '@/lib/supabase'; @@ -277,8 +270,6 @@ const EditSavingsSheet = forwardRef( onClose(); }, [onClose]); - const AmountInput = Platform.OS === 'web' ? TextInput : BottomSheetTextInput; - // --------------------------------------------------------------------------- // Styles // --------------------------------------------------------------------------- @@ -320,7 +311,7 @@ const EditSavingsSheet = forwardRef( Amount - Invite Code - Share your reflections, insights, or any challenges you faced with this task. - ({ applicationName: 'Sobers', })); +// Mock expo-crypto for cryptographically secure random generation +jest.mock('expo-crypto', () => ({ + getRandomBytesAsync: jest.fn().mockImplementation((length) => { + // Generate deterministic mock bytes for testing + const bytes = new Uint8Array(length); + for (let i = 0; i < length; i++) { + bytes[i] = (i * 17 + 42) % 256; + } + return Promise.resolve(bytes); + }), + randomUUID: jest.fn().mockReturnValue('mock-uuid-1234-5678-9abc-def012345678'), +})); + // Mock @amplitude/analytics-react-native jest.mock('@amplitude/analytics-react-native', () => ({ init: jest.fn().mockReturnValue({ promise: Promise.resolve() }), diff --git a/lib/platform-icons.tsx b/lib/platform-icons.tsx new file mode 100644 index 00000000..664ce713 --- /dev/null +++ b/lib/platform-icons.tsx @@ -0,0 +1,76 @@ +// ============================================================================= +// Imports +// ============================================================================= + +import React from 'react'; +import { MessageCircle, Send, Phone, Shield } from 'lucide-react-native'; + +// ============================================================================= +// Types & Interfaces +// ============================================================================= + +/** + * Supported platform keys for external handles. + */ +export type PlatformKey = 'discord' | 'telegram' | 'whatsapp' | 'signal' | 'phone'; + +/** + * Theme colors interface for icon rendering. + */ +export interface IconTheme { + primary: string; + info: string; + success: string; + warning: string; + textSecondary: string; +} + +// ============================================================================= +// Constants +// ============================================================================= + +/** + * Human-readable labels for platform keys. + */ +export const platformLabels: Record = { + discord: 'Discord', + telegram: 'Telegram', + whatsapp: 'WhatsApp', + signal: 'Signal', + phone: 'Phone', +}; + +// ============================================================================= +// Helpers +// ============================================================================= + +/** + * Get human-readable label for a platform key. + */ +export function getPlatformLabel(key: string): string { + return (platformLabels as Record)[key] || key; +} + +/** + * Get icon component for a platform. + * + * @param key - Platform key (discord, telegram, etc.) + * @param theme - Theme colors for icon coloring + * @param size - Icon size (default: 16) + * @returns React node for the platform icon + */ +export function getPlatformIcon(key: string, theme: IconTheme, size = 16): React.ReactNode { + switch (key) { + case 'discord': + return ; + case 'telegram': + return ; + case 'whatsapp': + case 'phone': + return ; + case 'signal': + return ; + default: + return ; + } +} diff --git a/lib/sheet-input.tsx b/lib/sheet-input.tsx new file mode 100644 index 00000000..eeacc2ab --- /dev/null +++ b/lib/sheet-input.tsx @@ -0,0 +1,12 @@ +/** + * Platform-specific input component for bottom sheets. + * + * On web, uses regular TextInput to avoid BottomSheetTextInput compatibility issues. + * On native, uses BottomSheetTextInput for proper keyboard handling in sheets. + */ + +import { Platform, TextInput } from 'react-native'; +import { BottomSheetTextInput } from '@gorhom/bottom-sheet'; + +// Use regular TextInput on web to avoid BottomSheetTextInput compatibility issues +export const SheetInputComponent = Platform.OS === 'web' ? TextInput : BottomSheetTextInput; diff --git a/lib/time-utils.ts b/lib/time-utils.ts new file mode 100644 index 00000000..dd72ba1c --- /dev/null +++ b/lib/time-utils.ts @@ -0,0 +1,84 @@ +/** + * Time calculation utilities for expiration tracking. + */ + +/** + * Result of time remaining calculation. + */ +export interface TimeRemaining { + days: number; + hours: number; + minutes: number; + isExpired: boolean; + isExpiringSoon: boolean; +} + +/** + * Calculate time remaining until expiration. + * + * @param expiresAt - ISO timestamp string of expiration + * @param expiringSoonDays - Days threshold for "expiring soon" (default: 2) + * @returns Time remaining breakdown with status flags + * + * @example + * ```ts + * const time = getTimeRemaining(inviteCode.expires_at); + * if (time.isExpired) showToast.error('Code expired'); + * ``` + */ +export function getTimeRemaining(expiresAt: string, expiringSoonDays = 2): TimeRemaining { + const now = new Date().getTime(); + const expiry = new Date(expiresAt).getTime(); + const diff = expiry - now; + + if (diff <= 0) { + return { days: 0, hours: 0, minutes: 0, isExpired: true, isExpiringSoon: false }; + } + + const days = Math.floor(diff / (1000 * 60 * 60 * 24)); + const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); + const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60)); + const isExpiringSoon = days < expiringSoonDays; + + return { days, hours, minutes, isExpired: false, isExpiringSoon }; +} + +/** + * Format time remaining as a human-readable string. + * + * @param time - TimeRemaining object + * @param format - 'short' for "2d 3h", 'long' for "2 days, 3 hours" + * @returns Formatted time string + */ +export function formatTimeRemaining( + time: TimeRemaining, + format: 'short' | 'long' = 'short' +): string { + if (time.isExpired) { + return 'Expired'; + } + + if (format === 'short') { + if (time.days > 0) { + return `${time.days}d ${time.hours}h`; + } + if (time.hours > 0) { + return `${time.hours}h ${time.minutes}m`; + } + return `${time.minutes}m`; + } + + // Long format + const parts: string[] = []; + if (time.days > 0) { + parts.push(`${time.days} day${time.days === 1 ? '' : 's'}`); + } + if (time.hours > 0) { + parts.push(`${time.hours} hour${time.hours === 1 ? '' : 's'}`); + } + if (time.minutes > 0 && time.days === 0) { + parts.push(`${time.minutes} minute${time.minutes === 1 ? '' : 's'}`); + } + + return parts.join(', ') || 'Less than a minute'; +} diff --git a/package.json b/package.json index b4fb0598..1ee99793 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "expo-build-properties": "~1.0.10", "expo-clipboard": "^8.0.8", "expo-constants": "~18.0.13", + "expo-crypto": "^15.0.8", "expo-dev-client": "~6.0.20", "expo-device": "^8.0.10", "expo-font": "~14.0.11", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 554c0fe9..41011240 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,10 +16,10 @@ importers: version: 2.34.0 '@amplitude/analytics-react-native': specifier: ^1.5.37 - version: 1.5.37(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 1.5.37(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) '@bottom-tabs/react-navigation': specifier: ^1.1.0 - version: 1.1.0(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-bottom-tabs@1.1.0(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 1.1.0(@react-navigation/native@7.1.26(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-bottom-tabs@1.1.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) '@date-fns/tz': specifier: ^1.4.1 version: 1.4.1 @@ -28,34 +28,34 @@ importers: version: 0.4.1 '@expo/vector-icons': specifier: ^15.0.3 - version: 15.0.3(expo-font@14.0.11(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 15.0.3(expo-font@14.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) '@gorhom/bottom-sheet': specifier: ^5.2.8 - version: 5.2.8(@types/react-native@0.70.19)(@types/react@19.1.17)(react-native-gesture-handler@2.28.0(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-reanimated@4.1.6(@babel/core@7.28.6)(react-native-worklets@0.5.1(@babel/core@7.28.6)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 5.2.8(@types/react-native@0.70.19)(@types/react@19.1.17)(react-native-gesture-handler@2.28.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-reanimated@4.1.6(@babel/core@7.28.5)(react-native-worklets@0.5.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) '@react-native-async-storage/async-storage': specifier: ^2.2.0 - version: 2.2.0(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0)) + version: 2.2.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)) '@react-native-community/datetimepicker': specifier: ^8.4.4 - version: 8.4.4(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 8.4.4(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) '@react-navigation/bottom-tabs': specifier: ^7.4.0 - version: 7.10.1(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 7.9.0(@react-navigation/native@7.1.26(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) '@react-navigation/elements': specifier: ^2.9.5 - version: 2.9.5(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 2.9.5(@react-navigation/native@7.1.26(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) '@react-navigation/native': specifier: ^7.1.8 - version: 7.1.28(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 7.1.26(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) '@sentry/cli': specifier: ^3.1.0 version: 3.1.0 '@sentry/react-native': specifier: 7.2.0 - version: 7.2.0(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 7.2.0(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) '@supabase/supabase-js': specifier: ^2.93.2 - version: 2.93.2 + version: 2.93.3 '@vercel/analytics': specifier: ^1.6.1 version: 1.6.1(react@19.1.0) @@ -64,76 +64,79 @@ importers: version: 4.1.0 expo: specifier: ~54.0.32 - version: 54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) expo-apple-authentication: specifier: ~8.0.8 - version: 8.0.8(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0)) + version: 8.0.8(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)) expo-application: specifier: ^7.0.8 - version: 7.0.8(expo@54.0.32) + version: 7.0.8(expo@54.0.33) expo-auth-session: specifier: ^7.0.10 - version: 7.0.10(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 7.0.10(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) expo-blur: specifier: ^15.0.8 - version: 15.0.8(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 15.0.8(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) expo-build-properties: specifier: ~1.0.10 - version: 1.0.10(expo@54.0.32) + version: 1.0.10(expo@54.0.33) expo-clipboard: specifier: ^8.0.8 - version: 8.0.8(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 8.0.8(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) expo-constants: specifier: ~18.0.13 - version: 18.0.13(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0)) + version: 18.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)) + expo-crypto: + specifier: ^15.0.8 + version: 15.0.8(expo@54.0.33) expo-dev-client: specifier: ~6.0.20 - version: 6.0.20(expo@54.0.32) + version: 6.0.20(expo@54.0.33) expo-device: specifier: ^8.0.10 - version: 8.0.10(expo@54.0.32) + version: 8.0.10(expo@54.0.33) expo-font: specifier: ~14.0.11 - version: 14.0.11(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 14.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) expo-glass-effect: specifier: ~0.1.8 - version: 0.1.8(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 0.1.8(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) expo-image: specifier: ~3.0.11 - version: 3.0.11(expo@54.0.32)(react-native-web@0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 3.0.11(expo@54.0.33)(react-native-web@0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) expo-insights: specifier: ~0.10.8 - version: 0.10.8(expo@54.0.32) + version: 0.10.8(expo@54.0.33) expo-linking: specifier: ~8.0.11 - version: 8.0.11(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 8.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) expo-router: specifier: ~6.0.22 - version: 6.0.22(9bf7c66754abf2b5ca1d2af58d1d27eb) + version: 6.0.23(34d93f6a50204070cbf56f6391ebf5fc) expo-secure-store: specifier: ^15.0.8 - version: 15.0.8(expo@54.0.32) + version: 15.0.8(expo@54.0.33) expo-splash-screen: specifier: ~31.0.13 - version: 31.0.13(expo@54.0.32) + version: 31.0.13(expo@54.0.33) expo-status-bar: specifier: ~3.0.9 - version: 3.0.9(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 3.0.9(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) expo-symbols: specifier: ~1.0.8 - version: 1.0.8(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0)) + version: 1.0.8(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)) expo-system-ui: specifier: ~6.0.9 - version: 6.0.9(expo@54.0.32)(react-native-web@0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0)) + version: 6.0.9(expo@54.0.33)(react-native-web@0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)) expo-updates: specifier: ^29.0.16 - version: 29.0.16(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 29.0.16(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) expo-web-browser: specifier: ~15.0.10 - version: 15.0.10(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0)) + version: 15.0.10(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)) lucide-react-native: specifier: ^0.562.0 - version: 0.562.0(react-native-svg@15.12.1(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 0.562.0(react-native-svg@15.12.1(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) react: specifier: 19.1.0 version: 19.1.0 @@ -142,53 +145,53 @@ importers: version: 19.1.0(react@19.1.0) react-native: specifier: 0.81.5 - version: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + version: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) react-native-bottom-tabs: specifier: ^1.1.0 - version: 1.1.0(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 1.1.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) react-native-edge-to-edge: specifier: ^1.7.0 - version: 1.7.0(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 1.7.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) react-native-gesture-handler: specifier: ~2.28.0 - version: 2.28.0(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 2.28.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) react-native-keyboard-controller: specifier: ^1.18.5 - version: 1.18.5(react-native-reanimated@4.1.6(@babel/core@7.28.6)(react-native-worklets@0.5.1(@babel/core@7.28.6)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 1.18.5(react-native-reanimated@4.1.6(@babel/core@7.28.5)(react-native-worklets@0.5.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) react-native-reanimated: specifier: ~4.1.6 - version: 4.1.6(@babel/core@7.28.6)(react-native-worklets@0.5.1(@babel/core@7.28.6)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 4.1.6(@babel/core@7.28.5)(react-native-worklets@0.5.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) react-native-safe-area-context: specifier: ~5.6.2 - version: 5.6.2(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) react-native-screens: specifier: ~4.16.0 - version: 4.16.0(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) react-native-svg: specifier: ^15.12.1 - version: 15.12.1(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 15.12.1(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) react-native-toast-message: specifier: ^2.3.3 - version: 2.3.3(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 2.3.3(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) react-native-url-polyfill: specifier: ^3.0.0 - version: 3.0.0(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0)) + version: 3.0.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)) react-native-web: specifier: ~0.21.2 version: 0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react-native-worklets: specifier: 0.5.1 - version: 0.5.1(@babel/core@7.28.6)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 0.5.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) sf-symbols-typescript: specifier: ^2.2.0 version: 2.2.0 devDependencies: '@playwright/test': specifier: ^1.58.0 - version: 1.58.0 + version: 1.58.1 '@testing-library/react-native': specifier: ^13.3.3 - version: 13.3.3(jest@29.7.0(@types/node@25.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0) + version: 13.3.3(jest@29.7.0(@types/node@25.0.3))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0) '@types/jest': specifier: ^29.5.14 version: 29.5.14 @@ -197,10 +200,10 @@ importers: version: 19.1.17 babel-jest: specifier: ^30.2.0 - version: 30.2.0(@babel/core@7.28.6) + version: 30.2.0(@babel/core@7.28.5) babel-preset-expo: specifier: ^54.0.10 - version: 54.0.10(@babel/core@7.28.6)(@babel/runtime@7.28.6)(expo@54.0.32)(react-refresh@0.14.2) + version: 54.0.10(@babel/core@7.28.5)(@babel/runtime@7.28.4)(expo@54.0.33)(react-refresh@0.14.2) dotenv: specifier: ^17.2.3 version: 17.2.3 @@ -218,7 +221,7 @@ importers: version: 9.1.7 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@25.1.0) + version: 29.7.0(@types/node@25.0.3) jest-environment-jsdom: specifier: ^29.7.0 version: 29.7.0 @@ -284,16 +287,24 @@ packages: '@babel/code-frame@7.10.4': resolution: {integrity: sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==} + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + '@babel/code-frame@7.28.6': resolution: {integrity: sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.28.6': - resolution: {integrity: sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==} + '@babel/compat-data@7.28.5': + resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==} engines: {node: '>=6.9.0'} - '@babel/core@7.28.6': - resolution: {integrity: sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==} + '@babel/core@7.28.5': + resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.28.5': + resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} engines: {node: '>=6.9.0'} '@babel/generator@7.28.6': @@ -304,12 +315,12 @@ packages: resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.28.6': - resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} engines: {node: '>=6.9.0'} - '@babel/helper-create-class-features-plugin@7.28.6': - resolution: {integrity: sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==} + '@babel/helper-create-class-features-plugin@7.28.5': + resolution: {integrity: sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -320,8 +331,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-define-polyfill-provider@0.6.6': - resolution: {integrity: sha512-mOAsxeeKkUKayvZR3HeTYD/fICpCPLJrU5ZjelT/PA6WHtNDBOE436YiaEUvHN454bRM3CebhDsIpieCc4texA==} + '@babel/helper-define-polyfill-provider@0.6.5': + resolution: {integrity: sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 @@ -333,12 +344,12 @@ packages: resolution: {integrity: sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==} engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.28.6': - resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + '@babel/helper-module-imports@7.27.1': + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} engines: {node: '>=6.9.0'} - '@babel/helper-module-transforms@7.28.6': - resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + '@babel/helper-module-transforms@7.28.3': + resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -347,8 +358,8 @@ packages: resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} engines: {node: '>=6.9.0'} - '@babel/helper-plugin-utils@7.28.6': - resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} + '@babel/helper-plugin-utils@7.27.1': + resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} engines: {node: '>=6.9.0'} '@babel/helper-remap-async-to-generator@7.27.1': @@ -357,8 +368,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-replace-supers@7.28.6': - resolution: {integrity: sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==} + '@babel/helper-replace-supers@7.27.1': + resolution: {integrity: sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -379,25 +390,30 @@ packages: resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} - '@babel/helper-wrap-function@7.28.6': - resolution: {integrity: sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==} + '@babel/helper-wrap-function@7.28.3': + resolution: {integrity: sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.28.6': - resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==} + '@babel/helpers@7.28.4': + resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} engines: {node: '>=6.9.0'} '@babel/highlight@7.25.9': resolution: {integrity: sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw==} engines: {node: '>=6.9.0'} + '@babel/parser@7.28.5': + resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/parser@7.28.6': resolution: {integrity: sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/plugin-proposal-decorators@7.28.6': - resolution: {integrity: sha512-RVdFPPyY9fCRAX68haPmOk2iyKW8PKJFthmm8NeSI3paNxKWGZIn99+VbIf0FrtCpFnPgnpF/L48tadi617ULg==} + '@babel/plugin-proposal-decorators@7.28.0': + resolution: {integrity: sha512-zOiZqvANjWDUaUS9xMxbMcK/Zccztbe/6ikvUXaG9nsPH3w6qh5UaPGAnirI/WhIbZ8m3OHU0ReyPrknG+ZKeg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -429,8 +445,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-decorators@7.28.6': - resolution: {integrity: sha512-71EYI0ONURHJBL4rSFXnITXqXrrY8q4P0q006DPfN+Rk+ASM+++IBXem/ruokgBZR8YNEWZ8R6B+rCb8VcUTqA==} + '@babel/plugin-syntax-decorators@7.27.1': + resolution: {integrity: sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -440,20 +456,20 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-export-default-from@7.28.6': - resolution: {integrity: sha512-Svlx1fjJFnNz0LZeUaybRukSxZI3KkpApUmIRzEdXC5k8ErTOz0OD0kNrICi5Vc3GlpP5ZCeRyRO+mfWTSz+iQ==} + '@babel/plugin-syntax-export-default-from@7.27.1': + resolution: {integrity: sha512-eBC/3KSekshx19+N40MzjWqJd7KTEdOoLesAfa4IDFI8eRz5a47i5Oszus6zG/cwIXN63YhgLOMSSNJx49sENg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-flow@7.28.6': - resolution: {integrity: sha512-D+OrJumc9McXNEBI/JmFnc/0uCM2/Y3PEBG3gfV3QIYkKv5pvnpzFrl1kYCrcHJP8nOeFB/SHi1IHz29pNGuew==} + '@babel/plugin-syntax-flow@7.27.1': + resolution: {integrity: sha512-p9OkPbZ5G7UT1MofwYFigGebnrzGJacoBSQM0/6bi/PUMVE+qlWDD/OalvQKbwgQzU6dl0xAv6r4X7Jme0RYxA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-import-attributes@7.28.6': - resolution: {integrity: sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==} + '@babel/plugin-syntax-import-attributes@7.27.1': + resolution: {integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -468,8 +484,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-jsx@7.28.6': - resolution: {integrity: sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==} + '@babel/plugin-syntax-jsx@7.27.1': + resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -516,8 +532,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-typescript@7.28.6': - resolution: {integrity: sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==} + '@babel/plugin-syntax-typescript@7.27.1': + resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -528,44 +544,44 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-async-generator-functions@7.28.6': - resolution: {integrity: sha512-9knsChgsMzBV5Yh3kkhrZNxH3oCYAfMBkNNaVN4cP2RVlFPe8wYdwwcnOsAbkdDoV9UjFtOXWrWB52M8W4jNeA==} + '@babel/plugin-transform-async-generator-functions@7.28.0': + resolution: {integrity: sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-async-to-generator@7.28.6': - resolution: {integrity: sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==} + '@babel/plugin-transform-async-to-generator@7.27.1': + resolution: {integrity: sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-block-scoping@7.28.6': - resolution: {integrity: sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==} + '@babel/plugin-transform-block-scoping@7.28.5': + resolution: {integrity: sha512-45DmULpySVvmq9Pj3X9B+62Xe+DJGov27QravQJU1LLcapR6/10i+gYVAucGGJpHBp5mYxIMK4nDAT/QDLr47g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-class-properties@7.28.6': - resolution: {integrity: sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==} + '@babel/plugin-transform-class-properties@7.27.1': + resolution: {integrity: sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-class-static-block@7.28.6': - resolution: {integrity: sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==} + '@babel/plugin-transform-class-static-block@7.28.3': + resolution: {integrity: sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.12.0 - '@babel/plugin-transform-classes@7.28.6': - resolution: {integrity: sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==} + '@babel/plugin-transform-classes@7.28.4': + resolution: {integrity: sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-computed-properties@7.28.6': - resolution: {integrity: sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==} + '@babel/plugin-transform-computed-properties@7.27.1': + resolution: {integrity: sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -606,14 +622,14 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-logical-assignment-operators@7.28.6': - resolution: {integrity: sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==} + '@babel/plugin-transform-logical-assignment-operators@7.28.5': + resolution: {integrity: sha512-axUuqnUTBuXyHGcJEVVh9pORaN6wC5bYfE7FGzPiaWa3syib9m7g+/IT/4VgCOe2Upef43PHzeAvcrVek6QuuA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-modules-commonjs@7.28.6': - resolution: {integrity: sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==} + '@babel/plugin-transform-modules-commonjs@7.27.1': + resolution: {integrity: sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -624,32 +640,32 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/plugin-transform-nullish-coalescing-operator@7.28.6': - resolution: {integrity: sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==} + '@babel/plugin-transform-nullish-coalescing-operator@7.27.1': + resolution: {integrity: sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-numeric-separator@7.28.6': - resolution: {integrity: sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==} + '@babel/plugin-transform-numeric-separator@7.27.1': + resolution: {integrity: sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-object-rest-spread@7.28.6': - resolution: {integrity: sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==} + '@babel/plugin-transform-object-rest-spread@7.28.4': + resolution: {integrity: sha512-373KA2HQzKhQCYiRVIRr+3MjpCObqzDlyrM6u4I201wL8Mp2wHf7uB8GhDwis03k2ti8Zr65Zyyqs1xOxUF/Ew==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-optional-catch-binding@7.28.6': - resolution: {integrity: sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==} + '@babel/plugin-transform-optional-catch-binding@7.27.1': + resolution: {integrity: sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-optional-chaining@7.28.6': - resolution: {integrity: sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==} + '@babel/plugin-transform-optional-chaining@7.28.5': + resolution: {integrity: sha512-N6fut9IZlPnjPwgiQkXNhb+cT8wQKFlJNqcZkWlcTqkcqx6/kU4ynGmLFoa4LViBSirn05YAwk+sQBbPfxtYzQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -660,14 +676,14 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-private-methods@7.28.6': - resolution: {integrity: sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==} + '@babel/plugin-transform-private-methods@7.27.1': + resolution: {integrity: sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-private-property-in-object@7.28.6': - resolution: {integrity: sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==} + '@babel/plugin-transform-private-property-in-object@7.27.1': + resolution: {integrity: sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -696,8 +712,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-jsx@7.28.6': - resolution: {integrity: sha512-61bxqhiRfAACulXSLd/GxqmAedUSrRZIu/cbaT18T1CetkTmtDN15it7i80ru4DVqRK1WMxQhXs+Lf9kajm5Ow==} + '@babel/plugin-transform-react-jsx@7.27.1': + resolution: {integrity: sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -708,8 +724,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-regenerator@7.28.6': - resolution: {integrity: sha512-eZhoEZHYQLL5uc1gS5e9/oTknS0sSSAtd5TkKMUp3J+S/CaUjagc0kOUPsEbDmMeva0nC3WWl4SxVY6+OBuxfw==} + '@babel/plugin-transform-regenerator@7.28.4': + resolution: {integrity: sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -726,8 +742,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-spread@7.28.6': - resolution: {integrity: sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==} + '@babel/plugin-transform-spread@7.27.1': + resolution: {integrity: sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -744,8 +760,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-typescript@7.28.6': - resolution: {integrity: sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw==} + '@babel/plugin-transform-typescript@7.28.5': + resolution: {integrity: sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -768,18 +784,30 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/runtime@7.28.6': - resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} + '@babel/runtime@7.28.4': + resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} '@babel/template@7.28.6': resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} engines: {node: '>=6.9.0'} + '@babel/traverse@7.28.5': + resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} + engines: {node: '>=6.9.0'} + '@babel/traverse@7.28.6': resolution: {integrity: sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==} engines: {node: '>=6.9.0'} + '@babel/types@7.28.5': + resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} + engines: {node: '>=6.9.0'} + '@babel/types@7.28.6': resolution: {integrity: sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==} engines: {node: '>=6.9.0'} @@ -802,17 +830,17 @@ packages: resolution: {integrity: sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==} engines: {node: '>=0.8.0'} - '@emnapi/core@1.8.1': - resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==} + '@emnapi/core@1.7.1': + resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==} - '@emnapi/runtime@1.8.1': - resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==} + '@emnapi/runtime@1.7.1': + resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==} '@emnapi/wasi-threads@1.1.0': resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} - '@eslint-community/eslint-utils@4.9.1': - resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + '@eslint-community/eslint-utils@4.9.0': + resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 @@ -852,8 +880,8 @@ packages: '@expo-google-fonts/jetbrains-mono@0.4.1': resolution: {integrity: sha512-CslACrtMOcRwoWXCO7OMEI+9w3fukWSoBtvNz46OqPoogEuuoY0tkDY1O8sFumk8t0pC6Cx0Xr95O0TOQhpkug==} - '@expo/cli@54.0.22': - resolution: {integrity: sha512-BTH2FCczhJLfj1cpfcKrzhKnvRLTOztgW4bVloKDqH+G3ZSohWLRFNAIz56XtdjPxBbi2/qWhGBAkl7kBon/Jw==} + '@expo/cli@54.0.23': + resolution: {integrity: sha512-km0h72SFfQCmVycH/JtPFTVy69w6Lx1cHNDmfLfQqgKFYeeHTjx7LVDP4POHCtNxFP2UeRazrygJhlh4zz498g==} hasBin: true peerDependencies: expo: '*' @@ -964,8 +992,8 @@ packages: '@expo/ws-tunnel@1.0.6': resolution: {integrity: sha512-nDRbLmSrJar7abvUjp3smDwH8HcbZcoOEa5jVPUv9/9CajgmWw20JNRwTuBRzWIWIkEJDkz20GoNA+tSwUqk0Q==} - '@expo/xcpretty@4.4.0': - resolution: {integrity: sha512-o2qDlTqJ606h4xR36H2zWTywmZ3v3842K6TU8Ik2n1mfW0S580VHlt3eItVYdLYz+klaPp7CXqanja8eASZjRw==} + '@expo/xcpretty@4.3.2': + resolution: {integrity: sha512-ReZxZ8pdnoI3tP/dNnJdnmAk7uLT4FjsKDGW7YeDdvdOMz2XCQSmSCM9IWlrXuWtMF9zeSB6WJtEhCQ41gQOfw==} hasBin: true '@gorhom/bottom-sheet@5.2.8': @@ -1149,8 +1177,8 @@ packages: resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} engines: {node: '>=12.4.0'} - '@playwright/test@1.58.0': - resolution: {integrity: sha512-fWza+Lpbj6SkQKCrU6si4iu+fD2dD3gxNHFhUPxsfXBPhnv3rRSQVd0NtBUT9Z/RhF/boCBcuUaMUSTRTopjZg==} + '@playwright/test@1.58.1': + resolution: {integrity: sha512-6LdVIUERWxQMmUSSQi0I53GgCBYgM2RpGngCPY7hSeju+VrKjq3lvs7HpJoPbDiY5QM5EYRtRX5fvrinnMAz3w==} engines: {node: '>=18'} hasBin: true @@ -1465,17 +1493,17 @@ packages: '@types/react': optional: true - '@react-navigation/bottom-tabs@7.10.1': - resolution: {integrity: sha512-MirOzKEe/rRwPSE9HMrS4niIo0LyUhewlvd01TpzQ1ipuXjH2wJbzAM9gS/r62zriB6HMHz2OY6oIRduwQJtTw==} + '@react-navigation/bottom-tabs@7.9.0': + resolution: {integrity: sha512-024FWdHp3ZsE5rP8tmGI4vh+1z3wg8u8E9Frep8eeGoYo1h9rQhvgofQDGxknmrKsb7t8o8Dim+IZSvl57cPFQ==} peerDependencies: - '@react-navigation/native': ^7.1.28 + '@react-navigation/native': ^7.1.26 react: '>= 18.2.0' react-native: '*' react-native-safe-area-context: '>= 4.0.0' react-native-screens: '>= 4.0.0' - '@react-navigation/core@7.14.0': - resolution: {integrity: sha512-tMpzskBzVp0E7CRNdNtJIdXjk54Kwe/TF9ViXAef+YFM1kSfGv4e/B2ozfXE+YyYgmh4WavTv8fkdJz1CNyu+g==} + '@react-navigation/core@7.13.7': + resolution: {integrity: sha512-k2ABo3250vq1ovOh/iVwXS6Hwr5PVRGXoPh/ewVFOOuEKTvOx9i//OBzt8EF+HokBxS2HBRlR2b+aCOmscRqBw==} peerDependencies: react: '>= 18.2.0' @@ -1491,17 +1519,17 @@ packages: '@react-native-masked-view/masked-view': optional: true - '@react-navigation/native-stack@7.11.0': - resolution: {integrity: sha512-yNx9Wr4dfpOHpqjf2sGog4eH6KCYwTAEPlUPrKbvWlQbCRm5bglwPmaTXw9hTovX9v3HIa42yo7bXpbYfq4jzg==} + '@react-navigation/native-stack@7.9.0': + resolution: {integrity: sha512-C/mNPhI0Pnerl7C2cB+6fAkdgSmfKECMERrbyfjx3P6JmEuTC54o+GV1c62FUmlRaRUassVHbtw4EeaY2uLh0g==} peerDependencies: - '@react-navigation/native': ^7.1.28 + '@react-navigation/native': ^7.1.26 react: '>= 18.2.0' react-native: '*' react-native-safe-area-context: '>= 4.0.0' react-native-screens: '>= 4.0.0' - '@react-navigation/native@7.1.28': - resolution: {integrity: sha512-d1QDn+KNHfHGt3UIwOZvupvdsDdiHYZBEj7+wL2yDVo3tMezamYy60H9s3EnNVE1Ae1ty0trc7F2OKqo/RmsdQ==} + '@react-navigation/native@7.1.26': + resolution: {integrity: sha512-RhKmeD0E2ejzKS6z8elAfdfwShpcdkYY8zJzvHYLq+wv183BBcElTeyMLcIX6wIn7QutXeI92Yi21t7aUWfqNQ==} peerDependencies: react: '>= 18.2.0' react-native: '*' @@ -1668,8 +1696,8 @@ packages: '@sinclair/typebox@0.27.8': resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} - '@sinclair/typebox@0.34.48': - resolution: {integrity: sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==} + '@sinclair/typebox@0.34.45': + resolution: {integrity: sha512-qJcFVfCa5jxBFSuv7S5WYbA8XdeCPmhnaVVfX/2Y6L8WYg8sk3XY2+6W0zH+3mq1Cz+YC7Ki66HfqX6IHAwnkg==} '@sinonjs/commons@3.0.1': resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} @@ -1677,28 +1705,28 @@ packages: '@sinonjs/fake-timers@10.3.0': resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} - '@supabase/auth-js@2.93.2': - resolution: {integrity: sha512-uifI5vkhvHCQjn+LUPL5QlsuDMP4oVBD5SiliREgYuTJvCkbPLOcAPGrw88q7VUX9S0J0QuJn+37hrcTITisuw==} + '@supabase/auth-js@2.93.3': + resolution: {integrity: sha512-JdnkHZPKexVGSNONtu89RHU4bxz3X9kxx+f5ZnR5osoCIX+vs/MckwWRPZEybAEvlJXt5xjomDb3IB876QCxWQ==} engines: {node: '>=20.0.0'} - '@supabase/functions-js@2.93.2': - resolution: {integrity: sha512-reSp7yj4KmvAFfmN+N7vYsHXOIZQh9cmRBh+VrZlm7qgIIUdYmzKuD85TvFnWApqcdI2pPnuZGKWE/2B4GXT1A==} + '@supabase/functions-js@2.93.3': + resolution: {integrity: sha512-qWO0gHNDm/5jRjROv/nv9L6sYabCWS1kzorOLUv3kqCwRvEJLYZga93ppJPrZwOgoZfXmJzvpjY8fODA4HQfBw==} engines: {node: '>=20.0.0'} - '@supabase/postgrest-js@2.93.2': - resolution: {integrity: sha512-W2AWDsYwRT217II5yD3jWaX3fJjB7DwyNi2KNi4sphdUI3DKY4fP2XYVDGfeb1clEFL18gw+GBhyQb3BcpNWkw==} + '@supabase/postgrest-js@2.93.3': + resolution: {integrity: sha512-+iJ96g94skO2e4clsRSmEXg22NUOjh9BziapsJSAvnB1grOBf/BA8vGtCHjNOA+Z6lvKXL1jwBqcL9+fS1W/Lg==} engines: {node: '>=20.0.0'} - '@supabase/realtime-js@2.93.2': - resolution: {integrity: sha512-YpAmJn7DLbMeYfQilcf3f0DKoY8O8TRbTF2oRpWFzHXTlEA+YWms8fBqM13Mf7RE72ouSNKDYyf5K2pWRSHvFw==} + '@supabase/realtime-js@2.93.3': + resolution: {integrity: sha512-gnYpcFzwy8IkezRP4CDbT5I8jOsiOjrWrqTY1B+7jIriXsnpifmlM6RRjLBm9oD7OwPG0/WksniGPdKW67sXOA==} engines: {node: '>=20.0.0'} - '@supabase/storage-js@2.93.2': - resolution: {integrity: sha512-abRSVClfIQn+SqpdqL7S7b3VeyS8270/o0gqmGFtiidb7Lu0COsIV6Mor/mK9xE99KYWzyd37vwYYwv/jaANhw==} + '@supabase/storage-js@2.93.3': + resolution: {integrity: sha512-cw4qXiLrx3apglDM02Tx/w/stvFlrkKocC6vCvuFAz3JtVEl1zH8MUfDQDTH59kJAQVaVdbewrMWSoBob7REnA==} engines: {node: '>=20.0.0'} - '@supabase/supabase-js@2.93.2': - resolution: {integrity: sha512-G3bZZi6rPwXcPtyHLXQTeHKa5ADZ2UW/+hv8YhwZFwngz4TlPnR4+TeO37EwU5+d/reD02qXozOZgz+QHv1Jtg==} + '@supabase/supabase-js@2.93.3': + resolution: {integrity: sha512-paUqEqdBI9ztr/4bbMoCgeJ6M8ZTm2fpfjSOlzarPuzYveKFM20ZfDZqUpi9CFfYagYj5Iv3m3ztUjaI9/tM1w==} engines: {node: '>=20.0.0'} '@testing-library/react-native@13.3.3': @@ -1762,8 +1790,8 @@ packages: '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} - '@types/node@25.1.0': - resolution: {integrity: sha512-t7frlewr6+cbx+9Ohpl0NOTKXZNV9xHRmNOvql47BFJKcEG1CxtxlPEEe+gR9uhVWM4DwhnvTF110mIL4yP9RA==} + '@types/node@25.0.3': + resolution: {integrity: sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==} '@types/phoenix@1.6.7': resolution: {integrity: sha512-oN9ive//QSBkf19rfDv45M7eZPi0eEXylht2OLEXicu5b4KoQ1OzXIw+xDSGWxSxe1JmepRR/ZH283vsu518/Q==} @@ -1792,63 +1820,63 @@ packages: '@types/zen-observable@0.8.3': resolution: {integrity: sha512-fbF6oTd4sGGy0xjHPKAt+eS2CrxJ3+6gQ3FGcBoIJR2TLAyCkCyI8JqZNy+FeON0AhVgNJoUumVoZQjBFUqHkw==} - '@typescript-eslint/eslint-plugin@8.54.0': - resolution: {integrity: sha512-hAAP5io/7csFStuOmR782YmTthKBJ9ND3WVL60hcOjvtGFb+HJxH4O5huAcmcZ9v9G8P+JETiZ/G1B8MALnWZQ==} + '@typescript-eslint/eslint-plugin@8.51.0': + resolution: {integrity: sha512-XtssGWJvypyM2ytBnSnKtHYOGT+4ZwTnBVl36TA4nRO2f4PRNGz5/1OszHzcZCvcBMh+qb7I06uoCmLTRdR9og==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.54.0 + '@typescript-eslint/parser': ^8.51.0 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.54.0': - resolution: {integrity: sha512-BtE0k6cjwjLZoZixN0t5AKP0kSzlGu7FctRXYuPAm//aaiZhmfq1JwdYpYr1brzEspYyFeF+8XF5j2VK6oalrA==} + '@typescript-eslint/parser@8.51.0': + resolution: {integrity: sha512-3xP4XzzDNQOIqBMWogftkwxhg5oMKApqY0BAflmLZiFYHqyhSOxv/cd/zPQLTcCXr4AkaKb25joocY0BD1WC6A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.54.0': - resolution: {integrity: sha512-YPf+rvJ1s7MyiWM4uTRhE4DvBXrEV+d8oC3P9Y2eT7S+HBS0clybdMIPnhiATi9vZOYDc7OQ1L/i6ga6NFYK/g==} + '@typescript-eslint/project-service@8.51.0': + resolution: {integrity: sha512-Luv/GafO07Z7HpiI7qeEW5NW8HUtZI/fo/kE0YbtQEFpJRUuR0ajcWfCE5bnMvL7QQFrmT/odMe8QZww8X2nfQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/scope-manager@8.54.0': - resolution: {integrity: sha512-27rYVQku26j/PbHYcVfRPonmOlVI6gihHtXFbTdB5sb6qA0wdAQAbyXFVarQ5t4HRojIz64IV90YtsjQSSGlQg==} + '@typescript-eslint/scope-manager@8.51.0': + resolution: {integrity: sha512-JhhJDVwsSx4hiOEQPeajGhCWgBMBwVkxC/Pet53EpBVs7zHHtayKefw1jtPaNRXpI9RA2uocdmpdfE7T+NrizA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.54.0': - resolution: {integrity: sha512-dRgOyT2hPk/JwxNMZDsIXDgyl9axdJI3ogZ2XWhBPsnZUv+hPesa5iuhdYt2gzwA9t8RE5ytOJ6xB0moV0Ujvw==} + '@typescript-eslint/tsconfig-utils@8.51.0': + resolution: {integrity: sha512-Qi5bSy/vuHeWyir2C8u/uqGMIlIDu8fuiYWv48ZGlZ/k+PRPHtaAu7erpc7p5bzw2WNNSniuxoMSO4Ar6V9OXw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/type-utils@8.54.0': - resolution: {integrity: sha512-hiLguxJWHjjwL6xMBwD903ciAwd7DmK30Y9Axs/etOkftC3ZNN9K44IuRD/EB08amu+Zw6W37x9RecLkOo3pMA==} + '@typescript-eslint/type-utils@8.51.0': + resolution: {integrity: sha512-0XVtYzxnobc9K0VU7wRWg1yiUrw4oQzexCG2V2IDxxCxhqBMSMbjB+6o91A+Uc0GWtgjCa3Y8bi7hwI0Tu4n5Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/types@8.54.0': - resolution: {integrity: sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA==} + '@typescript-eslint/types@8.51.0': + resolution: {integrity: sha512-TizAvWYFM6sSscmEakjY3sPqGwxZRSywSsPEiuZF6d5GmGD9Gvlsv0f6N8FvAAA0CD06l3rIcWNbsN1e5F/9Ag==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.54.0': - resolution: {integrity: sha512-BUwcskRaPvTk6fzVWgDPdUndLjB87KYDrN5EYGetnktoeAvPtO4ONHlAZDnj5VFnUANg0Sjm7j4usBlnoVMHwA==} + '@typescript-eslint/typescript-estree@8.51.0': + resolution: {integrity: sha512-1qNjGqFRmlq0VW5iVlcyHBbCjPB7y6SxpBkrbhNWMy/65ZoncXCEPJxkRZL8McrseNH6lFhaxCIaX+vBuFnRng==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.54.0': - resolution: {integrity: sha512-9Cnda8GS57AQakvRyG0PTejJNlA2xhvyNtEVIMlDWOOeEyBkYWhGPnfrIAnqxLMTSTo6q8g12XVjjev5l1NvMA==} + '@typescript-eslint/utils@8.51.0': + resolution: {integrity: sha512-11rZYxSe0zabiKaCP2QAwRf/dnmgFgvTmeDTtZvUvXG3UuAdg/GU02NExmmIXzz3vLGgMdtrIosI84jITQOxUA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/visitor-keys@8.54.0': - resolution: {integrity: sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA==} + '@typescript-eslint/visitor-keys@8.51.0': + resolution: {integrity: sha512-mM/JRQOzhVN1ykejrvwnBRV3+7yTKK8tVANVN3o1O0t0v7o+jqdVu9crPy5Y9dov15TJk/FTIgoUGHrTOVL3Zg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@ungap/structured-clone@1.3.0': @@ -2177,8 +2205,8 @@ packages: resolution: {integrity: sha512-ftzhzSGMUnOzcCXd6WHdBGMyuwy15Wnn0iyyWGKgBDLxf9/s5ABuraCSpBX2uG0jUg4rqJnxsLc5+oYBqoxVaA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - babel-plugin-polyfill-corejs2@0.4.15: - resolution: {integrity: sha512-hR3GwrRwHUfYwGfrisXPIDP3JcYfBrW7wKE7+Au6wDYl7fm/ka1NEII6kORzxNU556JjfidZeBsO10kYvtV1aw==} + babel-plugin-polyfill-corejs2@0.4.14: + resolution: {integrity: sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 @@ -2187,8 +2215,8 @@ packages: peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - babel-plugin-polyfill-regenerator@0.6.6: - resolution: {integrity: sha512-hYm+XLYRMvupxiQzrvXUj7YyvFFVfv5gI0R71AJzudg1g2AI2vyCPPIFEBjk162/wFzti3inBHo7isWFuEVS/A==} + babel-plugin-polyfill-regenerator@0.6.5: + resolution: {integrity: sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 @@ -2239,8 +2267,8 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.9.19: - resolution: {integrity: sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==} + baseline-browser-mapping@2.9.11: + resolution: {integrity: sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==} hasBin: true better-opn@3.0.2: @@ -2317,8 +2345,8 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - caniuse-lite@1.0.30001766: - resolution: {integrity: sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA==} + caniuse-lite@1.0.30001762: + resolution: {integrity: sha512-PxZwGNvH7Ak8WX5iXzoK1KPZttBXNPuaOvI2ZYU7NrlM+d9Ov+TUvlLOBNGzVXAntMSMMlJPd+jY6ovrVjSmUw==} chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} @@ -2351,8 +2379,8 @@ packages: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} - ci-info@4.3.1: - resolution: {integrity: sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==} + ci-info@4.4.0: + resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} engines: {node: '>=8'} cjs-module-lexer@1.4.3: @@ -2472,8 +2500,8 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - core-js-compat@3.48.0: - resolution: {integrity: sha512-OM4cAF3D6VtH/WkLtWvyNC56EZVXsZdU3iqaMG2B4WvYrlqU831pc4UtG5yp0sE9z8Y02wVN7PjW5Zf9Gt0f1Q==} + core-js-compat@3.47.0: + resolution: {integrity: sha512-IGfuznZ/n7Kp9+nypamBhvwdwLsW6KC8IOaURw2doAK5e98AG3acVLdh0woOnEqCfUtS+Vu882JE4k/DAm3ItQ==} create-jest@29.7.0: resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} @@ -2671,8 +2699,8 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.279: - resolution: {integrity: sha512-0bblUU5UNdOt5G7XqGiJtpZMONma6WAfq9vsFmtn9x1+joAObr6x1chfqyxFSDCAFwFhCQDrqeAr6MYdpwJ9Hg==} + electron-to-chromium@1.5.267: + resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} @@ -2877,8 +2905,8 @@ packages: engines: {node: '>=4'} hasBin: true - esquery@1.7.0: - resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} engines: {node: '>=0.10'} esrecurse@4.3.0: @@ -2901,8 +2929,8 @@ packages: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} - eventemitter3@5.0.4: - resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} exec-async@2.2.0: resolution: {integrity: sha512-87OpwcEiMia/DeiKFzaQNBNFeN3XkkpYIh9FyOqq5mS2oKv3CBE67PXoEKcr6nodWdXNogTiQ0jE2NGuoffXPw==} @@ -3067,8 +3095,8 @@ packages: react: '*' react-native: '*' - expo-router@6.0.22: - resolution: {integrity: sha512-6eOwobaVZQRsSQv0IoWwVlPbJru1zbreVsuPFIWwk7HApENStU2MggrceHXJqXjGho+FKeXxUop/gqOFDzpOMg==} + expo-router@6.0.23: + resolution: {integrity: sha512-qCxVAiCrCyu0npky6azEZ6dJDMt77OmCzEbpF6RbUTlfkaCA417LvY14SBkk0xyGruSxy/7pvJOI6tuThaUVCA==} peerDependencies: '@expo/metro-runtime': ^6.1.2 '@react-navigation/drawer': ^7.5.0 @@ -3084,7 +3112,7 @@ packages: react-native-safe-area-context: '>= 5.4.0' react-native-screens: '*' react-native-web: '*' - react-server-dom-webpack: ~19.0.3 || ~19.1.4 || ~19.2.3 + react-server-dom-webpack: ~19.0.4 || ~19.1.5 || ~19.2.4 peerDependenciesMeta: '@react-navigation/drawer': optional: true @@ -3159,8 +3187,8 @@ packages: expo: '*' react-native: '*' - expo@54.0.32: - resolution: {integrity: sha512-yL9eTxiQ/QKKggVDAWO5CLjUl6IS0lPYgEvC3QM4q4fxd6rs7ks3DnbXSGVU3KNFoY/7cRNYihvd0LKYP+MCXA==} + expo@54.0.33: + resolution: {integrity: sha512-3yOEfAKqo+gqHcV8vKcnq0uA5zxlohnhA3fu4G43likN8ct5ZZ3LjAh9wDdKteEkoad3tFPvwxmXW711S5OHUw==} hasBin: true peerDependencies: '@expo/dom-webview': '*' @@ -3931,78 +3959,78 @@ packages: lighthouse-logger@1.4.2: resolution: {integrity: sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g==} - lightningcss-android-arm64@1.31.1: - resolution: {integrity: sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==} + lightningcss-android-arm64@1.30.2: + resolution: {integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [android] - lightningcss-darwin-arm64@1.31.1: - resolution: {integrity: sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==} + lightningcss-darwin-arm64@1.30.2: + resolution: {integrity: sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [darwin] - lightningcss-darwin-x64@1.31.1: - resolution: {integrity: sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==} + lightningcss-darwin-x64@1.30.2: + resolution: {integrity: sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [darwin] - lightningcss-freebsd-x64@1.31.1: - resolution: {integrity: sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==} + lightningcss-freebsd-x64@1.30.2: + resolution: {integrity: sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [freebsd] - lightningcss-linux-arm-gnueabihf@1.31.1: - resolution: {integrity: sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==} + lightningcss-linux-arm-gnueabihf@1.30.2: + resolution: {integrity: sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==} engines: {node: '>= 12.0.0'} cpu: [arm] os: [linux] - lightningcss-linux-arm64-gnu@1.31.1: - resolution: {integrity: sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==} + lightningcss-linux-arm64-gnu@1.30.2: + resolution: {integrity: sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] libc: [glibc] - lightningcss-linux-arm64-musl@1.31.1: - resolution: {integrity: sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==} + lightningcss-linux-arm64-musl@1.30.2: + resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] libc: [musl] - lightningcss-linux-x64-gnu@1.31.1: - resolution: {integrity: sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==} + lightningcss-linux-x64-gnu@1.30.2: + resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] libc: [glibc] - lightningcss-linux-x64-musl@1.31.1: - resolution: {integrity: sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==} + lightningcss-linux-x64-musl@1.30.2: + resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] libc: [musl] - lightningcss-win32-arm64-msvc@1.31.1: - resolution: {integrity: sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==} + lightningcss-win32-arm64-msvc@1.30.2: + resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [win32] - lightningcss-win32-x64-msvc@1.31.1: - resolution: {integrity: sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==} + lightningcss-win32-x64-msvc@1.30.2: + resolution: {integrity: sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [win32] - lightningcss@1.31.1: - resolution: {integrity: sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==} + lightningcss@1.30.2: + resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==} engines: {node: '>= 12.0.0'} lines-and-columns@1.2.4: @@ -4049,8 +4077,8 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - lru-cache@11.2.5: - resolution: {integrity: sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==} + lru-cache@11.2.4: + resolution: {integrity: sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==} engines: {node: 20 || >=22} lru-cache@5.1.1: @@ -4461,13 +4489,13 @@ packages: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} - playwright-core@1.58.0: - resolution: {integrity: sha512-aaoB1RWrdNi3//rOeKuMiS65UCcgOVljU46At6eFcOFPFHWtd2weHRRow6z/n+Lec0Lvu0k9ZPKJSjPugikirw==} + playwright-core@1.58.1: + resolution: {integrity: sha512-bcWzOaTxcW+VOOGBCQgnaKToLJ65d6AqfLVKEWvexyS3AS6rbXl+xdpYRMGSRBClPvyj44njOWoxjNdL/H9UNg==} engines: {node: '>=18'} hasBin: true - playwright@1.58.0: - resolution: {integrity: sha512-2SVA0sbPktiIY/MCOPX8e86ehA/e+tDNq+e5Y8qjKYti2Z/JG7xnronT/TXTIkKbYGWlCbuucZ6dziEgkoEjQQ==} + playwright@1.58.1: + resolution: {integrity: sha512-+2uTZHxSCcxjvGc5C891LrS1/NlxglGxzrC4seZiVjcYVQfUa87wBL6rTDqzGjuoWNjnBzRqKmF6zRYGMvQUaQ==} engines: {node: '>=18'} hasBin: true @@ -4590,8 +4618,8 @@ packages: react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - react-is@19.2.4: - resolution: {integrity: sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA==} + react-is@19.2.3: + resolution: {integrity: sha512-qJNJfu81ByyabuG7hPFEbXqNcWSU3+eVus+KJs+0ncpGfMyYdvSmxiJxbWR65lYi1I+/0HBcliO029gc4F+PnA==} react-native-bottom-tabs@1.1.0: resolution: {integrity: sha512-Uu1gvM3i1Hb4DjVvR/38J1QVQEs0RkPc7K6yon99HgvRWWOyLs7kjPDhUswtb8ije4pKW712skIXWJ0lgKzbyQ==} @@ -4795,8 +4823,8 @@ packages: resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - resolve-workspace-root@2.0.1: - resolution: {integrity: sha512-nR23LHAvaI6aHtMg6RWoaHpdR4D881Nydkzi2CixINyg9T00KgaJdJI6Vwty+Ps8WLxZHuxsS0BseWjxSA4C+w==} + resolve-workspace-root@2.0.0: + resolution: {integrity: sha512-IsaBUZETJD5WsI11Wt8PKHwaIe45or6pwNc8yflvLJ4DWtImK9kuLoH5kUva/2Mmx/RdIyr4aONNSa2v9LTJsw==} resolve.exports@2.0.3: resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} @@ -4848,9 +4876,8 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - sax@1.4.4: - resolution: {integrity: sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw==} - engines: {node: '>=11.0.0'} + sax@1.4.3: + resolution: {integrity: sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==} saxes@6.0.0: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} @@ -5142,9 +5169,10 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - tar@7.5.7: - resolution: {integrity: sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==} + tar@7.5.2: + resolution: {integrity: sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==} engines: {node: '>=18'} + deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exhorbitant rates) by contacting i@izs.me temp-dir@2.0.0: resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==} @@ -5154,8 +5182,8 @@ packages: resolution: {integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==} engines: {node: '>=8'} - terser@5.46.0: - resolution: {integrity: sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==} + terser@5.44.1: + resolution: {integrity: sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==} engines: {node: '>=10'} hasBin: true @@ -5199,8 +5227,8 @@ packages: resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==} engines: {node: '>=12'} - ts-api-utils@2.4.0: - resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} + ts-api-utils@2.3.0: + resolution: {integrity: sha512-6eg3Y9SF7SsAvGzRHQvvc1skDAhwI4YQ32ui1scxD1Ccr0G5qIIbUBT3pFTKX8kmWIQClHobtUdNuaBgwdfdWg==} engines: {node: '>=18.12'} peerDependencies: typescript: '>=4.8.4' @@ -5434,8 +5462,8 @@ packages: resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} engines: {node: '>= 0.4'} - which-typed-array@1.1.20: - resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} + which-typed-array@1.1.19: + resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} engines: {node: '>= 0.4'} which@2.0.2: @@ -5492,8 +5520,8 @@ packages: utf-8-validate: optional: true - ws@8.19.0: - resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==} + ws@8.18.3: + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -5583,13 +5611,13 @@ snapshots: tslib: 2.8.1 zen-observable-ts: 1.1.0 - '@amplitude/analytics-react-native@1.5.37(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': + '@amplitude/analytics-react-native@1.5.37(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': dependencies: '@amplitude/analytics-core': 2.37.0 '@amplitude/ua-parser-js': 0.7.33 - '@react-native-async-storage/async-storage': 2.2.0(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0)) + '@react-native-async-storage/async-storage': 2.2.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)) react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) tslib: 2.8.1 '@amplitude/plugin-autocapture-browser@1.19.0': @@ -5624,25 +5652,31 @@ snapshots: dependencies: '@babel/highlight': 7.25.9 + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + '@babel/code-frame@7.28.6': dependencies: '@babel/helper-validator-identifier': 7.28.5 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.28.6': {} + '@babel/compat-data@7.28.5': {} - '@babel/core@7.28.6': + '@babel/core@7.28.5': dependencies: - '@babel/code-frame': 7.28.6 - '@babel/generator': 7.28.6 - '@babel/helper-compilation-targets': 7.28.6 - '@babel/helper-module-transforms': 7.28.6(@babel/core@7.28.6) - '@babel/helpers': 7.28.6 - '@babel/parser': 7.28.6 - '@babel/template': 7.28.6 - '@babel/traverse': 7.28.6 - '@babel/types': 7.28.6 + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) + '@babel/helpers': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 debug: 4.4.3 @@ -5652,6 +5686,14 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/generator@7.28.5': + dependencies: + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + '@babel/generator@7.28.6': dependencies: '@babel/parser': 7.28.6 @@ -5662,41 +5704,41 @@ snapshots: '@babel/helper-annotate-as-pure@7.27.3': dependencies: - '@babel/types': 7.28.6 + '@babel/types': 7.28.5 - '@babel/helper-compilation-targets@7.28.6': + '@babel/helper-compilation-targets@7.27.2': dependencies: - '@babel/compat-data': 7.28.6 + '@babel/compat-data': 7.28.5 '@babel/helper-validator-option': 7.27.1 browserslist: 4.28.1 lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-create-class-features-plugin@7.28.6(@babel/core@7.28.6)': + '@babel/helper-create-class-features-plugin@7.28.5(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 + '@babel/core': 7.28.5 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-member-expression-to-functions': 7.28.5 '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/helper-replace-supers': 7.28.6(@babel/core@7.28.6) + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.5) '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.28.5 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/helper-create-regexp-features-plugin@7.28.5(@babel/core@7.28.6)': + '@babel/helper-create-regexp-features-plugin@7.28.5(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 + '@babel/core': 7.28.5 '@babel/helper-annotate-as-pure': 7.27.3 regexpu-core: 6.4.0 semver: 6.3.1 - '@babel/helper-define-polyfill-provider@0.6.6(@babel/core@7.28.6)': + '@babel/helper-define-polyfill-provider@0.6.5(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-compilation-targets': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 debug: 4.4.3 lodash.debounce: 4.0.8 resolve: 1.22.11 @@ -5707,55 +5749,55 @@ snapshots: '@babel/helper-member-expression-to-functions@7.28.5': dependencies: - '@babel/traverse': 7.28.6 - '@babel/types': 7.28.6 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 transitivePeerDependencies: - supports-color - '@babel/helper-module-imports@7.28.6': + '@babel/helper-module-imports@7.27.1': dependencies: '@babel/traverse': 7.28.6 '@babel/types': 7.28.6 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.28.6(@babel/core@7.28.6)': + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-module-imports': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-module-imports': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color '@babel/helper-optimise-call-expression@7.27.1': dependencies: - '@babel/types': 7.28.6 + '@babel/types': 7.28.5 - '@babel/helper-plugin-utils@7.28.6': {} + '@babel/helper-plugin-utils@7.27.1': {} - '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.28.6)': + '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 + '@babel/core': 7.28.5 '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-wrap-function': 7.28.6 + '@babel/helper-wrap-function': 7.28.3 '@babel/traverse': 7.28.6 transitivePeerDependencies: - supports-color - '@babel/helper-replace-supers@7.28.6(@babel/core@7.28.6)': + '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 + '@babel/core': 7.28.5 '@babel/helper-member-expression-to-functions': 7.28.5 '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color '@babel/helper-skip-transparent-expression-wrappers@7.27.1': dependencies: - '@babel/traverse': 7.28.6 - '@babel/types': 7.28.6 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 transitivePeerDependencies: - supports-color @@ -5765,7 +5807,7 @@ snapshots: '@babel/helper-validator-option@7.27.1': {} - '@babel/helper-wrap-function@7.28.6': + '@babel/helper-wrap-function@7.28.3': dependencies: '@babel/template': 7.28.6 '@babel/traverse': 7.28.6 @@ -5773,10 +5815,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helpers@7.28.6': + '@babel/helpers@7.28.4': dependencies: - '@babel/template': 7.28.6 - '@babel/types': 7.28.6 + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 '@babel/highlight@7.25.9': dependencies: @@ -5785,427 +5827,437 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 + '@babel/parser@7.28.5': + dependencies: + '@babel/types': 7.28.5 + '@babel/parser@7.28.6': dependencies: '@babel/types': 7.28.6 - '@babel/plugin-proposal-decorators@7.28.6(@babel/core@7.28.6)': + '@babel/plugin-proposal-decorators@7.28.0(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.28.6) - '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-decorators': 7.28.6(@babel/core@7.28.6) + '@babel/core': 7.28.5 + '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-decorators': 7.27.1(@babel/core@7.28.5) transitivePeerDependencies: - supports-color - '@babel/plugin-proposal-export-default-from@7.27.1(@babel/core@7.28.6)': + '@babel/plugin-proposal-export-default-from@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.28.6)': + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.28.6)': + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.28.6)': + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.28.6)': + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-decorators@7.28.6(@babel/core@7.28.6)': + '@babel/plugin-syntax-decorators@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.28.6)': + '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-export-default-from@7.28.6(@babel/core@7.28.6)': + '@babel/plugin-syntax-export-default-from@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-flow@7.28.6(@babel/core@7.28.6)': + '@babel/plugin-syntax-flow@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@7.28.6)': + '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.6)': + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.28.6)': + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.28.6)': + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.28.6)': + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.28.6)': + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.28.6)': + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.28.6)': + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.28.6)': + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.28.6)': + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.28.6)': + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.28.6)': + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-typescript@7.28.6(@babel/core@7.28.6)': + '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.28.6)': + '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-async-generator-functions@7.28.6(@babel/core@7.28.6)': + '@babel/plugin-transform-async-generator-functions@7.28.0(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.6) + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.5) '@babel/traverse': 7.28.6 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-async-to-generator@7.28.6(@babel/core@7.28.6)': + '@babel/plugin-transform-async-to-generator@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-module-imports': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.6) + '@babel/core': 7.28.5 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.5) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-block-scoping@7.28.6(@babel/core@7.28.6)': + '@babel/plugin-transform-block-scoping@7.28.5(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-class-properties@7.28.6(@babel/core@7.28.6)': + '@babel/plugin-transform-class-properties@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.28.6) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-class-static-block@7.28.6(@babel/core@7.28.6)': + '@babel/plugin-transform-class-static-block@7.28.3(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.28.6) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-classes@7.28.6(@babel/core@7.28.6)': + '@babel/plugin-transform-classes@7.28.4(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 + '@babel/core': 7.28.5 '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-globals': 7.28.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-replace-supers': 7.28.6(@babel/core@7.28.6) - '@babel/traverse': 7.28.6 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.5) + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-computed-properties@7.28.6(@babel/core@7.28.6)': + '@babel/plugin-transform-computed-properties@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 '@babel/template': 7.28.6 - '@babel/plugin-transform-destructuring@7.28.5(@babel/core@7.28.6)': + '@babel/plugin-transform-destructuring@7.28.5(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 '@babel/traverse': 7.28.6 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.28.6)': + '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-flow-strip-types@7.27.1(@babel/core@7.28.6)': + '@babel/plugin-transform-flow-strip-types@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-flow': 7.28.6(@babel/core@7.28.6) + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-flow': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.28.6)': + '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.28.6)': + '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-compilation-targets': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 '@babel/traverse': 7.28.6 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-literals@7.27.1(@babel/core@7.28.6)': + '@babel/plugin-transform-literals@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-logical-assignment-operators@7.28.6(@babel/core@7.28.6)': + '@babel/plugin-transform-logical-assignment-operators@7.28.5(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-modules-commonjs@7.28.6(@babel/core@7.28.6)': + '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-module-transforms': 7.28.6(@babel/core@7.28.6) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-named-capturing-groups-regex@7.27.1(@babel/core@7.28.6)': + '@babel/plugin-transform-named-capturing-groups-regex@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.6) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-nullish-coalescing-operator@7.28.6(@babel/core@7.28.6)': + '@babel/plugin-transform-nullish-coalescing-operator@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-numeric-separator@7.28.6(@babel/core@7.28.6)': + '@babel/plugin-transform-numeric-separator@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-object-rest-spread@7.28.6(@babel/core@7.28.6)': + '@babel/plugin-transform-object-rest-spread@7.28.4(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-compilation-targets': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.28.6) - '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.6) + '@babel/core': 7.28.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.5) '@babel/traverse': 7.28.6 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-optional-catch-binding@7.28.6(@babel/core@7.28.6)': + '@babel/plugin-transform-optional-catch-binding@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-optional-chaining@7.28.6(@babel/core@7.28.6)': + '@babel/plugin-transform-optional-chaining@7.28.5(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.28.6)': + '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-private-methods@7.28.6(@babel/core@7.28.6)': + '@babel/plugin-transform-private-methods@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.28.6) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-private-property-in-object@7.28.6(@babel/core@7.28.6)': + '@babel/plugin-transform-private-property-in-object@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 + '@babel/core': 7.28.5 '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.28.6) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-display-name@7.28.0(@babel/core@7.28.6)': + '@babel/plugin-transform-react-display-name@7.28.0(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-react-jsx-development@7.27.1(@babel/core@7.28.6)': + '@babel/plugin-transform-react-jsx-development@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.28.6) + '@babel/core': 7.28.5 + '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.5) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.6)': + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.6)': + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-react-jsx@7.28.6(@babel/core@7.28.6)': + '@babel/plugin-transform-react-jsx@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 + '@babel/core': 7.28.5 '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-module-imports': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.28.6) + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5) '@babel/types': 7.28.6 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-pure-annotations@7.27.1(@babel/core@7.28.6)': + '@babel/plugin-transform-react-pure-annotations@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 + '@babel/core': 7.28.5 '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-regenerator@7.28.6(@babel/core@7.28.6)': + '@babel/plugin-transform-regenerator@7.28.4(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-runtime@7.28.5(@babel/core@7.28.6)': + '@babel/plugin-transform-runtime@7.28.5(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-module-imports': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 - babel-plugin-polyfill-corejs2: 0.4.15(@babel/core@7.28.6) - babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.28.6) - babel-plugin-polyfill-regenerator: 0.6.6(@babel/core@7.28.6) + '@babel/core': 7.28.5 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + babel-plugin-polyfill-corejs2: 0.4.14(@babel/core@7.28.5) + babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.28.5) + babel-plugin-polyfill-regenerator: 0.6.5(@babel/core@7.28.5) semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.28.6)': + '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-spread@7.28.6(@babel/core@7.28.6)': + '@babel/plugin-transform-spread@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.28.6)': + '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.28.6)': + '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-typescript@7.28.6(@babel/core@7.28.6)': + '@babel/plugin-transform-typescript@7.28.5(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 + '@babel/core': 7.28.5 '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.28.6) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.28.6) + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.5) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.28.6)': + '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.6) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 - '@babel/preset-react@7.28.5(@babel/core@7.28.6)': + '@babel/preset-react@7.28.5(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-validator-option': 7.27.1 - '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.28.6) - '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.28.6) - '@babel/plugin-transform-react-pure-annotations': 7.27.1(@babel/core@7.28.6) + '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.28.5) + '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-react-pure-annotations': 7.27.1(@babel/core@7.28.5) transitivePeerDependencies: - supports-color - '@babel/preset-typescript@7.28.5(@babel/core@7.28.6)': + '@babel/preset-typescript@7.28.5(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-validator-option': 7.27.1 - '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.28.6) + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.28.5) transitivePeerDependencies: - supports-color - '@babel/runtime@7.28.6': {} + '@babel/runtime@7.28.4': {} + + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 '@babel/template@7.28.6': dependencies: @@ -6213,6 +6265,18 @@ snapshots: '@babel/parser': 7.28.6 '@babel/types': 7.28.6 + '@babel/traverse@7.28.5': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + '@babel/traverse@7.28.6': dependencies: '@babel/code-frame': 7.28.6 @@ -6225,6 +6289,11 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/types@7.28.5': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/types@7.28.6': dependencies: '@babel/helper-string-parser': 7.27.1 @@ -6232,13 +6301,13 @@ snapshots: '@bcoe/v8-coverage@0.2.3': {} - '@bottom-tabs/react-navigation@1.1.0(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-bottom-tabs@1.1.0(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': + '@bottom-tabs/react-navigation@1.1.0(@react-navigation/native@7.1.26(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-bottom-tabs@1.1.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': dependencies: - '@react-navigation/native': 7.1.28(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + '@react-navigation/native': 7.1.26(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) color: 5.0.3 react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) - react-native-bottom-tabs: 1.1.0(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) + react-native-bottom-tabs: 1.1.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) '@date-fns/tz@1.4.1': {} @@ -6246,13 +6315,13 @@ snapshots: dependencies: '@types/hammerjs': 2.0.46 - '@emnapi/core@1.8.1': + '@emnapi/core@1.7.1': dependencies: '@emnapi/wasi-threads': 1.1.0 tslib: 2.8.1 optional: true - '@emnapi/runtime@1.8.1': + '@emnapi/runtime@1.7.1': dependencies: tslib: 2.8.1 optional: true @@ -6262,7 +6331,7 @@ snapshots: tslib: 2.8.1 optional: true - '@eslint-community/eslint-utils@4.9.1(eslint@9.39.2)': + '@eslint-community/eslint-utils@4.9.0(eslint@9.39.2)': dependencies: eslint: 9.39.2 eslint-visitor-keys: 3.4.3 @@ -6310,7 +6379,7 @@ snapshots: '@expo-google-fonts/jetbrains-mono@0.4.1': {} - '@expo/cli@54.0.22(expo-router@6.0.22)(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))': + '@expo/cli@54.0.23(expo-router@6.0.23)(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))': dependencies: '@0no-co/graphql.web': 1.2.0 '@expo/code-signing-certificates': 0.0.6 @@ -6321,15 +6390,15 @@ snapshots: '@expo/image-utils': 0.8.8 '@expo/json-file': 10.0.8 '@expo/metro': 54.2.0 - '@expo/metro-config': 54.0.14(expo@54.0.32) + '@expo/metro-config': 54.0.14(expo@54.0.33) '@expo/osascript': 2.3.8 '@expo/package-manager': 1.9.10 '@expo/plist': 0.4.8 - '@expo/prebuild-config': 54.0.8(expo@54.0.32) + '@expo/prebuild-config': 54.0.8(expo@54.0.33) '@expo/schema-utils': 0.1.8 '@expo/spawn-async': 1.7.2 '@expo/ws-tunnel': 1.0.6 - '@expo/xcpretty': 4.4.0 + '@expo/xcpretty': 4.3.2 '@react-native/dev-middleware': 0.81.5 '@urql/core': 5.2.0 '@urql/exchange-retry': 1.3.2(@urql/core@5.2.0) @@ -6344,7 +6413,7 @@ snapshots: connect: 3.7.0 debug: 4.4.3 env-editor: 0.4.2 - expo: 54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) expo-server: 1.0.5 freeport-async: 2.0.0 getenv: 2.0.0 @@ -6371,14 +6440,14 @@ snapshots: source-map-support: 0.5.21 stacktrace-parser: 0.1.11 structured-headers: 0.4.1 - tar: 7.5.7 + tar: 7.5.2 terminal-link: 2.1.1 undici: 6.23.0 wrap-ansi: 7.0.0 - ws: 8.19.0 + ws: 8.18.3 optionalDependencies: - expo-router: 6.0.22(9bf7c66754abf2b5ca1d2af58d1d27eb) - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + expo-router: 6.0.23(34d93f6a50204070cbf56f6391ebf5fc) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) transitivePeerDependencies: - bufferutil - graphql @@ -6421,7 +6490,7 @@ snapshots: glob: 13.0.0 require-from-string: 2.0.2 resolve-from: 5.0.0 - resolve-workspace-root: 2.0.1 + resolve-workspace-root: 2.0.0 semver: 7.7.3 slugify: 1.6.6 sucrase: 3.35.1 @@ -6435,12 +6504,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@expo/devtools@0.1.8(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': + '@expo/devtools@0.1.8(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': dependencies: chalk: 4.1.2 optionalDependencies: react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) '@expo/env@2.0.8': dependencies: @@ -6486,10 +6555,10 @@ snapshots: '@babel/code-frame': 7.10.4 json5: 2.2.3 - '@expo/metro-config@54.0.14(expo@54.0.32)': + '@expo/metro-config@54.0.14(expo@54.0.33)': dependencies: '@babel/code-frame': 7.28.6 - '@babel/core': 7.28.6 + '@babel/core': 7.28.5 '@babel/generator': 7.28.6 '@expo/config': 12.0.13 '@expo/env': 2.0.8 @@ -6505,24 +6574,24 @@ snapshots: glob: 13.0.0 hermes-parser: 0.29.1 jsc-safe-url: 0.2.4 - lightningcss: 1.31.1 + lightningcss: 1.30.2 minimatch: 9.0.5 postcss: 8.4.49 resolve-from: 5.0.0 optionalDependencies: - expo: 54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - '@expo/metro-runtime@6.1.2(expo@54.0.32)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': + '@expo/metro-runtime@6.1.2(expo@54.0.33)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': dependencies: anser: 1.4.10 - expo: 54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) pretty-format: 29.7.0 react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) stacktrace-parser: 0.1.11 whatwg-fetch: 3.6.20 optionalDependencies: @@ -6561,7 +6630,7 @@ snapshots: chalk: 4.1.2 npm-package-arg: 11.0.3 ora: 3.4.0 - resolve-workspace-root: 2.0.1 + resolve-workspace-root: 2.0.0 '@expo/plist@0.4.8': dependencies: @@ -6569,7 +6638,7 @@ snapshots: base64-js: 1.5.1 xmlbuilder: 15.1.1 - '@expo/prebuild-config@54.0.8(expo@54.0.32)': + '@expo/prebuild-config@54.0.8(expo@54.0.33)': dependencies: '@expo/config': 12.0.13 '@expo/config-plugins': 54.0.4 @@ -6578,7 +6647,7 @@ snapshots: '@expo/json-file': 10.0.8 '@react-native/normalize-colors': 0.81.5 debug: 4.4.3 - expo: 54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) resolve-from: 5.0.0 semver: 7.7.3 xml2js: 0.6.0 @@ -6595,37 +6664,38 @@ snapshots: '@expo/sudo-prompt@9.3.2': {} - '@expo/vector-icons@15.0.3(expo-font@14.0.11(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': + '@expo/vector-icons@15.0.3(expo-font@14.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': dependencies: - expo-font: 14.0.11(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo-font: 14.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) '@expo/ws-tunnel@1.0.6': {} - '@expo/xcpretty@4.4.0': + '@expo/xcpretty@4.3.2': dependencies: - '@babel/code-frame': 7.28.6 + '@babel/code-frame': 7.10.4 chalk: 4.1.2 + find-up: 5.0.0 js-yaml: 4.1.1 - '@gorhom/bottom-sheet@5.2.8(@types/react-native@0.70.19)(@types/react@19.1.17)(react-native-gesture-handler@2.28.0(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-reanimated@4.1.6(@babel/core@7.28.6)(react-native-worklets@0.5.1(@babel/core@7.28.6)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': + '@gorhom/bottom-sheet@5.2.8(@types/react-native@0.70.19)(@types/react@19.1.17)(react-native-gesture-handler@2.28.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-reanimated@4.1.6(@babel/core@7.28.5)(react-native-worklets@0.5.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': dependencies: - '@gorhom/portal': 1.0.14(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + '@gorhom/portal': 1.0.14(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) invariant: 2.2.4 react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) - react-native-gesture-handler: 2.28.0(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - react-native-reanimated: 4.1.6(@babel/core@7.28.6)(react-native-worklets@0.5.1(@babel/core@7.28.6)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) + react-native-gesture-handler: 2.28.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react-native-reanimated: 4.1.6(@babel/core@7.28.5)(react-native-worklets@0.5.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) optionalDependencies: '@types/react': 19.1.17 '@types/react-native': 0.70.19 - '@gorhom/portal@1.0.14(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': + '@gorhom/portal@1.0.14(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': dependencies: nanoid: 3.3.11 react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) '@humanfs/core@0.19.1': {} @@ -6663,7 +6733,7 @@ snapshots: '@jest/console@29.7.0': dependencies: '@jest/types': 29.6.3 - '@types/node': 25.1.0 + '@types/node': 25.0.3 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 @@ -6676,14 +6746,14 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 25.1.0 + '@types/node': 25.0.3 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@25.1.0) + jest-config: 29.7.0(@types/node@25.0.3) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -6714,7 +6784,7 @@ snapshots: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 25.1.0 + '@types/node': 25.0.3 jest-mock: 29.7.0 '@jest/expect-utils@29.7.0': @@ -6732,7 +6802,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 25.1.0 + '@types/node': 25.0.3 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -6750,7 +6820,7 @@ snapshots: '@jest/pattern@30.0.1': dependencies: - '@types/node': 25.1.0 + '@types/node': 25.0.3 jest-regex-util: 30.0.1 '@jest/reporters@29.7.0': @@ -6761,7 +6831,7 @@ snapshots: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.31 - '@types/node': 25.1.0 + '@types/node': 25.0.3 chalk: 4.1.2 collect-v8-coverage: 1.0.3 exit: 0.1.2 @@ -6788,7 +6858,7 @@ snapshots: '@jest/schemas@30.0.5': dependencies: - '@sinclair/typebox': 0.34.48 + '@sinclair/typebox': 0.34.45 '@jest/source-map@29.6.3': dependencies: @@ -6812,7 +6882,7 @@ snapshots: '@jest/transform@29.7.0': dependencies: - '@babel/core': 7.28.6 + '@babel/core': 7.28.5 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.31 babel-plugin-istanbul: 6.1.1 @@ -6832,7 +6902,7 @@ snapshots: '@jest/transform@30.2.0': dependencies: - '@babel/core': 7.28.6 + '@babel/core': 7.28.5 '@jest/types': 30.2.0 '@jridgewell/trace-mapping': 0.3.31 babel-plugin-istanbul: 7.0.1 @@ -6855,7 +6925,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 25.1.0 + '@types/node': 25.0.3 '@types/yargs': 17.0.35 chalk: 4.1.2 @@ -6865,7 +6935,7 @@ snapshots: '@jest/schemas': 30.0.5 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 25.1.0 + '@types/node': 25.0.3 '@types/yargs': 17.0.35 chalk: 4.1.2 @@ -6895,16 +6965,16 @@ snapshots: '@napi-rs/wasm-runtime@0.2.12': dependencies: - '@emnapi/core': 1.8.1 - '@emnapi/runtime': 1.8.1 + '@emnapi/core': 1.7.1 + '@emnapi/runtime': 1.7.1 '@tybys/wasm-util': 0.10.1 optional: true '@nolyfill/is-core-module@1.0.39': {} - '@playwright/test@1.58.0': + '@playwright/test@1.58.1': dependencies: - playwright: 1.58.0 + playwright: 1.58.1 '@radix-ui/primitive@1.1.3': {} @@ -7098,83 +7168,83 @@ snapshots: optionalDependencies: '@types/react': 19.1.17 - '@react-native-async-storage/async-storage@2.2.0(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))': + '@react-native-async-storage/async-storage@2.2.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))': dependencies: merge-options: 3.0.4 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) - '@react-native-community/datetimepicker@8.4.4(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': + '@react-native-community/datetimepicker@8.4.4(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': dependencies: invariant: 2.2.4 react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) optionalDependencies: - expo: 54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) '@react-native/assets-registry@0.81.5': {} - '@react-native/babel-plugin-codegen@0.81.5(@babel/core@7.28.6)': + '@react-native/babel-plugin-codegen@0.81.5(@babel/core@7.28.5)': dependencies: '@babel/traverse': 7.28.6 - '@react-native/codegen': 0.81.5(@babel/core@7.28.6) + '@react-native/codegen': 0.81.5(@babel/core@7.28.5) transitivePeerDependencies: - '@babel/core' - supports-color - '@react-native/babel-preset@0.81.5(@babel/core@7.28.6)': - dependencies: - '@babel/core': 7.28.6 - '@babel/plugin-proposal-export-default-from': 7.27.1(@babel/core@7.28.6) - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.28.6) - '@babel/plugin-syntax-export-default-from': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.28.6) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.28.6) - '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.28.6) - '@babel/plugin-transform-async-generator-functions': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-async-to-generator': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-block-scoping': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-classes': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-computed-properties': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.28.6) - '@babel/plugin-transform-flow-strip-types': 7.27.1(@babel/core@7.28.6) - '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.28.6) - '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.28.6) - '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.28.6) - '@babel/plugin-transform-logical-assignment-operators': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-named-capturing-groups-regex': 7.27.1(@babel/core@7.28.6) - '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-numeric-separator': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-object-rest-spread': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-optional-catch-binding': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.6) - '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-private-property-in-object': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.28.6) - '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.6) - '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.6) - '@babel/plugin-transform-regenerator': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-runtime': 7.28.5(@babel/core@7.28.6) - '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.28.6) - '@babel/plugin-transform-spread': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.28.6) - '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.28.6) + '@react-native/babel-preset@0.81.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/plugin-proposal-export-default-from': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.28.5) + '@babel/plugin-syntax-export-default-from': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.28.5) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.28.5) + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-async-generator-functions': 7.28.0(@babel/core@7.28.5) + '@babel/plugin-transform-async-to-generator': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-block-scoping': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-transform-class-properties': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-classes': 7.28.4(@babel/core@7.28.5) + '@babel/plugin-transform-computed-properties': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-transform-flow-strip-types': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-logical-assignment-operators': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-named-capturing-groups-regex': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-nullish-coalescing-operator': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-numeric-separator': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-object-rest-spread': 7.28.4(@babel/core@7.28.5) + '@babel/plugin-transform-optional-catch-binding': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-optional-chaining': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.5) + '@babel/plugin-transform-private-methods': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-private-property-in-object': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.28.5) + '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-regenerator': 7.28.4(@babel/core@7.28.5) + '@babel/plugin-transform-runtime': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-spread': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.28.5) '@babel/template': 7.28.6 - '@react-native/babel-plugin-codegen': 0.81.5(@babel/core@7.28.6) + '@react-native/babel-plugin-codegen': 0.81.5(@babel/core@7.28.5) babel-plugin-syntax-hermes-parser: 0.29.1 - babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.28.6) + babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.28.5) react-refresh: 0.14.2 transitivePeerDependencies: - supports-color - '@react-native/codegen@0.81.5(@babel/core@7.28.6)': + '@react-native/codegen@0.81.5(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/parser': 7.28.6 + '@babel/core': 7.28.5 + '@babel/parser': 7.28.5 glob: 7.2.3 hermes-parser: 0.29.1 invariant: 2.2.4 @@ -7223,29 +7293,29 @@ snapshots: '@react-native/normalize-colors@0.81.5': {} - '@react-native/virtualized-lists@0.81.5(@types/react@19.1.17)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': + '@react-native/virtualized-lists@0.81.5(@types/react@19.1.17)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': dependencies: invariant: 2.2.4 nullthrows: 1.1.1 react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) optionalDependencies: '@types/react': 19.1.17 - '@react-navigation/bottom-tabs@7.10.1(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': + '@react-navigation/bottom-tabs@7.9.0(@react-navigation/native@7.1.26(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': dependencies: - '@react-navigation/elements': 2.9.5(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - '@react-navigation/native': 7.1.28(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + '@react-navigation/elements': 2.9.5(@react-navigation/native@7.1.26(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + '@react-navigation/native': 7.1.26(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) color: 4.2.3 react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) - react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - react-native-screens: 4.16.0(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) + react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react-native-screens: 4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) sf-symbols-typescript: 2.2.0 transitivePeerDependencies: - '@react-native-masked-view/masked-view' - '@react-navigation/core@7.14.0(react@19.1.0)': + '@react-navigation/core@7.13.7(react@19.1.0)': dependencies: '@react-navigation/routers': 7.5.3 escape-string-regexp: 4.0.0 @@ -7253,42 +7323,42 @@ snapshots: nanoid: 3.3.11 query-string: 7.1.3 react: 19.1.0 - react-is: 19.2.4 + react-is: 19.2.3 use-latest-callback: 0.2.6(react@19.1.0) use-sync-external-store: 1.6.0(react@19.1.0) - '@react-navigation/elements@2.9.5(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': + '@react-navigation/elements@2.9.5(@react-navigation/native@7.1.26(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': dependencies: - '@react-navigation/native': 7.1.28(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + '@react-navigation/native': 7.1.26(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) color: 4.2.3 react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) - react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) + react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) use-latest-callback: 0.2.6(react@19.1.0) use-sync-external-store: 1.6.0(react@19.1.0) - '@react-navigation/native-stack@7.11.0(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': + '@react-navigation/native-stack@7.9.0(@react-navigation/native@7.1.26(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': dependencies: - '@react-navigation/elements': 2.9.5(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - '@react-navigation/native': 7.1.28(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + '@react-navigation/elements': 2.9.5(@react-navigation/native@7.1.26(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + '@react-navigation/native': 7.1.26(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) color: 4.2.3 react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) - react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - react-native-screens: 4.16.0(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) + react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react-native-screens: 4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) sf-symbols-typescript: 2.2.0 warn-once: 0.1.1 transitivePeerDependencies: - '@react-native-masked-view/masked-view' - '@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': + '@react-navigation/native@7.1.26(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': dependencies: - '@react-navigation/core': 7.14.0(react@19.1.0) + '@react-navigation/core': 7.13.7(react@19.1.0) escape-string-regexp: 4.0.0 fast-deep-equal: 3.1.3 nanoid: 3.3.11 react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) use-latest-callback: 0.2.6(react@19.1.0) '@react-navigation/routers@7.5.3': @@ -7411,7 +7481,7 @@ snapshots: '@sentry/core@10.12.0': {} - '@sentry/react-native@7.2.0(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': + '@sentry/react-native@7.2.0(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': dependencies: '@sentry/babel-plugin-component-annotate': 4.3.0 '@sentry/browser': 10.12.0 @@ -7420,9 +7490,9 @@ snapshots: '@sentry/react': 10.12.0(react@19.1.0) '@sentry/types': 10.12.0 react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) optionalDependencies: - expo: 54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) transitivePeerDependencies: - encoding - supports-color @@ -7440,7 +7510,7 @@ snapshots: '@sinclair/typebox@0.27.8': {} - '@sinclair/typebox@0.34.48': {} + '@sinclair/typebox@0.34.45': {} '@sinonjs/commons@3.0.1': dependencies: @@ -7450,55 +7520,55 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.1 - '@supabase/auth-js@2.93.2': + '@supabase/auth-js@2.93.3': dependencies: tslib: 2.8.1 - '@supabase/functions-js@2.93.2': + '@supabase/functions-js@2.93.3': dependencies: tslib: 2.8.1 - '@supabase/postgrest-js@2.93.2': + '@supabase/postgrest-js@2.93.3': dependencies: tslib: 2.8.1 - '@supabase/realtime-js@2.93.2': + '@supabase/realtime-js@2.93.3': dependencies: '@types/phoenix': 1.6.7 '@types/ws': 8.18.1 tslib: 2.8.1 - ws: 8.19.0 + ws: 8.18.3 transitivePeerDependencies: - bufferutil - utf-8-validate - '@supabase/storage-js@2.93.2': + '@supabase/storage-js@2.93.3': dependencies: iceberg-js: 0.8.1 tslib: 2.8.1 - '@supabase/supabase-js@2.93.2': + '@supabase/supabase-js@2.93.3': dependencies: - '@supabase/auth-js': 2.93.2 - '@supabase/functions-js': 2.93.2 - '@supabase/postgrest-js': 2.93.2 - '@supabase/realtime-js': 2.93.2 - '@supabase/storage-js': 2.93.2 + '@supabase/auth-js': 2.93.3 + '@supabase/functions-js': 2.93.3 + '@supabase/postgrest-js': 2.93.3 + '@supabase/realtime-js': 2.93.3 + '@supabase/storage-js': 2.93.3 transitivePeerDependencies: - bufferutil - utf-8-validate - '@testing-library/react-native@13.3.3(jest@29.7.0(@types/node@25.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0)': + '@testing-library/react-native@13.3.3(jest@29.7.0(@types/node@25.0.3))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: jest-matcher-utils: 30.2.0 picocolors: 1.1.1 pretty-format: 30.2.0 react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) react-test-renderer: 19.1.0(react@19.1.0) redent: 3.0.0 optionalDependencies: - jest: 29.7.0(@types/node@25.1.0) + jest: 29.7.0(@types/node@25.0.3) '@tootallnate/once@2.0.0': {} @@ -7526,13 +7596,13 @@ snapshots: '@types/babel__traverse@7.28.0': dependencies: - '@babel/types': 7.28.6 + '@babel/types': 7.28.5 '@types/estree@1.0.8': {} '@types/graceful-fs@4.1.9': dependencies: - '@types/node': 25.1.0 + '@types/node': 25.0.3 '@types/hammerjs@2.0.46': {} @@ -7553,7 +7623,7 @@ snapshots: '@types/jsdom@20.0.1': dependencies: - '@types/node': 25.1.0 + '@types/node': 25.0.3 '@types/tough-cookie': 4.0.5 parse5: 7.3.0 @@ -7561,7 +7631,7 @@ snapshots: '@types/json5@0.0.29': {} - '@types/node@25.1.0': + '@types/node@25.0.3': dependencies: undici-types: 7.16.0 @@ -7582,7 +7652,7 @@ snapshots: '@types/ws@8.18.1': dependencies: - '@types/node': 25.1.0 + '@types/node': 25.0.3 '@types/yargs-parser@21.0.3': {} @@ -7592,95 +7662,95 @@ snapshots: '@types/zen-observable@0.8.3': {} - '@typescript-eslint/eslint-plugin@8.54.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.51.0(@typescript-eslint/parser@8.51.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.54.0(eslint@9.39.2)(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.54.0 - '@typescript-eslint/type-utils': 8.54.0(eslint@9.39.2)(typescript@5.9.3) - '@typescript-eslint/utils': 8.54.0(eslint@9.39.2)(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.54.0 + '@typescript-eslint/parser': 8.51.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.51.0 + '@typescript-eslint/type-utils': 8.51.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/utils': 8.51.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.51.0 eslint: 9.39.2 ignore: 7.0.5 natural-compare: 1.4.0 - ts-api-utils: 2.4.0(typescript@5.9.3) + ts-api-utils: 2.3.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.54.0(eslint@9.39.2)(typescript@5.9.3)': + '@typescript-eslint/parser@8.51.0(eslint@9.39.2)(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.54.0 - '@typescript-eslint/types': 8.54.0 - '@typescript-eslint/typescript-estree': 8.54.0(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.54.0 + '@typescript-eslint/scope-manager': 8.51.0 + '@typescript-eslint/types': 8.51.0 + '@typescript-eslint/typescript-estree': 8.51.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.51.0 debug: 4.4.3 eslint: 9.39.2 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.54.0(typescript@5.9.3)': + '@typescript-eslint/project-service@8.51.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.54.0(typescript@5.9.3) - '@typescript-eslint/types': 8.54.0 + '@typescript-eslint/tsconfig-utils': 8.51.0(typescript@5.9.3) + '@typescript-eslint/types': 8.51.0 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.54.0': + '@typescript-eslint/scope-manager@8.51.0': dependencies: - '@typescript-eslint/types': 8.54.0 - '@typescript-eslint/visitor-keys': 8.54.0 + '@typescript-eslint/types': 8.51.0 + '@typescript-eslint/visitor-keys': 8.51.0 - '@typescript-eslint/tsconfig-utils@8.54.0(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.51.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.54.0(eslint@9.39.2)(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.51.0(eslint@9.39.2)(typescript@5.9.3)': dependencies: - '@typescript-eslint/types': 8.54.0 - '@typescript-eslint/typescript-estree': 8.54.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.54.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/types': 8.51.0 + '@typescript-eslint/typescript-estree': 8.51.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.51.0(eslint@9.39.2)(typescript@5.9.3) debug: 4.4.3 eslint: 9.39.2 - ts-api-utils: 2.4.0(typescript@5.9.3) + ts-api-utils: 2.3.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.54.0': {} + '@typescript-eslint/types@8.51.0': {} - '@typescript-eslint/typescript-estree@8.54.0(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.51.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.54.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.54.0(typescript@5.9.3) - '@typescript-eslint/types': 8.54.0 - '@typescript-eslint/visitor-keys': 8.54.0 + '@typescript-eslint/project-service': 8.51.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.51.0(typescript@5.9.3) + '@typescript-eslint/types': 8.51.0 + '@typescript-eslint/visitor-keys': 8.51.0 debug: 4.4.3 minimatch: 9.0.5 semver: 7.7.3 tinyglobby: 0.2.15 - ts-api-utils: 2.4.0(typescript@5.9.3) + ts-api-utils: 2.3.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.54.0(eslint@9.39.2)(typescript@5.9.3)': + '@typescript-eslint/utils@8.51.0(eslint@9.39.2)(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) - '@typescript-eslint/scope-manager': 8.54.0 - '@typescript-eslint/types': 8.54.0 - '@typescript-eslint/typescript-estree': 8.54.0(typescript@5.9.3) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2) + '@typescript-eslint/scope-manager': 8.51.0 + '@typescript-eslint/types': 8.51.0 + '@typescript-eslint/typescript-estree': 8.51.0(typescript@5.9.3) eslint: 9.39.2 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.54.0': + '@typescript-eslint/visitor-keys@8.51.0': dependencies: - '@typescript-eslint/types': 8.54.0 + '@typescript-eslint/types': 8.51.0 eslint-visitor-keys: 4.2.1 '@ungap/structured-clone@1.3.0': {} @@ -7938,26 +8008,26 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 - babel-jest@29.7.0(@babel/core@7.28.6): + babel-jest@29.7.0(@babel/core@7.28.5): dependencies: - '@babel/core': 7.28.6 + '@babel/core': 7.28.5 '@jest/transform': 29.7.0 '@types/babel__core': 7.20.5 babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.6.3(@babel/core@7.28.6) + babel-preset-jest: 29.6.3(@babel/core@7.28.5) chalk: 4.1.2 graceful-fs: 4.2.11 slash: 3.0.0 transitivePeerDependencies: - supports-color - babel-jest@30.2.0(@babel/core@7.28.6): + babel-jest@30.2.0(@babel/core@7.28.5): dependencies: - '@babel/core': 7.28.6 + '@babel/core': 7.28.5 '@jest/transform': 30.2.0 '@types/babel__core': 7.20.5 babel-plugin-istanbul: 7.0.1 - babel-preset-jest: 30.2.0(@babel/core@7.28.6) + babel-preset-jest: 30.2.0(@babel/core@7.28.5) chalk: 4.1.2 graceful-fs: 4.2.11 slash: 3.0.0 @@ -7966,7 +8036,7 @@ snapshots: babel-plugin-istanbul@6.1.1: dependencies: - '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-plugin-utils': 7.27.1 '@istanbuljs/load-nyc-config': 1.1.0 '@istanbuljs/schema': 0.1.3 istanbul-lib-instrument: 5.2.1 @@ -7976,7 +8046,7 @@ snapshots: babel-plugin-istanbul@7.0.1: dependencies: - '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-plugin-utils': 7.27.1 '@istanbuljs/load-nyc-config': 1.1.0 '@istanbuljs/schema': 0.1.3 istanbul-lib-instrument: 6.0.3 @@ -7986,8 +8056,8 @@ snapshots: babel-plugin-jest-hoist@29.6.3: dependencies: - '@babel/template': 7.28.6 - '@babel/types': 7.28.6 + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 '@types/babel__core': 7.20.5 '@types/babel__traverse': 7.28.0 @@ -7995,27 +8065,27 @@ snapshots: dependencies: '@types/babel__core': 7.20.5 - babel-plugin-polyfill-corejs2@0.4.15(@babel/core@7.28.6): + babel-plugin-polyfill-corejs2@0.4.14(@babel/core@7.28.5): dependencies: - '@babel/compat-data': 7.28.6 - '@babel/core': 7.28.6 - '@babel/helper-define-polyfill-provider': 0.6.6(@babel/core@7.28.6) + '@babel/compat-data': 7.28.5 + '@babel/core': 7.28.5 + '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.5) semver: 6.3.1 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-corejs3@0.13.0(@babel/core@7.28.6): + babel-plugin-polyfill-corejs3@0.13.0(@babel/core@7.28.5): dependencies: - '@babel/core': 7.28.6 - '@babel/helper-define-polyfill-provider': 0.6.6(@babel/core@7.28.6) - core-js-compat: 3.48.0 + '@babel/core': 7.28.5 + '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.5) + core-js-compat: 3.47.0 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-regenerator@0.6.6(@babel/core@7.28.6): + babel-plugin-polyfill-regenerator@0.6.5(@babel/core@7.28.5): dependencies: - '@babel/core': 7.28.6 - '@babel/helper-define-polyfill-provider': 0.6.6(@babel/core@7.28.6) + '@babel/core': 7.28.5 + '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.5) transitivePeerDependencies: - supports-color @@ -8029,80 +8099,80 @@ snapshots: dependencies: hermes-parser: 0.29.1 - babel-plugin-transform-flow-enums@0.0.2(@babel/core@7.28.6): + babel-plugin-transform-flow-enums@0.0.2(@babel/core@7.28.5): dependencies: - '@babel/plugin-syntax-flow': 7.28.6(@babel/core@7.28.6) + '@babel/plugin-syntax-flow': 7.27.1(@babel/core@7.28.5) transitivePeerDependencies: - '@babel/core' - babel-preset-current-node-syntax@1.2.0(@babel/core@7.28.6): - dependencies: - '@babel/core': 7.28.6 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.28.6) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.28.6) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.28.6) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.28.6) - '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.28.6) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.28.6) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.28.6) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.28.6) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.28.6) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.28.6) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.28.6) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.28.6) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.28.6) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.28.6) - - babel-preset-expo@54.0.10(@babel/core@7.28.6)(@babel/runtime@7.28.6)(expo@54.0.32)(react-refresh@0.14.2): - dependencies: - '@babel/helper-module-imports': 7.28.6 - '@babel/plugin-proposal-decorators': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-proposal-export-default-from': 7.27.1(@babel/core@7.28.6) - '@babel/plugin-syntax-export-default-from': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-class-static-block': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.28.6) - '@babel/plugin-transform-flow-strip-types': 7.27.1(@babel/core@7.28.6) - '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-object-rest-spread': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.6) - '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-private-property-in-object': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-runtime': 7.28.5(@babel/core@7.28.6) - '@babel/preset-react': 7.28.5(@babel/core@7.28.6) - '@babel/preset-typescript': 7.28.5(@babel/core@7.28.6) - '@react-native/babel-preset': 0.81.5(@babel/core@7.28.6) + babel-preset-current-node-syntax@1.2.0(@babel/core@7.28.5): + dependencies: + '@babel/core': 7.28.5 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.28.5) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.28.5) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.28.5) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.28.5) + '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.28.5) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.28.5) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.28.5) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.28.5) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.28.5) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.28.5) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.28.5) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.28.5) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.28.5) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.28.5) + + babel-preset-expo@54.0.10(@babel/core@7.28.5)(@babel/runtime@7.28.4)(expo@54.0.33)(react-refresh@0.14.2): + dependencies: + '@babel/helper-module-imports': 7.27.1 + '@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.28.5) + '@babel/plugin-proposal-export-default-from': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-syntax-export-default-from': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-class-static-block': 7.28.3(@babel/core@7.28.5) + '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-flow-strip-types': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-object-rest-spread': 7.28.4(@babel/core@7.28.5) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.5) + '@babel/plugin-transform-private-methods': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-private-property-in-object': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-runtime': 7.28.5(@babel/core@7.28.5) + '@babel/preset-react': 7.28.5(@babel/core@7.28.5) + '@babel/preset-typescript': 7.28.5(@babel/core@7.28.5) + '@react-native/babel-preset': 0.81.5(@babel/core@7.28.5) babel-plugin-react-compiler: 1.0.0 babel-plugin-react-native-web: 0.21.2 babel-plugin-syntax-hermes-parser: 0.29.1 - babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.28.6) + babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.28.5) debug: 4.4.3 react-refresh: 0.14.2 resolve-from: 5.0.0 optionalDependencies: - '@babel/runtime': 7.28.6 - expo: 54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + '@babel/runtime': 7.28.4 + expo: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) transitivePeerDependencies: - '@babel/core' - supports-color - babel-preset-jest@29.6.3(@babel/core@7.28.6): + babel-preset-jest@29.6.3(@babel/core@7.28.5): dependencies: - '@babel/core': 7.28.6 + '@babel/core': 7.28.5 babel-plugin-jest-hoist: 29.6.3 - babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.6) + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.5) - babel-preset-jest@30.2.0(@babel/core@7.28.6): + babel-preset-jest@30.2.0(@babel/core@7.28.5): dependencies: - '@babel/core': 7.28.6 + '@babel/core': 7.28.5 babel-plugin-jest-hoist: 30.2.0 - babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.6) + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.5) balanced-match@1.0.2: {} base64-js@1.5.1: {} - baseline-browser-mapping@2.9.19: {} + baseline-browser-mapping@2.9.11: {} better-opn@3.0.2: dependencies: @@ -8139,9 +8209,9 @@ snapshots: browserslist@4.28.1: dependencies: - baseline-browser-mapping: 2.9.19 - caniuse-lite: 1.0.30001766 - electron-to-chromium: 1.5.279 + baseline-browser-mapping: 2.9.11 + caniuse-lite: 1.0.30001762 + electron-to-chromium: 1.5.267 node-releases: 2.0.27 update-browserslist-db: 1.2.3(browserslist@4.28.1) @@ -8181,7 +8251,7 @@ snapshots: camelcase@6.3.0: {} - caniuse-lite@1.0.30001766: {} + caniuse-lite@1.0.30001762: {} chalk@2.4.2: dependencies: @@ -8200,7 +8270,7 @@ snapshots: chrome-launcher@0.15.2: dependencies: - '@types/node': 25.1.0 + '@types/node': 25.0.3 escape-string-regexp: 4.0.0 is-wsl: 2.2.0 lighthouse-logger: 1.4.2 @@ -8209,7 +8279,7 @@ snapshots: chromium-edge-launcher@0.2.0: dependencies: - '@types/node': 25.1.0 + '@types/node': 25.0.3 escape-string-regexp: 4.0.0 is-wsl: 2.2.0 lighthouse-logger: 1.4.2 @@ -8222,7 +8292,7 @@ snapshots: ci-info@3.9.0: {} - ci-info@4.3.1: {} + ci-info@4.4.0: {} cjs-module-lexer@1.4.3: {} @@ -8337,17 +8407,17 @@ snapshots: convert-source-map@2.0.0: {} - core-js-compat@3.48.0: + core-js-compat@3.47.0: dependencies: browserslist: 4.28.1 - create-jest@29.7.0(@types/node@25.1.0): + create-jest@29.7.0(@types/node@25.0.3): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@25.1.0) + jest-config: 29.7.0(@types/node@25.0.3) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -8523,7 +8593,7 @@ snapshots: ee-first@1.1.1: {} - electron-to-chromium@1.5.279: {} + electron-to-chromium@1.5.267: {} emittery@0.13.1: {} @@ -8606,7 +8676,7 @@ snapshots: typed-array-byte-offset: 1.0.4 typed-array-length: 1.0.7 unbox-primitive: 1.1.0 - which-typed-array: 1.1.20 + which-typed-array: 1.1.19 es-define-property@1.0.1: {} @@ -8672,12 +8742,12 @@ snapshots: eslint-config-expo@10.0.0(eslint@9.39.2)(typescript@5.9.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.54.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3) - '@typescript-eslint/parser': 8.54.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/eslint-plugin': 8.51.0(@typescript-eslint/parser@8.51.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/parser': 8.51.0(eslint@9.39.2)(typescript@5.9.3) eslint: 9.39.2 eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2) eslint-plugin-expo: 1.0.0(eslint@9.39.2)(typescript@5.9.3) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.51.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2) eslint-plugin-react: 7.37.5(eslint@9.39.2) eslint-plugin-react-hooks: 5.2.0(eslint@9.39.2) globals: 16.5.0 @@ -8710,15 +8780,15 @@ snapshots: tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.51.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.54.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.51.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.54.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/parser': 8.51.0(eslint@9.39.2)(typescript@5.9.3) eslint: 9.39.2 eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2) @@ -8727,14 +8797,14 @@ snapshots: eslint-plugin-expo@1.0.0(eslint@9.39.2)(typescript@5.9.3): dependencies: - '@typescript-eslint/types': 8.54.0 - '@typescript-eslint/utils': 8.54.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/types': 8.51.0 + '@typescript-eslint/utils': 8.51.0(eslint@9.39.2)(typescript@5.9.3) eslint: 9.39.2 transitivePeerDependencies: - supports-color - typescript - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.51.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -8745,7 +8815,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.39.2 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.54.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.51.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -8757,7 +8827,7 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.54.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/parser': 8.51.0(eslint@9.39.2)(typescript@5.9.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -8800,7 +8870,7 @@ snapshots: eslint@9.39.2: dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2) '@eslint-community/regexpp': 4.12.2 '@eslint/config-array': 0.21.1 '@eslint/config-helpers': 0.4.2 @@ -8820,7 +8890,7 @@ snapshots: eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 espree: 10.4.0 - esquery: 1.7.0 + esquery: 1.6.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 file-entry-cache: 8.0.0 @@ -8845,7 +8915,7 @@ snapshots: esprima@4.0.1: {} - esquery@1.7.0: + esquery@1.6.0: dependencies: estraverse: 5.3.0 @@ -8861,7 +8931,7 @@ snapshots: event-target-shim@5.0.1: {} - eventemitter3@5.0.4: {} + eventemitter3@5.0.1: {} exec-async@2.2.0: {} @@ -8887,159 +8957,159 @@ snapshots: jest-message-util: 29.7.0 jest-util: 29.7.0 - expo-apple-authentication@8.0.8(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0)): + expo-apple-authentication@8.0.8(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)): dependencies: - expo: 54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + expo: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) - expo-application@7.0.8(expo@54.0.32): + expo-application@7.0.8(expo@54.0.33): dependencies: - expo: 54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - expo-asset@12.0.12(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + expo-asset@12.0.12(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): dependencies: '@expo/image-utils': 0.8.8 - expo: 54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - expo-constants: 18.0.13(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0)) + expo: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo-constants: 18.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)) react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) transitivePeerDependencies: - supports-color - expo-auth-session@7.0.10(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + expo-auth-session@7.0.10(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): dependencies: - expo-application: 7.0.8(expo@54.0.32) - expo-constants: 18.0.13(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0)) - expo-crypto: 15.0.8(expo@54.0.32) - expo-linking: 8.0.11(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - expo-web-browser: 15.0.10(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0)) + expo-application: 7.0.8(expo@54.0.33) + expo-constants: 18.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)) + expo-crypto: 15.0.8(expo@54.0.33) + expo-linking: 8.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo-web-browser: 15.0.10(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)) invariant: 2.2.4 react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) transitivePeerDependencies: - expo - supports-color - expo-blur@15.0.8(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + expo-blur@15.0.8(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): dependencies: - expo: 54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) - expo-build-properties@1.0.10(expo@54.0.32): + expo-build-properties@1.0.10(expo@54.0.33): dependencies: ajv: 8.17.1 - expo: 54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) semver: 7.7.3 - expo-clipboard@8.0.8(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + expo-clipboard@8.0.8(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): dependencies: - expo: 54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) - expo-constants@18.0.13(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0)): + expo-constants@18.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)): dependencies: '@expo/config': 12.0.13 '@expo/env': 2.0.8 - expo: 54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + expo: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) transitivePeerDependencies: - supports-color - expo-crypto@15.0.8(expo@54.0.32): + expo-crypto@15.0.8(expo@54.0.33): dependencies: base64-js: 1.5.1 - expo: 54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - expo-dev-client@6.0.20(expo@54.0.32): + expo-dev-client@6.0.20(expo@54.0.33): dependencies: - expo: 54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - expo-dev-launcher: 6.0.20(expo@54.0.32) - expo-dev-menu: 7.0.18(expo@54.0.32) - expo-dev-menu-interface: 2.0.0(expo@54.0.32) - expo-manifests: 1.0.10(expo@54.0.32) - expo-updates-interface: 2.0.0(expo@54.0.32) + expo: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo-dev-launcher: 6.0.20(expo@54.0.33) + expo-dev-menu: 7.0.18(expo@54.0.33) + expo-dev-menu-interface: 2.0.0(expo@54.0.33) + expo-manifests: 1.0.10(expo@54.0.33) + expo-updates-interface: 2.0.0(expo@54.0.33) transitivePeerDependencies: - supports-color - expo-dev-launcher@6.0.20(expo@54.0.32): + expo-dev-launcher@6.0.20(expo@54.0.33): dependencies: ajv: 8.17.1 - expo: 54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - expo-dev-menu: 7.0.18(expo@54.0.32) - expo-manifests: 1.0.10(expo@54.0.32) + expo: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo-dev-menu: 7.0.18(expo@54.0.33) + expo-manifests: 1.0.10(expo@54.0.33) transitivePeerDependencies: - supports-color - expo-dev-menu-interface@2.0.0(expo@54.0.32): + expo-dev-menu-interface@2.0.0(expo@54.0.33): dependencies: - expo: 54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - expo-dev-menu@7.0.18(expo@54.0.32): + expo-dev-menu@7.0.18(expo@54.0.33): dependencies: - expo: 54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - expo-dev-menu-interface: 2.0.0(expo@54.0.32) + expo: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo-dev-menu-interface: 2.0.0(expo@54.0.33) - expo-device@8.0.10(expo@54.0.32): + expo-device@8.0.10(expo@54.0.33): dependencies: - expo: 54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) ua-parser-js: 0.7.41 expo-eas-client@1.0.8: {} - expo-file-system@19.0.21(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0)): + expo-file-system@19.0.21(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)): dependencies: - expo: 54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + expo: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) - expo-font@14.0.11(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + expo-font@14.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): dependencies: - expo: 54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) fontfaceobserver: 2.3.0 react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) - expo-glass-effect@0.1.8(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + expo-glass-effect@0.1.8(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): dependencies: - expo: 54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) - expo-image@3.0.11(expo@54.0.32)(react-native-web@0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + expo-image@3.0.11(expo@54.0.33)(react-native-web@0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): dependencies: - expo: 54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) optionalDependencies: react-native-web: 0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - expo-insights@0.10.8(expo@54.0.32): + expo-insights@0.10.8(expo@54.0.33): dependencies: - expo: 54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) expo-eas-client: 1.0.8 expo-json-utils@0.15.0: {} - expo-keep-awake@15.0.8(expo@54.0.32)(react@19.1.0): + expo-keep-awake@15.0.8(expo@54.0.33)(react@19.1.0): dependencies: - expo: 54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) react: 19.1.0 - expo-linking@8.0.11(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + expo-linking@8.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): dependencies: - expo-constants: 18.0.13(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0)) + expo-constants: 18.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)) invariant: 2.2.4 react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) transitivePeerDependencies: - expo - supports-color - expo-manifests@1.0.10(expo@54.0.32): + expo-manifests@1.0.10(expo@54.0.33): dependencies: '@expo/config': 12.0.13 - expo: 54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) expo-json-utils: 0.15.0 transitivePeerDependencies: - supports-color @@ -9052,27 +9122,27 @@ snapshots: require-from-string: 2.0.2 resolve-from: 5.0.0 - expo-modules-core@3.0.29(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + expo-modules-core@3.0.29(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): dependencies: invariant: 2.2.4 react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) - expo-router@6.0.22(9bf7c66754abf2b5ca1d2af58d1d27eb): + expo-router@6.0.23(34d93f6a50204070cbf56f6391ebf5fc): dependencies: - '@expo/metro-runtime': 6.1.2(expo@54.0.32)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + '@expo/metro-runtime': 6.1.2(expo@54.0.33)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) '@expo/schema-utils': 0.1.8 '@radix-ui/react-slot': 1.2.0(@types/react@19.1.17)(react@19.1.0) '@radix-ui/react-tabs': 1.1.13(@types/react@19.1.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@react-navigation/bottom-tabs': 7.10.1(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - '@react-navigation/native': 7.1.28(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - '@react-navigation/native-stack': 7.11.0(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + '@react-navigation/bottom-tabs': 7.9.0(@react-navigation/native@7.1.26(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + '@react-navigation/native': 7.1.26(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + '@react-navigation/native-stack': 7.9.0(@react-navigation/native@7.1.26(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) client-only: 0.0.1 debug: 4.4.3 escape-string-regexp: 4.0.0 - expo: 54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - expo-constants: 18.0.13(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0)) - expo-linking: 8.0.11(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo-constants: 18.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)) + expo-linking: 8.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) expo-server: 1.0.5 fast-deep-equal: 3.1.3 invariant: 2.2.4 @@ -9080,10 +9150,10 @@ snapshots: query-string: 7.1.3 react: 19.1.0 react-fast-compare: 3.2.2 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) - react-native-is-edge-to-edge: 1.2.1(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - react-native-screens: 4.16.0(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) + react-native-is-edge-to-edge: 1.2.1(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react-native-screens: 4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) semver: 7.6.3 server-only: 0.0.1 sf-symbols-typescript: 2.2.0 @@ -9091,10 +9161,10 @@ snapshots: use-latest-callback: 0.2.6(react@19.1.0) vaul: 1.1.2(@types/react@19.1.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) optionalDependencies: - '@testing-library/react-native': 13.3.3(jest@29.7.0(@types/node@25.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0) + '@testing-library/react-native': 13.3.3(jest@29.7.0(@types/node@25.0.3))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0) react-dom: 19.1.0(react@19.1.0) - react-native-gesture-handler: 2.28.0(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - react-native-reanimated: 4.1.6(@babel/core@7.28.6)(react-native-worklets@0.5.1(@babel/core@7.28.6)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react-native-gesture-handler: 2.28.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react-native-reanimated: 4.1.6(@babel/core@7.28.5)(react-native-worklets@0.5.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) react-native-web: 0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) transitivePeerDependencies: - '@react-native-masked-view/masked-view' @@ -9102,49 +9172,49 @@ snapshots: - '@types/react-dom' - supports-color - expo-secure-store@15.0.8(expo@54.0.32): + expo-secure-store@15.0.8(expo@54.0.33): dependencies: - expo: 54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) expo-server@1.0.5: {} - expo-splash-screen@31.0.13(expo@54.0.32): + expo-splash-screen@31.0.13(expo@54.0.33): dependencies: - '@expo/prebuild-config': 54.0.8(expo@54.0.32) - expo: 54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + '@expo/prebuild-config': 54.0.8(expo@54.0.33) + expo: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) transitivePeerDependencies: - supports-color - expo-status-bar@3.0.9(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + expo-status-bar@3.0.9(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): dependencies: react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) - react-native-is-edge-to-edge: 1.2.1(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) + react-native-is-edge-to-edge: 1.2.1(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) expo-structured-headers@5.0.0: {} - expo-symbols@1.0.8(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0)): + expo-symbols@1.0.8(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)): dependencies: - expo: 54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + expo: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) sf-symbols-typescript: 2.2.0 - expo-system-ui@6.0.9(expo@54.0.32)(react-native-web@0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0)): + expo-system-ui@6.0.9(expo@54.0.33)(react-native-web@0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)): dependencies: '@react-native/normalize-colors': 0.81.5 debug: 4.4.3 - expo: 54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + expo: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) optionalDependencies: react-native-web: 0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) transitivePeerDependencies: - supports-color - expo-updates-interface@2.0.0(expo@54.0.32): + expo-updates-interface@2.0.0(expo@54.0.33): dependencies: - expo: 54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - expo-updates@29.0.16(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + expo-updates@29.0.16(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): dependencies: '@expo/code-signing-certificates': 0.0.6 '@expo/plist': 0.4.8 @@ -9152,52 +9222,52 @@ snapshots: arg: 4.1.0 chalk: 4.1.2 debug: 4.4.3 - expo: 54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) expo-eas-client: 1.0.8 - expo-manifests: 1.0.10(expo@54.0.32) + expo-manifests: 1.0.10(expo@54.0.33) expo-structured-headers: 5.0.0 - expo-updates-interface: 2.0.0(expo@54.0.32) + expo-updates-interface: 2.0.0(expo@54.0.33) getenv: 2.0.0 glob: 13.0.0 ignore: 5.3.2 react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) resolve-from: 5.0.0 transitivePeerDependencies: - supports-color - expo-web-browser@15.0.10(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0)): + expo-web-browser@15.0.10(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)): dependencies: - expo: 54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + expo: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) - expo@54.0.32(@babel/core@7.28.6)(@expo/metro-runtime@6.1.2)(expo-router@6.0.22)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + expo@54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): dependencies: - '@babel/runtime': 7.28.6 - '@expo/cli': 54.0.22(expo-router@6.0.22)(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0)) + '@babel/runtime': 7.28.4 + '@expo/cli': 54.0.23(expo-router@6.0.23)(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)) '@expo/config': 12.0.13 '@expo/config-plugins': 54.0.4 - '@expo/devtools': 0.1.8(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + '@expo/devtools': 0.1.8(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) '@expo/fingerprint': 0.15.4 '@expo/metro': 54.2.0 - '@expo/metro-config': 54.0.14(expo@54.0.32) - '@expo/vector-icons': 15.0.3(expo-font@14.0.11(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + '@expo/metro-config': 54.0.14(expo@54.0.33) + '@expo/vector-icons': 15.0.3(expo-font@14.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) '@ungap/structured-clone': 1.3.0 - babel-preset-expo: 54.0.10(@babel/core@7.28.6)(@babel/runtime@7.28.6)(expo@54.0.32)(react-refresh@0.14.2) - expo-asset: 12.0.12(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - expo-constants: 18.0.13(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0)) - expo-file-system: 19.0.21(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0)) - expo-font: 14.0.11(expo@54.0.32)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - expo-keep-awake: 15.0.8(expo@54.0.32)(react@19.1.0) + babel-preset-expo: 54.0.10(@babel/core@7.28.5)(@babel/runtime@7.28.4)(expo@54.0.33)(react-refresh@0.14.2) + expo-asset: 12.0.12(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo-constants: 18.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)) + expo-file-system: 19.0.21(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)) + expo-font: 14.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo-keep-awake: 15.0.8(expo@54.0.33)(react@19.1.0) expo-modules-autolinking: 3.0.24 - expo-modules-core: 3.0.29(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo-modules-core: 3.0.29(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) pretty-format: 29.7.0 react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) react-refresh: 0.14.2 whatwg-url-without-unicode: 8.0.0-3 optionalDependencies: - '@expo/metro-runtime': 6.1.2(expo@54.0.32)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + '@expo/metro-runtime': 6.1.2(expo@54.0.33)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) transitivePeerDependencies: - '@babel/core' - bufferutil @@ -9656,7 +9726,7 @@ snapshots: is-typed-array@1.1.15: dependencies: - which-typed-array: 1.1.20 + which-typed-array: 1.1.19 is-weakmap@2.0.2: {} @@ -9681,8 +9751,8 @@ snapshots: istanbul-lib-instrument@5.2.1: dependencies: - '@babel/core': 7.28.6 - '@babel/parser': 7.28.6 + '@babel/core': 7.28.5 + '@babel/parser': 7.28.5 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 6.3.1 @@ -9691,8 +9761,8 @@ snapshots: istanbul-lib-instrument@6.0.3: dependencies: - '@babel/core': 7.28.6 - '@babel/parser': 7.28.6 + '@babel/core': 7.28.5 + '@babel/parser': 7.28.5 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 7.7.3 @@ -9739,7 +9809,7 @@ snapshots: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 25.1.0 + '@types/node': 25.0.3 chalk: 4.1.2 co: 4.6.0 dedent: 1.7.1 @@ -9759,16 +9829,16 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@29.7.0(@types/node@25.1.0): + jest-cli@29.7.0(@types/node@25.0.3): dependencies: '@jest/core': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@25.1.0) + create-jest: 29.7.0(@types/node@25.0.3) exit: 0.1.2 import-local: 3.2.0 - jest-config: 29.7.0(@types/node@25.1.0) + jest-config: 29.7.0(@types/node@25.0.3) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -9778,12 +9848,12 @@ snapshots: - supports-color - ts-node - jest-config@29.7.0(@types/node@25.1.0): + jest-config@29.7.0(@types/node@25.0.3): dependencies: - '@babel/core': 7.28.6 + '@babel/core': 7.28.5 '@jest/test-sequencer': 29.7.0 '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.28.6) + babel-jest: 29.7.0(@babel/core@7.28.5) chalk: 4.1.2 ci-info: 3.9.0 deepmerge: 4.3.1 @@ -9803,7 +9873,7 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 25.1.0 + '@types/node': 25.0.3 transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -9840,7 +9910,7 @@ snapshots: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 '@types/jsdom': 20.0.1 - '@types/node': 25.1.0 + '@types/node': 25.0.3 jest-mock: 29.7.0 jest-util: 29.7.0 jsdom: 20.0.3 @@ -9854,7 +9924,7 @@ snapshots: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 25.1.0 + '@types/node': 25.0.3 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -9864,7 +9934,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.9 - '@types/node': 25.1.0 + '@types/node': 25.0.3 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -9879,7 +9949,7 @@ snapshots: jest-haste-map@30.2.0: dependencies: '@jest/types': 30.2.0 - '@types/node': 25.1.0 + '@types/node': 25.0.3 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -9912,7 +9982,7 @@ snapshots: jest-message-util@29.7.0: dependencies: - '@babel/code-frame': 7.28.6 + '@babel/code-frame': 7.27.1 '@jest/types': 29.6.3 '@types/stack-utils': 2.0.3 chalk: 4.1.2 @@ -9925,7 +9995,7 @@ snapshots: jest-mock@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 25.1.0 + '@types/node': 25.0.3 jest-util: 29.7.0 jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): @@ -9962,7 +10032,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 25.1.0 + '@types/node': 25.0.3 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -9990,7 +10060,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 25.1.0 + '@types/node': 25.0.3 chalk: 4.1.2 cjs-module-lexer: 1.4.3 collect-v8-coverage: 1.0.3 @@ -10010,15 +10080,15 @@ snapshots: jest-snapshot@29.7.0: dependencies: - '@babel/core': 7.28.6 - '@babel/generator': 7.28.6 - '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.28.6) - '@babel/types': 7.28.6 + '@babel/core': 7.28.5 + '@babel/generator': 7.28.5 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.5) + '@babel/types': 7.28.5 '@jest/expect-utils': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.6) + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.5) chalk: 4.1.2 expect: 29.7.0 graceful-fs: 4.2.11 @@ -10036,7 +10106,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 25.1.0 + '@types/node': 25.0.3 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -10045,9 +10115,9 @@ snapshots: jest-util@30.2.0: dependencies: '@jest/types': 30.2.0 - '@types/node': 25.1.0 + '@types/node': 25.0.3 chalk: 4.1.2 - ci-info: 4.3.1 + ci-info: 4.4.0 graceful-fs: 4.2.11 picomatch: 4.0.3 @@ -10064,7 +10134,7 @@ snapshots: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 25.1.0 + '@types/node': 25.0.3 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -10073,25 +10143,25 @@ snapshots: jest-worker@29.7.0: dependencies: - '@types/node': 25.1.0 + '@types/node': 25.0.3 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 jest-worker@30.2.0: dependencies: - '@types/node': 25.1.0 + '@types/node': 25.0.3 '@ungap/structured-clone': 1.3.0 jest-util: 30.2.0 merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.7.0(@types/node@25.1.0): + jest@29.7.0(@types/node@25.0.3): dependencies: '@jest/core': 29.7.0 '@jest/types': 29.6.3 import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@25.1.0) + jest-cli: 29.7.0(@types/node@25.0.3) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -10139,7 +10209,7 @@ snapshots: whatwg-encoding: 2.0.0 whatwg-mimetype: 3.0.0 whatwg-url: 11.0.0 - ws: 8.19.0 + ws: 8.18.3 xml-name-validator: 4.0.0 transitivePeerDependencies: - bufferutil @@ -10193,54 +10263,54 @@ snapshots: transitivePeerDependencies: - supports-color - lightningcss-android-arm64@1.31.1: + lightningcss-android-arm64@1.30.2: optional: true - lightningcss-darwin-arm64@1.31.1: + lightningcss-darwin-arm64@1.30.2: optional: true - lightningcss-darwin-x64@1.31.1: + lightningcss-darwin-x64@1.30.2: optional: true - lightningcss-freebsd-x64@1.31.1: + lightningcss-freebsd-x64@1.30.2: optional: true - lightningcss-linux-arm-gnueabihf@1.31.1: + lightningcss-linux-arm-gnueabihf@1.30.2: optional: true - lightningcss-linux-arm64-gnu@1.31.1: + lightningcss-linux-arm64-gnu@1.30.2: optional: true - lightningcss-linux-arm64-musl@1.31.1: + lightningcss-linux-arm64-musl@1.30.2: optional: true - lightningcss-linux-x64-gnu@1.31.1: + lightningcss-linux-x64-gnu@1.30.2: optional: true - lightningcss-linux-x64-musl@1.31.1: + lightningcss-linux-x64-musl@1.30.2: optional: true - lightningcss-win32-arm64-msvc@1.31.1: + lightningcss-win32-arm64-msvc@1.30.2: optional: true - lightningcss-win32-x64-msvc@1.31.1: + lightningcss-win32-x64-msvc@1.30.2: optional: true - lightningcss@1.31.1: + lightningcss@1.30.2: dependencies: detect-libc: 2.1.2 optionalDependencies: - lightningcss-android-arm64: 1.31.1 - lightningcss-darwin-arm64: 1.31.1 - lightningcss-darwin-x64: 1.31.1 - lightningcss-freebsd-x64: 1.31.1 - lightningcss-linux-arm-gnueabihf: 1.31.1 - lightningcss-linux-arm64-gnu: 1.31.1 - lightningcss-linux-arm64-musl: 1.31.1 - lightningcss-linux-x64-gnu: 1.31.1 - lightningcss-linux-x64-musl: 1.31.1 - lightningcss-win32-arm64-msvc: 1.31.1 - lightningcss-win32-x64-msvc: 1.31.1 + 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 lines-and-columns@1.2.4: {} @@ -10258,7 +10328,7 @@ snapshots: dependencies: cli-truncate: 5.1.1 colorette: 2.0.20 - eventemitter3: 5.0.4 + eventemitter3: 5.0.1 log-update: 6.1.0 rfdc: 1.4.1 wrap-ansi: 9.0.2 @@ -10295,17 +10365,17 @@ snapshots: lru-cache@10.4.3: {} - lru-cache@11.2.5: {} + lru-cache@11.2.4: {} lru-cache@5.1.1: dependencies: yallist: 3.1.1 - lucide-react-native@0.562.0(react-native-svg@15.12.1(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + lucide-react-native@0.562.0(react-native-svg@15.12.1(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): dependencies: react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) - react-native-svg: 15.12.1(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) + react-native-svg: 15.12.1(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) make-dir@4.0.0: dependencies: @@ -10333,7 +10403,7 @@ snapshots: metro-babel-transformer@0.83.3: dependencies: - '@babel/core': 7.28.6 + '@babel/core': 7.28.5 flow-enums-runtime: 0.0.6 hermes-parser: 0.32.0 nullthrows: 1.1.1 @@ -10391,7 +10461,7 @@ snapshots: metro-minify-terser@0.83.3: dependencies: flow-enums-runtime: 0.0.6 - terser: 5.46.0 + terser: 5.44.1 metro-resolver@0.83.3: dependencies: @@ -10399,14 +10469,14 @@ snapshots: metro-runtime@0.83.3: dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.28.4 flow-enums-runtime: 0.0.6 metro-source-map@0.83.3: dependencies: - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.28.5 '@babel/traverse--for-generate-function-map': '@babel/traverse@7.28.6' - '@babel/types': 7.28.6 + '@babel/types': 7.28.5 flow-enums-runtime: 0.0.6 invariant: 2.2.4 metro-symbolicate: 0.83.3 @@ -10430,10 +10500,10 @@ snapshots: metro-transform-plugins@0.83.3: dependencies: - '@babel/core': 7.28.6 - '@babel/generator': 7.28.6 - '@babel/template': 7.28.6 - '@babel/traverse': 7.28.6 + '@babel/core': 7.28.5 + '@babel/generator': 7.28.5 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.5 flow-enums-runtime: 0.0.6 nullthrows: 1.1.1 transitivePeerDependencies: @@ -10441,10 +10511,10 @@ snapshots: metro-transform-worker@0.83.3: dependencies: - '@babel/core': 7.28.6 - '@babel/generator': 7.28.6 - '@babel/parser': 7.28.6 - '@babel/types': 7.28.6 + '@babel/core': 7.28.5 + '@babel/generator': 7.28.5 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 flow-enums-runtime: 0.0.6 metro: 0.83.3 metro-babel-transformer: 0.83.3 @@ -10461,13 +10531,13 @@ snapshots: metro@0.83.3: dependencies: - '@babel/code-frame': 7.28.6 - '@babel/core': 7.28.6 - '@babel/generator': 7.28.6 - '@babel/parser': 7.28.6 - '@babel/template': 7.28.6 - '@babel/traverse': 7.28.6 - '@babel/types': 7.28.6 + '@babel/code-frame': 7.27.1 + '@babel/core': 7.28.5 + '@babel/generator': 7.28.5 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 accepts: 1.3.8 chalk: 4.1.2 ci-info: 2.0.0 @@ -10737,7 +10807,7 @@ snapshots: parse-json@5.2.0: dependencies: - '@babel/code-frame': 7.28.6 + '@babel/code-frame': 7.27.1 error-ex: 1.3.4 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 @@ -10762,7 +10832,7 @@ snapshots: path-scurry@2.0.1: dependencies: - lru-cache: 11.2.5 + lru-cache: 11.2.4 minipass: 7.1.2 picocolors@1.1.1: {} @@ -10781,11 +10851,11 @@ snapshots: dependencies: find-up: 4.1.0 - playwright-core@1.58.0: {} + playwright-core@1.58.1: {} - playwright@1.58.0: + playwright@1.58.1: dependencies: - playwright-core: 1.58.0 + playwright-core: 1.58.1 optionalDependencies: fsevents: 2.3.2 @@ -10905,84 +10975,84 @@ snapshots: react-is@18.3.1: {} - react-is@19.2.4: {} + react-is@19.2.3: {} - react-native-bottom-tabs@1.1.0(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + react-native-bottom-tabs@1.1.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): dependencies: react: 19.1.0 react-freeze: 1.0.4(react@19.1.0) - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) sf-symbols-typescript: 2.2.0 use-latest-callback: 0.2.6(react@19.1.0) - react-native-edge-to-edge@1.7.0(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + react-native-edge-to-edge@1.7.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): dependencies: react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) - react-native-gesture-handler@2.28.0(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + react-native-gesture-handler@2.28.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): dependencies: '@egjs/hammerjs': 2.0.17 hoist-non-react-statics: 3.3.2 invariant: 2.2.4 react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) - react-native-is-edge-to-edge@1.2.1(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + react-native-is-edge-to-edge@1.2.1(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): dependencies: react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) - react-native-keyboard-controller@1.18.5(react-native-reanimated@4.1.6(@babel/core@7.28.6)(react-native-worklets@0.5.1(@babel/core@7.28.6)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + react-native-keyboard-controller@1.18.5(react-native-reanimated@4.1.6(@babel/core@7.28.5)(react-native-worklets@0.5.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): dependencies: react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) - react-native-is-edge-to-edge: 1.2.1(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - react-native-reanimated: 4.1.6(@babel/core@7.28.6)(react-native-worklets@0.5.1(@babel/core@7.28.6)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) + react-native-is-edge-to-edge: 1.2.1(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react-native-reanimated: 4.1.6(@babel/core@7.28.5)(react-native-worklets@0.5.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - react-native-reanimated@4.1.6(@babel/core@7.28.6)(react-native-worklets@0.5.1(@babel/core@7.28.6)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + react-native-reanimated@4.1.6(@babel/core@7.28.5)(react-native-worklets@0.5.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): dependencies: - '@babel/core': 7.28.6 + '@babel/core': 7.28.5 react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) - react-native-is-edge-to-edge: 1.2.1(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - react-native-worklets: 0.5.1(@babel/core@7.28.6)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) + react-native-is-edge-to-edge: 1.2.1(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react-native-worklets: 0.5.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) semver: 7.7.2 - react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): dependencies: react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) - react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): dependencies: react: 19.1.0 react-freeze: 1.0.4(react@19.1.0) - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) - react-native-is-edge-to-edge: 1.2.1(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) + react-native-is-edge-to-edge: 1.2.1(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) warn-once: 0.1.1 - react-native-svg@15.12.1(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + react-native-svg@15.12.1(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): dependencies: css-select: 5.2.2 css-tree: 1.1.3 react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) warn-once: 0.1.1 - react-native-toast-message@2.3.3(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + react-native-toast-message@2.3.3(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): dependencies: react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) - react-native-url-polyfill@3.0.0(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0)): + react-native-url-polyfill@3.0.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)): dependencies: - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) whatwg-url-without-unicode: 8.0.0-3 react-native-web@0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.28.4 '@react-native/normalize-colors': 0.74.89 fbjs: 3.0.5 inline-style-prefixer: 7.0.1 @@ -10995,39 +11065,39 @@ snapshots: transitivePeerDependencies: - encoding - react-native-worklets@0.5.1(@babel/core@7.28.6)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): - dependencies: - '@babel/core': 7.28.6 - '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.28.6) - '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-classes': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.28.6) - '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.28.6) - '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.28.6) - '@babel/preset-typescript': 7.28.5(@babel/core@7.28.6) + react-native-worklets@0.5.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + dependencies: + '@babel/core': 7.28.5 + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-class-properties': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-classes': 7.28.4(@babel/core@7.28.5) + '@babel/plugin-transform-nullish-coalescing-operator': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-optional-chaining': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.28.5) + '@babel/preset-typescript': 7.28.5(@babel/core@7.28.5) convert-source-map: 2.0.0 react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) semver: 7.7.2 transitivePeerDependencies: - supports-color - react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0): + react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0): dependencies: '@jest/create-cache-key-function': 29.7.0 '@react-native/assets-registry': 0.81.5 - '@react-native/codegen': 0.81.5(@babel/core@7.28.6) + '@react-native/codegen': 0.81.5(@babel/core@7.28.5) '@react-native/community-cli-plugin': 0.81.5 '@react-native/gradle-plugin': 0.81.5 '@react-native/js-polyfills': 0.81.5 '@react-native/normalize-colors': 0.81.5 - '@react-native/virtualized-lists': 0.81.5(@types/react@19.1.17)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + '@react-native/virtualized-lists': 0.81.5(@types/react@19.1.17)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) abort-controller: 3.0.0 anser: 1.4.10 ansi-regex: 5.0.1 - babel-jest: 29.7.0(@babel/core@7.28.6) + babel-jest: 29.7.0(@babel/core@7.28.5) babel-plugin-syntax-hermes-parser: 0.29.1 base64-js: 1.5.1 commander: 12.1.0 @@ -11093,7 +11163,7 @@ snapshots: react-test-renderer@19.1.0(react@19.1.0): dependencies: react: 19.1.0 - react-is: 19.2.4 + react-is: 19.2.3 scheduler: 0.26.0 react@19.1.0: {} @@ -11172,7 +11242,7 @@ snapshots: resolve-pkg-maps@1.0.0: {} - resolve-workspace-root@2.0.1: {} + resolve-workspace-root@2.0.0: {} resolve.exports@2.0.3: {} @@ -11231,7 +11301,7 @@ snapshots: safer-buffer@2.1.2: {} - sax@1.4.4: {} + sax@1.4.3: {} saxes@6.0.0: dependencies: @@ -11543,7 +11613,7 @@ snapshots: symbol-tree@3.2.4: {} - tar@7.5.7: + tar@7.5.2: dependencies: '@isaacs/fs-minipass': 4.0.1 chownr: 3.0.0 @@ -11558,7 +11628,7 @@ snapshots: ansi-escapes: 4.3.2 supports-hyperlinks: 2.3.0 - terser@5.46.0: + terser@5.44.1: dependencies: '@jridgewell/source-map': 0.3.11 acorn: 8.15.0 @@ -11607,7 +11677,7 @@ snapshots: dependencies: punycode: 2.3.1 - ts-api-utils@2.4.0(typescript@5.9.3): + ts-api-utils@2.3.0(typescript@5.9.3): dependencies: typescript: 5.9.3 @@ -11856,7 +11926,7 @@ snapshots: isarray: 2.0.5 which-boxed-primitive: 1.1.1 which-collection: 1.0.2 - which-typed-array: 1.1.20 + which-typed-array: 1.1.19 which-collection@1.0.2: dependencies: @@ -11865,7 +11935,7 @@ snapshots: is-weakmap: 2.0.2 is-weakset: 2.0.4 - which-typed-array@1.1.20: + which-typed-array@1.1.19: dependencies: available-typed-arrays: 1.0.7 call-bind: 1.0.8 @@ -11913,7 +11983,7 @@ snapshots: ws@7.5.10: {} - ws@8.19.0: {} + ws@8.18.3: {} xcode@3.0.1: dependencies: @@ -11924,7 +11994,7 @@ snapshots: xml2js@0.6.0: dependencies: - sax: 1.4.4 + sax: 1.4.3 xmlbuilder: 11.0.1 xmlbuilder@11.0.1: {} diff --git a/supabase/migrations/20260117000000_sponsor_sponsee_connection_system.sql b/supabase/migrations/20260117000000_sponsor_sponsee_connection_system.sql new file mode 100644 index 00000000..e4c30586 --- /dev/null +++ b/supabase/migrations/20260117000000_sponsor_sponsee_connection_system.sql @@ -0,0 +1,571 @@ +/* + # Sponsor/Sponsee Connection System + + Implements the connection system with intent & ownership as per issue #300. + + ## Changes + + ### Profile Enhancements + - `connection_intent`: User's stated intent for connections + - `external_handles`: JSONB storage for external platform handles (Discord, etc.) + + ### Relationship Enhancements + - `sponsor_reveal_consent`: Sponsor's consent to reveal external handles + - `sponsee_reveal_consent`: Sponsee's consent to reveal external handles + + ### Invite Code Enhancements + - `revoked_at`: Timestamp when code was manually revoked + - `intent`: The intent the sponsor had when creating the code +*/ + +-- Create enum type for connection intent +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'connection_intent_type') THEN + CREATE TYPE connection_intent_type AS ENUM ( + 'not_looking', + 'seeking_sponsor', + 'open_to_sponsoring', + 'open_to_both' + ); + END IF; +END$$; + +-- Add connection_intent to profiles +ALTER TABLE public.profiles +ADD COLUMN IF NOT EXISTS connection_intent connection_intent_type DEFAULT NULL; + +-- Add external_handles JSONB to profiles +-- Structure: { "discord": "@handle", "telegram": "@handle", "phone": "+1...", ... } +ALTER TABLE public.profiles +ADD COLUMN IF NOT EXISTS external_handles jsonb DEFAULT '{}'::jsonb; + +-- Add comment for documentation +COMMENT ON COLUMN public.profiles.connection_intent IS + 'User intent for sponsor/sponsee connections: not_looking, seeking_sponsor, open_to_sponsoring, open_to_both'; + +COMMENT ON COLUMN public.profiles.external_handles IS + 'External platform handles stored privately. Only revealed with mutual consent per-connection.'; + +-- Add symmetric reveal consent fields to relationships +ALTER TABLE public.sponsor_sponsee_relationships +ADD COLUMN IF NOT EXISTS sponsor_reveal_consent boolean DEFAULT false; + +ALTER TABLE public.sponsor_sponsee_relationships +ADD COLUMN IF NOT EXISTS sponsee_reveal_consent boolean DEFAULT false; + +COMMENT ON COLUMN public.sponsor_sponsee_relationships.sponsor_reveal_consent IS + 'Whether sponsor has opted in to reveal their external handles to this sponsee'; + +COMMENT ON COLUMN public.sponsor_sponsee_relationships.sponsee_reveal_consent IS + 'Whether sponsee has opted in to reveal their external handles to this sponsor'; + +-- Add revoked_at and intent to invite_codes for persistent invite management +ALTER TABLE public.invite_codes +ADD COLUMN IF NOT EXISTS revoked_at timestamptz DEFAULT NULL; + +ALTER TABLE public.invite_codes +ADD COLUMN IF NOT EXISTS intent connection_intent_type DEFAULT NULL; + +COMMENT ON COLUMN public.invite_codes.revoked_at IS + 'Timestamp when the invite code was manually revoked by the sponsor'; + +COMMENT ON COLUMN public.invite_codes.intent IS + 'The connection intent the sponsor had when creating this invite code'; + +-- Create index for finding users by connection intent (for future matching) +CREATE INDEX IF NOT EXISTS idx_profiles_connection_intent + ON public.profiles(connection_intent) + WHERE connection_intent IS NOT NULL; + +-- Update invite_codes policies to handle revocation +DROP POLICY IF EXISTS "Anyone can view valid invite codes" ON public.invite_codes; +CREATE POLICY "Anyone can view valid invite codes" + ON public.invite_codes FOR SELECT TO authenticated + USING (expires_at > now() AND used_by IS NULL AND revoked_at IS NULL); + +-- Allow sponsors to update their own invite codes (for revocation) +DROP POLICY IF EXISTS "Sponsors can update own invite codes" ON public.invite_codes; +CREATE POLICY "Sponsors can update own invite codes" + ON public.invite_codes FOR UPDATE TO authenticated + USING (sponsor_id = auth.uid()) + WITH CHECK (sponsor_id = auth.uid()); + +-- Allow sponsors to delete their own invite codes +DROP POLICY IF EXISTS "Sponsors can delete own invite codes" ON public.invite_codes; +CREATE POLICY "Sponsors can delete own invite codes" + ON public.invite_codes FOR DELETE TO authenticated + USING (sponsor_id = auth.uid()); + +-- Allow sponsors to view their own invite codes (including used/expired) +DROP POLICY IF EXISTS "Sponsors can view their own invite codes" ON public.invite_codes; +CREATE POLICY "Sponsors can view their own invite codes" + ON public.invite_codes FOR SELECT TO authenticated + USING (sponsor_id = auth.uid()); + +-- Allow users to update invite codes when claiming them (must not be revoked) +DROP POLICY IF EXISTS "Users can update invite codes when using them" ON public.invite_codes; +CREATE POLICY "Users can update invite codes when using them" + ON public.invite_codes FOR UPDATE TO authenticated + USING (expires_at > now() AND used_by IS NULL AND revoked_at IS NULL) + WITH CHECK (used_by = auth.uid() AND revoked_at IS NULL); + +-- ============================================================================= +-- Profile RLS Policies for Connection Flow +-- ============================================================================= + +-- Remove overly broad policy - replaced with SECURITY DEFINER function below +DROP POLICY IF EXISTS "Users can view sponsor profile via invite code" ON public.profiles; + +-- ============================================================================= +-- Function to get sponsor profile by invite code (secure alternative to RLS) +-- ============================================================================= + +-- This function allows users to look up a sponsor's profile ONLY when they +-- have the actual invite code. This is more secure than an RLS policy that +-- would allow viewing any sponsor with an active invite code. +CREATE OR REPLACE FUNCTION public.get_sponsor_profile_by_invite_code(invite_code TEXT) +RETURNS TABLE ( + id uuid, + display_name text, + sobriety_date date, + avatar_url text +) AS $$ +BEGIN + RETURN QUERY + SELECT + p.id, + p.display_name, + p.sobriety_date, + p.avatar_url + FROM public.profiles p + JOIN public.invite_codes ic ON ic.sponsor_id = p.id + WHERE ic.code = invite_code + AND ic.expires_at > now() + AND ic.used_by IS NULL + AND ic.revoked_at IS NULL + LIMIT 1; +END; +$$ LANGUAGE plpgsql SECURITY DEFINER; + +-- Grant execute permission to authenticated users +GRANT EXECUTE ON FUNCTION public.get_sponsor_profile_by_invite_code(TEXT) TO authenticated; + +-- Allow sponsees to view their sponsor's profile +DROP POLICY IF EXISTS "Users can view their sponsor's profile" ON public.profiles; +CREATE POLICY "Users can view their sponsor's profile" + ON public.profiles FOR SELECT TO authenticated + USING ( + EXISTS ( + SELECT 1 FROM public.sponsor_sponsee_relationships + WHERE sponsee_id = auth.uid() + AND sponsor_id = profiles.id + AND status = 'active' + ) + ); + +-- Allow sponsors to view their sponsees' profiles +DROP POLICY IF EXISTS "Users can view their sponsees' profiles" ON public.profiles; +CREATE POLICY "Users can view their sponsees' profiles" + ON public.profiles FOR SELECT TO authenticated + USING ( + EXISTS ( + SELECT 1 FROM public.sponsor_sponsee_relationships + WHERE sponsor_id = auth.uid() + AND sponsee_id = profiles.id + AND status = 'active' + ) + ); + +-- ============================================================================= +-- Sponsor-Sponsee Relationships RLS Policies +-- ============================================================================= + +-- Allow users to view their own relationships (as sponsor or sponsee) +DROP POLICY IF EXISTS "Users can view their own relationships" ON public.sponsor_sponsee_relationships; +CREATE POLICY "Users can view their own relationships" + ON public.sponsor_sponsee_relationships FOR SELECT TO authenticated + USING (sponsor_id = auth.uid() OR sponsee_id = auth.uid()); + +-- Allow sponsees to create relationships when using an invite code +DROP POLICY IF EXISTS "Sponsees can create relationships via invite code" ON public.sponsor_sponsee_relationships; +CREATE POLICY "Sponsees can create relationships via invite code" + ON public.sponsor_sponsee_relationships FOR INSERT TO authenticated + WITH CHECK (sponsee_id = auth.uid()); + +-- Allow users to update their relationships (for status changes, reveal consent) +DROP POLICY IF EXISTS "Users can update their relationships" ON public.sponsor_sponsee_relationships; +CREATE POLICY "Users can update their relationships" + ON public.sponsor_sponsee_relationships FOR UPDATE TO authenticated + USING (sponsor_id = auth.uid() OR sponsee_id = auth.uid()) + WITH CHECK (sponsor_id = auth.uid() OR sponsee_id = auth.uid()); + +-- ============================================================================= +-- Opt-in Matching System (connection_matches table) +-- ============================================================================= + +-- Create enum type for match status +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'match_status_type') THEN + CREATE TYPE match_status_type AS ENUM ( + 'pending', + 'accepted', + 'rejected', + 'expired' + ); + END IF; +END$$; + +-- Create connection_matches table for opt-in matching +-- System proposes matches based on mutual opposite needs (seeking_sponsor ↔ open_to_sponsoring) +-- Both parties must accept to create a relationship +CREATE TABLE IF NOT EXISTS public.connection_matches ( + id uuid DEFAULT gen_random_uuid() PRIMARY KEY, + -- The user seeking a sponsor + seeker_id uuid NOT NULL REFERENCES public.profiles(id) ON DELETE CASCADE, + -- The potential sponsor/provider + provider_id uuid NOT NULL REFERENCES public.profiles(id) ON DELETE CASCADE, + -- Bilateral acceptance (null = pending, true = accepted, false = rejected) + seeker_accepted boolean DEFAULT NULL, + provider_accepted boolean DEFAULT NULL, + -- Overall match status + status match_status_type DEFAULT 'pending' NOT NULL, + -- Relationship created when both accept + relationship_id uuid REFERENCES public.sponsor_sponsee_relationships(id) ON DELETE SET NULL, + -- Timestamps + created_at timestamptz DEFAULT now() NOT NULL, + expires_at timestamptz DEFAULT (now() + interval '7 days') NOT NULL, + resolved_at timestamptz DEFAULT NULL +); + +-- Drop old unconditional constraint if it exists (for existing databases) +ALTER TABLE public.connection_matches DROP CONSTRAINT IF EXISTS unique_active_match; + +-- Partial unique index: only prevent duplicates for PENDING matches +-- This allows rematching after rejection or expiry +DROP INDEX IF EXISTS idx_connection_matches_unique_pending; +CREATE UNIQUE INDEX idx_connection_matches_unique_pending + ON public.connection_matches(seeker_id, provider_id) + WHERE status = 'pending'; + +-- Add comments for documentation +COMMENT ON TABLE public.connection_matches IS + 'Stores system-proposed matches between users seeking sponsors and those open to sponsoring. Both parties must accept.'; + +COMMENT ON COLUMN public.connection_matches.seeker_id IS + 'User with intent seeking_sponsor or open_to_both who initiated the match request'; + +COMMENT ON COLUMN public.connection_matches.provider_id IS + 'User with intent open_to_sponsoring or open_to_both proposed as potential sponsor'; + +COMMENT ON COLUMN public.connection_matches.seeker_accepted IS + 'Seeker acceptance: null=pending, true=accepted, false=rejected'; + +COMMENT ON COLUMN public.connection_matches.provider_accepted IS + 'Provider acceptance: null=pending, true=accepted, false=rejected'; + +-- Create indexes for efficient queries +CREATE INDEX IF NOT EXISTS idx_connection_matches_seeker + ON public.connection_matches(seeker_id) + WHERE status = 'pending'; + +CREATE INDEX IF NOT EXISTS idx_connection_matches_provider + ON public.connection_matches(provider_id) + WHERE status = 'pending'; + +CREATE INDEX IF NOT EXISTS idx_connection_matches_status + ON public.connection_matches(status) + WHERE status = 'pending'; + +-- ============================================================================= +-- Connection Matches RLS Policies +-- ============================================================================= + +ALTER TABLE public.connection_matches ENABLE ROW LEVEL SECURITY; + +-- Users can view matches where they are seeker or provider +DROP POLICY IF EXISTS "Users can view their own matches" ON public.connection_matches; +CREATE POLICY "Users can view their own matches" + ON public.connection_matches FOR SELECT TO authenticated + USING (seeker_id = auth.uid() OR provider_id = auth.uid()); + +-- Users can update matches where they are seeker or provider (for accepting/rejecting) +DROP POLICY IF EXISTS "Users can update their own matches" ON public.connection_matches; +CREATE POLICY "Users can update their own matches" + ON public.connection_matches FOR UPDATE TO authenticated + USING (seeker_id = auth.uid() OR provider_id = auth.uid()) + WITH CHECK (seeker_id = auth.uid() OR provider_id = auth.uid()); + +-- Only authenticated users with seeking intent can create match requests +-- (In practice, this would be done via a server function, but policy allows it) +DROP POLICY IF EXISTS "Users can create match requests" ON public.connection_matches; +CREATE POLICY "Users can create match requests" + ON public.connection_matches FOR INSERT TO authenticated + WITH CHECK (seeker_id = auth.uid()); + +-- ============================================================================= +-- Function to find potential matches +-- ============================================================================= + +-- Function to find users with complementary intents +CREATE OR REPLACE FUNCTION public.find_potential_matches(user_id uuid, max_results integer DEFAULT 5) +RETURNS TABLE ( + matched_user_id uuid, + matched_intent connection_intent_type, + display_name text +) AS $$ +DECLARE + user_intent connection_intent_type; +BEGIN + -- Get the requesting user's intent + SELECT connection_intent INTO user_intent + FROM public.profiles + WHERE id = user_id; + + -- Return empty if user has no intent set or is not looking + IF user_intent IS NULL OR user_intent = 'not_looking' THEN + RETURN; + END IF; + + RETURN QUERY + SELECT + p.id as matched_user_id, + p.connection_intent as matched_intent, + p.display_name + FROM public.profiles p + WHERE + p.id != user_id + -- Match complementary intents + AND ( + -- User is seeking sponsor -> match with those open to sponsoring + (user_intent IN ('seeking_sponsor', 'open_to_both') + AND p.connection_intent IN ('open_to_sponsoring', 'open_to_both')) + OR + -- User is open to sponsoring -> match with those seeking sponsor + (user_intent IN ('open_to_sponsoring', 'open_to_both') + AND p.connection_intent IN ('seeking_sponsor', 'open_to_both')) + ) + -- Exclude users with ACTIVE relationships only (allow re-matching after disconnect) + AND NOT EXISTS ( + SELECT 1 FROM public.sponsor_sponsee_relationships r + WHERE r.status = 'active' + AND ((r.sponsor_id = user_id AND r.sponsee_id = p.id) + OR (r.sponsor_id = p.id AND r.sponsee_id = user_id)) + ) + -- Exclude users with pending matches with this user + AND NOT EXISTS ( + SELECT 1 FROM public.connection_matches m + WHERE m.status = 'pending' + AND ((m.seeker_id = user_id AND m.provider_id = p.id) + OR (m.seeker_id = p.id AND m.provider_id = user_id)) + ) + LIMIT max_results; +END; +$$ LANGUAGE plpgsql SECURITY DEFINER; + +-- Grant execute permission +GRANT EXECUTE ON FUNCTION public.find_potential_matches(uuid, integer) TO authenticated; + +-- ============================================================================= +-- Function to handle match acceptance +-- ============================================================================= + +-- Function to accept a match and create relationship if both accepted +CREATE OR REPLACE FUNCTION public.accept_match(match_id uuid) +RETURNS public.connection_matches AS $$ +DECLARE + match_record public.connection_matches; + new_relationship_id uuid; + existing_relationship_id uuid; + current_user_id uuid; +BEGIN + current_user_id := auth.uid(); + + -- Get and lock the match record + SELECT * INTO match_record + FROM public.connection_matches + WHERE id = match_id + FOR UPDATE; + + -- Verify user is part of this match + IF match_record.seeker_id != current_user_id AND match_record.provider_id != current_user_id THEN + RAISE EXCEPTION 'User is not part of this match'; + END IF; + + -- Verify match is still pending + IF match_record.status != 'pending' THEN + RAISE EXCEPTION 'Match is no longer pending'; + END IF; + + -- Verify match hasn't expired + IF match_record.expires_at < now() THEN + UPDATE public.connection_matches SET status = 'expired', resolved_at = now() + WHERE id = match_id; + RAISE EXCEPTION 'Match has expired'; + END IF; + + -- Update the appropriate acceptance field + IF match_record.seeker_id = current_user_id THEN + UPDATE public.connection_matches SET seeker_accepted = true WHERE id = match_id; + match_record.seeker_accepted := true; + ELSE + UPDATE public.connection_matches SET provider_accepted = true WHERE id = match_id; + match_record.provider_accepted := true; + END IF; + + -- Check if both have now accepted + IF match_record.seeker_accepted = true AND match_record.provider_accepted = true THEN + -- Check for existing relationship (may be inactive from previous disconnect) + SELECT id INTO existing_relationship_id + FROM public.sponsor_sponsee_relationships + WHERE sponsor_id = match_record.provider_id + AND sponsee_id = match_record.seeker_id; + + IF existing_relationship_id IS NOT NULL THEN + -- Reactivate existing relationship + UPDATE public.sponsor_sponsee_relationships + SET status = 'active', connected_at = now(), disconnected_at = NULL + WHERE id = existing_relationship_id; + new_relationship_id := existing_relationship_id; + ELSE + -- Create new sponsor-sponsee relationship + INSERT INTO public.sponsor_sponsee_relationships ( + sponsor_id, + sponsee_id, + status, + connected_at + ) VALUES ( + match_record.provider_id, -- Provider becomes sponsor + match_record.seeker_id, -- Seeker becomes sponsee + 'active', + now() + ) RETURNING id INTO new_relationship_id; + END IF; + + -- Update match as accepted with relationship reference + UPDATE public.connection_matches + SET + status = 'accepted', + relationship_id = new_relationship_id, + resolved_at = now() + WHERE id = match_id; + + match_record.status := 'accepted'; + match_record.relationship_id := new_relationship_id; + match_record.resolved_at := now(); + END IF; + + RETURN match_record; +END; +$$ LANGUAGE plpgsql SECURITY DEFINER; + +-- Grant execute permission +GRANT EXECUTE ON FUNCTION public.accept_match(uuid) TO authenticated; + +-- ============================================================================= +-- Function to reject a match +-- ============================================================================= + +CREATE OR REPLACE FUNCTION public.reject_match(match_id uuid) +RETURNS public.connection_matches AS $$ +DECLARE + match_record public.connection_matches; + current_user_id uuid; +BEGIN + current_user_id := auth.uid(); + + -- Get and lock the match record + SELECT * INTO match_record + FROM public.connection_matches + WHERE id = match_id + FOR UPDATE; + + -- Verify user is part of this match + IF match_record.seeker_id != current_user_id AND match_record.provider_id != current_user_id THEN + RAISE EXCEPTION 'User is not part of this match'; + END IF; + + -- Verify match is still pending + IF match_record.status != 'pending' THEN + RAISE EXCEPTION 'Match is no longer pending'; + END IF; + + -- Update the match as rejected + IF match_record.seeker_id = current_user_id THEN + UPDATE public.connection_matches + SET seeker_accepted = false, status = 'rejected', resolved_at = now() + WHERE id = match_id; + match_record.seeker_accepted := false; + ELSE + UPDATE public.connection_matches + SET provider_accepted = false, status = 'rejected', resolved_at = now() + WHERE id = match_id; + match_record.provider_accepted := false; + END IF; + + match_record.status := 'rejected'; + match_record.resolved_at := now(); + + RETURN match_record; +END; +$$ LANGUAGE plpgsql SECURITY DEFINER; + +-- Grant execute permission +GRANT EXECUTE ON FUNCTION public.reject_match(uuid) TO authenticated; + +-- ============================================================================= +-- Function to get external handles with consent verification +-- ============================================================================= + +-- This function returns the external_handles of a user only when both parties +-- in a sponsor/sponsee relationship have consented to reveal their contact info. +-- This ensures handles are never sent to the client without proper consent. +CREATE OR REPLACE FUNCTION public.get_handles_with_consent(relationship_id uuid) +RETURNS jsonb AS $$ +DECLARE + rel_record public.sponsor_sponsee_relationships; + requesting_user_id uuid; + target_user_id uuid; + target_handles jsonb; +BEGIN + requesting_user_id := auth.uid(); + + -- Get the relationship record + SELECT * INTO rel_record + FROM public.sponsor_sponsee_relationships + WHERE id = relationship_id; + + -- Verify user is part of this relationship + IF rel_record.sponsor_id != requesting_user_id AND rel_record.sponsee_id != requesting_user_id THEN + RAISE EXCEPTION 'User is not part of this relationship'; + END IF; + + -- Verify relationship is active + IF rel_record.status != 'active' THEN + RETURN NULL; + END IF; + + -- Check for mutual consent + IF NOT (rel_record.sponsor_reveal_consent AND rel_record.sponsee_reveal_consent) THEN + RETURN NULL; + END IF; + + -- Determine whose handles to return (the other person's) + IF requesting_user_id = rel_record.sponsor_id THEN + target_user_id := rel_record.sponsee_id; + ELSE + target_user_id := rel_record.sponsor_id; + END IF; + + -- Fetch and return the target user's handles + SELECT external_handles INTO target_handles + FROM public.profiles + WHERE id = target_user_id; + + RETURN COALESCE(target_handles, '{}'::jsonb); +END; +$$ LANGUAGE plpgsql SECURITY DEFINER; + +-- Grant execute permission +GRANT EXECUTE ON FUNCTION public.get_handles_with_consent(uuid) TO authenticated; diff --git a/types/database.ts b/types/database.ts index 12d221b2..89754a6e 100644 --- a/types/database.ts +++ b/types/database.ts @@ -10,6 +10,33 @@ export type NotificationType = | 'connection_request' | 'task_completed'; +/** + * Status of a connection match proposal. + */ +export type MatchStatus = 'pending' | 'accepted' | 'rejected' | 'expired'; + +/** + * User's stated intent for sponsor/sponsee connections. + * Used for opt-in matching and invite code context. + */ +export type ConnectionIntent = + | 'not_looking' + | 'seeking_sponsor' + | 'open_to_sponsoring' + | 'open_to_both'; + +/** + * External platform handles for out-of-app communication. + * Only revealed with mutual consent per-connection. + */ +export interface ExternalHandles { + discord?: string; + telegram?: string; + whatsapp?: string; + signal?: string; + phone?: string; +} + // ============================================================================= // Database Interfaces // ============================================================================= @@ -89,6 +116,16 @@ export interface Profile { * Existing users (null/undefined) are treated as true. */ show_program_content?: boolean; + /** + * User's stated intent for sponsor/sponsee connections. + * Used for opt-in matching and invite code context. + */ + connection_intent?: ConnectionIntent | null; + /** + * External platform handles stored privately. + * Only revealed with mutual consent per-connection. + */ + external_handles?: ExternalHandles; notification_preferences: { tasks: boolean; messages: boolean; @@ -106,6 +143,14 @@ export interface SponsorSponseeRelationship { status: RelationshipStatus; connected_at: string; disconnected_at?: string; + /** + * Whether sponsor has opted in to reveal their external handles to this sponsee. + */ + sponsor_reveal_consent?: boolean; + /** + * Whether sponsee has opted in to reveal their external handles to this sponsor. + */ + sponsee_reveal_consent?: boolean; created_at: string; sponsor?: Profile; sponsee?: Profile; @@ -118,6 +163,14 @@ export interface InviteCode { expires_at: string; used_by?: string; used_at?: string; + /** + * Timestamp when the invite code was manually revoked by the sponsor. + */ + revoked_at?: string | null; + /** + * The connection intent the sponsor had when creating this invite code. + */ + intent?: ConnectionIntent | null; created_at: string; sponsor?: Profile; } @@ -369,3 +422,48 @@ export interface TaskTemplate { created_at: string; updated_at: string; } + +/** + * A system-proposed match between a user seeking a sponsor and a potential sponsor. + * Part of the opt-in matching system where both parties must accept to connect. + * + * @remarks + * Matches are created when users with complementary intents are found: + * - seeking_sponsor ↔ open_to_sponsoring + * - open_to_both can match with either side + * + * Both seeker_accepted and provider_accepted must be true for a relationship to form. + */ +export interface ConnectionMatch { + id: string; + /** User with intent seeking_sponsor or open_to_both */ + seeker_id: string; + /** User with intent open_to_sponsoring or open_to_both */ + provider_id: string; + /** Seeker's acceptance: null=pending, true=accepted, false=rejected */ + seeker_accepted: boolean | null; + /** Provider's acceptance: null=pending, true=accepted, false=rejected */ + provider_accepted: boolean | null; + /** Overall match status */ + status: MatchStatus; + /** Relationship ID created when both accept */ + relationship_id?: string | null; + created_at: string; + /** Match expires after 7 days if not resolved */ + expires_at: string; + /** Timestamp when match was resolved (accepted/rejected/expired) */ + resolved_at?: string | null; + /** Joined seeker profile data */ + seeker?: Profile; + /** Joined provider profile data */ + provider?: Profile; +} + +/** + * Result from find_potential_matches database function. + */ +export interface PotentialMatch { + matched_user_id: string; + matched_intent: ConnectionIntent; + display_name: string | null; +}