From 25d9eda15b0f50a9f1afff9ce275d0dcaaae9c42 Mon Sep 17 00:00:00 2001 From: Mnehmos Date: Sun, 18 Jan 2026 00:31:03 -0700 Subject: [PATCH 01/22] feat(profile): implement sponsor/sponsee connection system with intent & ownership Implements Issue #300 - Sponsor/Sponsee Connection System: Intent & Ownership: - Add ConnectionIntentSelector for declaring connection intent (not_looking, seeking_sponsor, open_to_sponsoring, open_to_both) - Add PersistentInviteCard with expiration timer and actions (Copy, Share, Regenerate, Revoke) - Invite codes are now visible, persistent objects in the UI First-Class Relationships: - Integrate SymmetricRevealSection into RelationshipCard - Show connection cards for active sponsor/sponsee relationships Trust Mechanism: - Add ExternalHandlesSection to Settings for private handle storage (Discord, Telegram, WhatsApp, Signal, Phone) - Per-connection reveal consent toggles - Symmetric reveal: contacts only shown when both parties consent Database Schema: - Add migration for connection_intent, external_handles, reveal consent - Add RLS policies for secure data access Also includes: - Web compatibility fix for bottom sheet TextInput components - Update test mocks for new icon usage Closes #300 Co-Authored-By: Claude Opus 4.5 --- CHANGELOG.md | 13 + __mocks__/lucide-react-native.js | 1 + .../app/profile.keyboard-avoidance.test.tsx | 14 + __tests__/app/profile.test.tsx | 74 +++- __tests__/app/profile.web.test.tsx | 19 + __tests__/app/settings.test.tsx | 4 + __tests__/components/SettingsSheet.test.tsx | 5 + .../SettingsContent.analytics.test.tsx | 4 + .../settings/SettingsContent.dev.test.tsx | 4 + .../SettingsContent.sections.test.tsx | 4 + app/(app)/(tabs)/profile/index.tsx | 261 +++++++++--- components/TaskCreationSheet.tsx | 8 +- .../profile/ConnectionIntentSelector.tsx | 213 ++++++++++ components/profile/PersistentInviteCard.tsx | 376 ++++++++++++++++++ components/profile/RelationshipCard.tsx | 34 +- components/profile/SymmetricRevealSection.tsx | 339 ++++++++++++++++ .../settings/ExternalHandlesSection.tsx | 318 +++++++++++++++ components/settings/SettingsContent.tsx | 42 ++ components/sheets/EnterInviteCodeSheet.tsx | 15 +- components/sheets/TaskCompletionSheet.tsx | 15 +- ...0000_sponsor_sponsee_connection_system.sql | 178 +++++++++ types/database.ts | 48 +++ 22 files changed, 1918 insertions(+), 71 deletions(-) create mode 100644 components/profile/ConnectionIntentSelector.tsx create mode 100644 components/profile/PersistentInviteCard.tsx create mode 100644 components/profile/SymmetricRevealSection.tsx create mode 100644 components/settings/ExternalHandlesSection.tsx create mode 100644 supabase/migrations/20260117000000_sponsor_sponsee_connection_system.sql diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cf36d21..45efcfef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,16 @@ 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 database migration with RLS policies for connection intent, external handles, and symmetric reveal ### Changed @@ -21,6 +31,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### 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 + ## [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/app/(app)/(tabs)/profile/index.tsx b/app/(app)/(tabs)/profile/index.tsx index 8aa774a6..ec698a96 100644 --- a/app/(app)/(tabs)/profile/index.tsx +++ b/app/(app)/(tabs)/profile/index.tsx @@ -4,8 +4,6 @@ import { Text, StyleSheet, TouchableOpacity, - Share, - Platform, ActivityIndicator, ScrollView, } from 'react-native'; @@ -17,19 +15,21 @@ 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 } 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'; /** * Render the authenticated user's profile, sobriety stats, and sponsor/sponsee management interface, @@ -57,6 +57,131 @@ export default function ProfileScreen() { const [sponseeTaskStats, setSponseeTaskStats] = useState<{ [key: string]: { total: number; completed: number }; }>({}); + const [activeInviteCode, setActiveInviteCode] = useState(null); + const [loadingInviteCode, setLoadingInviteCode] = useState(false); + + /** + * 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]); + + /** + * 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 + const code = Math.random().toString(36).substring(2, 10).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]); const fetchRelationships = useCallback(async () => { if (!profile) return; @@ -152,7 +277,8 @@ export default function ProfileScreen() { useEffect(() => { fetchRelationships(); - }, [profile, fetchRelationships]); + fetchActiveInviteCode(); + }, [profile, fetchRelationships, fetchActiveInviteCode]); // Use hook for current user's days sober const { @@ -163,52 +289,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 +542,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 +615,28 @@ export default function ProfileScreen() { onLogSlipUp={handleLogSlipUp} /> + {/* Connection Intent Selector */} + { + 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'); + } + }} + theme={theme} + /> + {loadingRelationships ? ( Your Sponsees @@ -516,16 +647,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 +724,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..af158a85 100644 --- a/components/TaskCreationSheet.tsx +++ b/components/TaskCreationSheet.tsx @@ -18,6 +18,7 @@ import { Pressable, ActivityIndicator, Platform, + TextInput, } from 'react-native'; import { BottomSheetScrollView, BottomSheetTextInput } from '@gorhom/bottom-sheet'; import { supabase } from '@/lib/supabase'; @@ -29,6 +30,9 @@ import { logger, LogCategory } from '@/lib/logger'; import { formatLocalDate, parseDateAsLocal } from '@/lib/date'; import GlassBottomSheet, { GlassBottomSheetRef } from '@/components/GlassBottomSheet'; +// Use regular TextInput on web to avoid BottomSheetTextInput compatibility issues +const InputComponent = Platform.OS === 'web' ? TextInput : BottomSheetTextInput; + // ============================================================================= // Types & Interfaces // ============================================================================= @@ -510,7 +514,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/PersistentInviteCard.tsx b/components/profile/PersistentInviteCard.tsx new file mode 100644 index 00000000..ad3c1923 --- /dev/null +++ b/components/profile/PersistentInviteCard.tsx @@ -0,0 +1,376 @@ +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'; + +// ============================================================================= +// 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 +// ============================================================================= + +/** + * Calculate time remaining until expiration. + */ +function getTimeRemaining(expiresAt: string): { + days: number; + hours: number; + minutes: number; + isExpired: boolean; + isExpiringSoon: boolean; +} { + 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 < 3; + + return { days, hours, minutes, isExpired: false, isExpiringSoon }; +} + +/** + * Format time remaining as a human-readable string. + */ +function formatTimeRemaining(time: ReturnType): string { + if (time.isExpired) return 'Expired'; + if (time.days > 0) return `${time.days}d ${time.hours}h remaining`; + if (time.hours > 0) return `${time.hours}h ${time.minutes}m remaining`; + return `${time.minutes}m 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]); + const [timeRemaining, setTimeRemaining] = useState(() => getTimeRemaining(inviteCode.expires_at)); + + // Update timer every minute + useEffect(() => { + const interval = setInterval(() => { + setTimeRemaining(getTimeRemaining(inviteCode.expires_at)); + }, 60000); + + return () => clearInterval(interval); + }, [inviteCode.expires_at]); + + const handleCopy = async () => { + if (Platform.OS === 'web') { + await navigator.clipboard.writeText(inviteCode.code); + showToast.success('Code copied to clipboard'); + } else { + // Use expo-clipboard for React Native + const { setStringAsync } = await import('expo-clipboard'); + await setStringAsync(inviteCode.code); + showToast.success('Code copied to clipboard'); + } + }; + + 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 + + ) : ( + + + + {formatTimeRemaining(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..6b4984ab 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 './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,28 @@ export default function RelationshipCard({ )} + + {/* Symmetric Reveal Section */} + {relationship && onConsentChange && ( + + )} + void; + /** Whether actions are disabled */ + disabled?: boolean; +} + +// ============================================================================= +// Helpers +// ============================================================================= + +/** + * Get the reveal state based on both parties' consent. + */ +function getRevealState(myConsent: boolean, theirConsent: boolean): RevealState { + if (myConsent && theirConsent) return 'mutual'; + if (myConsent && !theirConsent) return 'you_pending'; + if (!myConsent && theirConsent) return 'them_pending'; + return 'none'; +} + +/** + * Get icon for a platform. + */ +function getPlatformIcon(key: string, theme: ThemeColors): React.ReactNode { + switch (key) { + case 'discord': + return ; + case 'telegram': + return ; + case 'whatsapp': + case 'phone': + return ; + case 'signal': + return ; + default: + return ; + } +} + +/** + * Get label for a platform key. + */ +function getPlatformLabel(key: string): string { + const labels: Record = { + discord: 'Discord', + telegram: 'Telegram', + whatsapp: 'WhatsApp', + signal: 'Signal', + phone: 'Phone', + }; + return labels[key] || key; +} + +// ============================================================================= +// 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({ + myConsent, + theirConsent, + otherProfile, + myHandles, + relationshipType, + theme, + onConsentChange, + disabled = false, +}: SymmetricRevealSectionProps): React.JSX.Element { + const styles = useMemo(() => createStyles(theme), [theme]); + const revealState = getRevealState(myConsent, theirConsent); + + const otherName = otherProfile?.display_name || 'Unknown'; + const otherHandles = otherProfile?.external_handles || {}; + const hasOtherHandles = Object.keys(otherHandles).length > 0; + const hasMyHandles = myHandles && Object.keys(myHandles).length > 0; + + return ( + + {/* Consent Toggle */} + + + {myConsent ? ( + + ) : ( + + )} + + Share my contact info + + {myConsent + ? 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' && hasOtherHandles && ( + + {otherName}'s Contact Info + + {Object.entries(otherHandles).map(([key, value]) => ( + + {getPlatformIcon(key, theme)} + {getPlatformLabel(key)}: + + {value} + + + ))} + + + )} + + {/* No handles warning */} + {revealState === 'mutual' && !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, + }, + }); diff --git a/components/settings/ExternalHandlesSection.tsx b/components/settings/ExternalHandlesSection.tsx new file mode 100644 index 00000000..c8d0badd --- /dev/null +++ b/components/settings/ExternalHandlesSection.tsx @@ -0,0 +1,318 @@ +import React, { useMemo, useState } from 'react'; +import { View, Text, StyleSheet, TextInput, TouchableOpacity } 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 the section is disabled */ + disabled?: 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, + disabled = 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) => { + setExpandedPlatforms((prev) => { + const next = new Set(prev); + if (next.has(key)) { + next.delete(key); + // Clear the value when collapsing + handleChange(key, ''); + } else { + next.add(key); + } + return next; + }); + }; + + const visiblePlatforms = platforms.filter((p) => expandedPlatforms.has(p.key)); + const hiddenPlatforms = platforms.filter((p) => !expandedPlatforms.has(p.key)); + + return ( + + + External Contacts + + + 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} + editable={!disabled} + autoCapitalize="none" + autoCorrect={false} + testID={`handle-input-${platform.key}`} + /> + + togglePlatform(platform.key)} + style={styles.removeButton} + accessibilityRole="button" + accessibilityLabel={`Remove ${platform.label}`} + disabled={disabled} + > + + + + ))} + + + {/* Add platform buttons */} + {hiddenPlatforms.length > 0 && ( + + Add contact method: + + {hiddenPlatforms.map((platform) => ( + togglePlatform(platform.key)} + disabled={disabled} + 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, + }, + 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..083f0bd6 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 './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,7 @@ 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); const [showSobrietyDatePicker, setShowSobrietyDatePicker] = useState(false); const [selectedSobrietyDate, setSelectedSobrietyDate] = useState(new Date()); const buildInfo = getBuildInfo(); @@ -766,6 +769,37 @@ 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. + */ + const handleExternalHandlesChange = useCallback( + async (handles: ExternalHandles) => { + if (!profile?.id || isSavingHandles) return; + + setIsSavingHandles(true); + try { + const { error } = await supabase + .from('profiles') + .update({ external_handles: handles }) + .eq('id', profile.id); + + if (error) throw error; + + await refreshProfile(); + // 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); + } + }, + [profile?.id, isSavingHandles, refreshProfile] + ); + /** * Opens the sobriety date picker with the current sobriety date pre-selected. */ @@ -1077,6 +1111,14 @@ export function SettingsContent({ onDismiss }: SettingsContentProps) { + {/* External Contacts Section */} + + {/* About Section */} About diff --git a/components/sheets/EnterInviteCodeSheet.tsx b/components/sheets/EnterInviteCodeSheet.tsx index e686f512..66067043 100644 --- a/components/sheets/EnterInviteCodeSheet.tsx +++ b/components/sheets/EnterInviteCodeSheet.tsx @@ -2,12 +2,23 @@ // Imports // ============================================================================= import React, { useState, useCallback, forwardRef, useImperativeHandle, useRef } from 'react'; -import { View, Text, StyleSheet, TouchableOpacity, ActivityIndicator } from 'react-native'; +import { + View, + Text, + StyleSheet, + TouchableOpacity, + ActivityIndicator, + TextInput, + Platform, +} from 'react-native'; import { BottomSheetScrollView, BottomSheetTextInput } from '@gorhom/bottom-sheet'; import { ThemeColors } from '@/contexts/ThemeContext'; import { X, QrCode } from 'lucide-react-native'; import GlassBottomSheet, { GlassBottomSheetRef } from '@/components/GlassBottomSheet'; +// Use regular TextInput on web to avoid BottomSheetTextInput compatibility issues +const InputComponent = Platform.OS === 'web' ? TextInput : BottomSheetTextInput; + // ============================================================================= // Types & Interfaces // ============================================================================= @@ -228,7 +239,7 @@ const EnterInviteCodeSheet = forwardRef Invite Code - Share your reflections, insights, or any challenges you faced with this task. - 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 +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) + WITH CHECK (used_by = auth.uid()); + +-- ============================================================================= +-- Profile RLS Policies for Connection Flow +-- ============================================================================= + +-- Allow users to view sponsor profiles when they have a valid invite code +DROP POLICY IF EXISTS "Users can view sponsor profile via invite code" ON public.profiles; +CREATE POLICY "Users can view sponsor profile via invite code" + ON public.profiles FOR SELECT TO authenticated + USING ( + EXISTS ( + SELECT 1 FROM public.invite_codes + WHERE sponsor_id = profiles.id + AND expires_at > now() + AND used_by IS NULL + AND revoked_at IS NULL + ) + ); + +-- 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()); diff --git a/types/database.ts b/types/database.ts index 12d221b2..d50836a4 100644 --- a/types/database.ts +++ b/types/database.ts @@ -10,6 +10,28 @@ export type NotificationType = | 'connection_request' | 'task_completed'; +/** + * 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 +111,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 +138,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 +158,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; } From 08aad2e27b6ba563a18234d1c28b9720b29b76ea Mon Sep 17 00:00:00 2001 From: Mnehmos Date: Sun, 18 Jan 2026 01:01:17 -0700 Subject: [PATCH 02/22] feat(profile): add opt-in matching system for sponsor/sponsee connections Add FindSupportSection component for discovering matches based on complementary connection intents. Implement bilateral acceptance pattern where both parties must accept before a relationship is established. Database changes: - Add connection_matches table with seeker/provider roles - Add find_potential_matches RPC for discovering compatible users - Add accept_match and reject_match RPCs for match resolution - Handle existing inactive relationships in accept flow UI changes: - Add FindSupportSection to profile when user has an active intent - Fix consent mapping in RelationshipCard for symmetric reveal Co-Authored-By: Claude Opus 4.5 --- CHANGELOG.md | 5 +- app/(app)/(tabs)/profile/index.tsx | 58 +- components/profile/FindSupportSection.tsx | 557 ++++++++++++++++++ components/profile/RelationshipCard.tsx | 10 +- ...0000_sponsor_sponsee_connection_system.sql | 306 ++++++++++ types/database.ts | 50 ++ 6 files changed, 979 insertions(+), 7 deletions(-) create mode 100644 components/profile/FindSupportSection.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index 45efcfef..44173832 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 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 database migration with RLS policies for connection intent, external handles, and symmetric reveal +- 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 diff --git a/app/(app)/(tabs)/profile/index.tsx b/app/(app)/(tabs)/profile/index.tsx index ec698a96..73efd40c 100644 --- a/app/(app)/(tabs)/profile/index.tsx +++ b/app/(app)/(tabs)/profile/index.tsx @@ -15,7 +15,12 @@ import { useTheme } from '@/contexts/ThemeContext'; import { supabase } from '@/lib/supabase'; import { useDaysSober } from '@/hooks/useDaysSober'; import { Settings } from 'lucide-react-native'; -import type { SponsorSponseeRelationship, InviteCode, ConnectionIntent } 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, { @@ -30,6 +35,7 @@ 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, @@ -59,6 +65,7 @@ export default function ProfileScreen() { }>({}); const [activeInviteCode, setActiveInviteCode] = useState(null); const [loadingInviteCode, setLoadingInviteCode] = useState(false); + const [pendingMatches, setPendingMatches] = useState([]); /** * Fetches the user's active invite code (not used, not revoked, not expired). @@ -96,6 +103,36 @@ export default function ProfileScreen() { } }, [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. */ @@ -278,7 +315,8 @@ export default function ProfileScreen() { useEffect(() => { fetchRelationships(); fetchActiveInviteCode(); - }, [profile, fetchRelationships, fetchActiveInviteCode]); + fetchPendingMatches(); + }, [profile, fetchRelationships, fetchActiveInviteCode, fetchPendingMatches]); // Use hook for current user's days sober const { @@ -637,6 +675,22 @@ export default function ProfileScreen() { theme={theme} /> + {/* Find Support Section (Opt-in Matching) */} + {profile && ( + + { + fetchPendingMatches(); + fetchRelationships(); + }} + /> + + )} + {loadingRelationships ? ( Your Sponsees diff --git a/components/profile/FindSupportSection.tsx b/components/profile/FindSupportSection.tsx new file mode 100644 index 00000000..f4823e3a --- /dev/null +++ b/components/profile/FindSupportSection.tsx @@ -0,0 +1,557 @@ +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 } from '@/lib/logger'; + +// ============================================================================= +// 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 */ + disabled?: boolean; +} + +// ============================================================================= +// Helper Functions +// ============================================================================= + +/** + * Get time remaining until match expires. + */ +function getTimeRemaining(expiresAt: string): { + days: number; + hours: number; + isExpired: boolean; + isExpiringSoon: boolean; +} { + const now = new Date().getTime(); + const expiry = new Date(expiresAt).getTime(); + const diff = expiry - now; + + if (diff <= 0) { + return { days: 0, hours: 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 isExpiringSoon = days < 2; + + return { days, hours, isExpired: false, isExpiringSoon }; +} + +/** + * 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, + disabled = 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 (disabled || 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)); + 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 + const isSeeking = intent === 'seeking_sponsor' || intent === 'open_to_both'; + + for (const match of potentialMatches) { + const matchData = isSeeking + ? { seeker_id: userId, provider_id: match.matched_user_id } + : { seeker_id: match.matched_user_id, provider_id: userId }; + + const { error: insertError } = await supabase.from('connection_matches').insert(matchData); + + if (insertError && !insertError.message.includes('unique')) { + logger.warn('Failed to create match', { errorMessage: insertError.message }); + } + } + + showToast.success(`Found ${potentialMatches.length} potential match(es)!`); + onMatchUpdate(); + } catch (error) { + logger.error('Error finding matches', error as Error); + showToast.error('Something went wrong'); + } finally { + setIsSearching(false); + } + }, [userId, intent, disabled, isSearching, onMatchUpdate, shouldShow]); + + const acceptMatch = useCallback( + async (matchId: string) => { + if (disabled || 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)); + 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); + showToast.error('Something went wrong'); + } finally { + setIsProcessing(null); + } + }, + [disabled, isProcessing, onMatchUpdate] + ); + + const rejectMatch = useCallback( + async (matchId: string) => { + if (disabled || 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)); + showToast.error('Failed to decline match'); + return; + } + + showToast.info('Match declined'); + onMatchUpdate(); + } catch (error) { + logger.error('Error rejecting match', error as Error); + showToast.error('Something went wrong'); + } finally { + setIsProcessing(null); + } + }, + [disabled, 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}h left`} + + + + + + rejectMatch(match.id)} + disabled={disabled || isProcessingThis} + accessibilityRole="button" + accessibilityLabel="Decline match" + > + {isProcessingThis ? ( + + ) : ( + + )} + + acceptMatch(match.id)} + disabled={disabled || 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}h`} + + + ); + })} + + )} + + {/* 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/RelationshipCard.tsx b/components/profile/RelationshipCard.tsx index 6b4984ab..04dab6ee 100644 --- a/components/profile/RelationshipCard.tsx +++ b/components/profile/RelationshipCard.tsx @@ -154,14 +154,16 @@ export default function RelationshipCard({ {relationship && onConsentChange && ( 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; diff --git a/types/database.ts b/types/database.ts index d50836a4..89754a6e 100644 --- a/types/database.ts +++ b/types/database.ts @@ -10,6 +10,11 @@ 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. @@ -417,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; +} From 9da4f6fd591ef0a491ec655d13020e3076882b9c Mon Sep 17 00:00:00 2001 From: Mnehmos Date: Sun, 18 Jan 2026 01:08:15 -0700 Subject: [PATCH 03/22] refactor(lib): extract shared utilities for DRY compliance - Extract getTimeRemaining and formatTimeRemaining to lib/time-utils.ts - Extract getPlatformIcon and getPlatformLabel to lib/platform-icons.tsx - Update FindSupportSection, PersistentInviteCard, SymmetricRevealSection to use shared utilities - Add comprehensive tests for new utility files (25 passing tests) Co-Authored-By: Claude Opus 4.5 --- CHANGELOG.md | 2 + __tests__/lib/platform-icons.test.tsx | 83 +++++++++ __tests__/lib/time-utils.test.ts | 162 ++++++++++++++++++ components/profile/FindSupportSection.tsx | 25 +-- components/profile/PersistentInviteCard.tsx | 45 ++--- components/profile/SymmetricRevealSection.tsx | 36 +--- lib/platform-icons.tsx | 65 +++++++ lib/time-utils.ts | 84 +++++++++ 8 files changed, 410 insertions(+), 92 deletions(-) create mode 100644 __tests__/lib/platform-icons.test.tsx create mode 100644 __tests__/lib/time-utils.test.ts create mode 100644 lib/platform-icons.tsx create mode 100644 lib/time-utils.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 44173832..1c397c08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 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) ### Removed diff --git a/__tests__/lib/platform-icons.test.tsx b/__tests__/lib/platform-icons.test.tsx new file mode 100644 index 00000000..6afd0d3e --- /dev/null +++ b/__tests__/lib/platform-icons.test.tsx @@ -0,0 +1,83 @@ +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); + expect(icon).toBeTruthy(); + }); + }); +}); diff --git a/__tests__/lib/time-utils.test.ts b/__tests__/lib/time-utils.test.ts new file mode 100644 index 00000000..51a62cd4 --- /dev/null +++ b/__tests__/lib/time-utils.test.ts @@ -0,0 +1,162 @@ +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', () => { + const now = new Date().toISOString(); + const result = getTimeRemaining(now); + + // Should be expired since we're at or past the time + expect(result.isExpired).toBe(true); + }); + }); + + 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/components/profile/FindSupportSection.tsx b/components/profile/FindSupportSection.tsx index f4823e3a..79788f6e 100644 --- a/components/profile/FindSupportSection.tsx +++ b/components/profile/FindSupportSection.tsx @@ -6,6 +6,7 @@ import type { ConnectionMatch, ConnectionIntent, PotentialMatch } from '@/types/ import { supabase } from '@/lib/supabase'; import { showToast } from '@/lib/toast'; import { logger } from '@/lib/logger'; +import { getTimeRemaining } from '@/lib/time-utils'; // ============================================================================= // Types & Interfaces @@ -33,30 +34,6 @@ interface FindSupportSectionProps { // Helper Functions // ============================================================================= -/** - * Get time remaining until match expires. - */ -function getTimeRemaining(expiresAt: string): { - days: number; - hours: number; - isExpired: boolean; - isExpiringSoon: boolean; -} { - const now = new Date().getTime(); - const expiry = new Date(expiresAt).getTime(); - const diff = expiry - now; - - if (diff <= 0) { - return { days: 0, hours: 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 isExpiringSoon = days < 2; - - return { days, hours, isExpired: false, isExpiringSoon }; -} - /** * Format the user's role in this match (seeker or provider). */ diff --git a/components/profile/PersistentInviteCard.tsx b/components/profile/PersistentInviteCard.tsx index ad3c1923..038592da 100644 --- a/components/profile/PersistentInviteCard.tsx +++ b/components/profile/PersistentInviteCard.tsx @@ -4,6 +4,7 @@ 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 @@ -30,39 +31,12 @@ interface PersistentInviteCardProps { // ============================================================================= /** - * Calculate time remaining until expiration. + * Format time remaining with "remaining" suffix for invite cards. */ -function getTimeRemaining(expiresAt: string): { - days: number; - hours: number; - minutes: number; - isExpired: boolean; - isExpiringSoon: boolean; -} { - 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 < 3; - - return { days, hours, minutes, isExpired: false, isExpiringSoon }; -} - -/** - * Format time remaining as a human-readable string. - */ -function formatTimeRemaining(time: ReturnType): string { +function formatInviteTimeRemaining(time: TimeRemaining): string { if (time.isExpired) return 'Expired'; - if (time.days > 0) return `${time.days}d ${time.hours}h remaining`; - if (time.hours > 0) return `${time.hours}h ${time.minutes}m remaining`; - return `${time.minutes}m remaining`; + const formatted = formatTimeRemaining(time, 'short'); + return `${formatted} remaining`; } // ============================================================================= @@ -91,12 +65,15 @@ export default function PersistentInviteCard({ disabled = false, }: PersistentInviteCardProps): React.JSX.Element { const styles = useMemo(() => createStyles(theme), [theme]); - const [timeRemaining, setTimeRemaining] = useState(() => getTimeRemaining(inviteCode.expires_at)); + // Use 3 days for "expiring soon" threshold on invite codes + const [timeRemaining, setTimeRemaining] = useState(() => + getTimeRemaining(inviteCode.expires_at, 3) + ); // Update timer every minute useEffect(() => { const interval = setInterval(() => { - setTimeRemaining(getTimeRemaining(inviteCode.expires_at)); + setTimeRemaining(getTimeRemaining(inviteCode.expires_at, 3)); }, 60000); return () => clearInterval(interval); @@ -187,7 +164,7 @@ export default function PersistentInviteCard({ timeRemaining.isExpiringSoon && styles.statusTextExpiringSoon, ]} > - {formatTimeRemaining(timeRemaining)} + {formatInviteTimeRemaining(timeRemaining)} )} diff --git a/components/profile/SymmetricRevealSection.tsx b/components/profile/SymmetricRevealSection.tsx index cb5b91cd..43a1bc09 100644 --- a/components/profile/SymmetricRevealSection.tsx +++ b/components/profile/SymmetricRevealSection.tsx @@ -1,8 +1,9 @@ import React, { useMemo } from 'react'; import { View, Text, StyleSheet, Switch } from 'react-native'; -import { Eye, EyeOff, MessageCircle, Phone, Send, Shield, Check, Clock } from 'lucide-react-native'; +import { Eye, EyeOff, Check, Clock } from 'lucide-react-native'; import type { ThemeColors } from '@/contexts/ThemeContext'; import type { ExternalHandles, Profile } from '@/types/database'; +import { getPlatformIcon, getPlatformLabel } from '@/lib/platform-icons'; // ============================================================================= // Types & Interfaces @@ -49,39 +50,6 @@ function getRevealState(myConsent: boolean, theirConsent: boolean): RevealState return 'none'; } -/** - * Get icon for a platform. - */ -function getPlatformIcon(key: string, theme: ThemeColors): React.ReactNode { - switch (key) { - case 'discord': - return ; - case 'telegram': - return ; - case 'whatsapp': - case 'phone': - return ; - case 'signal': - return ; - default: - return ; - } -} - -/** - * Get label for a platform key. - */ -function getPlatformLabel(key: string): string { - const labels: Record = { - discord: 'Discord', - telegram: 'Telegram', - whatsapp: 'WhatsApp', - signal: 'Signal', - phone: 'Phone', - }; - return labels[key] || key; -} - // ============================================================================= // Component // ============================================================================= diff --git a/lib/platform-icons.tsx b/lib/platform-icons.tsx new file mode 100644 index 00000000..b7d0a3dd --- /dev/null +++ b/lib/platform-icons.tsx @@ -0,0 +1,65 @@ +/** + * Platform icon mappings for external contact handles. + * Centralizes icon rendering for Discord, Telegram, WhatsApp, Signal, Phone, etc. + */ + +import React from 'react'; +import { MessageCircle, Send, Phone, Shield } from 'lucide-react-native'; + +/** + * Supported platform keys for external handles. + */ +export type PlatformKey = 'discord' | 'telegram' | 'whatsapp' | 'signal' | 'phone'; + +/** + * Human-readable labels for platform keys. + */ +export const platformLabels: Record = { + discord: 'Discord', + telegram: 'Telegram', + whatsapp: 'WhatsApp', + signal: 'Signal', + phone: 'Phone', +}; + +/** + * Get human-readable label for a platform key. + */ +export function getPlatformLabel(key: string): string { + return platformLabels[key] || key; +} + +/** + * Theme colors interface for icon rendering. + */ +interface IconTheme { + primary: string; + info: string; + success: string; + warning: string; + textSecondary: string; +} + +/** + * 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/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'; +} From cfe3a4179c8882c4dcd76764031b007c4aeb7607 Mon Sep 17 00:00:00 2001 From: Mnehmos Date: Sun, 18 Jan 2026 08:40:59 -0700 Subject: [PATCH 04/22] fix(supabase): add revoked_at check to invite code UPDATE policy Prevents users from claiming invite codes that have been revoked by the sponsor. Both USING and WITH CHECK clauses now verify revoked_at IS NULL. Co-Authored-By: Claude Opus 4.5 --- .../20260117000000_sponsor_sponsee_connection_system.sql | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/supabase/migrations/20260117000000_sponsor_sponsee_connection_system.sql b/supabase/migrations/20260117000000_sponsor_sponsee_connection_system.sql index a9409327..42839c39 100644 --- a/supabase/migrations/20260117000000_sponsor_sponsee_connection_system.sql +++ b/supabase/migrations/20260117000000_sponsor_sponsee_connection_system.sql @@ -103,12 +103,12 @@ 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 +-- 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) - WITH CHECK (used_by = auth.uid()); + 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 From c4eef81f16efb16b52f01767a52b769596953e7f Mon Sep 17 00:00:00 2001 From: Mnehmos Date: Sun, 18 Jan 2026 08:41:31 -0700 Subject: [PATCH 05/22] fix(supabase): replace broad RLS policy with SECURITY DEFINER RPC The previous RLS policy allowed any authenticated user to view profiles of sponsors who had active invite codes. This was overly permissive. Now replaced with get_sponsor_profile_by_invite_code() function that requires the actual invite code to look up sponsor info, and returns only non-sensitive fields (id, display_name, sobriety_date, avatar_url). Co-Authored-By: Claude Opus 4.5 --- ...0000_sponsor_sponsee_connection_system.sql | 47 ++++++++++++++----- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/supabase/migrations/20260117000000_sponsor_sponsee_connection_system.sql b/supabase/migrations/20260117000000_sponsor_sponsee_connection_system.sql index 42839c39..ed95f02c 100644 --- a/supabase/migrations/20260117000000_sponsor_sponsee_connection_system.sql +++ b/supabase/migrations/20260117000000_sponsor_sponsee_connection_system.sql @@ -114,19 +114,42 @@ CREATE POLICY "Users can update invite codes when using them" -- Profile RLS Policies for Connection Flow -- ============================================================================= --- Allow users to view sponsor profiles when they have a valid invite code +-- 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; -CREATE POLICY "Users can view sponsor profile via invite code" - ON public.profiles FOR SELECT TO authenticated - USING ( - EXISTS ( - SELECT 1 FROM public.invite_codes - WHERE sponsor_id = profiles.id - AND expires_at > now() - AND used_by IS NULL - AND revoked_at IS NULL - ) - ); + +-- ============================================================================= +-- 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; From 4a2257607e1a1995825d7a28a88459d8857b3ba1 Mon Sep 17 00:00:00 2001 From: Mnehmos Date: Sun, 18 Jan 2026 08:42:07 -0700 Subject: [PATCH 06/22] fix(supabase): convert UNIQUE constraint to partial index for rematching The unconditional UNIQUE(seeker_id, provider_id) constraint prevented users from ever rematching after a rejection or expiry. Now replaced with a partial unique index that only applies when status = 'pending'. This allows the app to create new match proposals between users who previously had a rejected/expired match, while still preventing duplicate pending matches. Co-Authored-By: Claude Opus 4.5 --- ...117000000_sponsor_sponsee_connection_system.sql | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/supabase/migrations/20260117000000_sponsor_sponsee_connection_system.sql b/supabase/migrations/20260117000000_sponsor_sponsee_connection_system.sql index ed95f02c..f1dcedda 100644 --- a/supabase/migrations/20260117000000_sponsor_sponsee_connection_system.sql +++ b/supabase/migrations/20260117000000_sponsor_sponsee_connection_system.sql @@ -236,11 +236,19 @@ CREATE TABLE IF NOT EXISTS public.connection_matches ( -- Timestamps created_at timestamptz DEFAULT now() NOT NULL, expires_at timestamptz DEFAULT (now() + interval '7 days') NOT NULL, - resolved_at timestamptz DEFAULT NULL, - -- Prevent duplicate active matches between same users - CONSTRAINT unique_active_match UNIQUE(seeker_id, provider_id) + 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.'; From cd033662e7f7efad516693b10aeac340e0b6edce Mon Sep 17 00:00:00 2001 From: Mnehmos Date: Sun, 18 Jan 2026 08:45:38 -0700 Subject: [PATCH 07/22] fix(privacy): fetch external handles only with mutual consent Previously, external_handles were included in relationship queries even before consent was verified, exposing contact info to the client prematurely. Changes: - Profile queries now exclude external_handles - Added get_handles_with_consent() RPC that verifies mutual consent - SymmetricRevealSection fetches handles via RPC only when mutual - Filter empty handle values before rendering This ensures external_handles are never sent to the client unless both parties in the relationship have opted in to reveal. Co-Authored-By: Claude Opus 4.5 --- app/(app)/(tabs)/profile/index.tsx | 6 +- components/profile/RelationshipCard.tsx | 1 + components/profile/SymmetricRevealSection.tsx | 78 +++++++++++++++++-- ...0000_sponsor_sponsee_connection_system.sql | 56 +++++++++++++ 4 files changed, 132 insertions(+), 9 deletions(-) diff --git a/app/(app)/(tabs)/profile/index.tsx b/app/(app)/(tabs)/profile/index.tsx index 73efd40c..b7ba6b85 100644 --- a/app/(app)/(tabs)/profile/index.tsx +++ b/app/(app)/(tabs)/profile/index.tsx @@ -225,9 +225,10 @@ export default function ProfileScreen() { 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'); @@ -242,9 +243,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'); diff --git a/components/profile/RelationshipCard.tsx b/components/profile/RelationshipCard.tsx index 04dab6ee..2f8131b8 100644 --- a/components/profile/RelationshipCard.tsx +++ b/components/profile/RelationshipCard.tsx @@ -153,6 +153,7 @@ export default function RelationshipCard({ {/* Symmetric Reveal Section */} {relationship && onConsentChange && ( createStyles(theme), [theme]); const revealState = getRevealState(myConsent, theirConsent); + // State for consent-checked handles fetched via RPC + const [otherHandles, setOtherHandles] = useState({}); + const [isLoadingHandles, setIsLoadingHandles] = useState(false); + const otherName = otherProfile?.display_name || 'Unknown'; - const otherHandles = otherProfile?.external_handles || {}; - const hasOtherHandles = Object.keys(otherHandles).length > 0; const hasMyHandles = myHandles && Object.keys(myHandles).length > 0; + // Fetch handles only when there's mutual consent (via secure RPC) + useEffect(() => { + if (revealState !== 'mutual') { + setOtherHandles({}); + return; + } + + const fetchHandles = async () => { + setIsLoadingHandles(true); + try { + const { data, error } = await supabase.rpc('get_handles_with_consent', { + relationship_id: relationshipId, + }); + + if (error) { + logger.error('Failed to fetch handles with consent', new Error(error.message), { + category: LogCategory.DATABASE, + }); + return; + } + + setOtherHandles((data as ExternalHandles) || {}); + } catch (err) { + logger.error('Unexpected error fetching handles', err as Error, { + category: LogCategory.DATABASE, + }); + } finally { + setIsLoadingHandles(false); + } + }; + + fetchHandles(); + }, [revealState, relationshipId]); + + // Filter out empty handle values + const filteredHandles = Object.entries(otherHandles).filter(([_, v]) => Boolean(v)); + const hasOtherHandles = filteredHandles.length > 0; + return ( {/* Consent Toggle */} @@ -160,11 +205,17 @@ export default function SymmetricRevealSection({ {/* Revealed Handles */} - {revealState === 'mutual' && hasOtherHandles && ( + {revealState === 'mutual' && isLoadingHandles && ( + + + Loading contact info... + + )} + {revealState === 'mutual' && !isLoadingHandles && hasOtherHandles && ( {otherName}'s Contact Info - {Object.entries(otherHandles).map(([key, value]) => ( + {filteredHandles.map(([key, value]) => ( {getPlatformIcon(key, theme)} {getPlatformLabel(key)}: @@ -178,7 +229,7 @@ export default function SymmetricRevealSection({ )} {/* No handles warning */} - {revealState === 'mutual' && !hasOtherHandles && ( + {revealState === 'mutual' && !isLoadingHandles && !hasOtherHandles && ( {otherName} hasn't added any contact methods yet. @@ -304,4 +355,17 @@ const createStyles = (theme: ThemeColors) => 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/supabase/migrations/20260117000000_sponsor_sponsee_connection_system.sql b/supabase/migrations/20260117000000_sponsor_sponsee_connection_system.sql index f1dcedda..e4c30586 100644 --- a/supabase/migrations/20260117000000_sponsor_sponsee_connection_system.sql +++ b/supabase/migrations/20260117000000_sponsor_sponsee_connection_system.sql @@ -513,3 +513,59 @@ $$ 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; From ef4dea6e9bc4637a9668cea1640cb8b26a23caac Mon Sep 17 00:00:00 2001 From: Mnehmos Date: Sun, 18 Jan 2026 08:47:39 -0700 Subject: [PATCH 08/22] fix(security): prevent RLS violation in provider match creation Providers (users with open_to_sponsoring intent) were attempting to create matches with seeker_id set to other users, which violated the RLS policy requiring seeker_id = auth.uid(). Now only seekers can directly create matches. Providers are shown to seekers who then initiate the match request. Also added LogCategory.DATABASE metadata to all logger calls in FindSupportSection for better log categorization. Co-Authored-By: Claude Opus 4.5 --- components/profile/FindSupportSection.tsx | 45 +++++++++++++++++------ 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/components/profile/FindSupportSection.tsx b/components/profile/FindSupportSection.tsx index 79788f6e..98d19f9f 100644 --- a/components/profile/FindSupportSection.tsx +++ b/components/profile/FindSupportSection.tsx @@ -5,7 +5,7 @@ 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 } from '@/lib/logger'; +import { logger, LogCategory } from '@/lib/logger'; import { getTimeRemaining } from '@/lib/time-utils'; // ============================================================================= @@ -105,7 +105,9 @@ export default function FindSupportSection({ }); if (error) { - logger.error('Failed to find matches', new Error(error.message)); + logger.error('Failed to find matches', new Error(error.message), { + category: LogCategory.DATABASE, + }); showToast.error('Failed to find matches'); return; } @@ -118,24 +120,37 @@ export default function FindSupportSection({ } // 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 = isSeeking - ? { seeker_id: userId, provider_id: match.matched_user_id } - : { seeker_id: match.matched_user_id, provider_id: userId }; + 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', { errorMessage: insertError.message }); + 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); + logger.error('Error finding matches', error as Error, { + category: LogCategory.DATABASE, + }); showToast.error('Something went wrong'); } finally { setIsSearching(false); @@ -153,7 +168,9 @@ export default function FindSupportSection({ }); if (error) { - logger.error('Failed to accept match', new Error(error.message)); + logger.error('Failed to accept match', new Error(error.message), { + category: LogCategory.DATABASE, + }); showToast.error('Failed to accept match'); return; } @@ -166,7 +183,9 @@ export default function FindSupportSection({ } onMatchUpdate(); } catch (error) { - logger.error('Error accepting match', error as Error); + logger.error('Error accepting match', error as Error, { + category: LogCategory.DATABASE, + }); showToast.error('Something went wrong'); } finally { setIsProcessing(null); @@ -186,7 +205,9 @@ export default function FindSupportSection({ }); if (error) { - logger.error('Failed to reject match', new Error(error.message)); + logger.error('Failed to reject match', new Error(error.message), { + category: LogCategory.DATABASE, + }); showToast.error('Failed to decline match'); return; } @@ -194,7 +215,9 @@ export default function FindSupportSection({ showToast.info('Match declined'); onMatchUpdate(); } catch (error) { - logger.error('Error rejecting match', error as Error); + logger.error('Error rejecting match', error as Error, { + category: LogCategory.DATABASE, + }); showToast.error('Something went wrong'); } finally { setIsProcessing(null); From f284125aac3fb96ac04f1fc89b6fff5dc10e58a0 Mon Sep 17 00:00:00 2001 From: Mnehmos Date: Sun, 18 Jan 2026 08:51:11 -0700 Subject: [PATCH 09/22] perf(settings): debounce external handles DB writes Previously, handleExternalHandlesChange triggered a database write on every keystroke, causing API spam and poor UX. Now: - Draft state provides immediate UI updates while typing - Database writes are debounced (500ms delay) - Inputs remain enabled during save (no blocking) - Small "Saving..." indicator shows when persisting This improves typing responsiveness and reduces Supabase load. Co-Authored-By: Claude Opus 4.5 --- .../settings/ExternalHandlesSection.tsx | 47 +++++++++++--- components/settings/SettingsContent.tsx | 63 ++++++++++++------- 2 files changed, 77 insertions(+), 33 deletions(-) diff --git a/components/settings/ExternalHandlesSection.tsx b/components/settings/ExternalHandlesSection.tsx index c8d0badd..f18f64bf 100644 --- a/components/settings/ExternalHandlesSection.tsx +++ b/components/settings/ExternalHandlesSection.tsx @@ -1,5 +1,12 @@ import React, { useMemo, useState } from 'react'; -import { View, Text, StyleSheet, TextInput, TouchableOpacity } from 'react-native'; +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'; @@ -33,8 +40,8 @@ interface ExternalHandlesSectionProps { onChange: (handles: ExternalHandles) => void; /** Theme object from ThemeContext */ theme: ThemeColors; - /** Whether the section is disabled */ - disabled?: boolean; + /** Whether changes are being saved (shows indicator, doesn't disable input) */ + isSaving?: boolean; } // ============================================================================= @@ -58,7 +65,7 @@ export default function ExternalHandlesSection({ value = {}, onChange, theme, - disabled = false, + isSaving = false, }: ExternalHandlesSectionProps): React.JSX.Element { const styles = useMemo(() => createStyles(theme), [theme]); const [expandedPlatforms, setExpandedPlatforms] = useState>(() => { @@ -137,9 +144,17 @@ export default function ExternalHandlesSection({ External Contacts - - - Private + + {isSaving && ( + + + Saving... + + )} + + + Private + @@ -159,7 +174,6 @@ export default function ExternalHandlesSection({ onChangeText={(text) => handleChange(platform.key, text)} placeholder={platform.placeholder} placeholderTextColor={theme.textSecondary} - editable={!disabled} autoCapitalize="none" autoCorrect={false} testID={`handle-input-${platform.key}`} @@ -170,7 +184,6 @@ export default function ExternalHandlesSection({ style={styles.removeButton} accessibilityRole="button" accessibilityLabel={`Remove ${platform.label}`} - disabled={disabled} > @@ -188,7 +201,6 @@ export default function ExternalHandlesSection({ key={platform.key} style={styles.addButton} onPress={() => togglePlatform(platform.key)} - disabled={disabled} accessibilityRole="button" accessibilityLabel={`Add ${platform.label}`} > @@ -219,6 +231,21 @@ const createStyles = (theme: ThemeColors) => 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, diff --git a/components/settings/SettingsContent.tsx b/components/settings/SettingsContent.tsx index 083f0bd6..3bb2a609 100644 --- a/components/settings/SettingsContent.tsx +++ b/components/settings/SettingsContent.tsx @@ -493,6 +493,9 @@ export function SettingsContent({ onDismiss }: SettingsContentProps) { 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(); @@ -772,32 +775,46 @@ export function SettingsContent({ onDismiss }: SettingsContentProps) { /** * 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( - async (handles: ExternalHandles) => { - if (!profile?.id || isSavingHandles) return; + (handles: ExternalHandles) => { + if (!profile?.id) return; - setIsSavingHandles(true); - try { - const { error } = await supabase - .from('profiles') - .update({ external_handles: handles }) - .eq('id', profile.id); - - if (error) throw error; + // Update draft state immediately for responsive UI + setDraftHandles(handles); - await refreshProfile(); - // 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); + // Clear existing debounce timeout + if (handlesDebounceRef.current) { + clearTimeout(handlesDebounceRef.current); } - }, - [profile?.id, isSavingHandles, refreshProfile] + + // 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] ); /** @@ -1113,10 +1130,10 @@ export function SettingsContent({ onDismiss }: SettingsContentProps) { {/* External Contacts Section */} {/* About Section */} From 830ff0696911084ffb88215e453aac7a1e11fbf5 Mon Sep 17 00:00:00 2001 From: Mnehmos Date: Sun, 18 Jan 2026 08:51:51 -0700 Subject: [PATCH 10/22] fix(profile): fix timer stale values and clipboard error handling PersistentInviteCard improvements: - Timer now updates immediately when expires_at changes (not just on first render), fixing stale countdown display - handleCopy now wrapped in try/catch with error toast for clipboard failures Co-Authored-By: Claude Opus 4.5 --- components/profile/PersistentInviteCard.tsx | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/components/profile/PersistentInviteCard.tsx b/components/profile/PersistentInviteCard.tsx index 038592da..40cc6135 100644 --- a/components/profile/PersistentInviteCard.tsx +++ b/components/profile/PersistentInviteCard.tsx @@ -72,6 +72,9 @@ export default function PersistentInviteCard({ // 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); @@ -80,14 +83,17 @@ export default function PersistentInviteCard({ }, [inviteCode.expires_at]); const handleCopy = async () => { - if (Platform.OS === 'web') { - await navigator.clipboard.writeText(inviteCode.code); - showToast.success('Code copied to clipboard'); - } else { - // Use expo-clipboard for React Native - const { setStringAsync } = await import('expo-clipboard'); - await setStringAsync(inviteCode.code); + 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'); } }; From 35bf3ba503cd2f3f01451e04c9f111216b6187bf Mon Sep 17 00:00:00 2001 From: Mnehmos Date: Sun, 18 Jan 2026 08:52:23 -0700 Subject: [PATCH 11/22] refactor(settings): separate togglePlatform UI and data state The togglePlatform function was mixing UI state updates with data mutations by calling handleChange inside the setExpandedPlatforms updater. Now properly separated: 1. Determine if collapsing first 2. Update UI state (expandedPlatforms) 3. Clear data separately when collapsing This improves testability and follows React best practices for state management. Co-Authored-By: Claude Opus 4.5 --- components/settings/ExternalHandlesSection.tsx | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/components/settings/ExternalHandlesSection.tsx b/components/settings/ExternalHandlesSection.tsx index f18f64bf..8a8c247b 100644 --- a/components/settings/ExternalHandlesSection.tsx +++ b/components/settings/ExternalHandlesSection.tsx @@ -124,17 +124,24 @@ export default function ExternalHandlesSection({ }; 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 (next.has(key)) { + if (isCollapsing) { next.delete(key); - // Clear the value when collapsing - handleChange(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)); From 4ed37a81c5e18cf46052d9b8112bede960a4e358 Mon Sep 17 00:00:00 2001 From: Mnehmos Date: Sun, 18 Jan 2026 08:52:56 -0700 Subject: [PATCH 12/22] test(time-utils): fix flaky "exactly now" test with fake timers The test was flaky because there was a race condition between creating the timestamp with new Date().toISOString() and getTimeRemaining() checking the current time - milliseconds could pass between these calls. Now uses jest.useFakeTimers() and jest.setSystemTime() to make the test deterministic by controlling the system clock. Co-Authored-By: Claude Opus 4.5 --- __tests__/lib/time-utils.test.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/__tests__/lib/time-utils.test.ts b/__tests__/lib/time-utils.test.ts index 51a62cd4..4fdc12e5 100644 --- a/__tests__/lib/time-utils.test.ts +++ b/__tests__/lib/time-utils.test.ts @@ -51,11 +51,18 @@ describe('time-utils', () => { }); it('handles edge case of exactly now', () => { - const now = new Date().toISOString(); + // 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(); }); }); From e409f6834b7a48a28aa94aa80f7943cd779d439b Mon Sep 17 00:00:00 2001 From: Mnehmos Date: Sun, 18 Jan 2026 08:54:52 -0700 Subject: [PATCH 13/22] refactor(style): rename boolean props to use is/has prefix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow naming convention for boolean variables: - FindSupportSection: disabled → isDisabled - profile/index.tsx: loadingInviteCode → isLoadingInviteCode This improves code readability and consistency with project guidelines. Co-Authored-By: Claude Opus 4.5 --- app/(app)/(tabs)/profile/index.tsx | 4 ++-- components/profile/FindSupportSection.tsx | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/app/(app)/(tabs)/profile/index.tsx b/app/(app)/(tabs)/profile/index.tsx index b7ba6b85..b6b0c7aa 100644 --- a/app/(app)/(tabs)/profile/index.tsx +++ b/app/(app)/(tabs)/profile/index.tsx @@ -64,7 +64,7 @@ export default function ProfileScreen() { [key: string]: { total: number; completed: number }; }>({}); const [activeInviteCode, setActiveInviteCode] = useState(null); - const [loadingInviteCode, setLoadingInviteCode] = useState(false); + const [isLoadingInviteCode, setLoadingInviteCode] = useState(false); const [pendingMatches, setPendingMatches] = useState([]); /** @@ -724,7 +724,7 @@ export default function ProfileScreen() { theme={theme} onRegenerate={regenerateInviteCode} onRevoke={revokeInviteCode} - disabled={loadingInviteCode} + disabled={isLoadingInviteCode} /> )} {sponseeRelationships.map((rel) => ( diff --git a/components/profile/FindSupportSection.tsx b/components/profile/FindSupportSection.tsx index 98d19f9f..7ae6f60d 100644 --- a/components/profile/FindSupportSection.tsx +++ b/components/profile/FindSupportSection.tsx @@ -27,7 +27,7 @@ interface FindSupportSectionProps { /** Callback to refresh matches after action */ onMatchUpdate: () => void; /** Whether the section is disabled */ - disabled?: boolean; + isDisabled?: boolean; } // ============================================================================= @@ -83,7 +83,7 @@ export default function FindSupportSection({ theme, pendingMatches, onMatchUpdate, - disabled = false, + isDisabled = false, }: FindSupportSectionProps): React.JSX.Element | null { const styles = useMemo(() => createStyles(theme), [theme]); const [isSearching, setIsSearching] = useState(false); @@ -94,7 +94,7 @@ export default function FindSupportSection({ const findMatches = useCallback(async () => { if (!shouldShow) return; - if (disabled || isSearching) return; + if (isDisabled || isSearching) return; setIsSearching(true); try { @@ -155,11 +155,11 @@ export default function FindSupportSection({ } finally { setIsSearching(false); } - }, [userId, intent, disabled, isSearching, onMatchUpdate, shouldShow]); + }, [userId, intent, isDisabled, isSearching, onMatchUpdate, shouldShow]); const acceptMatch = useCallback( async (matchId: string) => { - if (disabled || isProcessing) return; + if (isDisabled || isProcessing) return; setIsProcessing(matchId); try { @@ -191,12 +191,12 @@ export default function FindSupportSection({ setIsProcessing(null); } }, - [disabled, isProcessing, onMatchUpdate] + [isDisabled, isProcessing, onMatchUpdate] ); const rejectMatch = useCallback( async (matchId: string) => { - if (disabled || isProcessing) return; + if (isDisabled || isProcessing) return; setIsProcessing(matchId); try { @@ -223,7 +223,7 @@ export default function FindSupportSection({ setIsProcessing(null); } }, - [disabled, isProcessing, onMatchUpdate] + [isDisabled, isProcessing, onMatchUpdate] ); // Separate matches by user's role and acceptance status @@ -299,7 +299,7 @@ export default function FindSupportSection({ rejectMatch(match.id)} - disabled={disabled || isProcessingThis} + disabled={isDisabled || isProcessingThis} accessibilityRole="button" accessibilityLabel="Decline match" > @@ -312,7 +312,7 @@ export default function FindSupportSection({ acceptMatch(match.id)} - disabled={disabled || isProcessingThis} + disabled={isDisabled || isProcessingThis} accessibilityRole="button" accessibilityLabel="Accept match" > @@ -364,7 +364,7 @@ export default function FindSupportSection({ Date: Sun, 18 Jan 2026 08:55:30 -0700 Subject: [PATCH 14/22] refactor(imports): use @/ alias for internal imports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - RelationshipCard: './SymmetricRevealSection' → '@/components/profile/SymmetricRevealSection' - SettingsContent: './ExternalHandlesSection' → '@/components/settings/ExternalHandlesSection' Consistent use of project alias for internal imports improves refactoring safety and readability. Co-Authored-By: Claude Opus 4.5 --- components/profile/RelationshipCard.tsx | 2 +- components/settings/SettingsContent.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/profile/RelationshipCard.tsx b/components/profile/RelationshipCard.tsx index 2f8131b8..d6ec0daa 100644 --- a/components/profile/RelationshipCard.tsx +++ b/components/profile/RelationshipCard.tsx @@ -4,7 +4,7 @@ import { Heart, UserMinus, CheckCircle, Plus } from 'lucide-react-native'; import type { ThemeColors } from '@/contexts/ThemeContext'; import { useDaysSober } from '@/hooks/useDaysSober'; import type { Profile, ExternalHandles, SponsorSponseeRelationship } from '@/types/database'; -import SymmetricRevealSection from './SymmetricRevealSection'; +import SymmetricRevealSection from '@/components/profile/SymmetricRevealSection'; // ============================================================================= // Types & Interfaces diff --git a/components/settings/SettingsContent.tsx b/components/settings/SettingsContent.tsx index 3bb2a609..2c630e9d 100644 --- a/components/settings/SettingsContent.tsx +++ b/components/settings/SettingsContent.tsx @@ -64,7 +64,7 @@ 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 './ExternalHandlesSection'; +import ExternalHandlesSection from '@/components/settings/ExternalHandlesSection'; import packageJson from '../../package.json'; import type { SettingsContentProps } from './types'; From 4e6c0c318dc2e830d370bc65693c7929df78b881 Mon Sep 17 00:00:00 2001 From: Mnehmos Date: Sun, 18 Jan 2026 08:58:43 -0700 Subject: [PATCH 15/22] refactor(lib): extract shared SheetInputComponent for bottom sheets - Create lib/sheet-input.tsx with platform-specific input component - Update TaskCreationSheet to use shared SheetInputComponent - Update EditSavingsSheet to use shared SheetInputComponent - Update EnterInviteCodeSheet to use shared SheetInputComponent - Update TaskCompletionSheet to use shared SheetInputComponent - Remove duplicate InputComponent definitions from 4 sheet files Co-Authored-By: Claude Opus 4.5 --- components/TaskCreationSheet.tsx | 11 ++++------- components/sheets/EditSavingsSheet.tsx | 17 ++++------------- components/sheets/EnterInviteCodeSheet.tsx | 18 ++++-------------- components/sheets/TaskCompletionSheet.tsx | 18 ++++-------------- lib/sheet-input.tsx | 12 ++++++++++++ 5 files changed, 28 insertions(+), 48 deletions(-) create mode 100644 lib/sheet-input.tsx diff --git a/components/TaskCreationSheet.tsx b/components/TaskCreationSheet.tsx index af158a85..d89c8930 100644 --- a/components/TaskCreationSheet.tsx +++ b/components/TaskCreationSheet.tsx @@ -18,9 +18,9 @@ import { Pressable, ActivityIndicator, Platform, - TextInput, } 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'; @@ -30,9 +30,6 @@ import { logger, LogCategory } from '@/lib/logger'; import { formatLocalDate, parseDateAsLocal } from '@/lib/date'; import GlassBottomSheet, { GlassBottomSheetRef } from '@/components/GlassBottomSheet'; -// Use regular TextInput on web to avoid BottomSheetTextInput compatibility issues -const InputComponent = Platform.OS === 'web' ? TextInput : BottomSheetTextInput; - // ============================================================================= // Types & Interfaces // ============================================================================= @@ -514,7 +511,7 @@ const TaskCreationSheet = forwardRef Task Title * - Task Description * - ( 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. - Date: Sun, 18 Jan 2026 08:59:31 -0700 Subject: [PATCH 16/22] style(lib): add section dividers to platform-icons.tsx Add canonical section headers for better code organization: - Imports - Types & Interfaces - Constants - Helpers Co-Authored-By: Claude Opus 4.5 --- lib/platform-icons.tsx | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/lib/platform-icons.tsx b/lib/platform-icons.tsx index b7d0a3dd..27d6031f 100644 --- a/lib/platform-icons.tsx +++ b/lib/platform-icons.tsx @@ -1,16 +1,34 @@ -/** - * Platform icon mappings for external contact handles. - * Centralizes icon rendering for Discord, Telegram, WhatsApp, Signal, Phone, etc. - */ +// ============================================================================= +// 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. + */ +interface IconTheme { + primary: string; + info: string; + success: string; + warning: string; + textSecondary: string; +} + +// ============================================================================= +// Constants +// ============================================================================= + /** * Human-readable labels for platform keys. */ @@ -22,6 +40,10 @@ export const platformLabels: Record = { phone: 'Phone', }; +// ============================================================================= +// Helpers +// ============================================================================= + /** * Get human-readable label for a platform key. */ @@ -29,17 +51,6 @@ export function getPlatformLabel(key: string): string { return platformLabels[key] || key; } -/** - * Theme colors interface for icon rendering. - */ -interface IconTheme { - primary: string; - info: string; - success: string; - warning: string; - textSecondary: string; -} - /** * Get icon component for a platform. * From 1753fc66fa45d6b5a9887f0bee1a78140fbded3b Mon Sep 17 00:00:00 2001 From: Mnehmos Date: Sun, 18 Jan 2026 09:00:12 -0700 Subject: [PATCH 17/22] test(lib): add size verification to platform-icons tests - Verify custom size parameter is applied to icon props - Add test for default size of 16 when not specified - Use root.findByProps to inspect rendered icon properties Co-Authored-By: Claude Opus 4.5 --- __tests__/lib/platform-icons.test.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/__tests__/lib/platform-icons.test.tsx b/__tests__/lib/platform-icons.test.tsx index 6afd0d3e..41e05bf5 100644 --- a/__tests__/lib/platform-icons.test.tsx +++ b/__tests__/lib/platform-icons.test.tsx @@ -77,7 +77,17 @@ describe('platform-icons', () => { it('respects custom size parameter', () => { const icon = getPlatformIcon('discord', mockTheme, 24); - expect(icon).toBeTruthy(); + 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); }); }); }); From 5e45874c6476ec141928b1e9e409b9bf62822e8b Mon Sep 17 00:00:00 2001 From: Mnehmos Date: Sun, 18 Jan 2026 09:01:38 -0700 Subject: [PATCH 18/22] docs(changelog): consolidate duplicate Fixed sections and add PR review fixes - Merge duplicate Fixed sections under [Unreleased] - Add entries for all PR #302 CodeRabbit review fixes - Add Changed entries for new shared utilities Co-Authored-By: Claude Opus 4.5 --- CHANGELOG.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c397c08..04a75b29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 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 +- Add canonical section dividers to `lib/platform-icons.tsx` for better code organization +- Add size verification tests to platform-icons test suite ### Removed @@ -38,6 +41,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 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 ## [1.3.0] - 2026-01-27 From 07e85cc52d4964d490759b7468692d4525d3e78c Mon Sep 17 00:00:00 2001 From: Mnehmos Date: Sun, 18 Jan 2026 09:39:22 -0700 Subject: [PATCH 19/22] fix: address CodeRabbit review round 2 issues Security: - Replace Math.random() with expo-crypto for cryptographically secure invite codes Performance: - Memoize handleConnectionIntentChange with useCallback Bug fixes: - Fix timer display not showing minutes when hours = 0 - Fix potential memory leak in SymmetricRevealSection with isMounted guard Type safety: - Export IconTheme interface from lib/platform-icons.tsx - Strengthen platformLabels typing with Record Co-Authored-By: Claude Opus 4.5 --- CHANGELOG.md | 6 + app/(app)/(tabs)/profile/index.tsx | 50 +- components/profile/FindSupportSection.tsx | 10 +- components/profile/SymmetricRevealSection.tsx | 16 +- jest.setup.js | 13 + lib/platform-icons.tsx | 6 +- package.json | 1 + pnpm-lock.yaml | 2691 ++++++++--------- 8 files changed, 1374 insertions(+), 1419 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04a75b29..8e530950 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Extract `SheetInputComponent` to shared `lib/sheet-input.tsx` for platform-specific bottom sheet inputs - Add canonical section dividers to `lib/platform-icons.tsx` for better code organization - Add size verification tests to platform-icons test suite +- Replace `Math.random()` with cryptographically secure `expo-crypto` for invite code generation +- Memoize `handleConnectionIntentChange` with `useCallback` for performance optimization +- Export `IconTheme` interface from `lib/platform-icons.tsx` for type safety +- Strengthen `platformLabels` typing with `Record` constraint ### Removed @@ -53,6 +57,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 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 by adding isMounted cleanup guard ## [1.3.0] - 2026-01-27 diff --git a/app/(app)/(tabs)/profile/index.tsx b/app/(app)/(tabs)/profile/index.tsx index b6b0c7aa..67a50101 100644 --- a/app/(app)/(tabs)/profile/index.tsx +++ b/app/(app)/(tabs)/profile/index.tsx @@ -10,6 +10,7 @@ import { 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'; @@ -188,8 +189,13 @@ export default function ProfileScreen() { .eq('id', activeInviteCode.id); } - // Generate new code - const code = Math.random().toString(36).substring(2, 10).toUpperCase(); + // 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); @@ -220,6 +226,29 @@ export default function ProfileScreen() { } }, [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; @@ -658,22 +687,7 @@ export default function ProfileScreen() { {/* Connection Intent Selector */} { - 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'); - } - }} + onChange={handleConnectionIntentChange} theme={theme} /> diff --git a/components/profile/FindSupportSection.tsx b/components/profile/FindSupportSection.tsx index 7ae6f60d..8da07482 100644 --- a/components/profile/FindSupportSection.tsx +++ b/components/profile/FindSupportSection.tsx @@ -290,7 +290,9 @@ export default function FindSupportSection({ > {timeLeft.days > 0 ? `${timeLeft.days}d ${timeLeft.hours}h left` - : `${timeLeft.hours}h left`} + : timeLeft.hours > 0 + ? `${timeLeft.hours}h ${timeLeft.minutes}m left` + : `${timeLeft.minutes}m left`} @@ -352,7 +354,11 @@ export default function FindSupportSection({ - {timeLeft.days > 0 ? `${timeLeft.days}d` : `${timeLeft.hours}h`} + {timeLeft.days > 0 + ? `${timeLeft.days}d` + : timeLeft.hours > 0 + ? `${timeLeft.hours}h` + : `${timeLeft.minutes}m`} ); diff --git a/components/profile/SymmetricRevealSection.tsx b/components/profile/SymmetricRevealSection.tsx index 064ef759..f4815509 100644 --- a/components/profile/SymmetricRevealSection.tsx +++ b/components/profile/SymmetricRevealSection.tsx @@ -98,6 +98,8 @@ export default function SymmetricRevealSection({ // Fetch handles only when there's mutual consent (via secure RPC) useEffect(() => { + let isMounted = true; + if (revealState !== 'mutual') { setOtherHandles({}); return; @@ -110,6 +112,9 @@ export default function SymmetricRevealSection({ relationship_id: relationshipId, }); + // Guard: Only update state if component is still mounted + if (!isMounted) return; + if (error) { logger.error('Failed to fetch handles with consent', new Error(error.message), { category: LogCategory.DATABASE, @@ -119,15 +124,24 @@ export default function SymmetricRevealSection({ setOtherHandles((data as ExternalHandles) || {}); } catch (err) { + // Guard: Only update state if component is still mounted + if (!isMounted) return; + logger.error('Unexpected error fetching handles', err as Error, { category: LogCategory.DATABASE, }); } finally { - setIsLoadingHandles(false); + if (isMounted) { + setIsLoadingHandles(false); + } } }; fetchHandles(); + + return () => { + isMounted = false; + }; }, [revealState, relationshipId]); // Filter out empty handle values diff --git a/jest.setup.js b/jest.setup.js index ceb06bcc..9929aa2b 100644 --- a/jest.setup.js +++ b/jest.setup.js @@ -737,6 +737,19 @@ jest.mock('expo-application', () => ({ 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 index 27d6031f..664ce713 100644 --- a/lib/platform-icons.tsx +++ b/lib/platform-icons.tsx @@ -17,7 +17,7 @@ export type PlatformKey = 'discord' | 'telegram' | 'whatsapp' | 'signal' | 'phon /** * Theme colors interface for icon rendering. */ -interface IconTheme { +export interface IconTheme { primary: string; info: string; success: string; @@ -32,7 +32,7 @@ interface IconTheme { /** * Human-readable labels for platform keys. */ -export const platformLabels: Record = { +export const platformLabels: Record = { discord: 'Discord', telegram: 'Telegram', whatsapp: 'WhatsApp', @@ -48,7 +48,7 @@ export const platformLabels: Record = { * Get human-readable label for a platform key. */ export function getPlatformLabel(key: string): string { - return platformLabels[key] || key; + return (platformLabels as Record)[key] || key; } /** 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..32743d89 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,14 +12,14 @@ importers: .: dependencies: '@amplitude/analytics-browser': - specifier: ^2.34.0 - version: 2.34.0 + specifier: ^2.33.1 + version: 2.33.1 '@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) + specifier: ^1.5.32 + version: 1.5.32(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.10(expo@54.0.30)(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.30)(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) + specifier: ^2.9.3 + version: 2.9.3(@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 + specifier: ^3.0.1 + version: 3.0.1 '@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.30)(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 + specifier: ^2.89.0 + version: 2.89.0 '@vercel/analytics': specifier: ^1.6.1 version: 1.6.1(react@19.1.0) @@ -63,77 +63,80 @@ importers: specifier: ^4.1.0 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) + specifier: ~54.0.30 + version: 54.0.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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.30)(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.30) 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.30)(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.30)(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.30) 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.30)(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)) + specifier: ~18.0.12 + version: 18.0.12(expo@54.0.30)(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.30) expo-dev-client: specifier: ~6.0.20 - version: 6.0.20(expo@54.0.32) + version: 6.0.20(expo@54.0.30) expo-device: specifier: ^8.0.10 - version: 8.0.10(expo@54.0.32) + version: 8.0.10(expo@54.0.30) 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) + specifier: ~14.0.10 + version: 14.0.10(expo@54.0.30)(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.30)(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.30)(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.30) 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.30)(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) + specifier: ~6.0.21 + version: 6.0.21(f0d96ad57e690436ea0d7f67db788d54) expo-secure-store: specifier: ^15.0.8 - version: 15.0.8(expo@54.0.32) + version: 15.0.8(expo@54.0.30) expo-splash-screen: specifier: ~31.0.13 - version: 31.0.13(expo@54.0.32) + version: 31.0.13(expo@54.0.30) 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.30)(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.30)(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) + specifier: ^29.0.15 + version: 29.0.15(expo@54.0.30)(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.30)(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 + specifier: ^1.57.0 + version: 1.57.0 '@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 @@ -196,11 +199,11 @@ importers: specifier: ~19.1.17 version: 19.1.17 babel-jest: - specifier: ^30.2.0 - version: 30.2.0(@babel/core@7.28.6) + specifier: ^29.7.0 + version: 29.7.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) + specifier: ^54.0.9 + version: 54.0.9(@babel/core@7.28.5)(@babel/runtime@7.28.4)(expo@54.0.30)(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 @@ -229,8 +232,8 @@ importers: specifier: ^0.83.3 version: 0.83.3 prettier: - specifier: ^3.8.1 - version: 3.8.1 + specifier: ^3.7.4 + version: 3.7.4 react-test-renderer: specifier: 19.1.0 version: 19.1.0(react@19.1.0) @@ -248,35 +251,35 @@ packages: graphql: optional: true - '@amplitude/analytics-browser@2.34.0': - resolution: {integrity: sha512-a5AeUBs6AbgfEPBNVP1/FM8+0ZBjIzbfYcVJq2Lkvb0EaeEI09vrl+zFeJSDcjg5nsDBmz7oKV8j2kbGJHbZ9w==} + '@amplitude/analytics-browser@2.33.1': + resolution: {integrity: sha512-93wZjuAFJ7QdyptF82i1pezm5jKuBWITHI++XshDgpks1RstJvJ9n11Ak8MnE4L2BGQ93XDN2aVEHfmQkt0/Pw==} '@amplitude/analytics-connector@1.6.4': resolution: {integrity: sha512-SpIv0IQMNIq6SH3UqFGiaZyGSc7PBZwRdq7lvP0pBxW8i4Ny+8zwI0pV+VMfMHQwWY3wdIbWw5WQphNjpdq1/Q==} - '@amplitude/analytics-core@2.37.0': - resolution: {integrity: sha512-/2vIyquLMSA29MMM901d5DOhBZ5bc6Qf4s0KVfRk8Avn90mC7KlE5SQiHOAW8+63o5aT1NtB0djrY4cq8Y8Opw==} + '@amplitude/analytics-core@2.35.0': + resolution: {integrity: sha512-7RmHYELXCGu8yuO9D6lEXiqkMtiC5sePNhCWmwuP30dneDYHtH06gaYvAFH/YqOFuE6enwEEJfFYtcaPhyiqtA==} - '@amplitude/analytics-react-native@1.5.37': - resolution: {integrity: sha512-CXmKkQopPtonsdt9A3m27Y7NlUOrOwCdxIchkzo6IBpItfqqvxMW09kC67YOCxgmHuzTBzB/1MpDB9nwNCk9jw==} + '@amplitude/analytics-react-native@1.5.32': + resolution: {integrity: sha512-/85s1IttVnMK934ZwGDBKt+jEdoaXdMUstBPVF/SvjyP9Gqvk+D19IbKyeeVz/ri7H7mntqQxwIC+KDT0rqj1Q==} peerDependencies: react: '*' react-native: '*' - '@amplitude/plugin-autocapture-browser@1.19.0': - resolution: {integrity: sha512-ga0TXxE87wNMgFvVUPE7a+HGMqMz9/Gy6C7ux3cgcuy8D7Z/Te5iMZmMadK/ztmQ3/pSZjYv9iDvOFsT6ywMZw==} + '@amplitude/plugin-autocapture-browser@1.18.3': + resolution: {integrity: sha512-njYque5t1QCEEe5V8Ls4yVVklTM6V7OXxBk6pqznN/hj/Pc4X8Wjy898pZ2VtbnvpagBKKzGb5B6Syl8OXiicw==} - '@amplitude/plugin-network-capture-browser@1.7.8': - resolution: {integrity: sha512-SZj3m3O8yI040+Nto9uLi5eM0AclEi8hkZttLSKKVCJVIdi5xE1z9oC7lTir1h2b198unTqP2o7cmzEqI1wBoA==} + '@amplitude/plugin-network-capture-browser@1.7.3': + resolution: {integrity: sha512-zfWgAN7g6AigJAsgrGmlgVwydOHH6XvweBoxhU+qEvRydboiIVCDLSxuXczUsBG7kYVLWRdBK1DYoE5J7lqTGA==} - '@amplitude/plugin-page-url-enrichment-browser@0.5.14': - resolution: {integrity: sha512-vaAGxMgxQbsRKWtIKiMp3kVdg4RSsAzwlffDw7ifTw1yObVV2vVeBPb/O24cLf49NrhZ3ZuJlaxGmRxoY4FWow==} + '@amplitude/plugin-page-url-enrichment-browser@0.5.9': + resolution: {integrity: sha512-TqdELx4WrdRutCjHUFUzum/f/UjhbdTZw0UKkYFAj5gwAKDjaPEjL4waRvINOTaVLsne1A6ck4KEMfC8AKByFw==} - '@amplitude/plugin-page-view-tracking-browser@2.6.11': - resolution: {integrity: sha512-/UqXipdOWOsmn8Uw1BibOfgLMvjE8YYYI8bXL2vZ6D2mk6c0FdGe17BmccsLr1LVGx3HObZoIGnxje+0X1j07w==} + '@amplitude/plugin-page-view-tracking-browser@2.6.6': + resolution: {integrity: sha512-dBcJlrdKgPzSgS3exDRRrMLqhIaOjwlIy7o8sEMn1PpMawERlbumSSdtfII6L4L67HYUPo4PY4Kp4acqSzaLvQ==} - '@amplitude/plugin-web-vitals-browser@1.1.9': - resolution: {integrity: sha512-hVGsJWtJQpHlpfC+IubCixe7KNmqaDkWO0XkwkLkn4T/FjFgK+rp9edBJr5KSi0enfcN+49DxJ4qQKu7NncIMA==} + '@amplitude/plugin-web-vitals-browser@1.1.4': + resolution: {integrity: sha512-XQXI9OjTNSz2yi0lXw2VYMensDzzSkMCfvXNniTb1LgnHwBcQ1JWPcTqHLPFrvvNckeIdOT78vjs7yA+c1FyzA==} '@amplitude/ua-parser-js@0.7.33': resolution: {integrity: sha512-wKEtVR4vXuPT9cVEIJkYWnlF++Gx3BdLatPBM+SZ1ztVIvnhdGBZR/mn9x/PzyrMcRlZmyi6L56I2J3doVBnjA==} @@ -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.5': + resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==} engines: {node: '>=6.9.0'} - '@babel/core@7.28.6': - resolution: {integrity: sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==} + '@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.20': + resolution: {integrity: sha512-cwsXmhftvS0p9NNYOhXGnicBAZl9puWwRt19Qq5eQ6njLnaj8WvcR+kDZyADtgZxBsZiyVlrKXvnjt43HXywQA==} hasBin: true peerDependencies: expo: '*' @@ -865,8 +893,8 @@ packages: react-native: optional: true - '@expo/code-signing-certificates@0.0.6': - resolution: {integrity: sha512-iNe0puxwBNEcuua9gmTGzq+SuMDa0iATai1FlFTMHJ/vUmKvN/V//drXoLJkVb5i5H3iE/n/qIJxyoBnXouD0w==} + '@expo/code-signing-certificates@0.0.5': + resolution: {integrity: sha512-BNhXkY1bblxKZpltzAx98G2Egj9g1Q+JRcvR7E99DOj862FTCX+ZPsAUtPTr7aHxwtrL7+fL3r0JSmM9kBm+Bw==} '@expo/config-plugins@54.0.4': resolution: {integrity: sha512-g2yXGICdoOw5i3LkQSDxl2Q5AlQCrG7oniu0pCPPO+UxGb7He4AFqSvPSy8HpRUj55io17hT62FTjYRD+d6j3Q==} @@ -904,8 +932,8 @@ packages: '@expo/json-file@10.0.8': resolution: {integrity: sha512-9LOTh1PgKizD1VXfGQ88LtDH0lRwq9lsTb4aichWTWSWqy3Ugfkhfm3BhzBIkJJfQQ5iJu3m/BoRlEIjoCGcnQ==} - '@expo/metro-config@54.0.14': - resolution: {integrity: sha512-hxpLyDfOR4L23tJ9W1IbJJsG7k4lv2sotohBm/kTYyiG+pe1SYCAWsRmgk+H42o/wWf/HQjE5k45S5TomGLxNA==} + '@expo/metro-config@54.0.12': + resolution: {integrity: sha512-Xhv1z/ak/cuJWeLxlnWr2u22q2AM/klASbjpP5eE34y91lGWa2NUwrFWoS830MhJ6kuAqtGdoQhwyPa3TES7sA==} peerDependencies: expo: '*' peerDependenciesMeta: @@ -930,8 +958,8 @@ packages: resolution: {integrity: sha512-/TuOZvSG7Nn0I8c+FcEaoHeBO07yu6vwDgk7rZVvAXoeAK5rkA09jRyjYsZo+0tMEFaToBeywA6pj50Mb3ny9w==} engines: {node: '>=12'} - '@expo/package-manager@1.9.10': - resolution: {integrity: sha512-axJm+NOj3jVxep49va/+L3KkF3YW/dkV+RwzqUJedZrv4LeTqOG4rhrCaCPXHTvLqCTDKu6j0Xyd28N7mnxsGA==} + '@expo/package-manager@1.9.9': + resolution: {integrity: sha512-Nv5THOwXzPprMJwbnXU01iXSrCp3vJqly9M4EJ2GkKko9Ifer2ucpg7x6OUsE09/lw+npaoUnHMXwkw7gcKxlg==} '@expo/plist@0.4.8': resolution: {integrity: sha512-pfNtErGGzzRwHP+5+RqswzPDKkZrx+Cli0mzjQaus1ZWFsog5ibL+nVT3NcporW51o8ggnt7x813vtRbPiyOrQ==} @@ -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': @@ -1074,10 +1102,6 @@ packages: resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jest/pattern@30.0.1': - resolution: {integrity: sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/reporters@29.7.0': resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1111,18 +1135,10 @@ packages: resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jest/transform@30.2.0': - resolution: {integrity: sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/types@29.6.3': resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jest/types@30.2.0': - resolution: {integrity: sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -1149,8 +1165,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.57.0': + resolution: {integrity: sha512-6TyEnHgd6SArQO8UO2OMTxshln3QMWBtPGrOCgs3wVEmQmwyuNtB10IZMfmYDE0riwNR1cu4q+pPcxMVtaG3TA==} engines: {node: '>=18'} hasBin: true @@ -1465,25 +1481,25 @@ 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' - '@react-navigation/elements@2.9.5': - resolution: {integrity: sha512-iHZU8rRN1014Upz73AqNVXDvSMZDh5/ktQ1CMe21rdgnOY79RWtHHBp9qOS3VtqlUVYGkuX5GEw5mDt4tKdl0g==} + '@react-navigation/elements@2.9.3': + resolution: {integrity: sha512-3+eyvWiVPIEf6tN9UdduhOEHcTuNe3R5WovgiVkfH9+jApHMTZDc2loePTpY/i2HDJhObhhChpJzO6BVjrpdYQ==} peerDependencies: '@react-native-masked-view/masked-view': '>= 0.2.0' - '@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' @@ -1491,17 +1507,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: '*' @@ -1541,8 +1557,8 @@ packages: engines: {node: '>=10'} os: [darwin] - '@sentry/cli-darwin@3.1.0': - resolution: {integrity: sha512-xT1WlCHenGGO29Lq/wKaIthdqZzNzZhlPs7dXrzlBx9DyA2Jnl0g7WEau0oWi8GyJGVRXCJMiCydR//Tb5qVwA==} + '@sentry/cli-darwin@3.0.1': + resolution: {integrity: sha512-CRE6+7hEvQsu+hI8IxtH8b3MFOv4iLIZL3WKh+nDFnMzjyG94TjVOGicDguN9NIVY+9cr1dra2Xqb7E1FF08bw==} engines: {node: '>=18'} os: [darwin] @@ -1552,8 +1568,8 @@ packages: cpu: [arm64] os: [linux, freebsd, android] - '@sentry/cli-linux-arm64@3.1.0': - resolution: {integrity: sha512-Jm/iHLKiHxrZYlAq2tT07amiegEVCOAQT9Unilr6djjcZzS2tcI9ThSRQvjP9tFpFRKop+NyNGE3XHXf69r00g==} + '@sentry/cli-linux-arm64@3.0.1': + resolution: {integrity: sha512-Y4M33legybpXiq/iunKS5kFI5IckYlHuqqhcFe642SUHwSLieTK+ige16lxFnjZ8aXtoZggp3s4GTLNXezoMYw==} engines: {node: '>=18'} cpu: [arm64] os: [linux, freebsd, android] @@ -1564,8 +1580,8 @@ packages: cpu: [arm] os: [linux, freebsd, android] - '@sentry/cli-linux-arm@3.1.0': - resolution: {integrity: sha512-kbP3/8/Ct/Jbm569KDXbFIyMyPypIegObvIT7LdSsfdYSZdBd396GV7vUpSGKiLUVVN0xjn8OqQ48AVGfjmuMg==} + '@sentry/cli-linux-arm@3.0.1': + resolution: {integrity: sha512-uUfVgefHooIh1Zd0EjKIrIPdDkVMiH6heiMuOYFF7C3FmT04V56wmcDs27hoylPTKef/uZamAd9tijCSxvYGHg==} engines: {node: '>=18'} cpu: [arm] os: [linux, freebsd, android] @@ -1576,8 +1592,8 @@ packages: cpu: [x86, ia32] os: [linux, freebsd, android] - '@sentry/cli-linux-i686@3.1.0': - resolution: {integrity: sha512-f/PK/EGK5vFOy7LC4Riwb+BEE20Nk7RbEFEMjvRq26DpETCrZYUGlbpIKvJFKOaUmr79aAkFCA/EjJiYfcQP2Q==} + '@sentry/cli-linux-i686@3.0.1': + resolution: {integrity: sha512-VTEyFB2P8BzhNb6i/ihBlPglj8DPwlsyGdwxZcPsXMWZ7BF4Vfg6F3YRvtyyQP+jMshXOAwM7EbYsKoFBRX7zg==} engines: {node: '>=18'} cpu: [x86, ia32] os: [linux, freebsd, android] @@ -1588,8 +1604,8 @@ packages: cpu: [x64] os: [linux, freebsd, android] - '@sentry/cli-linux-x64@3.1.0': - resolution: {integrity: sha512-T+v8x1ujhixZrOrH0sVhsW6uLwK4n0WS+B+5xV46WqUKe32cbYotursp2y53ROjgat8SQDGeP/VnC0Qa3Y2fEA==} + '@sentry/cli-linux-x64@3.0.1': + resolution: {integrity: sha512-kYHkvYVJfecQcMQNiTBTLlNNHKvXMqH/WxQgVGX4L+vCH7Gd+L/AceZMBf/fZcyeh45IGqOnj1CH9zHhSlDXag==} engines: {node: '>=18'} cpu: [x64] os: [linux, freebsd, android] @@ -1600,8 +1616,8 @@ packages: cpu: [arm64] os: [win32] - '@sentry/cli-win32-arm64@3.1.0': - resolution: {integrity: sha512-2DIPq6aW2DC34EDC9J0xwD+9BpFnKdFGdIcQUZMS+5pXlU6V7o8wpZxZAM8TdYNmsPkkQGKp7Dhl/arWpvNgrw==} + '@sentry/cli-win32-arm64@3.0.1': + resolution: {integrity: sha512-4k7aB77HX0H8qxVA2ei+wUC5L4TzHsecSx80wR215UX05ImOXlC5QzKYF7Q7Fn5Xa8L26r5qRLeciUzSupg3bw==} engines: {node: '>=18'} cpu: [arm64] os: [win32] @@ -1612,8 +1628,8 @@ packages: cpu: [x86, ia32] os: [win32] - '@sentry/cli-win32-i686@3.1.0': - resolution: {integrity: sha512-2NuywEiiZn6xJ1yAV2xjv/nuHiy6kZU5XR3RSAIrPdEZD1nBoMsH/gB2FufQw58Ziz/7otFcX+vtGpJjbIT5mQ==} + '@sentry/cli-win32-i686@3.0.1': + resolution: {integrity: sha512-Y1RmBZuEHOHISeO5ma1hoTNIVSTw69tAGfhz2ckvv9naoeLYEj4ehJ9XJof42n0Yn9GQXtC1O2ugbBPCwq7ABg==} engines: {node: '>=18'} cpu: [x86, ia32] os: [win32] @@ -1624,8 +1640,8 @@ packages: cpu: [x64] os: [win32] - '@sentry/cli-win32-x64@3.1.0': - resolution: {integrity: sha512-Ip405Yqdrr+l9TImsZOJz6c9Nb4zvXcmtOIBKLHc9cowpfXfmlqsHbDp7Xh4+k4L0uLr9i+8ilgQ6ypcuF4UCg==} + '@sentry/cli-win32-x64@3.0.1': + resolution: {integrity: sha512-ZL1uBswTnDc0BYU0zEyA4zP+d7He88x9hghww95uFZZr4Cf8tp8Pg6VaJT7G4AVImGSsJ1DC9kVCtMD9mU2A/g==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -1635,8 +1651,8 @@ packages: engines: {node: '>= 10'} hasBin: true - '@sentry/cli@3.1.0': - resolution: {integrity: sha512-ngnx6E8XjXpg1uzma45INfKCS8yurb/fl3cZdXTCa2wmek8b4N6WIlmOlTKFTBrV54OauF6mloJxAlpuzoQR6g==} + '@sentry/cli@3.0.1': + resolution: {integrity: sha512-E2SAmRjJIQ1EUSc3/YnKy2q+rIlAR8YQ2m//w3Uvc/sM07o5b31M9cqrEDleIHUrlk0w3wqI3xCivAXd33jILQ==} engines: {node: '>= 18'} hasBin: true @@ -1668,8 +1684,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 +1693,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.89.0': + resolution: {integrity: sha512-wiWZdz8WMad8LQdJMWYDZ2SJtZP5MwMqzQq3ehtW2ngiI3UTgbKiFrvMUUS3KADiVlk4LiGfODB2mrYx7w2f8w==} engines: {node: '>=20.0.0'} - '@supabase/functions-js@2.93.2': - resolution: {integrity: sha512-reSp7yj4KmvAFfmN+N7vYsHXOIZQh9cmRBh+VrZlm7qgIIUdYmzKuD85TvFnWApqcdI2pPnuZGKWE/2B4GXT1A==} + '@supabase/functions-js@2.89.0': + resolution: {integrity: sha512-XEueaC5gMe5NufNYfBh9kPwJlP5M2f+Ogr8rvhmRDAZNHgY6mI35RCkYDijd92pMcNM7g8pUUJov93UGUnqfyw==} engines: {node: '>=20.0.0'} - '@supabase/postgrest-js@2.93.2': - resolution: {integrity: sha512-W2AWDsYwRT217II5yD3jWaX3fJjB7DwyNi2KNi4sphdUI3DKY4fP2XYVDGfeb1clEFL18gw+GBhyQb3BcpNWkw==} + '@supabase/postgrest-js@2.89.0': + resolution: {integrity: sha512-/b0fKrxV9i7RNOEXMno/I1862RsYhuUo+Q6m6z3ar1f4ulTMXnDfv0y4YYxK2POcgrOXQOgKYQx1eArybyNvtg==} engines: {node: '>=20.0.0'} - '@supabase/realtime-js@2.93.2': - resolution: {integrity: sha512-YpAmJn7DLbMeYfQilcf3f0DKoY8O8TRbTF2oRpWFzHXTlEA+YWms8fBqM13Mf7RE72ouSNKDYyf5K2pWRSHvFw==} + '@supabase/realtime-js@2.89.0': + resolution: {integrity: sha512-aMOvfDb2a52u6PX6jrrjvACHXGV3zsOlWRzZsTIOAJa0hOVvRp01AwC1+nLTGUzxzezejrYeCX+KnnM1xHdl+w==} engines: {node: '>=20.0.0'} - '@supabase/storage-js@2.93.2': - resolution: {integrity: sha512-abRSVClfIQn+SqpdqL7S7b3VeyS8270/o0gqmGFtiidb7Lu0COsIV6Mor/mK9xE99KYWzyd37vwYYwv/jaANhw==} + '@supabase/storage-js@2.89.0': + resolution: {integrity: sha512-6zKcXofk/M/4Eato7iqpRh+B+vnxeiTumCIP+Tz26xEqIiywzD9JxHq+udRrDuv6hXE+pmetvJd8n5wcf4MFRQ==} engines: {node: '>=20.0.0'} - '@supabase/supabase-js@2.93.2': - resolution: {integrity: sha512-G3bZZi6rPwXcPtyHLXQTeHKa5ADZ2UW/+hv8YhwZFwngz4TlPnR4+TeO37EwU5+d/reD02qXozOZgz+QHv1Jtg==} + '@supabase/supabase-js@2.89.0': + resolution: {integrity: sha512-KlaRwSfFA0fD73PYVMHj5/iXFtQGCcX7PSx0FdQwYEEw9b2wqM7GxadY+5YwcmuEhalmjFB/YvqaoNVF+sWUlg==} engines: {node: '>=20.0.0'} '@testing-library/react-native@13.3.3': @@ -1762,8 +1778,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 +1808,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': @@ -1893,49 +1909,41 @@ packages: resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==} cpu: [arm64] os: [linux] - libc: [glibc] '@unrs/resolver-binding-linux-arm64-musl@1.11.1': resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==} cpu: [arm64] os: [linux] - libc: [musl] '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==} cpu: [ppc64] os: [linux] - libc: [glibc] '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==} cpu: [riscv64] os: [linux] - libc: [glibc] '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==} cpu: [riscv64] os: [linux] - libc: [musl] '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==} cpu: [s390x] os: [linux] - libc: [glibc] '@unrs/resolver-binding-linux-x64-gnu@1.11.1': resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==} cpu: [x64] os: [linux] - libc: [glibc] '@unrs/resolver-binding-linux-x64-musl@1.11.1': resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==} cpu: [x64] os: [linux] - libc: [musl] '@unrs/resolver-binding-wasm32-wasi@1.11.1': resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==} @@ -2155,30 +2163,16 @@ packages: peerDependencies: '@babel/core': ^7.8.0 - babel-jest@30.2.0: - resolution: {integrity: sha512-0YiBEOxWqKkSQWL9nNGGEgndoeL0ZpWrbLMNL5u/Kaxrli3Eaxlt3ZtIDktEvXt4L/R9r3ODr2zKwGM/2BjxVw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - peerDependencies: - '@babel/core': ^7.11.0 || ^8.0.0-0 - babel-plugin-istanbul@6.1.1: resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} engines: {node: '>=8'} - babel-plugin-istanbul@7.0.1: - resolution: {integrity: sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==} - engines: {node: '>=12'} - babel-plugin-jest-hoist@29.6.3: resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - babel-plugin-jest-hoist@30.2.0: - 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 +2181,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 @@ -2209,8 +2203,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0 || ^8.0.0-0 - babel-preset-expo@54.0.10: - resolution: {integrity: sha512-wTt7POavLFypLcPW/uC5v8y+mtQKDJiyGLzYCjqr9tx0Qc3vCXcDKk1iCFIj/++Iy5CWhhTflEa7VvVPNWeCfw==} + babel-preset-expo@54.0.9: + resolution: {integrity: sha512-8J6hRdgEC2eJobjoft6mKJ294cLxmi3khCUy2JJQp4htOYYkllSLUq6vudWJkTJiIuGdVR4bR6xuz2EvJLWHNg==} peerDependencies: '@babel/runtime': ^7.20.0 expo: '*' @@ -2227,20 +2221,14 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - babel-preset-jest@30.2.0: - resolution: {integrity: sha512-US4Z3NOieAQumwFnYdUWKvUKh8+YSnS/gB3t6YBiz0bskpu7Pine8pPCheNxlPEW4wnUkma2a94YuW2q3guvCQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - peerDependencies: - '@babel/core': ^7.11.0 || ^8.0.0-beta.1 - balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 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 +2305,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,10 +2339,6 @@ packages: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} - ci-info@4.3.1: - resolution: {integrity: sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==} - engines: {node: '>=8'} - cjs-module-lexer@1.4.3: resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} @@ -2472,8 +2456,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 +2655,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 +2861,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 +2885,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==} @@ -2962,8 +2946,8 @@ packages: react: '*' react-native: '*' - expo-constants@18.0.13: - resolution: {integrity: sha512-FnZn12E1dRYKDHlAdIyNFhBurKTS3F9CrfrBDJI5m3D7U17KBHMQ6JEfYlSj7LG7t+Ulr+IKaj58L1k5gBwTcQ==} + expo-constants@18.0.12: + resolution: {integrity: sha512-WzcKYMVNRRu4NcSzfIVRD5aUQFnSpTZgXFrlWmm19xJoDa4S3/PQNi6PNTBRc49xz9h8FT7HMxRKaC8lr0gflA==} peerDependencies: expo: '*' react-native: '*' @@ -3007,8 +2991,8 @@ packages: expo: '*' react-native: '*' - expo-font@14.0.11: - resolution: {integrity: sha512-ga0q61ny4s/kr4k8JX9hVH69exVSIfcIc19+qZ7gt71Mqtm7xy2c6kwsPTCyhBW2Ro5yXTT8EaZOpuRi35rHbg==} + expo-font@14.0.10: + resolution: {integrity: sha512-UqyNaaLKRpj4pKAP4HZSLnuDQqueaO5tB1c/NWu5vh1/LF9ulItyyg2kF/IpeOp0DeOLk0GY0HrIXaKUMrwB+Q==} peerDependencies: expo: '*' react: '*' @@ -3057,8 +3041,8 @@ packages: peerDependencies: expo: '*' - expo-modules-autolinking@3.0.24: - resolution: {integrity: sha512-TP+6HTwhL7orDvsz2VzauyQlXJcAWyU3ANsZ7JGL4DQu8XaZv/A41ZchbtAYLfozNA2Ya1Hzmhx65hXryBMjaQ==} + expo-modules-autolinking@3.0.23: + resolution: {integrity: sha512-YZnaE0G+52xftjH5nsIRaWsoVBY38SQCECclpdgLisdbRY/6Mzo7ndokjauOv3mpFmzMZACHyJNu1YSAffQwTg==} hasBin: true expo-modules-core@3.0.29: @@ -3067,14 +3051,14 @@ packages: react: '*' react-native: '*' - expo-router@6.0.22: - resolution: {integrity: sha512-6eOwobaVZQRsSQv0IoWwVlPbJru1zbreVsuPFIWwk7HApENStU2MggrceHXJqXjGho+FKeXxUop/gqOFDzpOMg==} + expo-router@6.0.21: + resolution: {integrity: sha512-wjTUjrnWj6gRYjaYl1kYfcRnNE4ZAQ0kz0+sQf6/mzBd/OU6pnOdD7WrdAW3pTTpm52Q8sMoeX98tNQEddg2uA==} peerDependencies: '@expo/metro-runtime': ^6.1.2 '@react-navigation/drawer': ^7.5.0 '@testing-library/react-native': '>= 12.0.0' expo: '*' - expo-constants: ^18.0.13 + expo-constants: ^18.0.12 expo-linking: ^8.0.11 react: '*' react-dom: '*' @@ -3145,8 +3129,8 @@ packages: peerDependencies: expo: '*' - expo-updates@29.0.16: - resolution: {integrity: sha512-E9/fxRz/Eurtc7hxeI/6ZPyHH3To9Xoccm1kXoICZTRojmuTo+dx0Xv53UHyHn4G5zGMezyaKF2Qtj3AKcT93w==} + expo-updates@29.0.15: + resolution: {integrity: sha512-6Qj+g56nnCksKKnEPQFm19dfWvYB5EggQNN3SaLbIj4LI40k/pjQwqYStEuwTU+Ow+PG0AqxIhQ3NvgVPEzLvg==} hasBin: true peerDependencies: expo: '*' @@ -3159,8 +3143,8 @@ packages: expo: '*' react-native: '*' - expo@54.0.32: - resolution: {integrity: sha512-yL9eTxiQ/QKKggVDAWO5CLjUl6IS0lPYgEvC3QM4q4fxd6rs7ks3DnbXSGVU3KNFoY/7cRNYihvd0LKYP+MCXA==} + expo@54.0.30: + resolution: {integrity: sha512-6q+aFfKL0SpT8prfdpR3V8HcN51ov0mCGuwQTzyuk6eeO9rg7a7LWbgPv9rEVXGZEuyULstL8LGNwHqusand7Q==} hasBin: true peerDependencies: '@expo/dom-webview': '*' @@ -3755,10 +3739,6 @@ packages: resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-haste-map@30.2.0: - resolution: {integrity: sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-leak-detector@29.7.0: resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3792,10 +3772,6 @@ packages: resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-regex-util@30.0.1: - resolution: {integrity: sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-resolve-dependencies@29.7.0: resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3820,10 +3796,6 @@ packages: resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-util@30.2.0: - resolution: {integrity: sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-validate@29.7.0: resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3836,10 +3808,6 @@ packages: resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-worker@30.2.0: - resolution: {integrity: sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest@29.7.0: resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3931,78 +3899,74 @@ 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 +4013,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 +4425,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.57.0: + resolution: {integrity: sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==} engines: {node: '>=18'} hasBin: true - playwright@1.58.0: - resolution: {integrity: sha512-2SVA0sbPktiIY/MCOPX8e86ehA/e+tDNq+e5Y8qjKYti2Z/JG7xnronT/TXTIkKbYGWlCbuucZ6dziEgkoEjQQ==} + playwright@1.57.0: + resolution: {integrity: sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw==} engines: {node: '>=18'} hasBin: true @@ -4494,8 +4458,8 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - prettier@3.8.1: - resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==} + prettier@3.7.4: + resolution: {integrity: sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==} engines: {node: '>=14'} hasBin: true @@ -4590,8 +4554,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 +4759,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==} @@ -4830,6 +4794,9 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true + rxjs@7.8.2: + resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} + safe-array-concat@1.1.3: resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} engines: {node: '>=0.4'} @@ -4848,9 +4815,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,8 +5108,8 @@ 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'} temp-dir@2.0.0: @@ -5154,8 +5120,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 +5165,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 +5400,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: @@ -5465,10 +5431,6 @@ packages: resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - write-file-atomic@5.0.1: - resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - ws@6.2.3: resolution: {integrity: sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==} peerDependencies: @@ -5492,8 +5454,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 @@ -5565,56 +5527,57 @@ snapshots: '@0no-co/graphql.web@1.2.0': {} - '@amplitude/analytics-browser@2.34.0': + '@amplitude/analytics-browser@2.33.1': dependencies: - '@amplitude/analytics-core': 2.37.0 - '@amplitude/plugin-autocapture-browser': 1.19.0 - '@amplitude/plugin-network-capture-browser': 1.7.8 - '@amplitude/plugin-page-url-enrichment-browser': 0.5.14 - '@amplitude/plugin-page-view-tracking-browser': 2.6.11 - '@amplitude/plugin-web-vitals-browser': 1.1.9 + '@amplitude/analytics-core': 2.35.0 + '@amplitude/plugin-autocapture-browser': 1.18.3 + '@amplitude/plugin-network-capture-browser': 1.7.3 + '@amplitude/plugin-page-url-enrichment-browser': 0.5.9 + '@amplitude/plugin-page-view-tracking-browser': 2.6.6 + '@amplitude/plugin-web-vitals-browser': 1.1.4 tslib: 2.8.1 '@amplitude/analytics-connector@1.6.4': {} - '@amplitude/analytics-core@2.37.0': + '@amplitude/analytics-core@2.35.0': dependencies: '@amplitude/analytics-connector': 1.6.4 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.32(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/analytics-core': 2.35.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': + '@amplitude/plugin-autocapture-browser@1.18.3': dependencies: - '@amplitude/analytics-core': 2.37.0 + '@amplitude/analytics-core': 2.35.0 + rxjs: 7.8.2 tslib: 2.8.1 - '@amplitude/plugin-network-capture-browser@1.7.8': + '@amplitude/plugin-network-capture-browser@1.7.3': dependencies: - '@amplitude/analytics-core': 2.37.0 + '@amplitude/analytics-core': 2.35.0 tslib: 2.8.1 - '@amplitude/plugin-page-url-enrichment-browser@0.5.14': + '@amplitude/plugin-page-url-enrichment-browser@0.5.9': dependencies: - '@amplitude/analytics-core': 2.37.0 + '@amplitude/analytics-core': 2.35.0 tslib: 2.8.1 - '@amplitude/plugin-page-view-tracking-browser@2.6.11': + '@amplitude/plugin-page-view-tracking-browser@2.6.6': dependencies: - '@amplitude/analytics-core': 2.37.0 + '@amplitude/analytics-core': 2.35.0 tslib: 2.8.1 - '@amplitude/plugin-web-vitals-browser@1.1.9': + '@amplitude/plugin-web-vitals-browser@1.1.4': dependencies: - '@amplitude/analytics-core': 2.37.0 + '@amplitude/analytics-core': 2.35.0 tslib: 2.8.1 web-vitals: 5.1.0 @@ -5624,25 +5587,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 +5621,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 +5639,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 +5684,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 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 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/traverse': 7.28.6 + '@babel/helper-wrap-function': 7.28.3 + '@babel/traverse': 7.28.5 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,18 +5742,18 @@ 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 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.5 '@babel/types': 7.28.6 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 +5762,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/traverse': 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.5 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/template': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/template': 7.27.2 - '@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/traverse': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/traverse': 7.28.5 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/traverse': 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.5 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/traverse': 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.5 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/types': 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.5 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 +6200,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 +6224,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 +6236,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 +6250,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 +6266,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,10 +6314,10 @@ 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.20(expo-router@6.0.21)(expo@54.0.30)(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 + '@expo/code-signing-certificates': 0.0.5 '@expo/config': 12.0.13 '@expo/config-plugins': 54.0.4 '@expo/devcert': 1.2.1 @@ -6321,15 +6325,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.12(expo@54.0.30) '@expo/osascript': 2.3.8 - '@expo/package-manager': 1.9.10 + '@expo/package-manager': 1.9.9 '@expo/plist': 0.4.8 - '@expo/prebuild-config': 54.0.8(expo@54.0.32) + '@expo/prebuild-config': 54.0.8(expo@54.0.30) '@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 +6348,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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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,23 +6375,24 @@ 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.21(f0d96ad57e690436ea0d7f67db788d54) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) transitivePeerDependencies: - bufferutil - graphql - supports-color - utf-8-validate - '@expo/code-signing-certificates@0.0.6': + '@expo/code-signing-certificates@0.0.5': dependencies: node-forge: 1.3.3 + nullthrows: 1.1.1 '@expo/config-plugins@54.0.4': dependencies: @@ -6421,7 +6426,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 +6440,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,11 +6491,11 @@ 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.12(expo@54.0.30)': dependencies: - '@babel/code-frame': 7.28.6 - '@babel/core': 7.28.6 - '@babel/generator': 7.28.6 + '@babel/code-frame': 7.27.1 + '@babel/core': 7.28.5 + '@babel/generator': 7.28.5 '@expo/config': 12.0.13 '@expo/env': 2.0.8 '@expo/json-file': 10.0.8 @@ -6505,24 +6510,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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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.30)(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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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: @@ -6554,14 +6559,14 @@ snapshots: '@expo/spawn-async': 1.7.2 exec-async: 2.2.0 - '@expo/package-manager@1.9.10': + '@expo/package-manager@1.9.9': dependencies: '@expo/json-file': 10.0.8 '@expo/spawn-async': 1.7.2 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 +6574,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.30)': dependencies: '@expo/config': 12.0.13 '@expo/config-plugins': 54.0.4 @@ -6578,7 +6583,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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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 +6600,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.10(expo@54.0.30)(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.10(expo@54.0.30)(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 +6669,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 +6682,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 +6720,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 +6738,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 @@ -6748,11 +6754,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@jest/pattern@30.0.1': - dependencies: - '@types/node': 25.1.0 - jest-regex-util: 30.0.1 - '@jest/reporters@29.7.0': dependencies: '@bcoe/v8-coverage': 0.2.3 @@ -6761,7 +6762,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 +6789,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 +6813,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 @@ -6830,42 +6831,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@jest/transform@30.2.0': - dependencies: - '@babel/core': 7.28.6 - '@jest/types': 30.2.0 - '@jridgewell/trace-mapping': 0.3.31 - babel-plugin-istanbul: 7.0.1 - chalk: 4.1.2 - convert-source-map: 2.0.0 - fast-json-stable-stringify: 2.1.0 - graceful-fs: 4.2.11 - jest-haste-map: 30.2.0 - jest-regex-util: 30.0.1 - jest-util: 30.2.0 - micromatch: 4.0.8 - pirates: 4.0.7 - slash: 3.0.0 - write-file-atomic: 5.0.1 - transitivePeerDependencies: - - supports-color - '@jest/types@29.6.3': dependencies: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 25.1.0 - '@types/yargs': 17.0.35 - chalk: 4.1.2 - - '@jest/types@30.2.0': - dependencies: - '@jest/pattern': 30.0.1 - '@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 +6866,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.57.0': dependencies: - playwright: 1.58.0 + playwright: 1.57.0 '@radix-ui/primitive@1.1.3': {} @@ -7098,83 +7069,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.30)(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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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) + '@babel/traverse': 7.28.5 + '@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) - '@babel/template': 7.28.6 - '@react-native/babel-plugin-codegen': 0.81.5(@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.27.2 + '@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 +7194,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.3(@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 +7224,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.3(@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.3(@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': @@ -7328,49 +7299,49 @@ snapshots: '@sentry/cli-darwin@2.55.0': optional: true - '@sentry/cli-darwin@3.1.0': + '@sentry/cli-darwin@3.0.1': optional: true '@sentry/cli-linux-arm64@2.55.0': optional: true - '@sentry/cli-linux-arm64@3.1.0': + '@sentry/cli-linux-arm64@3.0.1': optional: true '@sentry/cli-linux-arm@2.55.0': optional: true - '@sentry/cli-linux-arm@3.1.0': + '@sentry/cli-linux-arm@3.0.1': optional: true '@sentry/cli-linux-i686@2.55.0': optional: true - '@sentry/cli-linux-i686@3.1.0': + '@sentry/cli-linux-i686@3.0.1': optional: true '@sentry/cli-linux-x64@2.55.0': optional: true - '@sentry/cli-linux-x64@3.1.0': + '@sentry/cli-linux-x64@3.0.1': optional: true '@sentry/cli-win32-arm64@2.55.0': optional: true - '@sentry/cli-win32-arm64@3.1.0': + '@sentry/cli-win32-arm64@3.0.1': optional: true '@sentry/cli-win32-i686@2.55.0': optional: true - '@sentry/cli-win32-i686@3.1.0': + '@sentry/cli-win32-i686@3.0.1': optional: true '@sentry/cli-win32-x64@2.55.0': optional: true - '@sentry/cli-win32-x64@3.1.0': + '@sentry/cli-win32-x64@3.0.1': optional: true '@sentry/cli@2.55.0': @@ -7393,25 +7364,25 @@ snapshots: - encoding - supports-color - '@sentry/cli@3.1.0': + '@sentry/cli@3.0.1': dependencies: progress: 2.0.3 proxy-from-env: 1.1.0 undici: 6.23.0 which: 2.0.2 optionalDependencies: - '@sentry/cli-darwin': 3.1.0 - '@sentry/cli-linux-arm': 3.1.0 - '@sentry/cli-linux-arm64': 3.1.0 - '@sentry/cli-linux-i686': 3.1.0 - '@sentry/cli-linux-x64': 3.1.0 - '@sentry/cli-win32-arm64': 3.1.0 - '@sentry/cli-win32-i686': 3.1.0 - '@sentry/cli-win32-x64': 3.1.0 + '@sentry/cli-darwin': 3.0.1 + '@sentry/cli-linux-arm': 3.0.1 + '@sentry/cli-linux-arm64': 3.0.1 + '@sentry/cli-linux-i686': 3.0.1 + '@sentry/cli-linux-x64': 3.0.1 + '@sentry/cli-win32-arm64': 3.0.1 + '@sentry/cli-win32-i686': 3.0.1 + '@sentry/cli-win32-x64': 3.0.1 '@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.30)(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 +7391,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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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 +7411,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 +7421,55 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.1 - '@supabase/auth-js@2.93.2': + '@supabase/auth-js@2.89.0': dependencies: tslib: 2.8.1 - '@supabase/functions-js@2.93.2': + '@supabase/functions-js@2.89.0': dependencies: tslib: 2.8.1 - '@supabase/postgrest-js@2.93.2': + '@supabase/postgrest-js@2.89.0': dependencies: tslib: 2.8.1 - '@supabase/realtime-js@2.93.2': + '@supabase/realtime-js@2.89.0': 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.89.0': dependencies: iceberg-js: 0.8.1 tslib: 2.8.1 - '@supabase/supabase-js@2.93.2': + '@supabase/supabase-js@2.89.0': 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.89.0 + '@supabase/functions-js': 2.89.0 + '@supabase/postgrest-js': 2.89.0 + '@supabase/realtime-js': 2.89.0 + '@supabase/storage-js': 2.89.0 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': {} @@ -7509,30 +7480,30 @@ snapshots: '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.28.6 - '@babel/types': 7.28.6 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 '@types/babel__generator': 7.27.0 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.28.0 '@types/babel__generator@7.27.0': dependencies: - '@babel/types': 7.28.6 + '@babel/types': 7.28.5 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.28.6 - '@babel/types': 7.28.6 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 '@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 +7524,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 +7532,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 +7553,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 +7563,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 +7909,13 @@ 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) - 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): - dependencies: - '@babel/core': 7.28.6 - '@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: 29.6.3(@babel/core@7.28.5) chalk: 4.1.2 graceful-fs: 4.2.11 slash: 3.0.0 @@ -7966,7 +7924,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 @@ -7974,54 +7932,40 @@ snapshots: transitivePeerDependencies: - supports-color - babel-plugin-istanbul@7.0.1: - dependencies: - '@babel/helper-plugin-utils': 7.28.6 - '@istanbuljs/load-nyc-config': 1.1.0 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-instrument: 6.0.3 - test-exclude: 6.0.0 - transitivePeerDependencies: - - supports-color - 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 - babel-plugin-jest-hoist@30.2.0: + babel-plugin-polyfill-corejs2@0.4.14(@babel/core@7.28.5): dependencies: - '@types/babel__core': 7.20.5 - - babel-plugin-polyfill-corejs2@0.4.15(@babel/core@7.28.6): - 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 babel-plugin-react-compiler@1.0.0: dependencies: - '@babel/types': 7.28.6 + '@babel/types': 7.28.5 babel-plugin-react-native-web@0.21.2: {} @@ -8029,80 +7973,74 @@ 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.9(@babel/core@7.28.5)(@babel/runtime@7.28.4)(expo@54.0.30)(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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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-jest@30.2.0(@babel/core@7.28.6): - dependencies: - '@babel/core': 7.28.6 - 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 +8077,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 +8119,7 @@ snapshots: camelcase@6.3.0: {} - caniuse-lite@1.0.30001766: {} + caniuse-lite@1.0.30001762: {} chalk@2.4.2: dependencies: @@ -8200,7 +8138,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 +8147,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,8 +8160,6 @@ snapshots: ci-info@3.9.0: {} - ci-info@4.3.1: {} - cjs-module-lexer@1.4.3: {} cli-cursor@2.1.0: @@ -8337,17 +8273,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 +8459,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 +8542,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 +8608,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 +8646,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 +8663,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 +8681,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 +8693,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 +8736,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 +8756,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 +8781,7 @@ snapshots: esprima@4.0.1: {} - esquery@1.7.0: + esquery@1.6.0: dependencies: estraverse: 5.3.0 @@ -8861,7 +8797,7 @@ snapshots: event-target-shim@5.0.1: {} - eventemitter3@5.0.4: {} + eventemitter3@5.0.1: {} exec-async@2.2.0: {} @@ -8887,164 +8823,164 @@ 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.30)(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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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.30): 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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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.30)(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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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.12(expo@54.0.30)(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.30)(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.30) + expo-constants: 18.0.12(expo@54.0.30)(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.30) + expo-linking: 8.0.11(expo@54.0.30)(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.30)(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.30)(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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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.30): 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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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.30)(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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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.12(expo@54.0.30)(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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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.30): 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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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.30): 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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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.30) + expo-dev-menu: 7.0.18(expo@54.0.30) + expo-dev-menu-interface: 2.0.0(expo@54.0.30) + expo-manifests: 1.0.10(expo@54.0.30) + expo-updates-interface: 2.0.0(expo@54.0.30) transitivePeerDependencies: - supports-color - expo-dev-launcher@6.0.20(expo@54.0.32): + expo-dev-launcher@6.0.20(expo@54.0.30): 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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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.30) + expo-manifests: 1.0.10(expo@54.0.30) transitivePeerDependencies: - supports-color - expo-dev-menu-interface@2.0.0(expo@54.0.32): + expo-dev-menu-interface@2.0.0(expo@54.0.30): 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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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.30): 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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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.30) - expo-device@8.0.10(expo@54.0.32): + expo-device@8.0.10(expo@54.0.30): 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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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.30)(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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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.10(expo@54.0.30)(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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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.30)(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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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.30)(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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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.30): 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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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.30)(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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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.30)(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.12(expo@54.0.30)(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.30): 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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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 - expo-modules-autolinking@3.0.24: + expo-modules-autolinking@3.0.23: dependencies: '@expo/spawn-async': 1.7.2 chalk: 4.1.2 @@ -9052,27 +8988,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.21(f0d96ad57e690436ea0d7f67db788d54): 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.30)(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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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.12(expo@54.0.30)(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.30)(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 +9016,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 +9027,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,102 +9038,102 @@ 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.30): 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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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.30): 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.30) + expo: 54.0.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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.30)(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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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.30)(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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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.30): 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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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.15(expo@54.0.30)(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/code-signing-certificates': 0.0.5 '@expo/plist': 0.4.8 '@expo/spawn-async': 1.7.2 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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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.30) 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.30) 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.30)(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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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.20(expo-router@6.0.21)(expo@54.0.30)(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.12(expo@54.0.30) + '@expo/vector-icons': 15.0.3(expo-font@14.0.10(expo@54.0.30)(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) - 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) + babel-preset-expo: 54.0.9(@babel/core@7.28.5)(@babel/runtime@7.28.4)(expo@54.0.30)(react-refresh@0.14.2) + expo-asset: 12.0.12(expo@54.0.30)(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.12(expo@54.0.30)(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.30)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)) + expo-font: 14.0.10(expo@54.0.30)(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.30)(react@19.1.0) + expo-modules-autolinking: 3.0.23 + 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.30)(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 +9592,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 +9617,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 +9627,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 +9675,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 +9695,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 +9714,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 +9739,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 +9776,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 +9790,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 +9800,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 @@ -9876,21 +9812,6 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - jest-haste-map@30.2.0: - dependencies: - '@jest/types': 30.2.0 - '@types/node': 25.1.0 - anymatch: 3.1.3 - fb-watchman: 2.0.2 - graceful-fs: 4.2.11 - jest-regex-util: 30.0.1 - jest-util: 30.2.0 - jest-worker: 30.2.0 - micromatch: 4.0.8 - walker: 1.0.8 - optionalDependencies: - fsevents: 2.3.3 - jest-leak-detector@29.7.0: dependencies: jest-get-type: 29.6.3 @@ -9912,7 +9833,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 +9846,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): @@ -9934,8 +9855,6 @@ snapshots: jest-regex-util@29.6.3: {} - jest-regex-util@30.0.1: {} - jest-resolve-dependencies@29.7.0: dependencies: jest-regex-util: 29.6.3 @@ -9962,7 +9881,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 +9909,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 +9929,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,21 +9955,12 @@ 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 picomatch: 2.3.1 - jest-util@30.2.0: - dependencies: - '@jest/types': 30.2.0 - '@types/node': 25.1.0 - chalk: 4.1.2 - ci-info: 4.3.1 - graceful-fs: 4.2.11 - picomatch: 4.0.3 - jest-validate@29.7.0: dependencies: '@jest/types': 29.6.3 @@ -10064,7 +9974,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 +9983,17 @@ 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 - '@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 +10041,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 +10095,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 +10160,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 +10197,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 +10235,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 +10293,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 +10301,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 +10332,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 +10343,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 +10363,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 +10639,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 +10664,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 +10683,11 @@ snapshots: dependencies: find-up: 4.1.0 - playwright-core@1.58.0: {} + playwright-core@1.57.0: {} - playwright@1.58.0: + playwright@1.57.0: dependencies: - playwright-core: 1.58.0 + playwright-core: 1.57.0 optionalDependencies: fsevents: 2.3.2 @@ -10809,7 +10711,7 @@ snapshots: prelude-ls@1.2.1: {} - prettier@3.8.1: {} + prettier@3.7.4: {} pretty-bytes@5.6.0: {} @@ -10905,84 +10807,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 +10897,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 +10995,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 +11074,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: {} @@ -11208,6 +11110,10 @@ snapshots: dependencies: glob: 7.2.3 + rxjs@7.8.2: + dependencies: + tslib: 2.8.1 + safe-array-concat@1.1.3: dependencies: call-bind: 1.0.8 @@ -11231,7 +11137,7 @@ snapshots: safer-buffer@2.1.2: {} - sax@1.4.4: {} + sax@1.4.3: {} saxes@6.0.0: dependencies: @@ -11543,7 +11449,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 +11464,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 +11513,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 +11762,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 +11771,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 @@ -11902,18 +11808,13 @@ snapshots: imurmurhash: 0.1.4 signal-exit: 3.0.7 - write-file-atomic@5.0.1: - dependencies: - imurmurhash: 0.1.4 - signal-exit: 4.1.0 - ws@6.2.3: dependencies: async-limiter: 1.0.1 ws@7.5.10: {} - ws@8.19.0: {} + ws@8.18.3: {} xcode@3.0.1: dependencies: @@ -11924,7 +11825,7 @@ snapshots: xml2js@0.6.0: dependencies: - sax: 1.4.4 + sax: 1.4.3 xmlbuilder: 11.0.1 xmlbuilder@11.0.1: {} From abec902263dbed9b14691e8e4e0a3732943680c2 Mon Sep 17 00:00:00 2001 From: Mnehmos Date: Sun, 18 Jan 2026 09:52:42 -0700 Subject: [PATCH 20/22] refactor(profile): use ref-based mount guard in SymmetricRevealSection Replace local isMounted boolean with ref-based pattern: - Add isMountedRef at component scope for persistent mount state - Add cleanup useEffect to set ref to false on unmount - Keep per-effect isActive flag for effect cancellation - Guard state updates with both isActive and isMountedRef.current This prevents stale closure issues that could occur with the local isMounted pattern when effect dependencies change. Co-Authored-By: Claude Opus 4.5 --- CHANGELOG.md | 2 +- components/profile/SymmetricRevealSection.tsx | 27 +++++++++++++------ 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e530950..a9293d15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,7 +58,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 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 by adding isMounted cleanup guard +- Fix potential memory leak in SymmetricRevealSection using ref-based mount guard pattern ## [1.3.0] - 2026-01-27 diff --git a/components/profile/SymmetricRevealSection.tsx b/components/profile/SymmetricRevealSection.tsx index f4815509..4e2cf5f9 100644 --- a/components/profile/SymmetricRevealSection.tsx +++ b/components/profile/SymmetricRevealSection.tsx @@ -1,4 +1,4 @@ -import React, { useMemo, useState, useEffect } from 'react'; +import React, { useMemo, useState, useEffect, useRef } from 'react'; import { View, Text, StyleSheet, Switch, ActivityIndicator } from 'react-native'; import { Eye, EyeOff, Check, Clock } from 'lucide-react-native'; import type { ThemeColors } from '@/contexts/ThemeContext'; @@ -93,12 +93,23 @@ export default function SymmetricRevealSection({ 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(() => { - let isMounted = true; + // Per-effect active flag for cancellation + let isActive = true; if (revealState !== 'mutual') { setOtherHandles({}); @@ -112,8 +123,8 @@ export default function SymmetricRevealSection({ relationship_id: relationshipId, }); - // Guard: Only update state if component is still mounted - if (!isMounted) return; + // 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), { @@ -124,14 +135,14 @@ export default function SymmetricRevealSection({ setOtherHandles((data as ExternalHandles) || {}); } catch (err) { - // Guard: Only update state if component is still mounted - if (!isMounted) return; + // 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 (isMounted) { + if (isActive && isMountedRef.current) { setIsLoadingHandles(false); } } @@ -140,7 +151,7 @@ export default function SymmetricRevealSection({ fetchHandles(); return () => { - isMounted = false; + isActive = false; }; }, [revealState, relationshipId]); From c50391f5b051de3ab495b15c2fede4a230862a6c Mon Sep 17 00:00:00 2001 From: Mnehmos Date: Sun, 18 Jan 2026 10:03:18 -0700 Subject: [PATCH 21/22] refactor(profile): rename boolean props to use is/has prefixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Align SymmetricRevealSection props with naming conventions: - myConsent → hasMyConsent - theirConsent → hasTheirConsent - disabled → isDisabled Updated props interface, JSDoc example, helper function params, component destructuring, JSX usages, and RelationshipCard call site. Co-Authored-By: Claude Opus 4.5 --- components/profile/RelationshipCard.tsx | 4 +-- components/profile/SymmetricRevealSection.tsx | 36 +++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/components/profile/RelationshipCard.tsx b/components/profile/RelationshipCard.tsx index d6ec0daa..46e5619f 100644 --- a/components/profile/RelationshipCard.tsx +++ b/components/profile/RelationshipCard.tsx @@ -154,13 +154,13 @@ export default function RelationshipCard({ {relationship && onConsentChange && ( void; /** Whether actions are disabled */ - disabled?: boolean; + isDisabled?: boolean; } // ============================================================================= @@ -47,10 +47,10 @@ interface SymmetricRevealSectionProps { /** * Get the reveal state based on both parties' consent. */ -function getRevealState(myConsent: boolean, theirConsent: boolean): RevealState { - if (myConsent && theirConsent) return 'mutual'; - if (myConsent && !theirConsent) return 'you_pending'; - if (!myConsent && theirConsent) return 'them_pending'; +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'; } @@ -65,8 +65,8 @@ function getRevealState(myConsent: boolean, theirConsent: boolean): RevealState * @example * ```tsx * createStyles(theme), [theme]); - const revealState = getRevealState(myConsent, theirConsent); + const revealState = getRevealState(hasMyConsent, hasTheirConsent); // State for consent-checked handles fetched via RPC const [otherHandles, setOtherHandles] = useState({}); @@ -164,7 +164,7 @@ export default function SymmetricRevealSection({ {/* Consent Toggle */} - {myConsent ? ( + {hasMyConsent ? ( ) : ( @@ -172,7 +172,7 @@ export default function SymmetricRevealSection({ Share my contact info - {myConsent + {hasMyConsent ? hasMyHandles ? 'Your handles will be visible when mutual' : 'Add contact methods in Settings first' @@ -181,11 +181,11 @@ export default function SymmetricRevealSection({ From 51d01f2620fe6f046362e046a9251bedf3cd5d30 Mon Sep 17 00:00:00 2001 From: Mnehmos Date: Mon, 2 Feb 2026 16:44:30 -0700 Subject: [PATCH 22/22] fix: address final CodeRabbit review comments CHANGELOG.md: - Reword "Add" entries in Changed section to use change verbs - Move boolean prop renaming from Fixed to Changed section SymmetricRevealSection.tsx: - Reset isLoadingHandles when revealState switches away from 'mutual' Co-Authored-By: Claude Opus 4.5 --- CHANGELOG.md | 7 +- components/profile/SymmetricRevealSection.tsx | 1 + pnpm-lock.yaml | 835 +++++++++++------- 3 files changed, 507 insertions(+), 336 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9293d15..1088e7b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,12 +32,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 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 -- Add canonical section dividers to `lib/platform-icons.tsx` for better code organization -- Add size verification tests to platform-icons test suite -- Replace `Math.random()` with cryptographically secure `expo-crypto` for invite code generation +- 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 diff --git a/components/profile/SymmetricRevealSection.tsx b/components/profile/SymmetricRevealSection.tsx index 1829aa5f..0bdbcaea 100644 --- a/components/profile/SymmetricRevealSection.tsx +++ b/components/profile/SymmetricRevealSection.tsx @@ -113,6 +113,7 @@ export default function SymmetricRevealSection({ if (revealState !== 'mutual') { setOtherHandles({}); + setIsLoadingHandles(false); return; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 32743d89..41011240 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,11 +12,11 @@ importers: .: dependencies: '@amplitude/analytics-browser': - specifier: ^2.33.1 - version: 2.33.1 + specifier: ^2.34.0 + version: 2.34.0 '@amplitude/analytics-react-native': - specifier: ^1.5.32 - version: 1.5.32(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + specifier: ^1.5.37 + 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.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) @@ -28,7 +28,7 @@ importers: version: 0.4.1 '@expo/vector-icons': specifier: ^15.0.3 - version: 15.0.3(expo-font@14.0.10(expo@54.0.30)(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) + 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.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) @@ -37,25 +37,25 @@ importers: 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.30)(react-native@0.81.5(@babel/core@7.28.5)(@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.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.3 - version: 2.9.3(@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) + specifier: ^2.9.5 + 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.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.0.1 - version: 3.0.1 + specifier: ^3.1.0 + version: 3.1.0 '@sentry/react-native': specifier: 7.2.0 - version: 7.2.0(expo@54.0.30)(react-native@0.81.5(@babel/core@7.28.5)(@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.89.0 - version: 2.89.0 + specifier: ^2.93.2 + version: 2.93.3 '@vercel/analytics': specifier: ^1.6.1 version: 1.6.1(react@19.1.0) @@ -63,77 +63,77 @@ importers: specifier: ^4.1.0 version: 4.1.0 expo: - specifier: ~54.0.30 - version: 54.0.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + specifier: ~54.0.32 + 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.30)(react-native@0.81.5(@babel/core@7.28.5)(@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.30) + version: 7.0.8(expo@54.0.33) expo-auth-session: specifier: ^7.0.10 - version: 7.0.10(expo@54.0.30)(react-native@0.81.5(@babel/core@7.28.5)(@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.30)(react-native@0.81.5(@babel/core@7.28.5)(@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.30) + version: 1.0.10(expo@54.0.33) expo-clipboard: specifier: ^8.0.8 - version: 8.0.8(expo@54.0.30)(react-native@0.81.5(@babel/core@7.28.5)(@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.12 - version: 18.0.12(expo@54.0.30)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)) + specifier: ~18.0.13 + 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.30) + version: 15.0.8(expo@54.0.33) expo-dev-client: specifier: ~6.0.20 - version: 6.0.20(expo@54.0.30) + version: 6.0.20(expo@54.0.33) expo-device: specifier: ^8.0.10 - version: 8.0.10(expo@54.0.30) + version: 8.0.10(expo@54.0.33) expo-font: - specifier: ~14.0.10 - version: 14.0.10(expo@54.0.30)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + specifier: ~14.0.11 + 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.30)(react-native@0.81.5(@babel/core@7.28.5)(@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.30)(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) + 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.30) + version: 0.10.8(expo@54.0.33) expo-linking: specifier: ~8.0.11 - version: 8.0.11(expo@54.0.30)(react-native@0.81.5(@babel/core@7.28.5)(@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.21 - version: 6.0.21(f0d96ad57e690436ea0d7f67db788d54) + specifier: ~6.0.22 + version: 6.0.23(34d93f6a50204070cbf56f6391ebf5fc) expo-secure-store: specifier: ^15.0.8 - version: 15.0.8(expo@54.0.30) + version: 15.0.8(expo@54.0.33) expo-splash-screen: specifier: ~31.0.13 - version: 31.0.13(expo@54.0.30) + 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.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.30)(react-native@0.81.5(@babel/core@7.28.5)(@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.30)(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)) + 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.15 - version: 29.0.15(expo@54.0.30)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + specifier: ^29.0.16 + 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.30)(react-native@0.81.5(@babel/core@7.28.5)(@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.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) @@ -187,8 +187,8 @@ importers: version: 2.2.0 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.57.0 + specifier: ^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.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) @@ -199,11 +199,11 @@ importers: specifier: ~19.1.17 version: 19.1.17 babel-jest: - specifier: ^29.7.0 - version: 29.7.0(@babel/core@7.28.5) + specifier: ^30.2.0 + version: 30.2.0(@babel/core@7.28.5) babel-preset-expo: - specifier: ^54.0.9 - version: 54.0.9(@babel/core@7.28.5)(@babel/runtime@7.28.4)(expo@54.0.30)(react-refresh@0.14.2) + specifier: ^54.0.10 + 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 @@ -232,8 +232,8 @@ importers: specifier: ^0.83.3 version: 0.83.3 prettier: - specifier: ^3.7.4 - version: 3.7.4 + specifier: ^3.8.1 + version: 3.8.1 react-test-renderer: specifier: 19.1.0 version: 19.1.0(react@19.1.0) @@ -251,35 +251,35 @@ packages: graphql: optional: true - '@amplitude/analytics-browser@2.33.1': - resolution: {integrity: sha512-93wZjuAFJ7QdyptF82i1pezm5jKuBWITHI++XshDgpks1RstJvJ9n11Ak8MnE4L2BGQ93XDN2aVEHfmQkt0/Pw==} + '@amplitude/analytics-browser@2.34.0': + resolution: {integrity: sha512-a5AeUBs6AbgfEPBNVP1/FM8+0ZBjIzbfYcVJq2Lkvb0EaeEI09vrl+zFeJSDcjg5nsDBmz7oKV8j2kbGJHbZ9w==} '@amplitude/analytics-connector@1.6.4': resolution: {integrity: sha512-SpIv0IQMNIq6SH3UqFGiaZyGSc7PBZwRdq7lvP0pBxW8i4Ny+8zwI0pV+VMfMHQwWY3wdIbWw5WQphNjpdq1/Q==} - '@amplitude/analytics-core@2.35.0': - resolution: {integrity: sha512-7RmHYELXCGu8yuO9D6lEXiqkMtiC5sePNhCWmwuP30dneDYHtH06gaYvAFH/YqOFuE6enwEEJfFYtcaPhyiqtA==} + '@amplitude/analytics-core@2.37.0': + resolution: {integrity: sha512-/2vIyquLMSA29MMM901d5DOhBZ5bc6Qf4s0KVfRk8Avn90mC7KlE5SQiHOAW8+63o5aT1NtB0djrY4cq8Y8Opw==} - '@amplitude/analytics-react-native@1.5.32': - resolution: {integrity: sha512-/85s1IttVnMK934ZwGDBKt+jEdoaXdMUstBPVF/SvjyP9Gqvk+D19IbKyeeVz/ri7H7mntqQxwIC+KDT0rqj1Q==} + '@amplitude/analytics-react-native@1.5.37': + resolution: {integrity: sha512-CXmKkQopPtonsdt9A3m27Y7NlUOrOwCdxIchkzo6IBpItfqqvxMW09kC67YOCxgmHuzTBzB/1MpDB9nwNCk9jw==} peerDependencies: react: '*' react-native: '*' - '@amplitude/plugin-autocapture-browser@1.18.3': - resolution: {integrity: sha512-njYque5t1QCEEe5V8Ls4yVVklTM6V7OXxBk6pqznN/hj/Pc4X8Wjy898pZ2VtbnvpagBKKzGb5B6Syl8OXiicw==} + '@amplitude/plugin-autocapture-browser@1.19.0': + resolution: {integrity: sha512-ga0TXxE87wNMgFvVUPE7a+HGMqMz9/Gy6C7ux3cgcuy8D7Z/Te5iMZmMadK/ztmQ3/pSZjYv9iDvOFsT6ywMZw==} - '@amplitude/plugin-network-capture-browser@1.7.3': - resolution: {integrity: sha512-zfWgAN7g6AigJAsgrGmlgVwydOHH6XvweBoxhU+qEvRydboiIVCDLSxuXczUsBG7kYVLWRdBK1DYoE5J7lqTGA==} + '@amplitude/plugin-network-capture-browser@1.7.8': + resolution: {integrity: sha512-SZj3m3O8yI040+Nto9uLi5eM0AclEi8hkZttLSKKVCJVIdi5xE1z9oC7lTir1h2b198unTqP2o7cmzEqI1wBoA==} - '@amplitude/plugin-page-url-enrichment-browser@0.5.9': - resolution: {integrity: sha512-TqdELx4WrdRutCjHUFUzum/f/UjhbdTZw0UKkYFAj5gwAKDjaPEjL4waRvINOTaVLsne1A6ck4KEMfC8AKByFw==} + '@amplitude/plugin-page-url-enrichment-browser@0.5.14': + resolution: {integrity: sha512-vaAGxMgxQbsRKWtIKiMp3kVdg4RSsAzwlffDw7ifTw1yObVV2vVeBPb/O24cLf49NrhZ3ZuJlaxGmRxoY4FWow==} - '@amplitude/plugin-page-view-tracking-browser@2.6.6': - resolution: {integrity: sha512-dBcJlrdKgPzSgS3exDRRrMLqhIaOjwlIy7o8sEMn1PpMawERlbumSSdtfII6L4L67HYUPo4PY4Kp4acqSzaLvQ==} + '@amplitude/plugin-page-view-tracking-browser@2.6.11': + resolution: {integrity: sha512-/UqXipdOWOsmn8Uw1BibOfgLMvjE8YYYI8bXL2vZ6D2mk6c0FdGe17BmccsLr1LVGx3HObZoIGnxje+0X1j07w==} - '@amplitude/plugin-web-vitals-browser@1.1.4': - resolution: {integrity: sha512-XQXI9OjTNSz2yi0lXw2VYMensDzzSkMCfvXNniTb1LgnHwBcQ1JWPcTqHLPFrvvNckeIdOT78vjs7yA+c1FyzA==} + '@amplitude/plugin-web-vitals-browser@1.1.9': + resolution: {integrity: sha512-hVGsJWtJQpHlpfC+IubCixe7KNmqaDkWO0XkwkLkn4T/FjFgK+rp9edBJr5KSi0enfcN+49DxJ4qQKu7NncIMA==} '@amplitude/ua-parser-js@0.7.33': resolution: {integrity: sha512-wKEtVR4vXuPT9cVEIJkYWnlF++Gx3BdLatPBM+SZ1ztVIvnhdGBZR/mn9x/PzyrMcRlZmyi6L56I2J3doVBnjA==} @@ -880,8 +880,8 @@ packages: '@expo-google-fonts/jetbrains-mono@0.4.1': resolution: {integrity: sha512-CslACrtMOcRwoWXCO7OMEI+9w3fukWSoBtvNz46OqPoogEuuoY0tkDY1O8sFumk8t0pC6Cx0Xr95O0TOQhpkug==} - '@expo/cli@54.0.20': - resolution: {integrity: sha512-cwsXmhftvS0p9NNYOhXGnicBAZl9puWwRt19Qq5eQ6njLnaj8WvcR+kDZyADtgZxBsZiyVlrKXvnjt43HXywQA==} + '@expo/cli@54.0.23': + resolution: {integrity: sha512-km0h72SFfQCmVycH/JtPFTVy69w6Lx1cHNDmfLfQqgKFYeeHTjx7LVDP4POHCtNxFP2UeRazrygJhlh4zz498g==} hasBin: true peerDependencies: expo: '*' @@ -893,8 +893,8 @@ packages: react-native: optional: true - '@expo/code-signing-certificates@0.0.5': - resolution: {integrity: sha512-BNhXkY1bblxKZpltzAx98G2Egj9g1Q+JRcvR7E99DOj862FTCX+ZPsAUtPTr7aHxwtrL7+fL3r0JSmM9kBm+Bw==} + '@expo/code-signing-certificates@0.0.6': + resolution: {integrity: sha512-iNe0puxwBNEcuua9gmTGzq+SuMDa0iATai1FlFTMHJ/vUmKvN/V//drXoLJkVb5i5H3iE/n/qIJxyoBnXouD0w==} '@expo/config-plugins@54.0.4': resolution: {integrity: sha512-g2yXGICdoOw5i3LkQSDxl2Q5AlQCrG7oniu0pCPPO+UxGb7He4AFqSvPSy8HpRUj55io17hT62FTjYRD+d6j3Q==} @@ -932,8 +932,8 @@ packages: '@expo/json-file@10.0.8': resolution: {integrity: sha512-9LOTh1PgKizD1VXfGQ88LtDH0lRwq9lsTb4aichWTWSWqy3Ugfkhfm3BhzBIkJJfQQ5iJu3m/BoRlEIjoCGcnQ==} - '@expo/metro-config@54.0.12': - resolution: {integrity: sha512-Xhv1z/ak/cuJWeLxlnWr2u22q2AM/klASbjpP5eE34y91lGWa2NUwrFWoS830MhJ6kuAqtGdoQhwyPa3TES7sA==} + '@expo/metro-config@54.0.14': + resolution: {integrity: sha512-hxpLyDfOR4L23tJ9W1IbJJsG7k4lv2sotohBm/kTYyiG+pe1SYCAWsRmgk+H42o/wWf/HQjE5k45S5TomGLxNA==} peerDependencies: expo: '*' peerDependenciesMeta: @@ -958,8 +958,8 @@ packages: resolution: {integrity: sha512-/TuOZvSG7Nn0I8c+FcEaoHeBO07yu6vwDgk7rZVvAXoeAK5rkA09jRyjYsZo+0tMEFaToBeywA6pj50Mb3ny9w==} engines: {node: '>=12'} - '@expo/package-manager@1.9.9': - resolution: {integrity: sha512-Nv5THOwXzPprMJwbnXU01iXSrCp3vJqly9M4EJ2GkKko9Ifer2ucpg7x6OUsE09/lw+npaoUnHMXwkw7gcKxlg==} + '@expo/package-manager@1.9.10': + resolution: {integrity: sha512-axJm+NOj3jVxep49va/+L3KkF3YW/dkV+RwzqUJedZrv4LeTqOG4rhrCaCPXHTvLqCTDKu6j0Xyd28N7mnxsGA==} '@expo/plist@0.4.8': resolution: {integrity: sha512-pfNtErGGzzRwHP+5+RqswzPDKkZrx+Cli0mzjQaus1ZWFsog5ibL+nVT3NcporW51o8ggnt7x813vtRbPiyOrQ==} @@ -1102,6 +1102,10 @@ packages: resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/pattern@30.0.1': + resolution: {integrity: sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/reporters@29.7.0': resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1135,10 +1139,18 @@ packages: resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/transform@30.2.0': + resolution: {integrity: sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/types@29.6.3': resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/types@30.2.0': + resolution: {integrity: sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -1165,8 +1177,8 @@ packages: resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} engines: {node: '>=12.4.0'} - '@playwright/test@1.57.0': - resolution: {integrity: sha512-6TyEnHgd6SArQO8UO2OMTxshln3QMWBtPGrOCgs3wVEmQmwyuNtB10IZMfmYDE0riwNR1cu4q+pPcxMVtaG3TA==} + '@playwright/test@1.58.1': + resolution: {integrity: sha512-6LdVIUERWxQMmUSSQi0I53GgCBYgM2RpGngCPY7hSeju+VrKjq3lvs7HpJoPbDiY5QM5EYRtRX5fvrinnMAz3w==} engines: {node: '>=18'} hasBin: true @@ -1495,11 +1507,11 @@ packages: peerDependencies: react: '>= 18.2.0' - '@react-navigation/elements@2.9.3': - resolution: {integrity: sha512-3+eyvWiVPIEf6tN9UdduhOEHcTuNe3R5WovgiVkfH9+jApHMTZDc2loePTpY/i2HDJhObhhChpJzO6BVjrpdYQ==} + '@react-navigation/elements@2.9.5': + resolution: {integrity: sha512-iHZU8rRN1014Upz73AqNVXDvSMZDh5/ktQ1CMe21rdgnOY79RWtHHBp9qOS3VtqlUVYGkuX5GEw5mDt4tKdl0g==} peerDependencies: '@react-native-masked-view/masked-view': '>= 0.2.0' - '@react-navigation/native': ^7.1.26 + '@react-navigation/native': ^7.1.28 react: '>= 18.2.0' react-native: '*' react-native-safe-area-context: '>= 4.0.0' @@ -1557,8 +1569,8 @@ packages: engines: {node: '>=10'} os: [darwin] - '@sentry/cli-darwin@3.0.1': - resolution: {integrity: sha512-CRE6+7hEvQsu+hI8IxtH8b3MFOv4iLIZL3WKh+nDFnMzjyG94TjVOGicDguN9NIVY+9cr1dra2Xqb7E1FF08bw==} + '@sentry/cli-darwin@3.1.0': + resolution: {integrity: sha512-xT1WlCHenGGO29Lq/wKaIthdqZzNzZhlPs7dXrzlBx9DyA2Jnl0g7WEau0oWi8GyJGVRXCJMiCydR//Tb5qVwA==} engines: {node: '>=18'} os: [darwin] @@ -1568,8 +1580,8 @@ packages: cpu: [arm64] os: [linux, freebsd, android] - '@sentry/cli-linux-arm64@3.0.1': - resolution: {integrity: sha512-Y4M33legybpXiq/iunKS5kFI5IckYlHuqqhcFe642SUHwSLieTK+ige16lxFnjZ8aXtoZggp3s4GTLNXezoMYw==} + '@sentry/cli-linux-arm64@3.1.0': + resolution: {integrity: sha512-Jm/iHLKiHxrZYlAq2tT07amiegEVCOAQT9Unilr6djjcZzS2tcI9ThSRQvjP9tFpFRKop+NyNGE3XHXf69r00g==} engines: {node: '>=18'} cpu: [arm64] os: [linux, freebsd, android] @@ -1580,8 +1592,8 @@ packages: cpu: [arm] os: [linux, freebsd, android] - '@sentry/cli-linux-arm@3.0.1': - resolution: {integrity: sha512-uUfVgefHooIh1Zd0EjKIrIPdDkVMiH6heiMuOYFF7C3FmT04V56wmcDs27hoylPTKef/uZamAd9tijCSxvYGHg==} + '@sentry/cli-linux-arm@3.1.0': + resolution: {integrity: sha512-kbP3/8/Ct/Jbm569KDXbFIyMyPypIegObvIT7LdSsfdYSZdBd396GV7vUpSGKiLUVVN0xjn8OqQ48AVGfjmuMg==} engines: {node: '>=18'} cpu: [arm] os: [linux, freebsd, android] @@ -1592,8 +1604,8 @@ packages: cpu: [x86, ia32] os: [linux, freebsd, android] - '@sentry/cli-linux-i686@3.0.1': - resolution: {integrity: sha512-VTEyFB2P8BzhNb6i/ihBlPglj8DPwlsyGdwxZcPsXMWZ7BF4Vfg6F3YRvtyyQP+jMshXOAwM7EbYsKoFBRX7zg==} + '@sentry/cli-linux-i686@3.1.0': + resolution: {integrity: sha512-f/PK/EGK5vFOy7LC4Riwb+BEE20Nk7RbEFEMjvRq26DpETCrZYUGlbpIKvJFKOaUmr79aAkFCA/EjJiYfcQP2Q==} engines: {node: '>=18'} cpu: [x86, ia32] os: [linux, freebsd, android] @@ -1604,8 +1616,8 @@ packages: cpu: [x64] os: [linux, freebsd, android] - '@sentry/cli-linux-x64@3.0.1': - resolution: {integrity: sha512-kYHkvYVJfecQcMQNiTBTLlNNHKvXMqH/WxQgVGX4L+vCH7Gd+L/AceZMBf/fZcyeh45IGqOnj1CH9zHhSlDXag==} + '@sentry/cli-linux-x64@3.1.0': + resolution: {integrity: sha512-T+v8x1ujhixZrOrH0sVhsW6uLwK4n0WS+B+5xV46WqUKe32cbYotursp2y53ROjgat8SQDGeP/VnC0Qa3Y2fEA==} engines: {node: '>=18'} cpu: [x64] os: [linux, freebsd, android] @@ -1616,8 +1628,8 @@ packages: cpu: [arm64] os: [win32] - '@sentry/cli-win32-arm64@3.0.1': - resolution: {integrity: sha512-4k7aB77HX0H8qxVA2ei+wUC5L4TzHsecSx80wR215UX05ImOXlC5QzKYF7Q7Fn5Xa8L26r5qRLeciUzSupg3bw==} + '@sentry/cli-win32-arm64@3.1.0': + resolution: {integrity: sha512-2DIPq6aW2DC34EDC9J0xwD+9BpFnKdFGdIcQUZMS+5pXlU6V7o8wpZxZAM8TdYNmsPkkQGKp7Dhl/arWpvNgrw==} engines: {node: '>=18'} cpu: [arm64] os: [win32] @@ -1628,8 +1640,8 @@ packages: cpu: [x86, ia32] os: [win32] - '@sentry/cli-win32-i686@3.0.1': - resolution: {integrity: sha512-Y1RmBZuEHOHISeO5ma1hoTNIVSTw69tAGfhz2ckvv9naoeLYEj4ehJ9XJof42n0Yn9GQXtC1O2ugbBPCwq7ABg==} + '@sentry/cli-win32-i686@3.1.0': + resolution: {integrity: sha512-2NuywEiiZn6xJ1yAV2xjv/nuHiy6kZU5XR3RSAIrPdEZD1nBoMsH/gB2FufQw58Ziz/7otFcX+vtGpJjbIT5mQ==} engines: {node: '>=18'} cpu: [x86, ia32] os: [win32] @@ -1640,8 +1652,8 @@ packages: cpu: [x64] os: [win32] - '@sentry/cli-win32-x64@3.0.1': - resolution: {integrity: sha512-ZL1uBswTnDc0BYU0zEyA4zP+d7He88x9hghww95uFZZr4Cf8tp8Pg6VaJT7G4AVImGSsJ1DC9kVCtMD9mU2A/g==} + '@sentry/cli-win32-x64@3.1.0': + resolution: {integrity: sha512-Ip405Yqdrr+l9TImsZOJz6c9Nb4zvXcmtOIBKLHc9cowpfXfmlqsHbDp7Xh4+k4L0uLr9i+8ilgQ6ypcuF4UCg==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -1651,8 +1663,8 @@ packages: engines: {node: '>= 10'} hasBin: true - '@sentry/cli@3.0.1': - resolution: {integrity: sha512-E2SAmRjJIQ1EUSc3/YnKy2q+rIlAR8YQ2m//w3Uvc/sM07o5b31M9cqrEDleIHUrlk0w3wqI3xCivAXd33jILQ==} + '@sentry/cli@3.1.0': + resolution: {integrity: sha512-ngnx6E8XjXpg1uzma45INfKCS8yurb/fl3cZdXTCa2wmek8b4N6WIlmOlTKFTBrV54OauF6mloJxAlpuzoQR6g==} engines: {node: '>= 18'} hasBin: true @@ -1693,28 +1705,28 @@ packages: '@sinonjs/fake-timers@10.3.0': resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} - '@supabase/auth-js@2.89.0': - resolution: {integrity: sha512-wiWZdz8WMad8LQdJMWYDZ2SJtZP5MwMqzQq3ehtW2ngiI3UTgbKiFrvMUUS3KADiVlk4LiGfODB2mrYx7w2f8w==} + '@supabase/auth-js@2.93.3': + resolution: {integrity: sha512-JdnkHZPKexVGSNONtu89RHU4bxz3X9kxx+f5ZnR5osoCIX+vs/MckwWRPZEybAEvlJXt5xjomDb3IB876QCxWQ==} engines: {node: '>=20.0.0'} - '@supabase/functions-js@2.89.0': - resolution: {integrity: sha512-XEueaC5gMe5NufNYfBh9kPwJlP5M2f+Ogr8rvhmRDAZNHgY6mI35RCkYDijd92pMcNM7g8pUUJov93UGUnqfyw==} + '@supabase/functions-js@2.93.3': + resolution: {integrity: sha512-qWO0gHNDm/5jRjROv/nv9L6sYabCWS1kzorOLUv3kqCwRvEJLYZga93ppJPrZwOgoZfXmJzvpjY8fODA4HQfBw==} engines: {node: '>=20.0.0'} - '@supabase/postgrest-js@2.89.0': - resolution: {integrity: sha512-/b0fKrxV9i7RNOEXMno/I1862RsYhuUo+Q6m6z3ar1f4ulTMXnDfv0y4YYxK2POcgrOXQOgKYQx1eArybyNvtg==} + '@supabase/postgrest-js@2.93.3': + resolution: {integrity: sha512-+iJ96g94skO2e4clsRSmEXg22NUOjh9BziapsJSAvnB1grOBf/BA8vGtCHjNOA+Z6lvKXL1jwBqcL9+fS1W/Lg==} engines: {node: '>=20.0.0'} - '@supabase/realtime-js@2.89.0': - resolution: {integrity: sha512-aMOvfDb2a52u6PX6jrrjvACHXGV3zsOlWRzZsTIOAJa0hOVvRp01AwC1+nLTGUzxzezejrYeCX+KnnM1xHdl+w==} + '@supabase/realtime-js@2.93.3': + resolution: {integrity: sha512-gnYpcFzwy8IkezRP4CDbT5I8jOsiOjrWrqTY1B+7jIriXsnpifmlM6RRjLBm9oD7OwPG0/WksniGPdKW67sXOA==} engines: {node: '>=20.0.0'} - '@supabase/storage-js@2.89.0': - resolution: {integrity: sha512-6zKcXofk/M/4Eato7iqpRh+B+vnxeiTumCIP+Tz26xEqIiywzD9JxHq+udRrDuv6hXE+pmetvJd8n5wcf4MFRQ==} + '@supabase/storage-js@2.93.3': + resolution: {integrity: sha512-cw4qXiLrx3apglDM02Tx/w/stvFlrkKocC6vCvuFAz3JtVEl1zH8MUfDQDTH59kJAQVaVdbewrMWSoBob7REnA==} engines: {node: '>=20.0.0'} - '@supabase/supabase-js@2.89.0': - resolution: {integrity: sha512-KlaRwSfFA0fD73PYVMHj5/iXFtQGCcX7PSx0FdQwYEEw9b2wqM7GxadY+5YwcmuEhalmjFB/YvqaoNVF+sWUlg==} + '@supabase/supabase-js@2.93.3': + resolution: {integrity: sha512-paUqEqdBI9ztr/4bbMoCgeJ6M8ZTm2fpfjSOlzarPuzYveKFM20ZfDZqUpi9CFfYagYj5Iv3m3ztUjaI9/tM1w==} engines: {node: '>=20.0.0'} '@testing-library/react-native@13.3.3': @@ -1909,41 +1921,49 @@ packages: resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==} cpu: [arm64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-arm64-musl@1.11.1': resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==} cpu: [arm64] os: [linux] + libc: [musl] '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==} cpu: [ppc64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==} cpu: [riscv64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==} cpu: [riscv64] os: [linux] + libc: [musl] '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==} cpu: [s390x] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-x64-gnu@1.11.1': resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==} cpu: [x64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-x64-musl@1.11.1': resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==} cpu: [x64] os: [linux] + libc: [musl] '@unrs/resolver-binding-wasm32-wasi@1.11.1': resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==} @@ -2163,14 +2183,28 @@ packages: peerDependencies: '@babel/core': ^7.8.0 + babel-jest@30.2.0: + resolution: {integrity: sha512-0YiBEOxWqKkSQWL9nNGGEgndoeL0ZpWrbLMNL5u/Kaxrli3Eaxlt3ZtIDktEvXt4L/R9r3ODr2zKwGM/2BjxVw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + '@babel/core': ^7.11.0 || ^8.0.0-0 + babel-plugin-istanbul@6.1.1: resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} engines: {node: '>=8'} + babel-plugin-istanbul@7.0.1: + resolution: {integrity: sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==} + engines: {node: '>=12'} + babel-plugin-jest-hoist@29.6.3: resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + babel-plugin-jest-hoist@30.2.0: + 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.14: resolution: {integrity: sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==} peerDependencies: @@ -2203,8 +2237,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0 || ^8.0.0-0 - babel-preset-expo@54.0.9: - resolution: {integrity: sha512-8J6hRdgEC2eJobjoft6mKJ294cLxmi3khCUy2JJQp4htOYYkllSLUq6vudWJkTJiIuGdVR4bR6xuz2EvJLWHNg==} + babel-preset-expo@54.0.10: + resolution: {integrity: sha512-wTt7POavLFypLcPW/uC5v8y+mtQKDJiyGLzYCjqr9tx0Qc3vCXcDKk1iCFIj/++Iy5CWhhTflEa7VvVPNWeCfw==} peerDependencies: '@babel/runtime': ^7.20.0 expo: '*' @@ -2221,6 +2255,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0 + babel-preset-jest@30.2.0: + resolution: {integrity: sha512-US4Z3NOieAQumwFnYdUWKvUKh8+YSnS/gB3t6YBiz0bskpu7Pine8pPCheNxlPEW4wnUkma2a94YuW2q3guvCQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + '@babel/core': ^7.11.0 || ^8.0.0-beta.1 + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -2339,6 +2379,10 @@ packages: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} + ci-info@4.4.0: + resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} + engines: {node: '>=8'} + cjs-module-lexer@1.4.3: resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} @@ -2946,8 +2990,8 @@ packages: react: '*' react-native: '*' - expo-constants@18.0.12: - resolution: {integrity: sha512-WzcKYMVNRRu4NcSzfIVRD5aUQFnSpTZgXFrlWmm19xJoDa4S3/PQNi6PNTBRc49xz9h8FT7HMxRKaC8lr0gflA==} + expo-constants@18.0.13: + resolution: {integrity: sha512-FnZn12E1dRYKDHlAdIyNFhBurKTS3F9CrfrBDJI5m3D7U17KBHMQ6JEfYlSj7LG7t+Ulr+IKaj58L1k5gBwTcQ==} peerDependencies: expo: '*' react-native: '*' @@ -2991,8 +3035,8 @@ packages: expo: '*' react-native: '*' - expo-font@14.0.10: - resolution: {integrity: sha512-UqyNaaLKRpj4pKAP4HZSLnuDQqueaO5tB1c/NWu5vh1/LF9ulItyyg2kF/IpeOp0DeOLk0GY0HrIXaKUMrwB+Q==} + expo-font@14.0.11: + resolution: {integrity: sha512-ga0q61ny4s/kr4k8JX9hVH69exVSIfcIc19+qZ7gt71Mqtm7xy2c6kwsPTCyhBW2Ro5yXTT8EaZOpuRi35rHbg==} peerDependencies: expo: '*' react: '*' @@ -3041,8 +3085,8 @@ packages: peerDependencies: expo: '*' - expo-modules-autolinking@3.0.23: - resolution: {integrity: sha512-YZnaE0G+52xftjH5nsIRaWsoVBY38SQCECclpdgLisdbRY/6Mzo7ndokjauOv3mpFmzMZACHyJNu1YSAffQwTg==} + expo-modules-autolinking@3.0.24: + resolution: {integrity: sha512-TP+6HTwhL7orDvsz2VzauyQlXJcAWyU3ANsZ7JGL4DQu8XaZv/A41ZchbtAYLfozNA2Ya1Hzmhx65hXryBMjaQ==} hasBin: true expo-modules-core@3.0.29: @@ -3051,14 +3095,14 @@ packages: react: '*' react-native: '*' - expo-router@6.0.21: - resolution: {integrity: sha512-wjTUjrnWj6gRYjaYl1kYfcRnNE4ZAQ0kz0+sQf6/mzBd/OU6pnOdD7WrdAW3pTTpm52Q8sMoeX98tNQEddg2uA==} + expo-router@6.0.23: + resolution: {integrity: sha512-qCxVAiCrCyu0npky6azEZ6dJDMt77OmCzEbpF6RbUTlfkaCA417LvY14SBkk0xyGruSxy/7pvJOI6tuThaUVCA==} peerDependencies: '@expo/metro-runtime': ^6.1.2 '@react-navigation/drawer': ^7.5.0 '@testing-library/react-native': '>= 12.0.0' expo: '*' - expo-constants: ^18.0.12 + expo-constants: ^18.0.13 expo-linking: ^8.0.11 react: '*' react-dom: '*' @@ -3068,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 @@ -3129,8 +3173,8 @@ packages: peerDependencies: expo: '*' - expo-updates@29.0.15: - resolution: {integrity: sha512-6Qj+g56nnCksKKnEPQFm19dfWvYB5EggQNN3SaLbIj4LI40k/pjQwqYStEuwTU+Ow+PG0AqxIhQ3NvgVPEzLvg==} + expo-updates@29.0.16: + resolution: {integrity: sha512-E9/fxRz/Eurtc7hxeI/6ZPyHH3To9Xoccm1kXoICZTRojmuTo+dx0Xv53UHyHn4G5zGMezyaKF2Qtj3AKcT93w==} hasBin: true peerDependencies: expo: '*' @@ -3143,8 +3187,8 @@ packages: expo: '*' react-native: '*' - expo@54.0.30: - resolution: {integrity: sha512-6q+aFfKL0SpT8prfdpR3V8HcN51ov0mCGuwQTzyuk6eeO9rg7a7LWbgPv9rEVXGZEuyULstL8LGNwHqusand7Q==} + expo@54.0.33: + resolution: {integrity: sha512-3yOEfAKqo+gqHcV8vKcnq0uA5zxlohnhA3fu4G43likN8ct5ZZ3LjAh9wDdKteEkoad3tFPvwxmXW711S5OHUw==} hasBin: true peerDependencies: '@expo/dom-webview': '*' @@ -3739,6 +3783,10 @@ packages: resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-haste-map@30.2.0: + resolution: {integrity: sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-leak-detector@29.7.0: resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3772,6 +3820,10 @@ packages: resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-regex-util@30.0.1: + resolution: {integrity: sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-resolve-dependencies@29.7.0: resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3796,6 +3848,10 @@ packages: resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-util@30.2.0: + resolution: {integrity: sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-validate@29.7.0: resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3808,6 +3864,10 @@ packages: resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-worker@30.2.0: + resolution: {integrity: sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest@29.7.0: resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3934,24 +3994,28 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] 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.30.2: resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [glibc] 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.30.2: resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==} @@ -4425,13 +4489,13 @@ packages: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} - playwright-core@1.57.0: - resolution: {integrity: sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==} + playwright-core@1.58.1: + resolution: {integrity: sha512-bcWzOaTxcW+VOOGBCQgnaKToLJ65d6AqfLVKEWvexyS3AS6rbXl+xdpYRMGSRBClPvyj44njOWoxjNdL/H9UNg==} engines: {node: '>=18'} hasBin: true - playwright@1.57.0: - resolution: {integrity: sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw==} + playwright@1.58.1: + resolution: {integrity: sha512-+2uTZHxSCcxjvGc5C891LrS1/NlxglGxzrC4seZiVjcYVQfUa87wBL6rTDqzGjuoWNjnBzRqKmF6zRYGMvQUaQ==} engines: {node: '>=18'} hasBin: true @@ -4458,8 +4522,8 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - prettier@3.7.4: - resolution: {integrity: sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==} + prettier@3.8.1: + resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==} engines: {node: '>=14'} hasBin: true @@ -4794,9 +4858,6 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true - rxjs@7.8.2: - resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} - safe-array-concat@1.1.3: resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} engines: {node: '>=0.4'} @@ -5111,6 +5172,7 @@ packages: 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==} @@ -5431,6 +5493,10 @@ packages: resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + write-file-atomic@5.0.1: + resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + ws@6.2.3: resolution: {integrity: sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==} peerDependencies: @@ -5527,57 +5593,56 @@ snapshots: '@0no-co/graphql.web@1.2.0': {} - '@amplitude/analytics-browser@2.33.1': + '@amplitude/analytics-browser@2.34.0': dependencies: - '@amplitude/analytics-core': 2.35.0 - '@amplitude/plugin-autocapture-browser': 1.18.3 - '@amplitude/plugin-network-capture-browser': 1.7.3 - '@amplitude/plugin-page-url-enrichment-browser': 0.5.9 - '@amplitude/plugin-page-view-tracking-browser': 2.6.6 - '@amplitude/plugin-web-vitals-browser': 1.1.4 + '@amplitude/analytics-core': 2.37.0 + '@amplitude/plugin-autocapture-browser': 1.19.0 + '@amplitude/plugin-network-capture-browser': 1.7.8 + '@amplitude/plugin-page-url-enrichment-browser': 0.5.14 + '@amplitude/plugin-page-view-tracking-browser': 2.6.11 + '@amplitude/plugin-web-vitals-browser': 1.1.9 tslib: 2.8.1 '@amplitude/analytics-connector@1.6.4': {} - '@amplitude/analytics-core@2.35.0': + '@amplitude/analytics-core@2.37.0': dependencies: '@amplitude/analytics-connector': 1.6.4 tslib: 2.8.1 zen-observable-ts: 1.1.0 - '@amplitude/analytics-react-native@1.5.32(react-native@0.81.5(@babel/core@7.28.5)(@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.35.0 + '@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.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) tslib: 2.8.1 - '@amplitude/plugin-autocapture-browser@1.18.3': + '@amplitude/plugin-autocapture-browser@1.19.0': dependencies: - '@amplitude/analytics-core': 2.35.0 - rxjs: 7.8.2 + '@amplitude/analytics-core': 2.37.0 tslib: 2.8.1 - '@amplitude/plugin-network-capture-browser@1.7.3': + '@amplitude/plugin-network-capture-browser@1.7.8': dependencies: - '@amplitude/analytics-core': 2.35.0 + '@amplitude/analytics-core': 2.37.0 tslib: 2.8.1 - '@amplitude/plugin-page-url-enrichment-browser@0.5.9': + '@amplitude/plugin-page-url-enrichment-browser@0.5.14': dependencies: - '@amplitude/analytics-core': 2.35.0 + '@amplitude/analytics-core': 2.37.0 tslib: 2.8.1 - '@amplitude/plugin-page-view-tracking-browser@2.6.6': + '@amplitude/plugin-page-view-tracking-browser@2.6.11': dependencies: - '@amplitude/analytics-core': 2.35.0 + '@amplitude/analytics-core': 2.37.0 tslib: 2.8.1 - '@amplitude/plugin-web-vitals-browser@1.1.4': + '@amplitude/plugin-web-vitals-browser@1.1.9': dependencies: - '@amplitude/analytics-core': 2.35.0 + '@amplitude/analytics-core': 2.37.0 tslib: 2.8.1 web-vitals: 5.1.0 @@ -5691,8 +5756,8 @@ snapshots: '@babel/helper-module-imports@7.27.1': dependencies: - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 + '@babel/traverse': 7.28.6 + '@babel/types': 7.28.6 transitivePeerDependencies: - supports-color @@ -5716,7 +5781,7 @@ snapshots: '@babel/core': 7.28.5 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-wrap-function': 7.28.3 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.6 transitivePeerDependencies: - supports-color @@ -5744,8 +5809,8 @@ snapshots: '@babel/helper-wrap-function@7.28.3': dependencies: - '@babel/template': 7.27.2 - '@babel/traverse': 7.28.5 + '@babel/template': 7.28.6 + '@babel/traverse': 7.28.6 '@babel/types': 7.28.6 transitivePeerDependencies: - supports-color @@ -5899,7 +5964,7 @@ snapshots: '@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.5 + '@babel/traverse': 7.28.6 transitivePeerDependencies: - supports-color @@ -5949,13 +6014,13 @@ snapshots: dependencies: '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/template': 7.27.2 + '@babel/template': 7.28.6 '@babel/plugin-transform-destructuring@7.28.5(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.6 transitivePeerDependencies: - supports-color @@ -5983,7 +6048,7 @@ snapshots: '@babel/core': 7.28.5 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.6 transitivePeerDependencies: - supports-color @@ -6028,7 +6093,7 @@ snapshots: '@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.5 + '@babel/traverse': 7.28.6 transitivePeerDependencies: - supports-color @@ -6096,7 +6161,7 @@ snapshots: '@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.5 + '@babel/types': 7.28.6 transitivePeerDependencies: - supports-color @@ -6314,10 +6379,10 @@ snapshots: '@expo-google-fonts/jetbrains-mono@0.4.1': {} - '@expo/cli@54.0.20(expo-router@6.0.21)(expo@54.0.30)(react-native@0.81.5(@babel/core@7.28.5)(@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.5 + '@expo/code-signing-certificates': 0.0.6 '@expo/config': 12.0.13 '@expo/config-plugins': 54.0.4 '@expo/devcert': 1.2.1 @@ -6325,11 +6390,11 @@ snapshots: '@expo/image-utils': 0.8.8 '@expo/json-file': 10.0.8 '@expo/metro': 54.2.0 - '@expo/metro-config': 54.0.12(expo@54.0.30) + '@expo/metro-config': 54.0.14(expo@54.0.33) '@expo/osascript': 2.3.8 - '@expo/package-manager': 1.9.9 + '@expo/package-manager': 1.9.10 '@expo/plist': 0.4.8 - '@expo/prebuild-config': 54.0.8(expo@54.0.30) + '@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 @@ -6348,7 +6413,7 @@ snapshots: connect: 3.7.0 debug: 4.4.3 env-editor: 0.4.2 - expo: 54.0.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@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 @@ -6381,7 +6446,7 @@ snapshots: wrap-ansi: 7.0.0 ws: 8.18.3 optionalDependencies: - expo-router: 6.0.21(f0d96ad57e690436ea0d7f67db788d54) + 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 @@ -6389,10 +6454,9 @@ snapshots: - supports-color - utf-8-validate - '@expo/code-signing-certificates@0.0.5': + '@expo/code-signing-certificates@0.0.6': dependencies: node-forge: 1.3.3 - nullthrows: 1.1.1 '@expo/config-plugins@54.0.4': dependencies: @@ -6491,11 +6555,11 @@ snapshots: '@babel/code-frame': 7.10.4 json5: 2.2.3 - '@expo/metro-config@54.0.12(expo@54.0.30)': + '@expo/metro-config@54.0.14(expo@54.0.33)': dependencies: - '@babel/code-frame': 7.27.1 + '@babel/code-frame': 7.28.6 '@babel/core': 7.28.5 - '@babel/generator': 7.28.5 + '@babel/generator': 7.28.6 '@expo/config': 12.0.13 '@expo/env': 2.0.8 '@expo/json-file': 10.0.8 @@ -6515,16 +6579,16 @@ snapshots: postcss: 8.4.49 resolve-from: 5.0.0 optionalDependencies: - expo: 54.0.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@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.30)(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/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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@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.5)(@types/react@19.1.17)(react@19.1.0) @@ -6559,7 +6623,7 @@ snapshots: '@expo/spawn-async': 1.7.2 exec-async: 2.2.0 - '@expo/package-manager@1.9.9': + '@expo/package-manager@1.9.10': dependencies: '@expo/json-file': 10.0.8 '@expo/spawn-async': 1.7.2 @@ -6574,7 +6638,7 @@ snapshots: base64-js: 1.5.1 xmlbuilder: 15.1.1 - '@expo/prebuild-config@54.0.8(expo@54.0.30)': + '@expo/prebuild-config@54.0.8(expo@54.0.33)': dependencies: '@expo/config': 12.0.13 '@expo/config-plugins': 54.0.4 @@ -6583,7 +6647,7 @@ snapshots: '@expo/json-file': 10.0.8 '@react-native/normalize-colors': 0.81.5 debug: 4.4.3 - expo: 54.0.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@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 @@ -6600,9 +6664,9 @@ snapshots: '@expo/sudo-prompt@9.3.2': {} - '@expo/vector-icons@15.0.3(expo-font@14.0.10(expo@54.0.30)(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)': + '@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.10(expo@54.0.30)(react-native@0.81.5(@babel/core@7.28.5)(@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.5)(@types/react@19.1.17)(react@19.1.0) @@ -6754,6 +6818,11 @@ snapshots: transitivePeerDependencies: - supports-color + '@jest/pattern@30.0.1': + dependencies: + '@types/node': 25.0.3 + jest-regex-util: 30.0.1 + '@jest/reporters@29.7.0': dependencies: '@bcoe/v8-coverage': 0.2.3 @@ -6831,6 +6900,26 @@ snapshots: transitivePeerDependencies: - supports-color + '@jest/transform@30.2.0': + dependencies: + '@babel/core': 7.28.5 + '@jest/types': 30.2.0 + '@jridgewell/trace-mapping': 0.3.31 + babel-plugin-istanbul: 7.0.1 + chalk: 4.1.2 + convert-source-map: 2.0.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.11 + jest-haste-map: 30.2.0 + jest-regex-util: 30.0.1 + jest-util: 30.2.0 + micromatch: 4.0.8 + pirates: 4.0.7 + slash: 3.0.0 + write-file-atomic: 5.0.1 + transitivePeerDependencies: + - supports-color + '@jest/types@29.6.3': dependencies: '@jest/schemas': 29.6.3 @@ -6840,6 +6929,16 @@ snapshots: '@types/yargs': 17.0.35 chalk: 4.1.2 + '@jest/types@30.2.0': + dependencies: + '@jest/pattern': 30.0.1 + '@jest/schemas': 30.0.5 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 25.0.3 + '@types/yargs': 17.0.35 + chalk: 4.1.2 + '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -6873,9 +6972,9 @@ snapshots: '@nolyfill/is-core-module@1.0.39': {} - '@playwright/test@1.57.0': + '@playwright/test@1.58.1': dependencies: - playwright: 1.57.0 + playwright: 1.58.1 '@radix-ui/primitive@1.1.3': {} @@ -7074,19 +7173,19 @@ snapshots: merge-options: 3.0.4 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.30)(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-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.5)(@types/react@19.1.17)(react@19.1.0) optionalDependencies: - expo: 54.0.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@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.5)': dependencies: - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.6 '@react-native/codegen': 0.81.5(@babel/core@7.28.5) transitivePeerDependencies: - '@babel/core' @@ -7134,7 +7233,7 @@ snapshots: '@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.27.2 + '@babel/template': 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.5) @@ -7205,7 +7304,7 @@ snapshots: '@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.3(@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/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 @@ -7228,7 +7327,7 @@ snapshots: 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.3(@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/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.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 @@ -7240,7 +7339,7 @@ snapshots: '@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.3(@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/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 @@ -7299,49 +7398,49 @@ snapshots: '@sentry/cli-darwin@2.55.0': optional: true - '@sentry/cli-darwin@3.0.1': + '@sentry/cli-darwin@3.1.0': optional: true '@sentry/cli-linux-arm64@2.55.0': optional: true - '@sentry/cli-linux-arm64@3.0.1': + '@sentry/cli-linux-arm64@3.1.0': optional: true '@sentry/cli-linux-arm@2.55.0': optional: true - '@sentry/cli-linux-arm@3.0.1': + '@sentry/cli-linux-arm@3.1.0': optional: true '@sentry/cli-linux-i686@2.55.0': optional: true - '@sentry/cli-linux-i686@3.0.1': + '@sentry/cli-linux-i686@3.1.0': optional: true '@sentry/cli-linux-x64@2.55.0': optional: true - '@sentry/cli-linux-x64@3.0.1': + '@sentry/cli-linux-x64@3.1.0': optional: true '@sentry/cli-win32-arm64@2.55.0': optional: true - '@sentry/cli-win32-arm64@3.0.1': + '@sentry/cli-win32-arm64@3.1.0': optional: true '@sentry/cli-win32-i686@2.55.0': optional: true - '@sentry/cli-win32-i686@3.0.1': + '@sentry/cli-win32-i686@3.1.0': optional: true '@sentry/cli-win32-x64@2.55.0': optional: true - '@sentry/cli-win32-x64@3.0.1': + '@sentry/cli-win32-x64@3.1.0': optional: true '@sentry/cli@2.55.0': @@ -7364,25 +7463,25 @@ snapshots: - encoding - supports-color - '@sentry/cli@3.0.1': + '@sentry/cli@3.1.0': dependencies: progress: 2.0.3 proxy-from-env: 1.1.0 undici: 6.23.0 which: 2.0.2 optionalDependencies: - '@sentry/cli-darwin': 3.0.1 - '@sentry/cli-linux-arm': 3.0.1 - '@sentry/cli-linux-arm64': 3.0.1 - '@sentry/cli-linux-i686': 3.0.1 - '@sentry/cli-linux-x64': 3.0.1 - '@sentry/cli-win32-arm64': 3.0.1 - '@sentry/cli-win32-i686': 3.0.1 - '@sentry/cli-win32-x64': 3.0.1 + '@sentry/cli-darwin': 3.1.0 + '@sentry/cli-linux-arm': 3.1.0 + '@sentry/cli-linux-arm64': 3.1.0 + '@sentry/cli-linux-i686': 3.1.0 + '@sentry/cli-linux-x64': 3.1.0 + '@sentry/cli-win32-arm64': 3.1.0 + '@sentry/cli-win32-i686': 3.1.0 + '@sentry/cli-win32-x64': 3.1.0 '@sentry/core@10.12.0': {} - '@sentry/react-native@7.2.0(expo@54.0.30)(react-native@0.81.5(@babel/core@7.28.5)(@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 @@ -7393,7 +7492,7 @@ snapshots: 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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@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 @@ -7421,19 +7520,19 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.1 - '@supabase/auth-js@2.89.0': + '@supabase/auth-js@2.93.3': dependencies: tslib: 2.8.1 - '@supabase/functions-js@2.89.0': + '@supabase/functions-js@2.93.3': dependencies: tslib: 2.8.1 - '@supabase/postgrest-js@2.89.0': + '@supabase/postgrest-js@2.93.3': dependencies: tslib: 2.8.1 - '@supabase/realtime-js@2.89.0': + '@supabase/realtime-js@2.93.3': dependencies: '@types/phoenix': 1.6.7 '@types/ws': 8.18.1 @@ -7443,18 +7542,18 @@ snapshots: - bufferutil - utf-8-validate - '@supabase/storage-js@2.89.0': + '@supabase/storage-js@2.93.3': dependencies: iceberg-js: 0.8.1 tslib: 2.8.1 - '@supabase/supabase-js@2.89.0': + '@supabase/supabase-js@2.93.3': dependencies: - '@supabase/auth-js': 2.89.0 - '@supabase/functions-js': 2.89.0 - '@supabase/postgrest-js': 2.89.0 - '@supabase/realtime-js': 2.89.0 - '@supabase/storage-js': 2.89.0 + '@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 @@ -7480,20 +7579,20 @@ snapshots: '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.28.5 - '@babel/types': 7.28.5 + '@babel/parser': 7.28.6 + '@babel/types': 7.28.6 '@types/babel__generator': 7.27.0 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.28.0 '@types/babel__generator@7.27.0': dependencies: - '@babel/types': 7.28.5 + '@babel/types': 7.28.6 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.28.5 - '@babel/types': 7.28.5 + '@babel/parser': 7.28.6 + '@babel/types': 7.28.6 '@types/babel__traverse@7.28.0': dependencies: @@ -7922,6 +8021,19 @@ snapshots: transitivePeerDependencies: - supports-color + babel-jest@30.2.0(@babel/core@7.28.5): + dependencies: + '@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.5) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + babel-plugin-istanbul@6.1.1: dependencies: '@babel/helper-plugin-utils': 7.27.1 @@ -7932,6 +8044,16 @@ snapshots: transitivePeerDependencies: - supports-color + babel-plugin-istanbul@7.0.1: + dependencies: + '@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 + test-exclude: 6.0.0 + transitivePeerDependencies: + - supports-color + babel-plugin-jest-hoist@29.6.3: dependencies: '@babel/template': 7.27.2 @@ -7939,6 +8061,10 @@ snapshots: '@types/babel__core': 7.20.5 '@types/babel__traverse': 7.28.0 + babel-plugin-jest-hoist@30.2.0: + dependencies: + '@types/babel__core': 7.20.5 + babel-plugin-polyfill-corejs2@0.4.14(@babel/core@7.28.5): dependencies: '@babel/compat-data': 7.28.5 @@ -7965,7 +8091,7 @@ snapshots: babel-plugin-react-compiler@1.0.0: dependencies: - '@babel/types': 7.28.5 + '@babel/types': 7.28.6 babel-plugin-react-native-web@0.21.2: {} @@ -7998,7 +8124,7 @@ snapshots: '@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.9(@babel/core@7.28.5)(@babel/runtime@7.28.4)(expo@54.0.30)(react-refresh@0.14.2): + 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) @@ -8025,7 +8151,7 @@ snapshots: resolve-from: 5.0.0 optionalDependencies: '@babel/runtime': 7.28.4 - expo: 54.0.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@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: - '@babel/core' - supports-color @@ -8036,6 +8162,12 @@ snapshots: babel-plugin-jest-hoist: 29.6.3 babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.5) + babel-preset-jest@30.2.0(@babel/core@7.28.5): + dependencies: + '@babel/core': 7.28.5 + babel-plugin-jest-hoist: 30.2.0 + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.5) + balanced-match@1.0.2: {} base64-js@1.5.1: {} @@ -8160,6 +8292,8 @@ snapshots: ci-info@3.9.0: {} + ci-info@4.4.0: {} + cjs-module-lexer@1.4.3: {} cli-cursor@2.1.0: @@ -8823,32 +8957,32 @@ snapshots: jest-message-util: 29.7.0 jest-util: 29.7.0 - expo-apple-authentication@8.0.8(expo@54.0.30)(react-native@0.81.5(@babel/core@7.28.5)(@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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@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: 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.30): + expo-application@7.0.8(expo@54.0.33): dependencies: - expo: 54.0.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@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.30)(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.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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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.12(expo@54.0.30)(react-native@0.81.5(@babel/core@7.28.5)(@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.5)(@types/react@19.1.17)(react@19.1.0) transitivePeerDependencies: - supports-color - expo-auth-session@7.0.10(expo@54.0.30)(react-native@0.81.5(@babel/core@7.28.5)(@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.30) - expo-constants: 18.0.12(expo@54.0.30)(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.30) - expo-linking: 8.0.11(expo@54.0.30)(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.30)(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.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.5)(@types/react@19.1.17)(react@19.1.0) @@ -8856,115 +8990,115 @@ snapshots: - expo - supports-color - expo-blur@15.0.8(expo@54.0.30)(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@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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@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.5)(@types/react@19.1.17)(react@19.1.0) - expo-build-properties@1.0.10(expo@54.0.30): + expo-build-properties@1.0.10(expo@54.0.33): dependencies: ajv: 8.17.1 - expo: 54.0.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@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.30)(react-native@0.81.5(@babel/core@7.28.5)(@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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@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.5)(@types/react@19.1.17)(react@19.1.0) - expo-constants@18.0.12(expo@54.0.30)(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.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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@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: 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.30): + expo-crypto@15.0.8(expo@54.0.33): dependencies: base64-js: 1.5.1 - expo: 54.0.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@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.30): + expo-dev-client@6.0.20(expo@54.0.33): dependencies: - expo: 54.0.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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.30) - expo-dev-menu: 7.0.18(expo@54.0.30) - expo-dev-menu-interface: 2.0.0(expo@54.0.30) - expo-manifests: 1.0.10(expo@54.0.30) - expo-updates-interface: 2.0.0(expo@54.0.30) + 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.30): + expo-dev-launcher@6.0.20(expo@54.0.33): dependencies: ajv: 8.17.1 - expo: 54.0.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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.30) - expo-manifests: 1.0.10(expo@54.0.30) + 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.30): + expo-dev-menu-interface@2.0.0(expo@54.0.33): dependencies: - expo: 54.0.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@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.30): + expo-dev-menu@7.0.18(expo@54.0.33): dependencies: - expo: 54.0.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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.30) + 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.30): + expo-device@8.0.10(expo@54.0.33): dependencies: - expo: 54.0.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@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.30)(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)): dependencies: - expo: 54.0.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@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: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) - expo-font@14.0.10(expo@54.0.30)(react-native@0.81.5(@babel/core@7.28.5)(@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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@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.5)(@types/react@19.1.17)(react@19.1.0) - expo-glass-effect@0.1.8(expo@54.0.30)(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@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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@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.5)(@types/react@19.1.17)(react@19.1.0) - expo-image@3.0.11(expo@54.0.30)(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-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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@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.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.30): + expo-insights@0.10.8(expo@54.0.33): dependencies: - expo: 54.0.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@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.30)(react@19.1.0): + expo-keep-awake@15.0.8(expo@54.0.33)(react@19.1.0): dependencies: - expo: 54.0.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@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.30)(react-native@0.81.5(@babel/core@7.28.5)(@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.12(expo@54.0.30)(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.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.5)(@types/react@19.1.17)(react@19.1.0) @@ -8972,15 +9106,15 @@ snapshots: - expo - supports-color - expo-manifests@1.0.10(expo@54.0.30): + expo-manifests@1.0.10(expo@54.0.33): dependencies: '@expo/config': 12.0.13 - expo: 54.0.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@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 - expo-modules-autolinking@3.0.23: + expo-modules-autolinking@3.0.24: dependencies: '@expo/spawn-async': 1.7.2 chalk: 4.1.2 @@ -8994,9 +9128,9 @@ snapshots: 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.21(f0d96ad57e690436ea0d7f67db788d54): + expo-router@6.0.23(34d93f6a50204070cbf56f6391ebf5fc): dependencies: - '@expo/metro-runtime': 6.1.2(expo@54.0.30)(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/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) @@ -9006,9 +9140,9 @@ snapshots: client-only: 0.0.1 debug: 4.4.3 escape-string-regexp: 4.0.0 - expo: 54.0.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(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.12(expo@54.0.30)(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.30)(react-native@0.81.5(@babel/core@7.28.5)(@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 @@ -9038,16 +9172,16 @@ snapshots: - '@types/react-dom' - supports-color - expo-secure-store@15.0.8(expo@54.0.30): + expo-secure-store@15.0.8(expo@54.0.33): dependencies: - expo: 54.0.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@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.30): + expo-splash-screen@31.0.13(expo@54.0.33): dependencies: - '@expo/prebuild-config': 54.0.8(expo@54.0.30) - expo: 54.0.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@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 @@ -9059,40 +9193,40 @@ snapshots: expo-structured-headers@5.0.0: {} - expo-symbols@1.0.8(expo@54.0.30)(react-native@0.81.5(@babel/core@7.28.5)(@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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@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: 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.30)(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-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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@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: 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.30): + expo-updates-interface@2.0.0(expo@54.0.33): dependencies: - expo: 54.0.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@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.15(expo@54.0.30)(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.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.5 + '@expo/code-signing-certificates': 0.0.6 '@expo/plist': 0.4.8 '@expo/spawn-async': 1.7.2 arg: 4.1.0 chalk: 4.1.2 debug: 4.4.3 - expo: 54.0.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@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.30) + expo-manifests: 1.0.10(expo@54.0.33) expo-structured-headers: 5.0.0 - expo-updates-interface: 2.0.0(expo@54.0.30) + expo-updates-interface: 2.0.0(expo@54.0.33) getenv: 2.0.0 glob: 13.0.0 ignore: 5.3.2 @@ -9102,30 +9236,30 @@ snapshots: transitivePeerDependencies: - supports-color - expo-web-browser@15.0.10(expo@54.0.30)(react-native@0.81.5(@babel/core@7.28.5)(@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.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@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: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) - expo@54.0.30(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@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.4 - '@expo/cli': 54.0.20(expo-router@6.0.21)(expo@54.0.30)(react-native@0.81.5(@babel/core@7.28.5)(@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)) '@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.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.12(expo@54.0.30) - '@expo/vector-icons': 15.0.3(expo-font@14.0.10(expo@54.0.30)(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) + '@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.9(@babel/core@7.28.5)(@babel/runtime@7.28.4)(expo@54.0.30)(react-refresh@0.14.2) - expo-asset: 12.0.12(expo@54.0.30)(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.12(expo@54.0.30)(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.30)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)) - expo-font: 14.0.10(expo@54.0.30)(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.30)(react@19.1.0) - expo-modules-autolinking: 3.0.23 + 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.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) pretty-format: 29.7.0 react: 19.1.0 @@ -9133,7 +9267,7 @@ snapshots: react-refresh: 0.14.2 whatwg-url-without-unicode: 8.0.0-3 optionalDependencies: - '@expo/metro-runtime': 6.1.2(expo@54.0.30)(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/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 @@ -9812,6 +9946,21 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + jest-haste-map@30.2.0: + dependencies: + '@jest/types': 30.2.0 + '@types/node': 25.0.3 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 30.0.1 + jest-util: 30.2.0 + jest-worker: 30.2.0 + micromatch: 4.0.8 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.3 + jest-leak-detector@29.7.0: dependencies: jest-get-type: 29.6.3 @@ -9855,6 +10004,8 @@ snapshots: jest-regex-util@29.6.3: {} + jest-regex-util@30.0.1: {} + jest-resolve-dependencies@29.7.0: dependencies: jest-regex-util: 29.6.3 @@ -9961,6 +10112,15 @@ snapshots: graceful-fs: 4.2.11 picomatch: 2.3.1 + jest-util@30.2.0: + dependencies: + '@jest/types': 30.2.0 + '@types/node': 25.0.3 + chalk: 4.1.2 + ci-info: 4.4.0 + graceful-fs: 4.2.11 + picomatch: 4.0.3 + jest-validate@29.7.0: dependencies: '@jest/types': 29.6.3 @@ -9988,6 +10148,14 @@ snapshots: merge-stream: 2.0.0 supports-color: 8.1.1 + jest-worker@30.2.0: + dependencies: + '@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.0.3): dependencies: '@jest/core': 29.7.0 @@ -10683,11 +10851,11 @@ snapshots: dependencies: find-up: 4.1.0 - playwright-core@1.57.0: {} + playwright-core@1.58.1: {} - playwright@1.57.0: + playwright@1.58.1: dependencies: - playwright-core: 1.57.0 + playwright-core: 1.58.1 optionalDependencies: fsevents: 2.3.2 @@ -10711,7 +10879,7 @@ snapshots: prelude-ls@1.2.1: {} - prettier@3.7.4: {} + prettier@3.8.1: {} pretty-bytes@5.6.0: {} @@ -11110,10 +11278,6 @@ snapshots: dependencies: glob: 7.2.3 - rxjs@7.8.2: - dependencies: - tslib: 2.8.1 - safe-array-concat@1.1.3: dependencies: call-bind: 1.0.8 @@ -11808,6 +11972,11 @@ snapshots: imurmurhash: 0.1.4 signal-exit: 3.0.7 + write-file-atomic@5.0.1: + dependencies: + imurmurhash: 0.1.4 + signal-exit: 4.1.0 + ws@6.2.3: dependencies: async-limiter: 1.0.1