diff --git a/app.config.js b/app.config.js index 72c73e9..63a41ea 100644 --- a/app.config.js +++ b/app.config.js @@ -4,7 +4,7 @@ module.exports = ({ config }) => ({ ...(config.expo || {}), name: "OpenBirding", slug: "OpenBirding", - version: "1.8.0", + version: "2.0.0", orientation: "portrait", icon: "./assets/images/logo.png", scheme: "openbirding", @@ -58,6 +58,7 @@ module.exports = ({ config }) => ({ "expo-web-browser", "expo-font", "expo-image", + "expo-localization", ], experiments: { typedRoutes: true, reactCompiler: true }, extra: { diff --git a/app/_layout.tsx b/app/_layout.tsx index 0747e93..ecadf68 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -104,6 +104,15 @@ export default function RootLayout() { headerShadowVisible: false, }} /> + (null); const isMapTouchActiveRef = useRef(false); const insets = useSafeAreaInsets(); + const activeFilterCount = useActiveFilterCount(); const { isLoadingLocation, savedLocation, updateLocation, hadSavedLocationOnInit } = useSavedLocation(); const { @@ -39,7 +46,7 @@ export default function HomeScreen() { setIsMapAttributionOpen, } = useMapStore(); const { data: installedPacks, isLoading: isLoadingInstalledPacks } = useInstalledPacks(); - const { hasUpdates } = usePackUpdates(); + const { updateCount } = usePackUpdates(); const handleMapPress = (_event: any) => { if (isMenuOpen) handleCloseBottomSheet(); @@ -84,10 +91,26 @@ export default function HomeScreen() { setIsHotspotListOpen(true); }; + const handleOpenFilters = () => { + setIsFilterSheetOpen(true); + }; + + const handleCloseFilters = () => { + setIsFilterSheetOpen(false); + }; + const handleCloseHotspotList = () => { setIsHotspotListOpen(false); }; + const handleOpenSearch = () => { + setIsSearchOpen(true); + }; + + const handleCloseSearch = () => { + setIsSearchOpen(false); + }; + const handleMapTouchActiveChange = useCallback((isActive: boolean) => { isMapTouchActiveRef.current = isActive; }, []); @@ -121,6 +144,18 @@ export default function HomeScreen() { [setCustomPinCoordinates, setPlaceId, setHotspotId] ); + const handleSelectPlaceFromList = useCallback( + (selectedPlaceId: string, lat: number, lng: number) => { + setCustomPinCoordinates(null); + setHotspotId(null); + setPlaceId(selectedPlaceId); + setTimeout(() => { + mapRef.current?.centerOnCoordinates(lng, lat, 200); + }, 500); + }, + [setCustomPinCoordinates, setHotspotId, setPlaceId] + ); + if (isLoadingLocation) return null; const initialCenter = savedLocation?.center ?? [-98.5, 39.5]; @@ -171,17 +206,34 @@ export default function HomeScreen() { - - + + - {hasUpdates && } + + + + + + diff --git a/app/settings-units.tsx b/app/settings-units.tsx new file mode 100644 index 0000000..c5e58ee --- /dev/null +++ b/app/settings-units.tsx @@ -0,0 +1,90 @@ +import tw from "@/lib/tw"; +import { DistanceUnits, useSettingsStore } from "@/stores/settingsStore"; +import { Ionicons } from "@expo/vector-icons"; +import { GlassView, isLiquidGlassAvailable } from "expo-glass-effect"; +import { useRouter } from "expo-router"; +import React from "react"; +import { Platform, ScrollView, Text, TouchableOpacity, View, ViewStyle } from "react-native"; + +type OptionRowProps = { + label: string; + selected: boolean; + onPress: () => void; + isLast?: boolean; +}; + +function OptionRow({ label, selected, onPress, isLast }: OptionRowProps) { + const borderStyle = isLast ? {} : tw`border-b border-gray-200/50`; + + return ( + + {label} + {selected && } + + ); +} + +type OptionsGroupProps = { + children: React.ReactNode; + footer?: string; +}; + +function OptionsGroup({ children, footer }: OptionsGroupProps) { + const useGlass = Platform.OS === "ios" && isLiquidGlassAvailable(); + + const cardStyle: ViewStyle = { + borderRadius: 12, + overflow: "hidden", + }; + + const content = useGlass ? ( + + {children} + + ) : ( + {children} + ); + + return ( + + {content} + {footer && {footer}} + + ); +} + +const OPTIONS: { value: DistanceUnits; label: string }[] = [ + { value: "metric", label: "Kilometers" }, + { value: "imperial", label: "Miles" }, +]; + +export default function UnitsPage() { + const router = useRouter(); + const distanceUnits = useSettingsStore((state) => state.distanceUnits); + const setDistanceUnits = useSettingsStore((state) => state.setDistanceUnits); + + const handleSelect = (units: DistanceUnits) => { + setDistanceUnits(units); + router.back(); + }; + + return ( + + + {OPTIONS.map((option, index) => ( + handleSelect(option.value)} + isLast={index === OPTIONS.length - 1} + /> + ))} + + + ); +} diff --git a/app/settings.tsx b/app/settings.tsx index 0ce4496..f59d0c7 100644 --- a/app/settings.tsx +++ b/app/settings.tsx @@ -112,6 +112,7 @@ export default function SettingsPage() { const lifelistExclusions = useSettingsStore((state) => state.lifelistExclusions); const disableSunTimes = useSettingsStore((state) => state.disableSunTimes); const setDisableSunTimes = useSettingsStore((state) => state.setDisableSunTimes); + const distanceUnits = useSettingsStore((state) => state.distanceUnits); const providers = getExternalMapProviders(); const getProviderName = (providerId: string | null) => { @@ -157,6 +158,12 @@ export default function SettingsPage() { + router.push("/settings-units" as Href)} + icon={{ family: "fontawesome5", name: "ruler", bgColor: "#5856D6" }} + /> router.push("/settings-life-list-exclusions" as Href)} - icon={{ name: "eye-off", bgColor: "#FF9500" }} + icon={{ name: "eye-off", bgColor: "#FF3B30" }} /> router.push("/settings-import-life-list" as Href)} - icon={{ name: "cloud-upload", bgColor: "#5856D6" }} + icon={{ name: "cloud-upload", bgColor: "#30B0C7" }} isLast /> diff --git a/components/ActionButtonRow.tsx b/components/ActionButtonRow.tsx index 50e2f9c..da81ee7 100644 --- a/components/ActionButtonRow.tsx +++ b/components/ActionButtonRow.tsx @@ -17,7 +17,7 @@ export default function ActionButtonRow({ children, stacked = false }: ActionBut {child} )); - const containerStyle = [tw`w-full mt-2`, stacked ? tw`flex-col` : tw`flex-row`, { gap: GAP }] as const; + const containerStyle = [tw`w-full mt-2`, stacked ? tw`flex-col` : tw`flex-row`, { gap: GAP }]; return useGlass ? ( diff --git a/components/CountBadge.tsx b/components/CountBadge.tsx new file mode 100644 index 0000000..a0d8ba0 --- /dev/null +++ b/components/CountBadge.tsx @@ -0,0 +1,15 @@ +import tw from "@/lib/tw"; +import React from "react"; +import { Text, View } from "react-native"; + +export default function CountBadge({ count }: { count: number }) { + if (count <= 0) return null; + + return ( + + {count} + + ); +} diff --git a/components/FilterSection.tsx b/components/FilterSection.tsx deleted file mode 100644 index e0af35b..0000000 --- a/components/FilterSection.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import tw from "@/lib/tw"; -import { useFiltersStore } from "@/stores/filtersStore"; -import React from "react"; -import { Platform, Switch, Text, View } from "react-native"; -import { BorderlessButton } from "react-native-gesture-handler"; - -export default function FilterSection() { - const { showSavedOnly, setShowSavedOnly } = useFiltersStore(); - - const content = ( - - Show saved only - - - ); - - if (Platform.OS === "android") { - return ( - setShowSavedOnly(!showSavedOnly)} style={tw`pl-6 pr-5 py-4`} activeOpacity={1}> - {content} - - ); - } - - return {content}; -} diff --git a/components/FilterSheet.tsx b/components/FilterSheet.tsx new file mode 100644 index 0000000..acbc9b1 --- /dev/null +++ b/components/FilterSheet.tsx @@ -0,0 +1,31 @@ +import tw from "@/lib/tw"; +import { useFiltersStore } from "@/stores/filtersStore"; +import { useSettingsStore } from "@/stores/settingsStore"; +import React from "react"; +import { Switch, Text, View } from "react-native"; +import BaseBottomSheet from "./BaseBottomSheet"; +import TargetRichHotspotControls from "./TargetRichHotspotControls"; + +type FilterSheetProps = { + isOpen: boolean; + onClose: () => void; +}; + +export default function FilterSheet({ isOpen, onClose }: FilterSheetProps) { + const showSavedOnly = useFiltersStore((state) => state.showSavedOnly); + const setShowSavedOnly = useFiltersStore((state) => state.setShowSavedOnly); + const lifelist = useSettingsStore((state) => state.lifelist); + const hasLifeList = (lifelist?.length ?? 0) > 0; + + return ( + + + + Show saved only + + + + + + ); +} diff --git a/components/HotspotDialog.tsx b/components/HotspotDialog.tsx index 13be887..d71e55a 100644 --- a/components/HotspotDialog.tsx +++ b/components/HotspotDialog.tsx @@ -96,7 +96,7 @@ function HotspotDialogContent({ isOpen, hotspotId, onClose }: HotspotDialogProps const handleViewDetails = () => { if (!hotspot) return; - const url = `https://ebird.org/hotspot/${hotspot.id}`; + const url = `https://ebird.org/hotspot/${hotspot.id}/about`; Linking.openURL(url).catch(() => { Alert.alert("Error", "Could not open eBird hotspot page"); }); diff --git a/components/HotspotItem.tsx b/components/HotspotItem.tsx index b290cd2..7b0bee7 100644 --- a/components/HotspotItem.tsx +++ b/components/HotspotItem.tsx @@ -1,36 +1,21 @@ import tw from "@/lib/tw"; import { Hotspot } from "@/lib/types"; -import { getMarkerColor } from "@/lib/utils"; +import { getSavedHotspotIconImage } from "@/lib/hotspotIconImages"; +import { formatDistance, getMarkerColor } from "@/lib/utils"; +import { useSettingsStore } from "@/stores/settingsStore"; import { Ionicons } from "@expo/vector-icons"; import React, { useCallback } from "react"; -import { Pressable, Text, View } from "react-native"; - -const MILES_COUNTRIES = ["US", "GB", "MM", "LR", "PR", "VI", "GU", "MP", "AS", "KY", "TC", "VG", "AI", "MS", "FK"]; - -const formatDistance = (distanceKm: number, country: string | null) => { - const useMiles = country && MILES_COUNTRIES.includes(country); - if (useMiles) { - const distanceMiles = distanceKm * 0.621371; - const rounded = Math.round(distanceMiles); - if (rounded >= 10) { - return `${rounded} mi`; - } - return `${distanceMiles.toFixed(1)} mi`; - } - const rounded = Math.round(distanceKm); - if (rounded >= 10) { - return `${rounded} km`; - } - return `${distanceKm.toFixed(1)} km`; -}; +import { Image, Pressable, Text, View } from "react-native"; type HotspotItemProps = { item: Hotspot & { distance?: number }; onSelect: (hotspot: Hotspot & { distance?: number }) => void; + isSaved?: boolean; }; const HotspotItem = React.memo( - ({ item, onSelect }: HotspotItemProps) => { + ({ item, onSelect, isSaved = false }: HotspotItemProps) => { + const useMiles = useSettingsStore((state) => state.distanceUnits === "imperial"); const handlePress = useCallback(() => { onSelect(item); }, [item, onSelect]); @@ -47,12 +32,20 @@ const HotspotItem = React.memo( {item.name} - + {isSaved ? ( + + ) : ( + + )} {item.species} species {item.distance !== undefined && ( - {formatDistance(item.distance, item.country)} + {formatDistance(item.distance, useMiles)} )} @@ -67,6 +60,7 @@ const HotspotItem = React.memo( prevProps.item.species === nextProps.item.species && prevProps.item.distance === nextProps.item.distance && prevProps.item.country === nextProps.item.country && + prevProps.isSaved === nextProps.isSaved && prevProps.onSelect === nextProps.onSelect ); } diff --git a/components/HotspotList.tsx b/components/HotspotList.tsx index bc8ad2f..2dc73ab 100644 --- a/components/HotspotList.tsx +++ b/components/HotspotList.tsx @@ -1,112 +1,170 @@ +import { useActiveFilterCount } from "@/hooks/useActiveFilterCount"; import { useLocation } from "@/hooks/useLocation"; -import { useScrollRestore } from "@/hooks/useScrollRestore"; -import { getAllHotspots, getNearbyHotspots, searchHotspots } from "@/lib/database"; +import { useTargetRichHotspots } from "@/hooks/useTargetRichHotspots"; +import { getHotspotsWithinBounds, getSavedHotspots, getSavedPlaces } from "@/lib/database"; import tw from "@/lib/tw"; -import { Hotspot } from "@/lib/types"; -import { calculateDistance, getBoundingBoxFromLocation } from "@/lib/utils"; +import { Bounds, Hotspot, SavedPlace } from "@/lib/types"; +import { calculateDistance, isWithinBounds, padBoundsBySize } from "@/lib/utils"; import { useFiltersStore } from "@/stores/filtersStore"; import { useLocationPermissionStore } from "@/stores/locationPermissionStore"; +import { useMapStore } from "@/stores/mapStore"; import { FlashList } from "@shopify/flash-list"; import { useQuery } from "@tanstack/react-query"; -import debounce from "lodash/debounce"; import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; -import { ActivityIndicator, Switch, Text, View } from "react-native"; +import { ActivityIndicator, Text, View } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; +import FilterSlidersIcon from "./icons/FilterSlidersIcon"; import BaseBottomSheet from "./BaseBottomSheet"; +import FilterSheet from "./FilterSheet"; import HotspotItem from "./HotspotItem"; import IconButton from "./IconButton"; import IconButtonGroup from "./IconButtonGroup"; -import SearchInput from "./SearchInput"; +import PlaceItem from "./PlaceItem"; type HotspotListProps = { isOpen: boolean; onClose: () => void; onSelectHotspot: (hotspotId: string, lat: number, lng: number) => void; + onSelectPlace: (placeId: string, lat: number, lng: number) => void; }; -const NEARBY_LIMIT = 200; -const SEARCH_LIMIT = 100; -const ALL_HOTSPOTS_LIMIT = 1000; -const NEARBY_BUCKETS_KM = [50, 100, 200, 500]; +type HotspotRow = Hotspot & { kind: "hotspot"; distance?: number }; +type PlaceRow = SavedPlace & { kind: "place"; distance?: number }; +type ListRow = HotspotRow | PlaceRow; -export default function HotspotList({ isOpen, onClose, onSelectHotspot }: HotspotListProps) { +// Captured when the list opens so it reflects the map's viewport at that moment, +// rather than shifting if the map keeps reporting bounds during the animation. +type ListSnapshot = { + bounds: Bounds | null; + center: { lat: number; lng: number } | null; + zoomedTooFarOut: boolean; +}; + +export default function HotspotList({ isOpen, onClose, onSelectHotspot, onSelectPlace }: HotspotListProps) { const insets = useSafeAreaInsets(); - const { status: permissionStatus, isLoading: isLoadingPermission } = useLocationPermissionStore(); + const { status: permissionStatus } = useLocationPermissionStore(); const { location, isLoading: isLoadingUserLocation } = useLocation(isOpen); - const isLoadingLocation = isLoadingPermission || isLoadingUserLocation; - const { showSavedOnly, setShowSavedOnly } = useFiltersStore(); - const activeFilterCount = [showSavedOnly].filter(Boolean).length; + const showSavedOnly = useFiltersStore((state) => state.showSavedOnly); + const activeFilterCount = useActiveFilterCount(); const dismissRef = useRef<(() => Promise) | null>(null); - const [searchQuery, setSearchQuery] = useState(""); - const [isFilterPanelOpen, setIsFilterPanelOpen] = useState(false); - const [debouncedQuery, setDebouncedQuery] = useState(""); + const storeBounds = useMapStore((state) => state.bounds); + const storeMapCenter = useMapStore((state) => state.mapCenter); + const storeZoomedTooFarOut = useMapStore((state) => state.isZoomedTooFarOut); - const debouncedSetQuery = useMemo(() => debounce(setDebouncedQuery, 150), []); + const [snapshot, setSnapshot] = useState(null); + const [isFilterSheetOpen, setIsFilterSheetOpen] = useState(false); + const wasOpenRef = useRef(false); + // Snapshot the current viewport on the closed -> open transition only. useEffect(() => { - debouncedSetQuery(searchQuery); - return () => debouncedSetQuery.cancel(); - }, [searchQuery, debouncedSetQuery]); + if (isOpen && !wasOpenRef.current) { + setSnapshot({ bounds: storeBounds, center: storeMapCenter, zoomedTooFarOut: storeZoomedTooFarOut }); + } + wasOpenRef.current = isOpen; + }, [isOpen, storeBounds, storeMapCenter, storeZoomedTooFarOut]); useEffect(() => { - if (!isOpen) { - setIsFilterPanelOpen(false); - } + if (!isOpen) setIsFilterSheetOpen(false); }, [isOpen]); - const hasLocationAccess = permissionStatus === "granted" && location !== null; - - const { data: searchResults = [], dataUpdatedAt: searchUpdatedAt } = useQuery({ - queryKey: ["hotspotSearch", debouncedQuery, showSavedOnly], - queryFn: () => searchHotspots(debouncedQuery, SEARCH_LIMIT, showSavedOnly), - enabled: isOpen && debouncedQuery.length >= 2 && !isLoadingLocation, - staleTime: 60 * 1000, - placeholderData: (prev) => prev, - }); + const snapshotBounds = snapshot?.bounds ?? null; + const isZoomedOut = snapshot?.zoomedTooFarOut ?? false; - const { data: allHotspots = [] } = useQuery({ - queryKey: - hasLocationAccess && location - ? ["nearbyHotspots", location.lat, location.lng, showSavedOnly] - : ["allHotspots", showSavedOnly], + const { data: hotspots = [], isFetching: isFetchingHotspots } = useQuery({ + // Same key + padding as Mapbox so we hit the same cached viewport query. + queryKey: ["hotspots", snapshotBounds], queryFn: async () => { - if (hasLocationAccess && location) { - // Try the smallest buckets first to get a result quickly - let hotspots: Hotspot[] = []; - for (const radiusKm of NEARBY_BUCKETS_KM) { - const bbox = getBoundingBoxFromLocation(location.lat, location.lng, radiusKm); - hotspots = await getNearbyHotspots(bbox, showSavedOnly); - if (hotspots.length >= NEARBY_LIMIT) { - return hotspots; - } - } - return hotspots; - } - return getAllHotspots(ALL_HOTSPOTS_LIMIT, showSavedOnly); + if (!snapshotBounds) return []; + const padded = padBoundsBySize(snapshotBounds); + return getHotspotsWithinBounds(padded.west, padded.south, padded.east, padded.north); }, - enabled: isOpen && debouncedQuery.length < 2 && !isLoadingLocation, + enabled: isOpen && snapshotBounds !== null, staleTime: 5 * 60 * 1000, + gcTime: 10 * 60 * 1000, placeholderData: (prev) => prev, }); - const displayedHotspots = useMemo(() => { - const hotspots = debouncedQuery.length >= 2 ? searchResults : allHotspots; + const { data: savedHotspots = [] } = useQuery({ + queryKey: ["savedHotspots"], + queryFn: getSavedHotspots, + staleTime: 5 * 60 * 1000, + gcTime: 10 * 60 * 1000, + }); + + const { data: savedPlaces = [] } = useQuery({ + queryKey: ["savedPlaces"], + queryFn: getSavedPlaces, + staleTime: 5 * 60 * 1000, + gcTime: 10 * 60 * 1000, + }); + + const savedHotspotsSet = useMemo(() => new Set(savedHotspots.map((s) => s.hotspot_id)), [savedHotspots]); + // Fetch uses padded bounds (shared cache with the map), but clip the list back to the + // unpadded viewport so it shows only what's actually visible on the map — not the + // off-screen margin hotspots the padding loads ahead of a pan. + const candidateHotspots = useMemo( + () => + hotspots.filter( + (hotspot) => + (!showSavedOnly || savedHotspotsSet.has(hotspot.id)) && + (!snapshotBounds || isWithinBounds(hotspot.lat, hotspot.lng, snapshotBounds)) + ), + [hotspots, savedHotspotsSet, showSavedOnly, snapshotBounds] + ); + + const targetRichFilter = useTargetRichHotspots(candidateHotspots.map((hotspot) => hotspot.id), { + enabled: isOpen && snapshotBounds !== null && !isFetchingHotspots, + blockWhileDisabled: true, + }); + const targetRichIds = useMemo(() => new Set(targetRichFilter.filteredIds), [targetRichFilter.filteredIds]); + const isTargetRichLoading = targetRichFilter.isActive && (isFetchingHotspots || targetRichFilter.isLoading); + + const filteredHotspots = useMemo(() => { + if (!targetRichFilter.isActive) return candidateHotspots; + return candidateHotspots.filter((hotspot) => targetRichIds.has(hotspot.id)); + }, [candidateHotspots, targetRichFilter.isActive, targetRichIds]); + + // Saved places within the same viewport. Excluded from the list when the + // target-rich filter is active, since they have no species data to qualify. + const placesInView = useMemo(() => { + if (!snapshotBounds) return []; + return savedPlaces.filter((place) => isWithinBounds(place.lat, place.lng, snapshotBounds)); + }, [savedPlaces, snapshotBounds]); - if (hasLocationAccess && location) { - const hotspotsWithDistance = hotspots.map((h) => ({ - ...h, - distance: calculateDistance(location.lat, location.lng, h.lat, h.lng), - })); - hotspotsWithDistance.sort((a, b) => a.distance - b.distance); - return hotspotsWithDistance.slice(0, debouncedQuery.length >= 2 ? hotspots.length : NEARBY_LIMIT); + const hasUserLocation = location !== null; + const originPoint = location ?? snapshot?.center ?? null; + + const rows = useMemo(() => { + const placesForList = targetRichFilter.isActive ? [] : placesInView; + const base: ListRow[] = [ + ...filteredHotspots.map((hotspot) => ({ ...hotspot, kind: "hotspot" as const })), + ...placesForList.map((place) => ({ ...place, kind: "place" as const })), + ]; + + const withDistance = base.map((row) => ({ + row, + sortDistance: originPoint ? calculateDistance(originPoint.lat, originPoint.lng, row.lat, row.lng) : 0, + })); + + if (originPoint) { + withDistance.sort((a, b) => a.sortDistance - b.sortDistance); } else { - const sorted = [...hotspots].sort((a, b) => a.name.localeCompare(b.name)); - return sorted.slice(0, debouncedQuery.length >= 2 ? hotspots.length : ALL_HOTSPOTS_LIMIT); + withDistance.sort((a, b) => a.row.name.localeCompare(b.row.name)); } - }, [debouncedQuery, searchResults, allHotspots, hasLocationAccess, location]); - const { listRef, onScroll } = useScrollRestore(isOpen, searchUpdatedAt); + return withDistance.map(({ row, sortDistance }) => { + const distance = hasUserLocation && originPoint ? sortDistance : undefined; + if (row.kind === "hotspot") { + return { ...row, distance }; + } + return { ...row, distance }; + }); + }, [filteredHotspots, placesInView, targetRichFilter.isActive, originPoint, hasUserLocation]); + + const matchingCount = rows.length; + + const isLocationLoading = isLoadingUserLocation && permissionStatus === "granted" && location === null; const handleSelectHotspot = useCallback( async (hotspot: Hotspot & { distance?: number }) => { @@ -116,40 +174,72 @@ export default function HotspotList({ isOpen, onClose, onSelectHotspot }: Hotspo [onSelectHotspot] ); - const renderHotspotItem = useCallback( - ({ item }: { item: Hotspot & { distance?: number } }) => , - [handleSelectHotspot] + const handleSelectPlace = useCallback( + async (place: SavedPlace & { distance?: number }) => { + await dismissRef.current?.(); + onSelectPlace(place.id, place.lat, place.lng); + }, + [onSelectPlace] + ); + + const renderItem = useCallback( + ({ item }: { item: ListRow }) => + item.kind === "hotspot" ? ( + + ) : ( + + ), + [handleSelectHotspot, handleSelectPlace, savedHotspotsSet] ); + const keyExtractor = useCallback((item: ListRow) => `${item.kind}:${item.id}`, []); + + const showEmptyState = isZoomedOut || isTargetRichLoading || isLocationLoading || rows.length === 0; + const listEmptyComponent = ( - {isLoadingLocation && permissionStatus === "granted" ? ( + {isZoomedOut ? ( + Zoom in to list hotspots + ) : isTargetRichLoading ? ( + <> + + Filtering hotspots... + + ) : isLocationLoading ? ( <> Getting current location... + ) : activeFilterCount > 0 ? ( + No hotspots match your filters ) : ( - No hotspots found + No hotspots in view )} ); - const headerText = hasLocationAccess ? "Nearby Hotspots" : "Hotspots"; - - const keyExtractor = useCallback((item: Hotspot & { distance?: number }) => item.id, []); + let headerTitle: string; + if (isZoomedOut || snapshotBounds === null || isTargetRichLoading || isLocationLoading) { + headerTitle = "Locations"; + } else { + headerTitle = `${matchingCount} ${matchingCount === 1 ? "location" : "locations"}`; + } const headerContent = (dismiss: () => Promise) => { dismissRef.current = dismiss; return ( - + - {headerText} + {headerTitle} - setIsFilterPanelOpen((open) => !open)} /> + } + onPress={() => setIsFilterSheetOpen(true)} + /> {activeFilterCount > 0 && ( - - - {isFilterPanelOpen && ( - - Show saved only - - - )} - - ); }; @@ -180,27 +260,26 @@ export default function HotspotList({ isOpen, onClose, onSelectHotspot }: Hotspo + setIsFilterSheetOpen(false)} /> ); } diff --git a/components/MapViewControls.tsx b/components/MapViewControls.tsx new file mode 100644 index 0000000..8c0478b --- /dev/null +++ b/components/MapViewControls.tsx @@ -0,0 +1,44 @@ +import FilterSlidersIcon from "@/components/icons/FilterSlidersIcon"; +import tw from "@/lib/tw"; +import { GlassView, isLiquidGlassAvailable } from "expo-glass-effect"; +import React from "react"; +import { Platform, Text, TouchableOpacity, View } from "react-native"; + +type MapViewControlsProps = { + onOpenFilters: () => void; + onOpenList: () => void; + filterCount: number; +}; + +export default function MapViewControls({ onOpenFilters, onOpenList, filterCount }: MapViewControlsProps) { + const useGlass = Platform.OS === "ios" && isLiquidGlassAvailable(); + + const inner = ( + + + + {filterCount > 0 && ( + + {filterCount} + + )} + + + + List + + + ); + + if (useGlass) { + return ( + + + {inner} + + + ); + } + + return {inner}; +} diff --git a/components/Mapbox.tsx b/components/Mapbox.tsx index 9459cd3..26c2382 100644 --- a/components/Mapbox.tsx +++ b/components/Mapbox.tsx @@ -1,3 +1,5 @@ +import { useDelayedFlag } from "@/hooks/useDelayedFlag"; +import { useTargetRichHotspots } from "@/hooks/useTargetRichHotspots"; import { getHotspotsWithinBounds, getSavedHotspots, getSavedPlaces } from "@/lib/database"; import { haloInnerStyle, @@ -6,19 +8,21 @@ import { savedHotspotSymbolStyle, savedPlaceSymbolStyle, } from "@/lib/layers"; +import { targetRichHotspotCache } from "@/lib/targetRichHotspots"; import tw from "@/lib/tw"; import { OnPressEvent } from "@/lib/types"; import { findClosestFeature, getMarkerColorIndex, padBoundsBySize } from "@/lib/utils"; import { useFiltersStore } from "@/stores/filtersStore"; import { useLocationPermissionStore } from "@/stores/locationPermissionStore"; import { useMapStore } from "@/stores/mapStore"; +import { useSettingsStore } from "@/stores/settingsStore"; import Mapbox from "@rnmapbox/maps"; import { useQuery } from "@tanstack/react-query"; import Constants from "expo-constants"; import { GlassView, isLiquidGlassAvailable } from "expo-glass-effect"; import debounce from "lodash/debounce"; import throttle from "lodash/throttle"; -import React, { forwardRef, useCallback, useImperativeHandle, useMemo, useRef, useState } from "react"; +import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react"; import { Linking, Platform, Text, TouchableOpacity, View, ViewStyle } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import BaseBottomSheet from "./BaseBottomSheet"; @@ -102,9 +106,31 @@ export type MapboxMapRef = { const THROTTLE_DELAY = 750; const THROTTLE_DELAY_WITH_OPEN_HOTSPOT = 250; // Load hotspots faster when jumping to a hotspot from list modal +const SETTLED_BOUNDS_DELAY = 300; +// Only surface the "Filtering hotspots..." badge if filtering outlasts this; incremental pans resolve faster and shouldn't flicker it. +const TARGET_RICH_BADGE_DELAY = 350; const MIN_ZOOM = 8; const DEFAULT_USER_ZOOM = 14; const DEFAULT_HOTSPOT_ZOOM = 13; +const BOUNDS_EPSILON = 0.0001; + +function areBoundsEquivalent(left: Bounds | null, right: Bounds | null): boolean { + if (left === right) { + return true; + } + + if (!left || !right) { + return false; + } + + return ( + Math.abs(left.west - right.west) < BOUNDS_EPSILON && + Math.abs(left.south - right.south) < BOUNDS_EPSILON && + Math.abs(left.east - right.east) < BOUNDS_EPSILON && + Math.abs(left.north - right.north) < BOUNDS_EPSILON + ); +} + const isValidUserCoord = (coord: [number, number] | null) => { if (!coord) return false; const [lng, lat] = coord; @@ -134,12 +160,16 @@ const MapboxMap = forwardRef( const currentLayer = useMapStore((state) => state.currentLayer); const placeId = useMapStore((state) => state.placeId); const setMapCenter = useMapStore((state) => state.setMapCenter); + const setStoreBounds = useMapStore((state) => state.setBounds); + const setInViewCount = useMapStore((state) => state.setInViewCount); + const setDisplayedCount = useMapStore((state) => state.setDisplayedCount); const isZoomedTooFarOut = useMapStore((state) => state.isZoomedTooFarOut); const setIsZoomedTooFarOut = useMapStore((state) => state.setIsZoomedTooFarOut); const showAttribution = useMapStore((state) => state.isMapAttributionOpen); const setShowAttribution = useMapStore((state) => state.setIsMapAttributionOpen); const { status: permissionStatus } = useLocationPermissionStore(); - const { showSavedOnly } = useFiltersStore(); + const { showSavedOnly, targetRichEnabled } = useFiltersStore(); + const lifelist = useSettingsStore((state) => state.lifelist); const mapRef = useRef(null); const cameraRef = useRef(null); @@ -147,9 +177,12 @@ const MapboxMap = forwardRef( const centeredToUserRef = useRef(false); const userCoordRef = useRef<[number, number] | null>(null); const isTouchActiveRef = useRef(false); + const lastResolvedHotspotsRef = useRef<{ id: string; lat: number; lng: number; species: number }[]>([]); const [isMapReady, setIsMapReady] = useState(false); const [bounds, setBounds] = useState(null); + const hasLifeList = (lifelist?.length ?? 0) > 0; + const isTargetRichMapFiltering = targetRichEnabled && hasLifeList; const mapStyle = useMemo(() => { return currentLayer === "satellite" @@ -157,7 +190,7 @@ const MapboxMap = forwardRef( : "mapbox://styles/mapbox/outdoors-v12"; }, [currentLayer]); - const { data: hotspots = [] } = useQuery({ + const { data: hotspots = [], isFetching: isFetchingHotspots } = useQuery({ queryKey: ["hotspots", bounds], queryFn: async () => { if (!bounds) return []; @@ -192,9 +225,19 @@ const MapboxMap = forwardRef( } const throttledSetBounds = useMemo( - () => throttle((b: Bounds | null) => setBounds(b), throttleDelay), + () => + throttle((nextBounds: Bounds | null) => { + setBounds((currentBounds) => (areBoundsEquivalent(currentBounds, nextBounds) ? currentBounds : nextBounds)); + }, throttleDelay), [throttleDelay] ); + const debouncedSetSettledBounds = useMemo( + () => + debounce((nextBounds: Bounds | null) => { + setBounds((currentBounds) => (areBoundsEquivalent(currentBounds, nextBounds) ? currentBounds : nextBounds)); + }, SETTLED_BOUNDS_DELAY), + [] + ); const debouncedSaveLocation = useMemo( () => debounce(async () => { @@ -228,13 +271,41 @@ const MapboxMap = forwardRef( return null; }, [isZoomedTooFarOut, setIsZoomedTooFarOut]); - const syncViewport = useCallback(async () => { + const syncViewport = useCallback(async (immediate = false) => { if (!mapRef.current) return; - const b = await readBoundsIfZoomed(); - throttledSetBounds(b); + const nextBounds = await readBoundsIfZoomed(); + if (immediate) { + throttledSetBounds.cancel(); + debouncedSetSettledBounds.cancel(); + setBounds((currentBounds) => (areBoundsEquivalent(currentBounds, nextBounds) ? currentBounds : nextBounds)); + } else if (isTargetRichMapFiltering) { + debouncedSetSettledBounds(nextBounds); + } else { + throttledSetBounds(nextBounds); + } debouncedSaveLocation(); throttledSetMapCenter(); - }, [readBoundsIfZoomed, throttledSetBounds, debouncedSaveLocation, throttledSetMapCenter]); + }, [ + debouncedSaveLocation, + debouncedSetSettledBounds, + isTargetRichMapFiltering, + readBoundsIfZoomed, + throttledSetBounds, + throttledSetMapCenter, + ]); + + useEffect(() => { + return () => { + throttledSetBounds.cancel(); + debouncedSetSettledBounds.cancel(); + }; + }, [debouncedSetSettledBounds, throttledSetBounds]); + + useEffect(() => { + throttledSetBounds.cancel(); + debouncedSetSettledBounds.cancel(); + void syncViewport(true); + }, [debouncedSetSettledBounds, isTargetRichMapFiltering, syncViewport, throttledSetBounds]); const setTouchActive = useCallback( (isActive: boolean) => { @@ -285,6 +356,72 @@ const MapboxMap = forwardRef( ); const savedHotspotsSet = useMemo(() => new Set(savedHotspots.map((s) => s.hotspot_id)), [savedHotspots]); + const mapCandidateHotspots = useMemo( + () => hotspots.filter((hotspot) => !showSavedOnly || savedHotspotsSet.has(hotspot.id)), + [hotspots, savedHotspotsSet, showSavedOnly] + ); + const targetRichFilter = useTargetRichHotspots(mapCandidateHotspots.map((hotspot) => hotspot.id), { + enabled: bounds !== null && !isFetchingHotspots, + blockWhileDisabled: true, + }); + const targetRichHotspotIds = useMemo(() => new Set(targetRichFilter.filteredIds), [targetRichFilter.filteredIds]); + const unresolvedCandidateCount = targetRichFilter.isActive + ? mapCandidateHotspots.reduce((count, hotspot) => count + (targetRichHotspotCache.has(hotspot.id) ? 0 : 1), 0) + : 0; + const isInitialTargetRichFetch = + targetRichFilter.isActive && + bounds !== null && + isFetchingHotspots && + lastResolvedHotspotsRef.current.length === 0; + const isTargetRichLoading = + targetRichFilter.isActive && + (isInitialTargetRichFetch || unresolvedCandidateCount > 0 || targetRichFilter.isLoading); + // Delay the badge so brief filtering during incremental pans doesn't flash it; markers still use isTargetRichLoading directly. + const isTargetRichBadgeVisible = useDelayedFlag(isTargetRichLoading, TARGET_RICH_BADGE_DELAY); + const displayedHotspots = useMemo(() => { + if (showSavedOnly || targetRichFilter.isActive) { + const resolvedHotspots = mapCandidateHotspots.filter((hotspot) => + targetRichFilter.isActive ? targetRichHotspotIds.has(hotspot.id) : true + ); + + if (isTargetRichLoading) { + return lastResolvedHotspotsRef.current; + } + + return resolvedHotspots; + } + + return hotspots; + }, [ + hotspots, + isTargetRichLoading, + mapCandidateHotspots, + targetRichFilter.isActive, + targetRichHotspotIds, + showSavedOnly, + ]); + + useEffect(() => { + if (!isTargetRichLoading) { + lastResolvedHotspotsRef.current = displayedHotspots; + } + }, [displayedHotspots, isTargetRichLoading]); + + // Mirror the viewport + result counts into the store so the hotspot list can + // snapshot the same bounds and the toggle pill can show how many are in view. + useEffect(() => { + setStoreBounds(bounds); + }, [bounds, setStoreBounds]); + + useEffect(() => { + if (bounds === null) { + setInViewCount(null); + setDisplayedCount(null); + return; + } + setInViewCount(mapCandidateHotspots.length); + setDisplayedCount(displayedHotspots.length); + }, [bounds, mapCandidateHotspots.length, displayedHotspots.length, setInViewCount, setDisplayedCount]); const handleFeaturePress = useCallback( (event: any) => { @@ -346,10 +483,14 @@ const MapboxMap = forwardRef( style={tw`flex-1`} styleURL={mapStyle} onDidFinishLoadingMap={() => setIsMapReady(true)} - onDidFinishLoadingStyle={syncViewport} - onCameraChanged={syncViewport} - onMapIdle={() => { + onDidFinishLoadingStyle={() => { + syncViewport(true); + }} + onCameraChanged={() => { syncViewport(); + }} + onMapIdle={() => { + syncViewport(true); centerMapOnUserInitial(); }} onPress={handleFeaturePress} @@ -393,14 +534,14 @@ const MapboxMap = forwardRef( )} - {isMapReady && (hotspots.length > 0 || savedPlaces.length > 0) && ( + {isMapReady && (displayedHotspots.length > 0 || savedPlaces.length > 0) && ( { + ...displayedHotspots.map((h: any) => { const isSaved = savedHotspotsSet.has(h.id); return { type: "Feature" as const, @@ -427,15 +568,10 @@ const MapboxMap = forwardRef( ], }} > - {/* Hotspot - hidden when showSavedOnly filter is active */} + {/* Hotspot */} @@ -446,7 +582,7 @@ const MapboxMap = forwardRef( style={savedHotspotSymbolStyle() as any} /> - {/* Selected hotspot - hidden when showSavedOnly filter is active */} + {/* Selected hotspot */} ( ["==", ["get", "featureType"], "hotspot"], ["==", ["get", "isSaved"], false], ["==", ["get", "isSelected"], true], - ["literal", !showSavedOnly], ]} style={haloInnerStyle() as any} /> @@ -465,7 +600,6 @@ const MapboxMap = forwardRef( ["==", ["get", "featureType"], "hotspot"], ["==", ["get", "isSaved"], false], ["==", ["get", "isSelected"], true], - ["literal", !showSavedOnly], ]} style={haloOuterStyle() as any} /> @@ -476,7 +610,6 @@ const MapboxMap = forwardRef( ["==", ["get", "featureType"], "hotspot"], ["==", ["get", "isSaved"], false], ["==", ["get", "isSelected"], true], - ["literal", !showSavedOnly], ]} style={hotspotSymbolStyle() as any} /> @@ -591,6 +724,22 @@ const MapboxMap = forwardRef( )} + {isTargetRichBadgeVisible && !isZoomedTooFarOut && ( + + {Platform.OS === "ios" && isLiquidGlassAvailable() ? ( + + + Filtering hotspots... + + + ) : ( + + Filtering hotspots... + + )} + + )} + ( - { await dismiss(); diff --git a/components/MenuList.tsx b/components/MenuList.tsx index 1b31d36..7b8f2b0 100644 --- a/components/MenuList.tsx +++ b/components/MenuList.tsx @@ -54,7 +54,7 @@ export default function MenuList({ onNavigateToPacks, onNavigateToSettings, pack ); return ( - + {menuOptions.map((item) => ( {renderMenuItem({ item })} ))} diff --git a/components/PlaceEditSheet.tsx b/components/PlaceEditSheet.tsx index 80b197a..0fd353e 100644 --- a/components/PlaceEditSheet.tsx +++ b/components/PlaceEditSheet.tsx @@ -1,6 +1,7 @@ import IconButton from "@/components/IconButton"; import Input from "@/components/Input"; import { deletePlace, getSavedPlaceById, savePlace } from "@/lib/database"; +import { placeIconImages } from "@/lib/placeIconImages"; import { placeIcons, type PlaceIconT } from "@/lib/placeIcons"; import tw from "@/lib/tw"; import { generateId } from "@/lib/utils"; @@ -11,30 +12,6 @@ import { FlatList, Image, ScrollView, Text, TextInput, TouchableOpacity, View } import Toast from "react-native-toast-message"; import BaseBottomSheet from "./BaseBottomSheet"; -const placeIconImages: Record = { - hike: require("@/assets/markers/place-hike.png"), - mountain: require("@/assets/markers/place-mountain.png"), - tent: require("@/assets/markers/place-tent.png"), - house: require("@/assets/markers/place-house.png"), - airbnb: require("@/assets/markers/place-airbnb.png"), - bed: require("@/assets/markers/place-bed.png"), - bins: require("@/assets/markers/place-bins.png"), - camera: require("@/assets/markers/place-camera.png"), - airport: require("@/assets/markers/place-airport.png"), - boat: require("@/assets/markers/place-boat.png"), - car: require("@/assets/markers/place-car.png"), - bus: require("@/assets/markers/place-bus.png"), - utensils: require("@/assets/markers/place-utensils.png"), - mug: require("@/assets/markers/place-mug.png"), - trolley: require("@/assets/markers/place-trolley.png"), - bike: require("@/assets/markers/place-bike.png"), - dog: require("@/assets/markers/place-dog.png"), - fuel: require("@/assets/markers/place-fuel.png"), - parking: require("@/assets/markers/place-parking.png"), - building: require("@/assets/markers/place-building.png"), - hotspot: require("@/assets/markers/place-hotspot.png"), -}; - const placeIconKeys = Object.keys(placeIcons) as PlaceIconT[]; type PlaceEditSheetProps = { diff --git a/components/PlaceItem.tsx b/components/PlaceItem.tsx new file mode 100644 index 0000000..192e5e7 --- /dev/null +++ b/components/PlaceItem.tsx @@ -0,0 +1,61 @@ +import { getPlaceIconImage } from "@/lib/placeIconImages"; +import tw from "@/lib/tw"; +import { SavedPlace } from "@/lib/types"; +import { formatDistance } from "@/lib/utils"; +import { useSettingsStore } from "@/stores/settingsStore"; +import { Ionicons } from "@expo/vector-icons"; +import React, { useCallback } from "react"; +import { Image, Pressable, Text, View } from "react-native"; + +type PlaceItemProps = { + item: SavedPlace & { distance?: number }; + onSelect: (place: SavedPlace & { distance?: number }) => void; +}; + +const PlaceItem = React.memo( + ({ item, onSelect }: PlaceItemProps) => { + const useMiles = useSettingsStore((state) => state.distanceUnits === "imperial"); + const handlePress = useCallback(() => { + onSelect(item); + }, [item, onSelect]); + + return ( + // Prevent the parent list from canceling row presses after the list is reopened. + [tw`flex-row items-center px-4 py-3 border-b border-gray-200/50`, pressed && tw`opacity-70`]} + > + + + {item.name} + + + + {item.notes ? item.notes : "Saved Pin"} + + + {item.distance !== undefined && ( + {formatDistance(item.distance, useMiles)} + )} + + + ); + }, + (prevProps, nextProps) => { + return ( + prevProps.item.id === nextProps.item.id && + prevProps.item.lat === nextProps.item.lat && + prevProps.item.lng === nextProps.item.lng && + prevProps.item.name === nextProps.item.name && + prevProps.item.icon === nextProps.item.icon && + prevProps.item.notes === nextProps.item.notes && + prevProps.item.distance === nextProps.item.distance && + prevProps.onSelect === nextProps.onSelect + ); + } +); + +PlaceItem.displayName = "PlaceItem"; + +export default PlaceItem; diff --git a/components/SearchInput.tsx b/components/SearchInput.tsx index 43bf1d9..851a814 100644 --- a/components/SearchInput.tsx +++ b/components/SearchInput.tsx @@ -9,7 +9,10 @@ type SearchInputProps = { value: string; onChangeText: (text: string) => void; placeholder?: string; -} & Pick; + // When false, the inline clear (✕) button is never shown — useful when the + // surrounding UI already provides a close affordance and a second ✕ would clash. + clearable?: boolean; +} & Pick; const AnimatedPressable = Animated.createAnimatedComponent(Pressable); @@ -28,12 +31,14 @@ export default function SearchInput({ autoCapitalize = "none", autoComplete = "off", returnKeyType = "search", + autoFocus = false, + clearable = true, }: SearchInputProps) { const inputRef = useRef(null); const [isFocused, setIsFocused] = useState(false); const useGlass = Platform.OS === "ios" && isLiquidGlassAvailable(); - const showClear = value.length > 0 || isFocused; + const showClear = clearable && (value.length > 0 || isFocused); const handleClear = () => { onChangeText(""); @@ -59,6 +64,7 @@ export default function SearchInput({ autoCapitalize={autoCapitalize} autoComplete={autoComplete} returnKeyType={returnKeyType} + autoFocus={autoFocus} /> ); diff --git a/components/SearchSheet.tsx b/components/SearchSheet.tsx new file mode 100644 index 0000000..763ae78 --- /dev/null +++ b/components/SearchSheet.tsx @@ -0,0 +1,228 @@ +import { useLocation } from "@/hooks/useLocation"; +import { getSavedHotspots, getSavedPlaces, searchHotspots } from "@/lib/database"; +import tw from "@/lib/tw"; +import { Hotspot, SavedPlace } from "@/lib/types"; +import { calculateDistance } from "@/lib/utils"; +import { useMapStore } from "@/stores/mapStore"; +import { useQuery } from "@tanstack/react-query"; +import { FlashList } from "@shopify/flash-list"; +import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { Text, View } from "react-native"; +import BaseBottomSheet from "./BaseBottomSheet"; +import HotspotItem from "./HotspotItem"; +import IconButton from "./IconButton"; +import PlaceItem from "./PlaceItem"; +import SearchInput from "./SearchInput"; + +type SearchSheetProps = { + isOpen: boolean; + onClose: () => void; + onSelectHotspot: (hotspotId: string, lat: number, lng: number) => void; + onSelectPlace: (placeId: string, lat: number, lng: number) => void; +}; + +// Nothing is searched until the query is meaningful — a 1-char `LIKE %x%` +// against the full table matches almost everything. +const MIN_QUERY = 2; +const HOTSPOT_LIMIT = 30; + +type PlaceWithDistance = SavedPlace & { distance?: number }; +type HotspotWithDistance = Hotspot & { distance?: number }; + +type SearchRow = + | { type: "section"; key: string; title: string } + | { type: "place"; key: string; place: PlaceWithDistance } + | { type: "hotspot"; key: string; hotspot: HotspotWithDistance }; + +export default function SearchSheet({ isOpen, onClose, onSelectHotspot, onSelectPlace }: SearchSheetProps) { + const [query, setQuery] = useState(""); + const [debouncedQuery, setDebouncedQuery] = useState(""); + const dismissRef = useRef<(() => Promise) | null>(null); + const { location } = useLocation(isOpen); + const mapCenter = useMapStore((state) => state.mapCenter); + + // Sort origin falls back to map center so duplicate-named saved places (e.g. + // two "Airport"s) still order nearest-first without GPS. The displayed + // distance label stays gated on real location only — see `withDistance`. + const originPoint = location ?? mapCenter ?? null; + + useEffect(() => { + if (!isOpen) { + setQuery(""); + setDebouncedQuery(""); + } + }, [isOpen]); + + // Debounce the hotspot SQL search; place filtering stays live off `query`. + useEffect(() => { + const handle = setTimeout(() => setDebouncedQuery(query.trim()), 250); + return () => clearTimeout(handle); + }, [query]); + + const { data: savedPlaces = [] } = useQuery({ + queryKey: ["savedPlaces"], + queryFn: getSavedPlaces, + staleTime: 5 * 60 * 1000, + gcTime: 10 * 60 * 1000, + }); + + const { data: savedHotspots = [] } = useQuery({ + queryKey: ["savedHotspots"], + queryFn: getSavedHotspots, + staleTime: 5 * 60 * 1000, + gcTime: 10 * 60 * 1000, + }); + + const savedHotspotsSet = useMemo(() => new Set(savedHotspots.map((s) => s.hotspot_id)), [savedHotspots]); + + const hotspotQueryEnabled = isOpen && debouncedQuery.length >= MIN_QUERY; + const { data: hotspotResults = [] } = useQuery({ + queryKey: ["searchHotspots", debouncedQuery], + queryFn: () => searchHotspots(debouncedQuery, HOTSPOT_LIMIT), + enabled: hotspotQueryEnabled, + staleTime: 5 * 60 * 1000, + gcTime: 10 * 60 * 1000, + placeholderData: (prev) => prev, + }); + + const withDistance = useCallback( + (item: T): T & { distance?: number } => { + if (!location) return item; + return { ...item, distance: calculateDistance(location.lat, location.lng, item.lat, item.lng) }; + }, + [location] + ); + + const matchingPlaces = useMemo(() => { + const trimmed = query.trim().toLowerCase(); + const matched = trimmed.length >= MIN_QUERY + ? savedPlaces.filter((place) => place.name.toLowerCase().includes(trimmed)) + : []; + const decorated = matched.map((place) => ({ + place: withDistance(place), + sortDistance: originPoint ? calculateDistance(originPoint.lat, originPoint.lng, place.lat, place.lng) : null, + })); + decorated.sort((a, b) => { + if (a.sortDistance !== null && b.sortDistance !== null) return a.sortDistance - b.sortDistance; + return a.place.name.localeCompare(b.place.name); + }); + return decorated.map((d) => d.place); + }, [savedPlaces, query, withDistance, originPoint]); + + // Hotspots come back alphabetical from SQL; surface prefix matches first so + // the most likely target sits at the top. + const rankedHotspots = useMemo(() => { + if (!hotspotQueryEnabled) return []; + const trimmed = debouncedQuery.toLowerCase(); + const decorated = hotspotResults.map(withDistance); + decorated.sort((a, b) => { + const aPrefix = a.name.toLowerCase().startsWith(trimmed) ? 0 : 1; + const bPrefix = b.name.toLowerCase().startsWith(trimmed) ? 0 : 1; + if (aPrefix !== bPrefix) return aPrefix - bPrefix; + return a.name.localeCompare(b.name); + }); + return decorated; + }, [hotspotResults, debouncedQuery, hotspotQueryEnabled, withDistance]); + + const rows = useMemo(() => { + const result: SearchRow[] = []; + if (matchingPlaces.length > 0) { + result.push({ type: "section", key: "section:saved", title: "Saved locations" }); + for (const place of matchingPlaces) { + result.push({ type: "place", key: `place:${place.id}`, place }); + } + } + if (rankedHotspots.length > 0) { + result.push({ type: "section", key: "section:hotspots", title: "Hotspots" }); + for (const hotspot of rankedHotspots) { + result.push({ type: "hotspot", key: `hotspot:${hotspot.id}`, hotspot }); + } + } + return result; + }, [matchingPlaces, rankedHotspots]); + + const handleSelectHotspot = useCallback( + async (hotspot: HotspotWithDistance) => { + await dismissRef.current?.(); + onSelectHotspot(hotspot.id, hotspot.lat, hotspot.lng); + }, + [onSelectHotspot] + ); + + const handleSelectPlace = useCallback( + async (place: PlaceWithDistance) => { + await dismissRef.current?.(); + onSelectPlace(place.id, place.lat, place.lng); + }, + [onSelectPlace] + ); + + const renderItem = useCallback( + ({ item }: { item: SearchRow }) => { + if (item.type === "section") { + return ( + + {item.title} + + ); + } + if (item.type === "place") { + return ; + } + return ; + }, + [handleSelectHotspot, handleSelectPlace, savedHotspotsSet] + ); + + const keyExtractor = useCallback((item: SearchRow) => item.key, []); + + // Distinguish empty-because-nothing-typed from empty-because-no-matches. + const isSearching = query.trim().length >= MIN_QUERY; + const emptyMessage = isSearching ? "No matches" : "Search for hotspots or saved locations"; + + const headerContent = (dismiss: () => Promise) => { + dismissRef.current = dismiss; + return ( + + + + + + + ); + }; + + return ( + + + {emptyMessage} + + } + /> + + ); +} diff --git a/components/TargetRichHotspotControls.tsx b/components/TargetRichHotspotControls.tsx new file mode 100644 index 0000000..f1550f7 --- /dev/null +++ b/components/TargetRichHotspotControls.tsx @@ -0,0 +1,90 @@ +import tw from "@/lib/tw"; +import { useFiltersStore } from "@/stores/filtersStore"; +import { Ionicons } from "@expo/vector-icons"; +import React from "react"; +import { Pressable, Switch, Text, View } from "react-native"; + +type TargetRichHotspotControlsProps = { + hasLifeList: boolean; +}; + +const PERCENT_STEP = 10; +const MIN_PERCENT = 10; + +function StepperButton({ + icon, + onPress, + disabled, +}: { + icon: keyof typeof Ionicons.glyphMap; + onPress: () => void; + disabled?: boolean; +}) { + return ( + [ + tw`w-9 h-9 rounded-full items-center justify-center border border-gray-300`, + disabled && tw`opacity-40`, + pressed && !disabled && tw`bg-gray-100`, + ]} + > + + + ); +} + +export default function TargetRichHotspotControls({ + hasLifeList, +}: TargetRichHotspotControlsProps) { + const enabled = useFiltersStore((state) => state.targetRichEnabled); + const setEnabled = useFiltersStore((state) => state.setTargetRichEnabled); + const minCount = useFiltersStore((state) => state.minTargets); + const setMinCount = useFiltersStore((state) => state.setMinTargets); + const minPercent = useFiltersStore((state) => state.minTargetFrequency); + const setMinPercent = useFiltersStore((state) => state.setMinTargetFrequency); + + // Snap to clean tens as the user steps, so values stay 0/10/20/… even if a + // legacy stored value (e.g. 12) was off the grid. + const stepPercent = (delta: number) => { + const base = Math.round(minPercent / PERCENT_STEP) * PERCENT_STEP; + setMinPercent(Math.min(100, Math.max(MIN_PERCENT, base + delta))); + }; + + return ( + + + + Target-Rich Hotspots + + + + + {!hasLifeList ? ( + Import a life list to enable this filter. + ) : enabled ? ( + + + Minimum targets + + setMinCount(minCount - 1)} disabled={minCount <= 1} /> + {minCount} + setMinCount(minCount + 1)} /> + + + + + Minimum frequency + + stepPercent(-PERCENT_STEP)} disabled={minPercent <= MIN_PERCENT} /> + {minPercent}% + stepPercent(PERCENT_STEP)} disabled={minPercent >= 100} /> + + + + ) : null} + + ); +} diff --git a/components/icons/FilterSlidersIcon.tsx b/components/icons/FilterSlidersIcon.tsx new file mode 100644 index 0000000..2c187d3 --- /dev/null +++ b/components/icons/FilterSlidersIcon.tsx @@ -0,0 +1,19 @@ +import React from "react"; +import Svg, { Path } from "react-native-svg"; + +type Props = { + color?: string; + size?: number; +}; + +// Font Awesome Pro "sliders" (filter) icon. +export default function FilterSlidersIcon({ color = "#374151", size = 20 }: Props) { + return ( + + + + ); +} diff --git a/components/ui/IconSymbol.tsx b/components/ui/IconSymbol.tsx index f3687fa..e22a33a 100644 --- a/components/ui/IconSymbol.tsx +++ b/components/ui/IconSymbol.tsx @@ -5,7 +5,7 @@ import { SymbolWeight, SymbolViewProps } from 'expo-symbols'; import { ComponentProps } from 'react'; import { OpaqueColorValue, type StyleProp, type TextStyle } from 'react-native'; -type IconMapping = Record['name']>; +type IconMapping = Record, ComponentProps['name']>; type IconSymbolName = keyof typeof MAPPING; /** diff --git a/hooks/useActiveFilterCount.ts b/hooks/useActiveFilterCount.ts new file mode 100644 index 0000000..4d210b6 --- /dev/null +++ b/hooks/useActiveFilterCount.ts @@ -0,0 +1,12 @@ +import { useFiltersStore } from "@/stores/filtersStore"; +import { useSettingsStore } from "@/stores/settingsStore"; + +// The target-rich filter only counts as active once a life list is imported. +export function useActiveFilterCount() { + const showSavedOnly = useFiltersStore((state) => state.showSavedOnly); + const targetRichEnabled = useFiltersStore((state) => state.targetRichEnabled); + const lifelist = useSettingsStore((state) => state.lifelist); + const hasLifeList = (lifelist?.length ?? 0) > 0; + + return [showSavedOnly, targetRichEnabled && hasLifeList].filter(Boolean).length; +} diff --git a/hooks/useDelayedFlag.ts b/hooks/useDelayedFlag.ts new file mode 100644 index 0000000..97fe06c --- /dev/null +++ b/hooks/useDelayedFlag.ts @@ -0,0 +1,40 @@ +import { useEffect, useRef, useState } from "react"; + +/** + * Delays surfacing a transient `true` flag so brief flickers never render. + * + * The returned value only becomes `true` after `source` has stayed `true` + * continuously for `delayMs`. It clears immediately when `source` goes `false`, + * so quick on/off cycles (e.g. a loading badge during incremental map pans) + * never appear. Use to suppress loading indicators for work that usually + * resolves faster than the delay. + */ +export function useDelayedFlag(source: boolean, delayMs: number): boolean { + const [delayed, setDelayed] = useState(false); + const timeoutRef = useRef | null>(null); + + useEffect(() => { + if (!source) { + if (timeoutRef.current !== null) { + clearTimeout(timeoutRef.current); + timeoutRef.current = null; + } + setDelayed(false); + return; + } + + timeoutRef.current = setTimeout(() => { + timeoutRef.current = null; + setDelayed(true); + }, delayMs); + + return () => { + if (timeoutRef.current !== null) { + clearTimeout(timeoutRef.current); + timeoutRef.current = null; + } + }; + }, [source, delayMs]); + + return delayed; +} diff --git a/hooks/useManagePack.ts b/hooks/useManagePack.ts index 8b8f08b..d446b8d 100644 --- a/hooks/useManagePack.ts +++ b/hooks/useManagePack.ts @@ -1,5 +1,6 @@ import { cleanupPartialInstall, installPackWithTargets, uninstallPack } from "@/lib/database"; import { downloadWithProgress } from "@/lib/download"; +import { resetTargetRichHotspotCache } from "@/lib/targetRichHotspots"; import { StaticPack, StaticPackResponse } from "@/lib/types"; import { refreshTaxonomy } from "@/lib/taxonomy"; import { API_URL } from "@/lib/utils"; @@ -69,6 +70,8 @@ export function useManagePack(packId: number) { downloadStore.setProgress(100); + resetTargetRichHotspotCache(); + await queryClient.invalidateQueries({ queryKey: ["installed-packs"] }); queryClient.invalidateQueries({ queryKey: ["hotspots"], refetchType: "active" }); queryClient.invalidateQueries({ queryKey: ["hotspotSearch"] }); @@ -111,6 +114,8 @@ export function useManagePack(packId: number) { await uninstallPack(packId); + resetTargetRichHotspotCache(); + queryClient.invalidateQueries({ queryKey: ["installed-packs"] }); queryClient.invalidateQueries({ queryKey: ["hotspots"], refetchType: "active" }); queryClient.invalidateQueries({ queryKey: ["hotspotSearch"] }); diff --git a/hooks/useScrollRestore.ts b/hooks/useScrollRestore.ts deleted file mode 100644 index 0cc5f15..0000000 --- a/hooks/useScrollRestore.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { useCallback, useEffect, useRef } from "react"; -import type { NativeScrollEvent, NativeSyntheticEvent } from "react-native"; - -export function useScrollRestore(isOpen: boolean, resetKey: number) { - const listRef = useRef(null); - const scrollOffsetRef = useRef(0); - const prevResetKeyRef = useRef(resetKey); - - // Restore scroll position when modal opens - useEffect(() => { - if (isOpen && scrollOffsetRef.current > 0) { - const timeoutId = setTimeout(() => { - listRef.current?.scrollToOffset({ offset: scrollOffsetRef.current, animated: false }); - }, 100); - return () => clearTimeout(timeoutId); - } - }, [isOpen]); - - // Reset scroll when resetKey changes - useEffect(() => { - if (resetKey !== prevResetKeyRef.current) { - prevResetKeyRef.current = resetKey; - scrollOffsetRef.current = 0; - listRef.current?.scrollToOffset({ offset: 0, animated: false }); - } - }, [resetKey]); - - const onScroll = useCallback((e: NativeSyntheticEvent) => { - scrollOffsetRef.current = e.nativeEvent.contentOffset.y; - }, []); - - return { listRef, onScroll }; -} diff --git a/hooks/useTargetRichHotspots.ts b/hooks/useTargetRichHotspots.ts new file mode 100644 index 0000000..90d6e07 --- /dev/null +++ b/hooks/useTargetRichHotspots.ts @@ -0,0 +1,221 @@ +import { + createTargetRichHotspotBasis, + getTargetRichHotspotCacheGeneration, + subscribeToTargetRichHotspotCacheReset, + targetRichHotspotCache, + syncTargetRichHotspotCacheBasis, +} from "@/lib/targetRichHotspots"; +import { useFiltersStore } from "@/stores/filtersStore"; +import { useSettingsStore } from "@/stores/settingsStore"; +import { useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react"; + +type UseTargetRichHotspotsOptions = { + enabled?: boolean; + blockWhileDisabled?: boolean; +}; + +type TargetRichHotspotState = { + filteredIds: string[]; + isActive: boolean; + isLoading: boolean; + hasLifeList: boolean; +}; + +type AsyncTargetRichHotspotState = { + filteredIds: string[]; + isLoading: boolean; +}; + +function filterResolvedHotspotIds(hotspotIds: string[]): string[] { + return hotspotIds.filter((hotspotId) => targetRichHotspotCache.get(hotspotId) === true); +} + +function areStringArraysEqual(left: string[], right: string[]): boolean { + if (left.length !== right.length) { + return false; + } + + return left.every((value, index) => value === right[index]); +} + +export function useTargetRichHotspots( + hotspotIds: string[], + options: UseTargetRichHotspotsOptions = {} +): TargetRichHotspotState { + const targetRichEnabled = useFiltersStore((state) => state.targetRichEnabled); + const minTargets = useFiltersStore((state) => state.minTargets); + const minTargetFrequency = useFiltersStore((state) => state.minTargetFrequency); + const lifelist = useSettingsStore((state) => state.lifelist); + const lifelistExclusions = useSettingsStore((state) => state.lifelistExclusions); + const targetMonths = useSettingsStore((state) => state.targetMonths); + + const basis = useMemo( + () => + createTargetRichHotspotBasis({ + lifelist, + lifelistExclusions, + targetMonths, + minTargets, + minTargetFrequency, + }), + [lifelist, lifelistExclusions, targetMonths, minTargets, minTargetFrequency] + ); + + const cacheGeneration = useSyncExternalStore( + subscribeToTargetRichHotspotCacheReset, + getTargetRichHotspotCacheGeneration + ); + + const hasLifeList = basis !== null; + const isActive = targetRichEnabled && hasLifeList; + const isEnabled = options.enabled ?? true; + const candidateKey = useMemo(() => hotspotIds.join("|"), [hotspotIds]); + const stableHotspotIdsRef = useRef(hotspotIds); + const basisRef = useRef(basis); + + if (stableHotspotIdsRef.current !== hotspotIds && stableHotspotIdsRef.current.join("|") !== candidateKey) { + stableHotspotIdsRef.current = hotspotIds; + } + + const stableHotspotIds = stableHotspotIdsRef.current; + + const [asyncState, setAsyncState] = useState({ + filteredIds: [], + isLoading: false, + }); + + useEffect(() => { + basisRef.current = basis; + }, [basis]); + + useEffect(() => { + syncTargetRichHotspotCacheBasis(basis?.cacheKey ?? null); + }, [basis?.cacheKey]); + + useEffect(() => { + if (!isActive || !isEnabled || stableHotspotIds.length === 0 || !basisRef.current) { + return; + } + + const basisForRun = basisRef.current; + const unresolvedHotspotIds = stableHotspotIds.filter((hotspotId) => !targetRichHotspotCache.has(hotspotId)); + if (unresolvedHotspotIds.length === 0) { + const filteredIds = filterResolvedHotspotIds(stableHotspotIds); + setAsyncState((currentState) => { + if (!currentState.isLoading && areStringArraysEqual(currentState.filteredIds, filteredIds)) { + return currentState; + } + + return { + filteredIds, + isLoading: false, + }; + }); + return; + } + + const abortController = new AbortController(); + + setAsyncState((currentState) => ({ + filteredIds: currentState.isLoading ? currentState.filteredIds : [], + isLoading: true, + })); + + void targetRichHotspotCache + .evaluateMany(stableHotspotIds, basisForRun, abortController.signal) + .then(() => { + if (abortController.signal.aborted) { + return; + } + + const stillUnresolved = stableHotspotIds.some((hotspotId) => !targetRichHotspotCache.has(hotspotId)); + if (stillUnresolved) { + setAsyncState((currentState) => ({ + filteredIds: currentState.filteredIds, + isLoading: true, + })); + return; + } + + const filteredIds = filterResolvedHotspotIds(stableHotspotIds); + setAsyncState((currentState) => { + if (!currentState.isLoading && areStringArraysEqual(currentState.filteredIds, filteredIds)) { + return currentState; + } + + return { + filteredIds, + isLoading: false, + }; + }); + }) + .catch((error) => { + if (abortController.signal.aborted || error?.name === "AbortError") { + return; + } + + console.error("Failed to evaluate target-rich hotspot filter", error); + setAsyncState((currentState) => ({ + filteredIds: currentState.filteredIds, + isLoading: false, + })); + }); + + return () => { + abortController.abort(); + }; + }, [basis?.cacheKey, cacheGeneration, candidateKey, hasLifeList, isActive, isEnabled, stableHotspotIds]); + + return useMemo(() => { + if (!isActive) { + return { + filteredIds: stableHotspotIds, + isActive: false, + isLoading: false, + hasLifeList, + }; + } + + if (!isEnabled && !options.blockWhileDisabled) { + return { + filteredIds: stableHotspotIds, + isActive: true, + isLoading: false, + hasLifeList, + }; + } + + if (!isEnabled && options.blockWhileDisabled) { + return { + filteredIds: [], + isActive: true, + isLoading: stableHotspotIds.length > 0, + hasLifeList, + }; + } + + if (stableHotspotIds.length === 0) { + return { + filteredIds: [], + isActive: true, + isLoading: false, + hasLifeList, + }; + } + + return { + filteredIds: asyncState.filteredIds, + isActive: true, + isLoading: asyncState.isLoading, + hasLifeList, + }; + }, [ + asyncState.filteredIds, + asyncState.isLoading, + hasLifeList, + isActive, + isEnabled, + options.blockWhileDisabled, + stableHotspotIds, + ]); +} diff --git a/lib/database.ts b/lib/database.ts index f7b8ecc..b992003 100644 --- a/lib/database.ts +++ b/lib/database.ts @@ -1,5 +1,6 @@ import * as SQLite from "expo-sqlite"; import { BirdPlanTripData, SavedPlace, StaticPackHotspot, StaticPackTarget, Trip } from "./types"; +import { aggregateHotspotTargets, getMonthIndices, getTotalSamplesForMonths, parseHotspotTargetData } from "./hotspotTargets"; let db: SQLite.SQLiteDatabase | null = null; let isInstallingPack = false; @@ -207,7 +208,7 @@ export async function getHotspotsWithinBounds( south: number, east: number, north: number -): Promise<{ id: string; lat: number; lng: number; species: number }[]> { +): Promise<{ id: string; name: string; lat: number; lng: number; species: number; country: string | null }[]> { if (!db) throw new Error("Database not initialized"); // When west > east, the bounding box crosses the international date line @@ -215,16 +216,18 @@ export async function getHotspotsWithinBounds( const lngCondition = crossesDateLine ? `(lng >= ? OR lng <= ?)` : `(lng >= ? AND lng <= ?)`; const result = await db.getAllAsync( - `SELECT id, lat, lng, species FROM hotspots + `SELECT id, name, lat, lng, species, country FROM hotspots WHERE lat >= ? AND lat <= ? AND ${lngCondition}`, [south, north, west, east] ); return result.map((row: any) => ({ id: row.id, + name: row.name, lat: row.lat, lng: row.lng, species: row.species, + country: row.country ?? null, })); } @@ -637,6 +640,34 @@ export type HotspotTargetsResult = { version: string | null; }; +const TARGET_QUERY_BATCH_SIZE = 400; + +export async function getTargetDataForHotspots(hotspotIds: string[]): Promise> { + if (!db) throw new Error("Database not initialized"); + + const uniqueHotspotIds = [...new Set(hotspotIds)]; + const targetData = new Map(); + + for (let index = 0; index < uniqueHotspotIds.length; index += TARGET_QUERY_BATCH_SIZE) { + const batch = uniqueHotspotIds.slice(index, index + TARGET_QUERY_BATCH_SIZE); + if (batch.length === 0) { + continue; + } + + const placeholders = batch.map(() => "?").join(", "); + const rows = await db.getAllAsync<{ id: string; data: string }>( + `SELECT id, data FROM targets WHERE id IN (${placeholders})`, + batch + ); + + for (const row of rows) { + targetData.set(row.id, row.data); + } + } + + return targetData; +} + export async function getTargetsForHotspot(hotspotId: string, months?: number[]): Promise { if (!db) throw new Error("Database not initialized"); @@ -648,40 +679,12 @@ export async function getTargetsForHotspot(hotspotId: string, months?: number[]) if (!result) return null; const row = result as { data: string; version: string | null }; - const data = JSON.parse(row.data) as { - samples: (number | null)[]; - species: (string | number)[][]; - }; - - // Determine which month indices to aggregate (0-11) - const monthIndices = months && months.length > 0 ? months : data.samples.map((_, i) => i); - - const totalSamples = monthIndices.reduce((sum, i) => sum + (data.samples[i] ?? 0), 0); + const data = parseHotspotTargetData(row.data); + const monthIndices = getMonthIndices(data, months); + const totalSamples = getTotalSamplesForMonths(data, monthIndices); if (totalSamples === 0) return { samples: 0, targets: [], version: row.version }; - - // Aggregate observations per species for selected months - const speciesMap = new Map(); - for (const speciesEntry of data.species) { - const speciesCode = String(speciesEntry[0]); - // Species entry layout: [code, janObs, febObs, ..., decObs] — index i+1 for month i - const totalObs = monthIndices.reduce((sum, i) => { - const val = speciesEntry[i + 1]; - return sum + (typeof val === "number" ? val : 0); - }, 0); - if (totalObs > 0) { - speciesMap.set(speciesCode, (speciesMap.get(speciesCode) ?? 0) + totalObs); - } - } - - // Convert to array, calculate percentages, and sort by percentage descending - const targets: HotspotTarget[] = Array.from(speciesMap.entries()) - .map(([speciesCode, observations]) => ({ - speciesCode, - observations, - percentage: (observations / totalSamples) * 100, - })) - .sort((a, b) => b.percentage - a.percentage); + const targets = aggregateHotspotTargets(data, monthIndices, totalSamples); return { samples: totalSamples, targets, version: row.version }; } diff --git a/lib/hotspotIconImages.ts b/lib/hotspotIconImages.ts new file mode 100644 index 0000000..a9f25f4 --- /dev/null +++ b/lib/hotspotIconImages.ts @@ -0,0 +1,22 @@ +import { getMarkerColorIndex } from "./utils"; + +// Saved-hotspot marker PNGs keyed by species-color index. These are the circle +// markers (colored disc + star) used on the map; the hotspot list reuses them +// so a saved row's icon matches its map marker. +const savedHotspotImages: Record = { + 0: require("@/assets/markers/saved-hotspot-0.png"), + 1: require("@/assets/markers/saved-hotspot-1.png"), + 2: require("@/assets/markers/saved-hotspot-2.png"), + 3: require("@/assets/markers/saved-hotspot-3.png"), + 4: require("@/assets/markers/saved-hotspot-4.png"), + 5: require("@/assets/markers/saved-hotspot-5.png"), + 6: require("@/assets/markers/saved-hotspot-6.png"), + 7: require("@/assets/markers/saved-hotspot-7.png"), + 8: require("@/assets/markers/saved-hotspot-8.png"), + 9: require("@/assets/markers/saved-hotspot-9.png"), +}; + +export function getSavedHotspotIconImage(species: number): any { + const index = getMarkerColorIndex(species || 0); + return savedHotspotImages[index] ?? savedHotspotImages[0]; +} diff --git a/lib/hotspotTargets.ts b/lib/hotspotTargets.ts new file mode 100644 index 0000000..4e3f9ad --- /dev/null +++ b/lib/hotspotTargets.ts @@ -0,0 +1,54 @@ +export type RawHotspotTargetData = { + samples: (number | null)[]; + species: (string | number)[][]; +}; + +export type AggregatedHotspotTarget = { + speciesCode: string; + observations: number; + percentage: number; +}; + +export function parseHotspotTargetData(rawData: string): RawHotspotTargetData { + return JSON.parse(rawData) as RawHotspotTargetData; +} + +export function getMonthIndices(data: RawHotspotTargetData, months?: number[]): number[] { + return months && months.length > 0 ? months : data.samples.map((_, index) => index); +} + +export function getTotalSamplesForMonths(data: RawHotspotTargetData, monthIndices: number[]): number { + return monthIndices.reduce((sum, monthIndex) => sum + (data.samples[monthIndex] ?? 0), 0); +} + +export function aggregateHotspotTargets( + data: RawHotspotTargetData, + monthIndices: number[], + totalSamples: number +): AggregatedHotspotTarget[] { + if (totalSamples === 0) { + return []; + } + + const speciesMap = new Map(); + + for (const speciesEntry of data.species) { + const speciesCode = String(speciesEntry[0]); + const totalObservations = monthIndices.reduce((sum, monthIndex) => { + const value = speciesEntry[monthIndex + 1]; + return sum + (typeof value === "number" ? value : 0); + }, 0); + + if (totalObservations > 0) { + speciesMap.set(speciesCode, (speciesMap.get(speciesCode) ?? 0) + totalObservations); + } + } + + return Array.from(speciesMap.entries()) + .map(([speciesCode, observations]) => ({ + speciesCode, + observations, + percentage: (observations / totalSamples) * 100, + })) + .sort((a, b) => b.percentage - a.percentage); +} diff --git a/lib/placeIconImages.ts b/lib/placeIconImages.ts new file mode 100644 index 0000000..abe0aba --- /dev/null +++ b/lib/placeIconImages.ts @@ -0,0 +1,33 @@ +import { PlaceIconT } from "./placeIcons"; + +// Marker PNGs keyed by saved-place icon. Shared by the map markers, the place +// edit sheet, and the hotspot list's place rows. +export const placeIconImages: Record = { + hike: require("@/assets/markers/place-hike.png"), + mountain: require("@/assets/markers/place-mountain.png"), + tent: require("@/assets/markers/place-tent.png"), + house: require("@/assets/markers/place-house.png"), + airbnb: require("@/assets/markers/place-airbnb.png"), + bed: require("@/assets/markers/place-bed.png"), + bins: require("@/assets/markers/place-bins.png"), + camera: require("@/assets/markers/place-camera.png"), + airport: require("@/assets/markers/place-airport.png"), + boat: require("@/assets/markers/place-boat.png"), + car: require("@/assets/markers/place-car.png"), + bus: require("@/assets/markers/place-bus.png"), + utensils: require("@/assets/markers/place-utensils.png"), + mug: require("@/assets/markers/place-mug.png"), + trolley: require("@/assets/markers/place-trolley.png"), + bike: require("@/assets/markers/place-bike.png"), + dog: require("@/assets/markers/place-dog.png"), + fuel: require("@/assets/markers/place-fuel.png"), + parking: require("@/assets/markers/place-parking.png"), + building: require("@/assets/markers/place-building.png"), + hotspot: require("@/assets/markers/place-hotspot.png"), +}; + +// Resolve a (possibly legacy) saved-place icon value to an image, defaulting to +// the generic hotspot pin for unknown icons (e.g. the old "star" value). +export function getPlaceIconImage(icon: string): any { + return placeIconImages[icon as PlaceIconT] ?? placeIconImages.hotspot; +} diff --git a/lib/targetRichHotspots.ts b/lib/targetRichHotspots.ts new file mode 100644 index 0000000..4415005 --- /dev/null +++ b/lib/targetRichHotspots.ts @@ -0,0 +1,259 @@ +import { LifeListEntry } from "@/stores/settingsStore"; +import { getTargetDataForHotspots } from "./database"; +import { getMonthIndices, getTotalSamplesForMonths, parseHotspotTargetData } from "./hotspotTargets"; + +const CACHE_CAPACITY = 5_000; +const COMPUTE_BATCH_SIZE = 50; + +export type TargetRichHotspotBasis = { + cacheKey: string; + lifeListCodes: ReadonlySet; + excludedCodes: ReadonlySet; + selectedMonths: number[]; + minTargets: number; + minTargetFrequency: number; +}; + +export function normalizeMinTargets(value: number): number { + if (!Number.isFinite(value)) return 1; + return Math.max(1, Math.floor(value)); +} + +export function normalizeMinTargetFrequency(value: number): number { + if (!Number.isFinite(value)) return 1; + return Math.min(100, Math.max(0, value)); +} + +export function createTargetRichHotspotBasis(params: { + lifelist: LifeListEntry[] | null; + lifelistExclusions: string[] | null; + targetMonths: number[]; + minTargets: number; + minTargetFrequency: number; +}): TargetRichHotspotBasis | null { + const lifeListCodes = [...new Set((params.lifelist ?? []).map((entry) => entry.code))].sort(); + + if (lifeListCodes.length === 0) { + return null; + } + + const excludedCodes = [...new Set(params.lifelistExclusions ?? [])].sort(); + const selectedMonths = [...new Set(params.targetMonths)].sort((a, b) => a - b); + const minTargets = normalizeMinTargets(params.minTargets); + const minTargetFrequency = normalizeMinTargetFrequency(params.minTargetFrequency); + + return { + cacheKey: JSON.stringify({ + lifeListCodes, + excludedCodes, + selectedMonths, + minTargets, + minTargetFrequency, + }), + lifeListCodes: new Set(lifeListCodes), + excludedCodes: new Set(excludedCodes), + selectedMonths, + minTargets, + minTargetFrequency, + }; +} + +function createAbortError(): Error { + const error = new Error("Target-rich hotspot evaluation aborted"); + error.name = "AbortError"; + return error; +} + +function throwIfAborted(signal?: AbortSignal) { + if (signal?.aborted) { + throw createAbortError(); + } +} + +function matchesTargetRichHotspot(rawData: string, basis: TargetRichHotspotBasis): boolean { + const parsed = parseHotspotTargetData(rawData); + const monthIndices = getMonthIndices(parsed, basis.selectedMonths); + const totalSamples = getTotalSamplesForMonths(parsed, monthIndices); + + if (totalSamples === 0) { + return false; + } + + let qualifyingSpeciesCount = 0; + + for (const speciesEntry of parsed.species) { + const speciesCode = String(speciesEntry[0]); + + if (basis.lifeListCodes.has(speciesCode) || basis.excludedCodes.has(speciesCode)) { + continue; + } + + const observations = monthIndices.reduce((sum, monthIndex) => { + const value = speciesEntry[monthIndex + 1]; + return sum + (typeof value === "number" ? value : 0); + }, 0); + + if (observations === 0) { + continue; + } + + const percentage = (observations / totalSamples) * 100; + if (percentage >= basis.minTargetFrequency) { + qualifyingSpeciesCount += 1; + if (qualifyingSpeciesCount >= basis.minTargets) { + return true; + } + } + } + + return false; +} + +class TargetRichHotspotCache { + private cache = new Map(); + private activeSignals = new Set(); + + clear() { + this.cache.clear(); + } + + has(hotspotId: string): boolean { + return this.cache.has(hotspotId); + } + + get(hotspotId: string): boolean | undefined { + const value = this.cache.get(hotspotId); + if (value === undefined) { + return undefined; + } + + this.cache.delete(hotspotId); + this.cache.set(hotspotId, value); + return value; + } + + cancelActiveRun() { + for (const controller of this.activeSignals) { + controller.abort(); + } + this.activeSignals.clear(); + } + + async evaluateMany( + hotspotIds: string[], + basis: TargetRichHotspotBasis, + signal?: AbortSignal + ): Promise { + const requestedHotspotIds = new Set(hotspotIds); + const missingHotspotIds = [...requestedHotspotIds].filter((hotspotId) => !this.cache.has(hotspotId)); + if (missingHotspotIds.length === 0) { + return; + } + + const controller = new AbortController(); + this.activeSignals.add(controller); + + const combinedSignal = controller.signal; + const isAborted = () => combinedSignal.aborted || signal?.aborted; + + try { + throwIfAborted(signal); + const targetData = await getTargetDataForHotspots(missingHotspotIds); + throwIfAborted(signal); + + for (let index = 0; index < missingHotspotIds.length; index += COMPUTE_BATCH_SIZE) { + if (isAborted()) { + throw createAbortError(); + } + + const batch = missingHotspotIds.slice(index, index + COMPUTE_BATCH_SIZE); + for (const hotspotId of batch) { + if (isAborted()) { + throw createAbortError(); + } + + const rawData = targetData.get(hotspotId); + this.set(hotspotId, rawData ? matchesTargetRichHotspot(rawData, basis) : false); + } + + if (index + COMPUTE_BATCH_SIZE < missingHotspotIds.length) { + await new Promise((resolve) => setTimeout(resolve, 0)); + } + } + + // Trim only AFTER the run completes, and never evict entries belonging to + // the viewport we just evaluated. This keeps a single over-capacity viewport + // fully resolved (it would otherwise evict its own earliest results mid-run + // and never converge), while still bounding memory across distinct viewports. + this.trim(requestedHotspotIds); + } finally { + this.activeSignals.delete(controller); + } + } + + private set(hotspotId: string, value: boolean) { + if (this.cache.has(hotspotId)) { + this.cache.delete(hotspotId); + } + + this.cache.set(hotspotId, value); + } + + private trim(protectedHotspotIds: ReadonlySet) { + if (this.cache.size <= CACHE_CAPACITY) { + return; + } + + let removable = this.cache.size - CACHE_CAPACITY; + for (const hotspotId of [...this.cache.keys()]) { + if (removable <= 0) { + break; + } + if (protectedHotspotIds.has(hotspotId)) { + continue; + } + this.cache.delete(hotspotId); + removable -= 1; + } + } +} + +export const targetRichHotspotCache = new TargetRichHotspotCache(); + +let activeBasisKey: string | null = null; + +export function syncTargetRichHotspotCacheBasis(nextBasisKey: string | null) { + if (activeBasisKey === nextBasisKey) { + return; + } + + activeBasisKey = nextBasisKey; + targetRichHotspotCache.cancelActiveRun(); + targetRichHotspotCache.clear(); +} + +// Bumped whenever the underlying target data changes (e.g. a pack install / +// update / uninstall). Cached per-hotspot results are keyed only by hotspot id, +// so they must be discarded when the data behind those ids is rewritten. +let cacheGeneration = 0; +const cacheResetListeners = new Set<() => void>(); + +export function getTargetRichHotspotCacheGeneration(): number { + return cacheGeneration; +} + +export function subscribeToTargetRichHotspotCacheReset(listener: () => void): () => void { + cacheResetListeners.add(listener); + return () => { + cacheResetListeners.delete(listener); + }; +} + +export function resetTargetRichHotspotCache() { + cacheGeneration += 1; + targetRichHotspotCache.cancelActiveRun(); + targetRichHotspotCache.clear(); + for (const listener of cacheResetListeners) { + listener(); + } +} diff --git a/lib/types.ts b/lib/types.ts index 183656f..64ca5c6 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -49,6 +49,13 @@ export type BoundingBox = { maxLng: number; }; +export type Bounds = { + west: number; + south: number; + east: number; + north: number; +}; + export type MapFeature = { geometry: { coordinates: [number, number]; diff --git a/lib/utils.ts b/lib/utils.ts index f751de8..a593375 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -155,6 +155,30 @@ export function calculateDistance(lat1: number, lon1: number, lat2: number, lon2 return R * c; } +export function formatDistance(distanceKm: number, useMiles: boolean): string { + if (useMiles) { + const distanceMiles = distanceKm * 0.621371; + const rounded = Math.round(distanceMiles); + if (rounded >= 10) { + return `${rounded} mi`; + } + return `${distanceMiles.toFixed(1)} mi`; + } + const rounded = Math.round(distanceKm); + if (rounded >= 10) { + return `${rounded} km`; + } + return `${distanceKm.toFixed(1)} km`; +} + +// Whether a point falls inside a viewport bbox. Mirrors the SQL date-line logic +// in getHotspotsWithinBounds (west > east means the box crosses the date line). +export function isWithinBounds(lat: number, lng: number, bounds: Bbox): boolean { + if (lat < bounds.south || lat > bounds.north) return false; + const crossesDateLine = bounds.west > bounds.east; + return crossesDateLine ? lng >= bounds.west || lng <= bounds.east : lng >= bounds.west && lng <= bounds.east; +} + export function getBoundingBoxFromLocation(lat: number, lng: number, radiusKm: number): Bbox { const latDelta = radiusKm / 111; const lngDelta = radiusKm / (111 * Math.cos((lat * Math.PI) / 180)); diff --git a/package-lock.json b/package-lock.json index 0936298..8ed2b89 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "name": "openbirding", "version": "1.0.0", "dependencies": { - "@expo/metro-runtime": "~55.0.6", + "@expo/metro-runtime": "~55.0.11", "@expo/react-native-action-sheet": "^4.1.1", "@expo/vector-icons": "^15.0.2", "@lodev09/react-native-true-sheet": "^3.9.2", @@ -21,31 +21,31 @@ "@tanstack/react-query": "^5.87.1", "@types/lodash": "^4.17.20", "dayjs": "^1.11.19", - "expo": "~55.0.15", + "expo": "~55.0.26", "expo-blur": "~55.0.14", - "expo-constants": "~55.0.9", - "expo-dev-client": "~55.0.27", + "expo-constants": "~55.0.16", + "expo-dev-client": "~55.0.35", "expo-document-picker": "~55.0.13", - "expo-file-system": "~55.0.11", - "expo-font": "~55.0.4", - "expo-glass-effect": "~55.0.8", + "expo-file-system": "~55.0.22", + "expo-font": "~55.0.8", + "expo-glass-effect": "~55.0.11", "expo-haptics": "~55.0.14", - "expo-image": "~55.0.6", - "expo-linking": "~55.0.13", - "expo-location": "~55.1.8", - "expo-router": "~55.0.12", - "expo-splash-screen": "~55.0.18", - "expo-sqlite": "~55.0.15", - "expo-status-bar": "~55.0.4", - "expo-symbols": "~55.0.5", - "expo-system-ui": "~55.0.15", - "expo-updates": "~55.0.20", - "expo-web-browser": "~55.0.14", + "expo-image": "~55.0.11", + "expo-linking": "~55.0.15", + "expo-localization": "~55.0.15", + "expo-location": "~55.1.10", + "expo-router": "~55.0.16", + "expo-splash-screen": "~55.0.21", + "expo-sqlite": "~55.0.16", + "expo-status-bar": "~55.0.6", + "expo-symbols": "~55.0.9", + "expo-system-ui": "~55.0.18", + "expo-updates": "~55.0.24", + "expo-web-browser": "~55.0.16", "lodash": "^4.17.21", - "nativewind": "^4.1.23", "react": "19.2.0", "react-dom": "19.2.0", - "react-native": "0.83.4", + "react-native": "0.83.6", "react-native-gesture-handler": "~2.30.0", "react-native-popover-view": "^6.1.0", "react-native-reanimated": "4.2.1", @@ -55,7 +55,7 @@ "react-native-toast-message": "^2.3.3", "react-native-web": "^0.21.0", "react-native-webview": "13.16.0", - "react-native-worklets": "0.7.2", + "react-native-worklets": "0.7.4", "suncalc": "^1.9.0", "tailwindcss": "^3.4.17", "twrnc": "^4.9.1", @@ -63,10 +63,11 @@ }, "devDependencies": { "@types/react": "~19.2.10", + "@types/sharp": "^0.31.1", "@types/suncalc": "^1.9.2", "dotenv-cli": "^11.0.0", "eslint": "^9.25.0", - "eslint-config-expo": "~55.0.0", + "eslint-config-expo": "~55.0.1", "tsx": "^4.21.0", "typescript": "~5.9.2" } @@ -84,12 +85,12 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", - "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", + "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -98,9 +99,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", - "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -137,13 +138,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.29.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", - "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.29.0", - "@babel/types": "^7.29.0", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -153,25 +154,25 @@ } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", - "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz", + "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==", "license": "MIT", "dependencies": { - "@babel/types": "^7.27.3" + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -181,17 +182,17 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.6.tgz", - "integrity": "sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.7.tgz", + "integrity": "sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==", "license": "MIT", "dependencies": { - "@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/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/traverse": "^7.28.6", + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/helper-replace-supers": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/traverse": "^7.29.7", "semver": "^6.3.1" }, "engines": { @@ -202,12 +203,12 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.28.5.tgz", - "integrity": "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.29.7.tgz", + "integrity": "sha512-907Uymvqgg1dwUA+7IGwFAOSYzQOuzPXKNJ1yxzwPffzkYFg2q2eHi1fIOs6sXkG9NbIUMunnUlkYsfRFNvomg==", "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-annotate-as-pure": "^7.29.7", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, @@ -256,49 +257,49 @@ } }, "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", - "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.29.7.tgz", + "integrity": "sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==", "license": "MIT", "dependencies": { - "@babel/traverse": "^7.28.5", - "@babel/types": "^7.28.5" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", "license": "MIT", "dependencies": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -308,35 +309,35 @@ } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", - "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.29.7.tgz", + "integrity": "sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==", "license": "MIT", "dependencies": { - "@babel/types": "^7.27.1" + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", - "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", - "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.29.7.tgz", + "integrity": "sha512-16AMiW26DbXWBbr3B8wNozKM0ydMLB892vaOaJW/fPJdnT8vJk5sdkQcU/isqUxyCE0cEoa8wZOcbgDuC4b6Og==", "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-wrap-function": "^7.27.1", - "@babel/traverse": "^7.27.1" + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-wrap-function": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -346,14 +347,14 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz", - "integrity": "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.29.7.tgz", + "integrity": "sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==", "license": "MIT", "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.28.5", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/traverse": "^7.28.6" + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -363,54 +364,54 @@ } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", - "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz", + "integrity": "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==", "license": "MIT", "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.6.tgz", - "integrity": "sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.29.7.tgz", + "integrity": "sha512-iES0Skag9ERIF68aXadpO6dbXa03mNWK3sEqJaMnLNs/eC3l0lkImdfoy6Y09/SfkpawdAB4RjQ7PVA7TcVGdw==", "license": "MIT", "dependencies": { - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -429,13 +430,99 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/highlight": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.9.tgz", + "integrity": "sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/parser": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", - "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", "license": "MIT", "dependencies": { - "@babel/types": "^7.29.0" + "@babel/types": "^7.29.7" }, "bin": { "parser": "bin/babel-parser.js" @@ -445,14 +532,14 @@ } }, "node_modules/@babel/plugin-proposal-decorators": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.29.0.tgz", - "integrity": "sha512-CVBVv3VY/XRMxRYq5dwr2DS7/MvqPm23cOCjbwNnVrfOqcWlnefua1uUs0sjdKOGjvPUG633o07uWzJq4oI6dA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.29.7.tgz", + "integrity": "sha512-EtU0Hi3GvrTqD56xKmZvV/uCXK2ZbwVNPNLAquVItcAZpUhkXwWlo3Fmj0c2LxgSf2I8IDULeAepwNP1OefLXg==", "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/plugin-syntax-decorators": "^7.28.6" + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/plugin-syntax-decorators": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -462,12 +549,12 @@ } }, "node_modules/@babel/plugin-proposal-export-default-from": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.27.1.tgz", - "integrity": "sha512-hjlsMBl1aJc5lp8MoCDEZCiYzlgdRAShOjAfRw6X+GlpLpUPU7c3XNLsKFZbQk/1cRzBlJ7CXg3xJAJMrFa1Uw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.29.7.tgz", + "integrity": "sha512-p+G5BNXDcy3bOXplhY4HybQ1GxH3i2Tppmdm/3epyRu2VgJJZuUlZ61MqRTg582Q7ZLBdP7fePYvsumSEkMxcQ==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -528,12 +615,12 @@ } }, "node_modules/@babel/plugin-syntax-decorators": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.28.6.tgz", - "integrity": "sha512-71EYI0ONURHJBL4rSFXnITXqXrrY8q4P0q006DPfN+Rk+ASM+++IBXem/ruokgBZR8YNEWZ8R6B+rCb8VcUTqA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.29.7.tgz", + "integrity": "sha512-9MTTLbF39X6sqM92JPEsoI7++26hjZvzkxKZy64aMhWLH2mPkJ/Q3AV4QLmls3R14FpSpkOwQQfUh962JGQxxg==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -555,12 +642,12 @@ } }, "node_modules/@babel/plugin-syntax-export-default-from": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.28.6.tgz", - "integrity": "sha512-Svlx1fjJFnNz0LZeUaybRukSxZI3KkpApUmIRzEdXC5k8ErTOz0OD0kNrICi5Vc3GlpP5ZCeRyRO+mfWTSz+iQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.29.7.tgz", + "integrity": "sha512-foag0BB37ROhdeIX9O8G0jX7hw0UekJc04cHMrYLOnrErsnBKqJGHJ8eDRpoCFZBvEPPygmmtw4qyU97qa4oOw==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -570,12 +657,12 @@ } }, "node_modules/@babel/plugin-syntax-flow": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.28.6.tgz", - "integrity": "sha512-D+OrJumc9McXNEBI/JmFnc/0uCM2/Y3PEBG3gfV3QIYkKv5pvnpzFrl1kYCrcHJP8nOeFB/SHi1IHz29pNGuew==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.29.7.tgz", + "integrity": "sha512-ajMX6QPcyomotqwpzhkYGxcK2i/us0rs1Qo9QvUpa+Fca0FTmqrzKrctoIYLMxcOhGZldGT/BAVkRGTWBiR8gQ==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -624,12 +711,12 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", - "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", + "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -741,12 +828,12 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", - "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz", + "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -771,14 +858,14 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.0.tgz", - "integrity": "sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.7.tgz", + "integrity": "sha512-d98gXZkgswvkyohMBABkhm3GeXhYj8psWfwQ2C7gtfrKGTykQa/iOIi+JJhwMjPlZ6Vm2XN+DCf3Es1EoG4ZLA==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-remap-async-to-generator": "^7.27.1", - "@babel/traverse": "^7.29.0" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-remap-async-to-generator": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -788,14 +875,14 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.28.6.tgz", - "integrity": "sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.29.7.tgz", + "integrity": "sha512-pcUb2SS+RMo9TWVBwKGI5ShtoG7R+zBsFmCKDa6fe8c+hPr3XJlZgoE5j6i8W7gDjhyvy+85vmYexanvXh3d1w==", "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-remap-async-to-generator": "^7.27.1" + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-remap-async-to-generator": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -805,12 +892,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.6.tgz", - "integrity": "sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.29.7.tgz", + "integrity": "sha512-ONyr4+AZhKh8yKWInVxU9AXA9EbsyeLcL6V0dJy6M2/62vuvpGm29zzuymbTpdc451GEpDIdAyPLP3r+P61yKQ==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -820,13 +907,13 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.28.6.tgz", - "integrity": "sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.29.7.tgz", + "integrity": "sha512-GtcpjFvanPfzNQi3eTitsCqtRRmmqzpy/A+yhTR1HaZo1Ly3EA8ZXxlPyHdR8/IuRMYc3E4wdGBewB2QKQjAaA==", "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -836,13 +923,13 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.6.tgz", - "integrity": "sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.29.7.tgz", + "integrity": "sha512-kibJgmEdX2iMwsHY2tSZNDgj8PwIlCQz7FK9KuGKO8zsuoUwSEhoNnNVp/emKWrbY4HeO6kkXfdMqRKKKXBm2A==", "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -852,17 +939,17 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.6.tgz", - "integrity": "sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.29.7.tgz", + "integrity": "sha512-qV0OGGBVacduzQHE649JyCneOFI/maT+YKsO+K4Yi3xv2wTPNjM/W2o2gdzMwEAZz7fXNTHAe0NcSg30bIN69g==", "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-globals": "^7.28.0", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-replace-supers": "^7.28.6", - "@babel/traverse": "^7.28.6" + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-replace-supers": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -872,13 +959,13 @@ } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.28.6.tgz", - "integrity": "sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.29.7.tgz", + "integrity": "sha512-RK7/IyU5phpuCdBAuig5VkzG/EnbDaui5SQGdU9BFrHdV+mV4cUjLMQ9lJDjLNtWHsqtiefpGZUXQP2BiTYMsA==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/template": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/template": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -888,13 +975,13 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz", - "integrity": "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.29.7.tgz", + "integrity": "sha512-iPX8aD6H9zV5s7ZsqTdNocPN/MGQ5sSMnElKrktxjJRMnB2jN/1p2+R7GkfD6CAYoVFqy5A4XnSIUeGgJzIWpg==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.5" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -904,12 +991,12 @@ } }, "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", - "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.29.7.tgz", + "integrity": "sha512-24B2nOy2TeJSMheqwPD4DDQOV/elLSIlKxjZt4i05H5AgdPdWR3n18HnNrcJ+j76WJd9gbwb9jPjNYUy6RautA==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -919,13 +1006,13 @@ } }, "node_modules/@babel/plugin-transform-flow-strip-types": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.27.1.tgz", - "integrity": "sha512-G5eDKsu50udECw7DL2AcsysXiQyB7Nfg521t2OAJ4tbfTJ27doHLeF/vlI1NZGlLdbb/v+ibvtL1YBQqYOwJGg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.29.7.tgz", + "integrity": "sha512-wRHeUjUjCZnMHmiO5bRgjFLcoEh7JyTdByOW11ahhwNa4V0bmeGEaIvt51yq0zQp2yWIpqfxXXPyUP6GFJZHOQ==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/plugin-syntax-flow": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/plugin-syntax-flow": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -935,13 +1022,13 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", - "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.29.7.tgz", + "integrity": "sha512-zeSIHh0+E1Um1WJRXCFlHQYu2ieJNdivLLjlBEp+dIBu3S51n+SZZmIXjxnItw6pz56Cn+KvK68BIBVsxq2JiQ==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -951,14 +1038,14 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", - "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.29.7.tgz", + "integrity": "sha512-otRWaHXE6fbAGkePvaj/kvs3HsqXfPhlnzwSOlnFgbqCPMd975dW+4wZ00WFBt+/YlBGcJwNrARQTOJOb4ZrIg==", "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.27.1" + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -968,12 +1055,12 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", - "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.29.7.tgz", + "integrity": "sha512-DZ/oLP21ZuWx1vKqnoNv6/tvEK48AQOBRai40CX9dTjGluvT/YZCyY3rryDtyUqCEoyNroy5KKPwX2iQCiRvyw==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -983,12 +1070,12 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.6.tgz", - "integrity": "sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.29.7.tgz", + "integrity": "sha512-A0H91hh6W8MFRkp5TqJmMr39jzGD1A1E1Ysiv2O06Sfbhkapm+XyIzxWCEh5kqwOZ1/8QZ0dY3SeQ7XBqfJd5Q==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -998,13 +1085,13 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.28.6.tgz", - "integrity": "sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.29.7.tgz", + "integrity": "sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==", "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1014,13 +1101,13 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.0.tgz", - "integrity": "sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.7.tgz", + "integrity": "sha512-vuFoLwr4qnv2xbZ16SQd6uPcH5FNrLHhk/Jzo++0XJFcaDsr4gjJVg6j398oMHiC+83k/GiBzviwF5KBJkPUtQ==", "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1030,12 +1117,12 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.28.6.tgz", - "integrity": "sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.29.7.tgz", + "integrity": "sha512-idmp1dFaekP9GbcMvG24Kvw2BfhFZjHnNJCkV4WuIY4PskJzwI3f1N5OdgYke38T7rftO6ERulFRn2cFeZwRkg==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1045,12 +1132,12 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.28.6.tgz", - "integrity": "sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.29.7.tgz", + "integrity": "sha512-zR7fv/z14OjgHl4AgRtkDBvBMhIzCxqV/qN/2BCRC7LjFwvuzjYe7gDWxC4Wl/SNsLM6SE1IWvRPYMgSJaUvNw==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1060,16 +1147,16 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.6.tgz", - "integrity": "sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.29.7.tgz", + "integrity": "sha512-Ld98jn4c0smUywL57m7SgsHq3OpThOa6LqZJif3G6jYOovPleoFhVrBJ1WegRApSFB2wu4+RelAj9AC9G08Z4A==", "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/plugin-transform-destructuring": "^7.28.5", - "@babel/plugin-transform-parameters": "^7.27.7", - "@babel/traverse": "^7.28.6" + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/plugin-transform-destructuring": "^7.29.7", + "@babel/plugin-transform-parameters": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1079,12 +1166,12 @@ } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.28.6.tgz", - "integrity": "sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.29.7.tgz", + "integrity": "sha512-sLsyndxK2VwX6yNUOakMb7Sh553ZTe/vVM1XJ+9Z5aW1ytsc8xOIwmyk05NNjN60vkc5/KqoTH6hB4V41LJhng==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1094,13 +1181,13 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.6.tgz", - "integrity": "sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.29.7.tgz", + "integrity": "sha512-6GM1dhvK3gNODkXcEcMCOLEDCLSoZ/sBbro2Ax8HURyasQ4NshagQixkRFdh5niI6E4gmA/jYI/4aT7rRos3ZQ==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1110,12 +1197,12 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.27.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", - "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.29.7.tgz", + "integrity": "sha512-ZDOBqV/qLYJI0YElr8DcENEyARsFQeESqWXH6gZlghYXuPPjvweuDhP4VyEi4BlUBlLRFZVjxoZDMjxhLW766g==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1125,13 +1212,13 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.28.6.tgz", - "integrity": "sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.29.7.tgz", + "integrity": "sha512-/6Rz4DK1ETDEM/bWHsPHcaEe7ZaT1EqSXjtSP/L0DijOYuaUhiRiOKcwpZ8P7zR4xXEHc2ITdiCgBm9Tpyv9ug==", "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1141,14 +1228,14 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.28.6.tgz", - "integrity": "sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.29.7.tgz", + "integrity": "sha512-+BNo06dnrzdNNqCm1X6YUaVv0DKk8Q+JYcoZfOkLhYWNCXzlwTSRq8zGWayT1csjcpNXV9CQTBRRbmTLZac5cA==", "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1158,12 +1245,12 @@ } }, "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.28.0.tgz", - "integrity": "sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.29.7.tgz", + "integrity": "sha512-+1wdDMGNb4UPeY3Q4L5yLiYe6TXPXubs4NjrgRFw13hPRLJfEMw2Q5OXkee6/IfdqePIeW4Jjwe3aBh7SdKz4Q==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1173,16 +1260,16 @@ } }, "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.28.6.tgz", - "integrity": "sha512-61bxqhiRfAACulXSLd/GxqmAedUSrRZIu/cbaT18T1CetkTmtDN15it7i80ru4DVqRK1WMxQhXs+Lf9kajm5Ow==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.29.7.tgz", + "integrity": "sha512-WsZulLVBUHXVj2cUcPVx6UE21TpalB6bHbSFErKT0Ib++ax24jjXe73FqlWvdylFOjiuPHYi6VCcgRad1ItN+A==", "license": "MIT", "dependencies": { - "@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/types": "^7.28.6" + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/plugin-syntax-jsx": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1192,12 +1279,12 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-development": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.27.1.tgz", - "integrity": "sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.29.7.tgz", + "integrity": "sha512-Xfy3UVMF04+ypnFbkhvfqtmvwfe92qwQdbGZVonhE+6v35GzlofmOnA1szaZqzb9xYWr0nl1e5EMmzi0DNON1g==", "license": "MIT", "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.27.1" + "@babel/plugin-transform-react-jsx": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1207,12 +1294,12 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", - "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz", + "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1222,12 +1309,12 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", - "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz", + "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1237,13 +1324,13 @@ } }, "node_modules/@babel/plugin-transform-react-pure-annotations": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.27.1.tgz", - "integrity": "sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.29.7.tgz", + "integrity": "sha512-H5E+HBgDpr6Q5t+Aj11tL7XkIui1jhbIoArVQnqjgXo5/3YxkN7ZEBcWF4RQlB0T4rrxJQbXS6kiFV6B7XTqUA==", "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1253,12 +1340,12 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.0.tgz", - "integrity": "sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.7.tgz", + "integrity": "sha512-rNNFV0DBAJp988xW2DOntfDoYn1eR8GGF5AT5vYc+rjyfaQkM242c9tZUHHPe7KYaiJizXPWhQTzzdbXySyhBw==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1268,13 +1355,13 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.29.0.tgz", - "integrity": "sha512-jlaRT5dJtMaMCV6fAuLbsQMSwz/QkvaHOHOSXRitGGwSpR1blCY4KUKoyP2tYO8vJcqYe8cEj96cqSztv3uF9w==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.29.7.tgz", + "integrity": "sha512-xmAscdE/AsqRW7vutbPNoUmu/nF5SrLKPs7aoJgEjo35lLKA/Bc0i2rMv/hr1+Y0o1bQCiVtith3u2vdgRL39Q==", "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", "babel-plugin-polyfill-corejs2": "^0.4.14", "babel-plugin-polyfill-corejs3": "^0.13.0", "babel-plugin-polyfill-regenerator": "^0.6.5", @@ -1303,13 +1390,13 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.28.6.tgz", - "integrity": "sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.29.7.tgz", + "integrity": "sha512-/u5K1QWada7tbYNqTjMh96718g9NTwh9tfPJMsSmVsQwGT447FskV+KcfeXkXq2GWki4EM/MuTdmBec+hOuVTQ==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1319,12 +1406,12 @@ } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", - "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.29.7.tgz", + "integrity": "sha512-BCHzNYJGe9l7EpwwDBN/ztlL2NYFFq8hp9ddjtUEM9f2O7S7kKV/lL6Fwo7IF7NSkYhPK2vO+86nIGltA90MsA==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1349,16 +1436,16 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.6.tgz", - "integrity": "sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.29.7.tgz", + "integrity": "sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw==", "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-syntax-typescript": "^7.28.6" + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/plugin-syntax-typescript": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1384,17 +1471,17 @@ } }, "node_modules/@babel/preset-react": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.28.5.tgz", - "integrity": "sha512-Z3J8vhRq7CeLjdC58jLv4lnZ5RKFUJWqH5emvxmv9Hv3BD1T9R/Im713R4MTKwvFaV74ejZ3sM01LyEKk4ugNQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.29.7.tgz", + "integrity": "sha512-C+PV1TFUPTmBQGoPBL8j2QmLpZ117YTCwxIZeJOM96GbYMFSc7/pOXU5lVykwnZxyTqQxRsvoRk6f2FktZgGHA==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-transform-react-display-name": "^7.28.0", - "@babel/plugin-transform-react-jsx": "^7.27.1", - "@babel/plugin-transform-react-jsx-development": "^7.27.1", - "@babel/plugin-transform-react-pure-annotations": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "@babel/plugin-transform-react-display-name": "^7.29.7", + "@babel/plugin-transform-react-jsx": "^7.29.7", + "@babel/plugin-transform-react-jsx-development": "^7.29.7", + "@babel/plugin-transform-react-pure-annotations": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1404,16 +1491,16 @@ } }, "node_modules/@babel/preset-typescript": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.28.5.tgz", - "integrity": "sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.29.7.tgz", + "integrity": "sha512-/Foi8vKY2EVbed/1eZx0gJEEwHAIxogrySI7rULcRIvhZzbvoE/b5qG5Ghc0WKAFKOHA9SD1x7RsFlOYdutIiQ==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-syntax-jsx": "^7.27.1", - "@babel/plugin-transform-modules-commonjs": "^7.27.1", - "@babel/plugin-transform-typescript": "^7.28.5" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "@babel/plugin-syntax-jsx": "^7.29.7", + "@babel/plugin-transform-modules-commonjs": "^7.29.7", + "@babel/plugin-transform-typescript": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1432,31 +1519,31 @@ } }, "node_modules/@babel/template": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", "debug": "^4.3.1" }, "engines": { @@ -1464,13 +1551,13 @@ } }, "node_modules/@babel/types": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1529,7 +1616,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1546,7 +1632,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1563,7 +1648,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1580,7 +1664,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1597,7 +1680,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1614,7 +1696,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1631,7 +1712,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1648,7 +1728,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1665,7 +1744,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1682,7 +1760,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1699,7 +1776,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1716,7 +1792,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1733,7 +1808,6 @@ "cpu": [ "mips64el" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1750,7 +1824,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1767,7 +1840,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1784,7 +1856,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1801,7 +1872,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1818,7 +1888,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1835,7 +1904,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1852,7 +1920,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1869,7 +1936,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1886,7 +1952,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1903,7 +1968,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1920,7 +1984,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1937,7 +2000,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1954,7 +2016,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2109,9 +2170,9 @@ } }, "node_modules/@expo-google-fonts/material-symbols": { - "version": "0.4.33", - "resolved": "https://registry.npmjs.org/@expo-google-fonts/material-symbols/-/material-symbols-0.4.33.tgz", - "integrity": "sha512-GQFy5tx7LBiRJttLYhz+KPmoVCQSVXiJPXVgMt/d8BWYbnJmw0nFixZtOaZQJE9F/L6vni3totwPNmbrdMi3ow==", + "version": "0.4.38", + "resolved": "https://registry.npmjs.org/@expo-google-fonts/material-symbols/-/material-symbols-0.4.38.tgz", + "integrity": "sha512-IJkBtN1o8u9BW5fvSii1MyHPQ7Q0HxbWcVBvOrOzgMLpVtZw7R2w94wBTVR7kZwv3w1JNTESMmLA5Sqn1+Z36A==", "license": "MIT AND Apache-2.0" }, "node_modules/@expo/code-signing-certificates": { @@ -2124,15 +2185,15 @@ } }, "node_modules/@expo/config": { - "version": "55.0.15", - "resolved": "https://registry.npmjs.org/@expo/config/-/config-55.0.15.tgz", - "integrity": "sha512-lHc0ELIQ8126jYOMZpLv3WIuvordW98jFg5aT/J1/12n2ycuXu01XLZkJsdw0avO34cusUYb1It+MvY8JiMduA==", + "version": "55.0.17", + "resolved": "https://registry.npmjs.org/@expo/config/-/config-55.0.17.tgz", + "integrity": "sha512-Y3VaRg7Jllg3MhlUOTQqHm6/dttsqcjYlnS9enhAllZvPUpTHnRA4YPETtUZlxkdMJy6y3UZe986pd/KfJ6OTg==", "license": "MIT", "dependencies": { - "@expo/config-plugins": "~55.0.8", + "@expo/config-plugins": "~55.0.9", "@expo/config-types": "^55.0.5", - "@expo/json-file": "^10.0.13", - "@expo/require-utils": "^55.0.4", + "@expo/json-file": "^10.0.14", + "@expo/require-utils": "^55.0.5", "deepmerge": "^4.3.1", "getenv": "^2.0.0", "glob": "^13.0.0", @@ -2142,14 +2203,14 @@ } }, "node_modules/@expo/config-plugins": { - "version": "55.0.8", - "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-55.0.8.tgz", - "integrity": "sha512-8WfWTRntTCcowfOS+tHdB0z98gKetTwktg4G5TWkCkXVa8Jt1NUnvzaaU4UHk2vbR2U4N84RyZJFizSwfF6C9g==", + "version": "55.0.10", + "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-55.0.10.tgz", + "integrity": "sha512-1txnRnMLIO5lM/Of/VyvDkCwZap0YFvCyfSTIlUQamhwhx6Rh7r8TXfcIstaDYUQ7X6GTMkNxLXWbcYS6ZAFDw==", "license": "MIT", "dependencies": { "@expo/config-types": "^55.0.5", - "@expo/json-file": "~10.0.13", - "@expo/plist": "^0.5.2", + "@expo/json-file": "~10.0.15", + "@expo/plist": "^0.5.4", "@expo/sdk-runtime-versions": "^1.0.0", "chalk": "^4.1.2", "debug": "^4.3.5", @@ -2180,6 +2241,16 @@ "integrity": "sha512-sCmSUZG4mZ/ySXvfyyBdhjivz8Q539X1NondwDdYG7s3SBsk+wsgPJzYsqgAG/P9+l0xWjUD2F+kQ1cAJ6NNLg==", "license": "MIT" }, + "node_modules/@expo/config/node_modules/@expo/json-file": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-10.2.0.tgz", + "integrity": "sha512-S6XzKe3R9GQeHiUPXc3xJjOv2VJhOEwFYf7xdC2z2cUqt3kZJ9mSO877sNQloVdnW/SUCtPY3bexlM7nwq+CAQ==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.20.0", + "json5": "^2.2.3" + } + }, "node_modules/@expo/config/node_modules/semver": { "version": "7.7.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", @@ -2212,9 +2283,9 @@ } }, "node_modules/@expo/devtools": { - "version": "55.0.2", - "resolved": "https://registry.npmjs.org/@expo/devtools/-/devtools-55.0.2.tgz", - "integrity": "sha512-4VsFn9MUriocyuhyA+ycJP3TJhUsOFHDc270l9h3LhNpXMf6wvIdGcA0QzXkZtORXmlDybWXRP2KT1k36HcQkA==", + "version": "55.0.3", + "resolved": "https://registry.npmjs.org/@expo/devtools/-/devtools-55.0.3.tgz", + "integrity": "sha512-KoIDgo0NoXeWLsIcOdZqtAG/1LlsM+JL0DA3bo0vCYaOYTBLXi/ZvRBqa20Ub8D2vKLNa+FgRQW0gRg04Ps1Pg==", "license": "MIT", "dependencies": { "chalk": "^4.1.2" @@ -2233,9 +2304,9 @@ } }, "node_modules/@expo/dom-webview": { - "version": "55.0.5", - "resolved": "https://registry.npmjs.org/@expo/dom-webview/-/dom-webview-55.0.5.tgz", - "integrity": "sha512-lt3uxYOCk3wmWvtOOvsC35CKGbDAOx5C2EaY8SH1JVSfBzqmF8Cs0Xp1MPxncDPMyxpMiWx5SvvV/iLF1rJU4A==", + "version": "55.0.6", + "resolved": "https://registry.npmjs.org/@expo/dom-webview/-/dom-webview-55.0.6.tgz", + "integrity": "sha512-ZNm8tiNEZysxrr36J0x4mOCGyJDcaIvL/3tMxBz0VJIJDcV19xjuJAhJQxHovu+jKx6s9tRyEAINa1mdrzV39g==", "license": "MIT", "peerDependencies": { "expo": "*", @@ -2244,9 +2315,9 @@ } }, "node_modules/@expo/env": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@expo/env/-/env-2.1.1.tgz", - "integrity": "sha512-rVvHC4I6xlPcg+mAO09ydUi2Wjv1ZytpLmHOSzvXzBAz9mMrJggqCe4s4dubjJvi/Ino/xQCLhbaLCnTtLpikg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@expo/env/-/env-2.1.2.tgz", + "integrity": "sha512-RJtGFfj/ygO/6zcVbV3cckHf4THcEkv5IZft1GjCB3dfT6axvzvIwXE9EiQqQYmGHcQ+ZrvC8xZcIhiHba0pYg==", "license": "MIT", "dependencies": { "chalk": "^4.0.0", @@ -2258,12 +2329,12 @@ } }, "node_modules/@expo/fingerprint": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@expo/fingerprint/-/fingerprint-0.16.6.tgz", - "integrity": "sha512-nRITNbnu3RKSHPvKVehrSU4KG2VY9V8nvULOHBw98ukHCAU4bGrU5APvcblOkX3JAap+xEHsg/mZvqlvkLInmQ==", + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@expo/fingerprint/-/fingerprint-0.16.7.tgz", + "integrity": "sha512-BH8sicYOqZ1iBMwCVEGIz6uTTfylosjc49FoMmCYIzKOiYdiVehsfoYBwyfxwWIiya1VMhm1gv0cgOP8fxHpDw==", "license": "MIT", "dependencies": { - "@expo/env": "^2.0.11", + "@expo/env": "^2.1.2", "@expo/spawn-async": "^1.7.2", "arg": "^5.0.2", "chalk": "^4.1.2", @@ -2279,19 +2350,33 @@ "fingerprint": "bin/cli.js" } }, - "node_modules/@expo/fingerprint/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "node_modules/@expo/fingerprint/node_modules/@expo/env": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@expo/env/-/env-2.3.0.tgz", + "integrity": "sha512-9HnnIbzwTTdbwSjNLXTk0fPm9ZwMJ7c1/31tsni8HZ8Q62KzYCyspahH+V365vg5J6lr001DzNwBxVWSaYCQLg==", + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "debug": "^4.3.4", + "getenv": "^2.0.0" + }, + "engines": { + "node": ">=20.12.0" + } + }, + "node_modules/@expo/fingerprint/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "license": "MIT", "engines": { "node": "18 || 20 || >=22" } }, "node_modules/@expo/fingerprint/node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" @@ -2316,9 +2401,9 @@ } }, "node_modules/@expo/fingerprint/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", + "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -2328,12 +2413,12 @@ } }, "node_modules/@expo/image-utils": { - "version": "0.8.13", - "resolved": "https://registry.npmjs.org/@expo/image-utils/-/image-utils-0.8.13.tgz", - "integrity": "sha512-1I//yBQeTY6p0u1ihqGNDAr35EbSG8uFEupFrIF0jd++h9EWH33521yZJU1yE+mwGlzCb61g3ehu78siMhXBlA==", + "version": "0.8.14", + "resolved": "https://registry.npmjs.org/@expo/image-utils/-/image-utils-0.8.14.tgz", + "integrity": "sha512-5Sn+jG4Cw+shC2wDMXoqSAJnvERbiwzHn05FpWtD5IBflfTIs5gUmjzwiGVyjOdlMSQhgRrw/AymPbmO9h9mpQ==", "license": "MIT", "dependencies": { - "@expo/require-utils": "^55.0.4", + "@expo/require-utils": "^55.0.5", "@expo/spawn-async": "^1.7.2", "chalk": "^4.0.0", "getenv": "^2.0.0", @@ -2355,106 +2440,80 @@ } }, "node_modules/@expo/json-file": { - "version": "10.0.13", - "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-10.0.13.tgz", - "integrity": "sha512-pX/XjQn7tgNw6zuuV2ikmegmwe/S7uiwhrs2wXrANMkq7ozrA+JcZwgW9Q/8WZgciBzfAhNp5hnackHcrmapQA==", + "version": "10.0.16", + "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-10.0.16.tgz", + "integrity": "sha512-fcVkWEj+hLuP2yt5W0aw6LmDRqSPWDLUSxOMcmFeV+algmIF59sQVKCwB9btjQLd4V6x9N0pISkQEkBubUHrCw==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.20.0", + "@babel/code-frame": "~7.10.4", "json5": "^2.2.3" } }, + "node_modules/@expo/json-file/node_modules/@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, "node_modules/@expo/local-build-cache-provider": { - "version": "55.0.11", - "resolved": "https://registry.npmjs.org/@expo/local-build-cache-provider/-/local-build-cache-provider-55.0.11.tgz", - "integrity": "sha512-rJ4RTCrkeKaXaido/bVyhl90ZRtVTOEbj59F1PWVjIEIVgjdlfc1J3VD9v7hEsbf/+8Tbr/PgvWhT6Visi5sLQ==", + "version": "55.0.13", + "resolved": "https://registry.npmjs.org/@expo/local-build-cache-provider/-/local-build-cache-provider-55.0.13.tgz", + "integrity": "sha512-Vg5BE10UL+0yg3BVtIeiSoeHU31Qe1m3UxhBPS478ACY1zzKuxZE30x2sym/B2OIWypjmPzXDRt8J9TOGFuFNw==", "license": "MIT", "dependencies": { - "@expo/config": "~55.0.15", + "@expo/config": "~55.0.17", "chalk": "^4.1.2" } }, "node_modules/@expo/log-box": { - "version": "55.0.11", - "resolved": "https://registry.npmjs.org/@expo/log-box/-/log-box-55.0.11.tgz", - "integrity": "sha512-JQHFLWkskIbJi6cxYMjErx8lQqfFJilDQLKmdTO3m3YkdmN9GE/CrzjOfVlCG0DGEGZJ90br0pGKvGPdXNsHKw==", + "version": "55.0.12", + "resolved": "https://registry.npmjs.org/@expo/log-box/-/log-box-55.0.12.tgz", + "integrity": "sha512-f9ARS8J60cq3LLNdIqmUjYwyerBzVS5Ecp7KjIf3GOIPjW0571rkcwLz4/U18l/1DeSkSzIkYsNl2TC9oTdWaQ==", "license": "MIT", "dependencies": { - "@expo/dom-webview": "^55.0.5", + "@expo/dom-webview": "^55.0.6", "anser": "^1.4.9", "stacktrace-parser": "^0.1.10" }, "peerDependencies": { - "@expo/dom-webview": "^55.0.5", + "@expo/dom-webview": "^55.0.6", "expo": "*", "react": "*", "react-native": "*" } }, "node_modules/@expo/metro": { - "version": "55.1.0", - "resolved": "https://registry.npmjs.org/@expo/metro/-/metro-55.1.0.tgz", - "integrity": "sha512-bb/LOncsz9KiP6cHmMy0MCDG1COZOn+k+pRpDrvJUmxLdOOuniJSYyCc/Dgv1bR9E/6YR+fh3EXGg9MUrVNy4Q==", - "license": "MIT", - "dependencies": { - "metro": "0.83.6", - "metro-babel-transformer": "0.83.6", - "metro-cache": "0.83.6", - "metro-cache-key": "0.83.6", - "metro-config": "0.83.6", - "metro-core": "0.83.6", - "metro-file-map": "0.83.6", - "metro-minify-terser": "0.83.6", - "metro-resolver": "0.83.6", - "metro-runtime": "0.83.6", - "metro-source-map": "0.83.6", - "metro-symbolicate": "0.83.6", - "metro-transform-plugins": "0.83.6", - "metro-transform-worker": "0.83.6" - } - }, - "node_modules/@expo/metro-config": { - "version": "55.0.17", - "resolved": "https://registry.npmjs.org/@expo/metro-config/-/metro-config-55.0.17.tgz", - "integrity": "sha512-o11VyNoRDXv0T5320D9cH+nSsrR/OMHTjtysKLIfDlidsBswDk1DMApPv9Kw0/gluArCSnbx8JC1G0Yh2Y4P3g==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.20.0", - "@babel/core": "^7.20.0", - "@babel/generator": "^7.20.5", - "@expo/config": "~55.0.15", - "@expo/env": "~2.1.1", - "@expo/json-file": "~10.0.13", - "@expo/metro": "~55.1.0", - "@expo/spawn-async": "^1.7.2", - "browserslist": "^4.25.0", - "chalk": "^4.1.0", - "debug": "^4.3.2", - "getenv": "^2.0.0", - "glob": "^13.0.0", - "hermes-parser": "^0.32.0", - "jsc-safe-url": "^0.2.4", - "lightningcss": "^1.30.1", - "picomatch": "^4.0.3", - "postcss": "~8.4.32", - "resolve-from": "^5.0.0" - }, - "peerDependencies": { - "expo": "*" - }, - "peerDependenciesMeta": { - "expo": { - "optional": true - } + "version": "55.1.1", + "resolved": "https://registry.npmjs.org/@expo/metro/-/metro-55.1.1.tgz", + "integrity": "sha512-/wfXo5hTuAVpVLG/4hzlmD9NBGJkzkmBEMm/4VICajYRbj7y8OmqqPWbbymzHiBiHB6tI9BnsyXpQM6zVZEECg==", + "license": "MIT", + "dependencies": { + "metro": "0.83.7", + "metro-babel-transformer": "0.83.7", + "metro-cache": "0.83.7", + "metro-cache-key": "0.83.7", + "metro-config": "0.83.7", + "metro-core": "0.83.7", + "metro-file-map": "0.83.7", + "metro-minify-terser": "0.83.7", + "metro-resolver": "0.83.7", + "metro-runtime": "0.83.7", + "metro-source-map": "0.83.7", + "metro-symbolicate": "0.83.7", + "metro-transform-plugins": "0.83.7", + "metro-transform-worker": "0.83.7" } }, "node_modules/@expo/metro-runtime": { - "version": "55.0.10", - "resolved": "https://registry.npmjs.org/@expo/metro-runtime/-/metro-runtime-55.0.10.tgz", - "integrity": "sha512-7v+ldTvMWRa1ml83Jel9W2f8qT/NZZWrlHaEjf29nb72JTEO50+Xac9PWLo+X3LCDAAuyYuBGKYXOJwfqxV0fQ==", + "version": "55.0.11", + "resolved": "https://registry.npmjs.org/@expo/metro-runtime/-/metro-runtime-55.0.11.tgz", + "integrity": "sha512-4KKi/jGrIEXi2YGu0hYTVr0CEeRJy5SXbCrz9+KDZkuD3ROwKNpM1DBawni5rhPVovFnR323HBck9GaxhnfrRw==", "license": "MIT", "dependencies": { - "@expo/log-box": "55.0.11", + "@expo/log-box": "55.0.12", "anser": "^1.4.9", "pretty-format": "^29.7.0", "stacktrace-parser": "^0.1.10", @@ -2473,35 +2532,45 @@ } }, "node_modules/@expo/osascript": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@expo/osascript/-/osascript-2.4.2.tgz", - "integrity": "sha512-/XP7PSYF2hzOZzqfjgkoWtllyeTN8dW3aM4P6YgKcmmPikKL5FdoyQhti4eh6RK5a5VrUXJTOlTNIpIHsfB5Iw==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@expo/osascript/-/osascript-2.6.0.tgz", + "integrity": "sha512-QvqDBlJXa8CS2vRORJ4wEflY1m0vVI07uSJdIRgBrLxRPBcsrXxrtU7+wXRXMqfq9zLwNP9XbvRsXF2omoDylg==", "license": "MIT", "dependencies": { - "@expo/spawn-async": "^1.7.2" + "@expo/spawn-async": "^1.8.0" }, "engines": { "node": ">=12" } }, "node_modules/@expo/package-manager": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/@expo/package-manager/-/package-manager-1.10.4.tgz", - "integrity": "sha512-y9Mr4Kmpk4abAVZrNNPCdzOZr8nLLyi18p1SXr0RCVA8IfzqZX/eY4H+50a0HTmXqIsPZrQdcdb4I3ekMS9GvQ==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@expo/package-manager/-/package-manager-1.12.1.tgz", + "integrity": "sha512-fQLiFAcFRWF53mtuLK32SUJQ1ahhrTcBZPZPedYTiUT5ha5FF+UO6bPtCc0Y/hgj0/m3HCGBAuSHjbg2kI9oPQ==", "license": "MIT", "dependencies": { - "@expo/json-file": "^10.0.13", - "@expo/spawn-async": "^1.7.2", + "@expo/json-file": "^10.2.0", + "@expo/spawn-async": "^1.8.0", "chalk": "^4.0.0", "npm-package-arg": "^11.0.0", "ora": "^3.4.0", "resolve-workspace-root": "^2.0.0" } }, + "node_modules/@expo/package-manager/node_modules/@expo/json-file": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-10.2.0.tgz", + "integrity": "sha512-S6XzKe3R9GQeHiUPXc3xJjOv2VJhOEwFYf7xdC2z2cUqt3kZJ9mSO877sNQloVdnW/SUCtPY3bexlM7nwq+CAQ==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.20.0", + "json5": "^2.2.3" + } + }, "node_modules/@expo/plist": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@expo/plist/-/plist-0.5.2.tgz", - "integrity": "sha512-o4xdVdBpe4aTl3sPMZ2u3fJH4iG1I768EIRk1xRZP+GaFI93MaR3JvoFibYqxeTmLQ1p1kNEVqylfUjezxx45g==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@expo/plist/-/plist-0.5.4.tgz", + "integrity": "sha512-Jqppj0FULNq6Zp5JtQrFICl8TtpMjwwUbxEcEC2T3z7m+TOrTQEHZXz3D3Ay7vhbmvD+VMgfWJ4ARclJXeN8Eg==", "license": "MIT", "dependencies": { "@xmldom/xmldom": "^0.8.8", @@ -2510,16 +2579,16 @@ } }, "node_modules/@expo/prebuild-config": { - "version": "55.0.16", - "resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-55.0.16.tgz", - "integrity": "sha512-o4EAVgDGk1lISirtMD8hciO2vyMp7cWlPdfTtjjd5AXSfODVYDIDhygXrfvVQHmJXAztVqPUTKJT+BYOsVkYGQ==", + "version": "55.0.18", + "resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-55.0.18.tgz", + "integrity": "sha512-2oKXyy5pyM87DJqXW5Z+Sakle6rApFFtpPhWOiNsOdoh6rOAD+EqVgyrs2OEEic8CE0tTt27w3SRfSZe/PZrxg==", "license": "MIT", "dependencies": { - "@expo/config": "~55.0.15", - "@expo/config-plugins": "~55.0.8", + "@expo/config": "~55.0.17", + "@expo/config-plugins": "~55.0.9", "@expo/config-types": "^55.0.5", - "@expo/image-utils": "^0.8.13", - "@expo/json-file": "^10.0.13", + "@expo/image-utils": "^0.8.14", + "@expo/json-file": "^10.0.14", "@react-native/normalize-colors": "0.83.6", "debug": "^4.3.1", "resolve-from": "^5.0.0", @@ -2531,9 +2600,9 @@ } }, "node_modules/@expo/prebuild-config/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", + "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -2556,9 +2625,9 @@ } }, "node_modules/@expo/require-utils": { - "version": "55.0.4", - "resolved": "https://registry.npmjs.org/@expo/require-utils/-/require-utils-55.0.4.tgz", - "integrity": "sha512-JAANvXqV7MOysWeVWgaiDzikoyDjJWOV/ulOW60Zb3kXJfrx2oZOtGtDXDFKD1mXuahQgoM5QOjuZhF7gFRNjA==", + "version": "55.0.5", + "resolved": "https://registry.npmjs.org/@expo/require-utils/-/require-utils-55.0.5.tgz", + "integrity": "sha512-U4K/CQ2VpXuwfNGsN+daKmYOt15hCP8v/pXaYH6eut7kdYZo6SfJ1yr67BIcJ+1Gzzs+QzTxswAZChKpXmceyw==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.20.0", @@ -2575,9 +2644,9 @@ } }, "node_modules/@expo/schema-utils": { - "version": "55.0.3", - "resolved": "https://registry.npmjs.org/@expo/schema-utils/-/schema-utils-55.0.3.tgz", - "integrity": "sha512-l9KHVjTo6MvoeyvwNr6AjckGJm8NIcqZ3QSAh51cWozXW9v2AUjyCyqYtFtyntLWRZ0x/ByYJishpQo4ZQq45Q==", + "version": "55.0.4", + "resolved": "https://registry.npmjs.org/@expo/schema-utils/-/schema-utils-55.0.4.tgz", + "integrity": "sha512-65IdeeE8dAZR3n3J5Eq7LYiQ8BFGeEYCWPBCzycvafL7PkskbCyIclTQarRwf/HXFoRvezKCjaLwy/8v9Prk6g==", "license": "MIT" }, "node_modules/@expo/sdk-runtime-versions": { @@ -2587,12 +2656,12 @@ "license": "MIT" }, "node_modules/@expo/spawn-async": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@expo/spawn-async/-/spawn-async-1.7.2.tgz", - "integrity": "sha512-QdWi16+CHB9JYP7gma19OVVg0BFkvU8zNj9GjWorYI8Iv8FUxjOCcYRuAmX4s/h91e4e7BPsskc8cSrZYho9Ew==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@expo/spawn-async/-/spawn-async-1.8.0.tgz", + "integrity": "sha512-eb9xxd/LbuEGSdua4NumCu/McVB9EM+F/JxB9pWgnERw4HQ9XyTNH1KapG6oqLWR8TuRK2LQfzJlmNi94CVobw==", "license": "MIT", "dependencies": { - "cross-spawn": "^7.0.3" + "cross-spawn": "^7.0.6" }, "engines": { "node": ">=12" @@ -2622,9 +2691,9 @@ "license": "MIT" }, "node_modules/@expo/xcpretty": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/@expo/xcpretty/-/xcpretty-4.4.3.tgz", - "integrity": "sha512-wC562eD3gS6vO2tWHToFhlFnmHKfKHgF1oyvojeSkLK/ZYop1bMU+7cOMiF9Sq70CzcsLy/EMRy/uRc76QmNRw==", + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/@expo/xcpretty/-/xcpretty-4.4.4.tgz", + "integrity": "sha512-4aQzz9vgxcNXFfo/iyNgDDYfsU5XGKKxWxZopw0cVotHiW+U8IJbIxMaxsINs6bHhtkG3StKNPcOrn3eBuxKPw==", "license": "BSD-3-Clause", "dependencies": { "@babel/code-frame": "^7.20.0", @@ -3564,132 +3633,68 @@ } }, "node_modules/@react-native/assets-registry": { - "version": "0.83.4", - "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.83.4.tgz", - "integrity": "sha512-aqKtpbJDSQeSX/Dwv0yMe1/Rd2QfXi12lnyZDXNn/OEKz59u6+LuPBVgO/9CRyclHmdlvwg8c7PJ9eX2ZMnjWg==", + "version": "0.83.6", + "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.83.6.tgz", + "integrity": "sha512-iljb4ue1yWJ3EhySz7EjV6CzSVrI2uNtR8BI2jzP5+QS5E4Cl3fdIJRmVwDEx1pu8uE97PGEusGRHnoaZ9Q3jg==", "license": "MIT", "engines": { "node": ">= 20.19.4" } }, - "node_modules/@react-native/babel-plugin-codegen": { - "version": "0.85.2", - "resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.85.2.tgz", - "integrity": "sha512-5Dqn08kRTUIxPLYju9hExI0cR1ESX+P5tEv5yv0q0UZcisRTw0VB8iUWDIph2LdY1i5Dc8PIvuaWMRNCw3vnKg==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@babel/traverse": "^7.29.0", - "@react-native/codegen": "0.85.2" - }, - "engines": { - "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" - } - }, - "node_modules/@react-native/babel-preset": { - "version": "0.85.2", - "resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.85.2.tgz", - "integrity": "sha512-7d2yW23eKkVt0FbbnZLxqO7KybGLtQXOuvvcO1NUOYGtjzVh6ihNKn0TIHrhSNpMyHwYLDoiiuj95wLtcg3IwQ==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@babel/core": "^7.25.2", - "@babel/plugin-proposal-export-default-from": "^7.24.7", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-default-from": "^7.24.7", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-transform-async-generator-functions": "^7.25.4", - "@babel/plugin-transform-async-to-generator": "^7.24.7", - "@babel/plugin-transform-block-scoping": "^7.25.0", - "@babel/plugin-transform-class-properties": "^7.25.4", - "@babel/plugin-transform-classes": "^7.25.4", - "@babel/plugin-transform-destructuring": "^7.24.8", - "@babel/plugin-transform-flow-strip-types": "^7.25.2", - "@babel/plugin-transform-for-of": "^7.24.7", - "@babel/plugin-transform-modules-commonjs": "^7.24.8", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", - "@babel/plugin-transform-optional-catch-binding": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.8", - "@babel/plugin-transform-private-methods": "^7.24.7", - "@babel/plugin-transform-private-property-in-object": "^7.24.7", - "@babel/plugin-transform-react-display-name": "^7.24.7", - "@babel/plugin-transform-react-jsx": "^7.25.2", - "@babel/plugin-transform-react-jsx-self": "^7.24.7", - "@babel/plugin-transform-react-jsx-source": "^7.24.7", - "@babel/plugin-transform-regenerator": "^7.24.7", - "@babel/plugin-transform-runtime": "^7.24.7", - "@babel/plugin-transform-typescript": "^7.25.2", - "@babel/plugin-transform-unicode-regex": "^7.24.7", - "@react-native/babel-plugin-codegen": "0.85.2", - "babel-plugin-syntax-hermes-parser": "0.33.3", - "babel-plugin-transform-flow-enums": "^0.0.2", - "react-refresh": "^0.14.0" - }, - "engines": { - "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" - }, - "peerDependencies": { - "@babel/core": "*" - } - }, "node_modules/@react-native/codegen": { - "version": "0.85.2", - "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.85.2.tgz", - "integrity": "sha512-XCginmxh0//++EXVOEJHBVZxHla294FzLCFF6jXwAUjvXVhqyIKyxhABfz+r4OOmaiuWk4Rtd4arqdAzeHeprg==", + "version": "0.83.6", + "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.83.6.tgz", + "integrity": "sha512-doB/Pq6Cf6IjF3wlQXTIiZOnsX9X8mEEk+CdGfyuCwZjWrf7IB8KaZEXXckJmfUcIwvJ9u/a72ZoTTCIoxAc9A==", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { "@babel/core": "^7.25.2", - "@babel/parser": "^7.29.0", - "hermes-parser": "0.33.3", + "@babel/parser": "^7.25.3", + "glob": "^7.1.1", + "hermes-parser": "0.32.0", "invariant": "^2.2.4", "nullthrows": "^1.1.1", - "tinyglobby": "^0.2.15", "yargs": "^17.6.2" }, "engines": { - "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + "node": ">= 20.19.4" }, "peerDependencies": { "@babel/core": "*" } }, - "node_modules/@react-native/codegen/node_modules/hermes-estree": { - "version": "0.33.3", - "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.33.3.tgz", - "integrity": "sha512-6kzYZHCk8Fy1Uc+t3HGYyJn3OL4aeqKLTyina4UFtWl8I0kSL7OmKThaiX+Uh2f8nGw3mo4Ifxg0M5Zk3/Oeqg==", - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/@react-native/codegen/node_modules/hermes-parser": { - "version": "0.33.3", - "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.33.3.tgz", - "integrity": "sha512-Yg3HgaG4CqgyowtYjX/FsnPAuZdHOqSMtnbpylbptsQ9nwwSKsy6uRWcGO5RK0EqiX12q8HvDWKgeAVajRO5DA==", - "license": "MIT", - "optional": true, - "peer": true, + "node_modules/@react-native/codegen/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob 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 exorbitant rates) by contacting i@izs.me", + "license": "ISC", "dependencies": { - "hermes-estree": "0.33.3" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/@react-native/community-cli-plugin": { - "version": "0.83.4", - "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.83.4.tgz", - "integrity": "sha512-8os0weQEnjUhWy7Db881+JKRwNHVGM40VtTRvltAyA/YYkrGg4kPCqiTybMxQDEcF3rnviuxHyI+ITiglfmgmQ==", + "version": "0.83.6", + "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.83.6.tgz", + "integrity": "sha512-Mko6mywoHYJmpBnjwAC95vQWaUUh//71knFadH0BrhHDq2m7i/IrpLwcQsPAy8855ucXflBs5zQyGTpNbPBAaw==", "license": "MIT", "dependencies": { - "@react-native/dev-middleware": "0.83.4", + "@react-native/dev-middleware": "0.83.6", "debug": "^4.4.0", "invariant": "^2.2.4", - "metro": "^0.83.3", - "metro-config": "^0.83.3", - "metro-core": "^0.83.3", + "metro": "^0.83.6", + "metro-config": "^0.83.6", + "metro-core": "^0.83.6", "semver": "^7.1.3" }, "engines": { @@ -3709,9 +3714,9 @@ } }, "node_modules/@react-native/community-cli-plugin/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", + "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -3721,18 +3726,18 @@ } }, "node_modules/@react-native/debugger-frontend": { - "version": "0.83.4", - "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.83.4.tgz", - "integrity": "sha512-mCE2s/S7SEjax3gZb6LFAraAI3x13gRVWJWqT0HIm71e4ITObENNTDuMw4mvZ/wr4Gz2wv4FcBH5/Nla9LXOcg==", + "version": "0.83.6", + "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.83.6.tgz", + "integrity": "sha512-TyWXEpAjVundrc87fPWg91piOUg75+X9iutcfDe7cO3NrAEYCsl7Z09rKHuiAGkxfG9/rFD13dPsYIixUFkSFA==", "license": "BSD-3-Clause", "engines": { "node": ">= 20.19.4" } }, "node_modules/@react-native/debugger-shell": { - "version": "0.83.4", - "resolved": "https://registry.npmjs.org/@react-native/debugger-shell/-/debugger-shell-0.83.4.tgz", - "integrity": "sha512-FtAnrvXqy1xeZ+onwilvxEeeBsvBlhtfrHVIC2R/BOJAK9TbKEtFfjio0wsn3DQIm+UZq48DSa+p9jJZ2aJUww==", + "version": "0.83.6", + "resolved": "https://registry.npmjs.org/@react-native/debugger-shell/-/debugger-shell-0.83.6.tgz", + "integrity": "sha512-684TJMBCU0l0ZjJWzrnK0HH+ERaM9KLyxyArE1k7BrP+gVl4X9GO0Pi94RoInOxvW/nyV65sOU6Ip1F3ygS0cg==", "license": "MIT", "dependencies": { "cross-spawn": "^7.0.6", @@ -3743,14 +3748,14 @@ } }, "node_modules/@react-native/dev-middleware": { - "version": "0.83.4", - "resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.83.4.tgz", - "integrity": "sha512-3s9nXZc/kj986nI2RPqxiIJeTS3o7pvZDxbHu7GE9WVIGX9YucA1l/tEiXd7BAm3TBFOfefDOT08xD46wH+R3Q==", + "version": "0.83.6", + "resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.83.6.tgz", + "integrity": "sha512-22xoddLTelpcVnF385SNH2hdP7X2av5pu7yRl/WnM5jBznbcl0+M9Ce94cj+WVeomsoUF/vlfuB0Ooy+RMlRiA==", "license": "MIT", "dependencies": { "@isaacs/ttlcache": "^1.4.1", - "@react-native/debugger-frontend": "0.83.4", - "@react-native/debugger-shell": "0.83.4", + "@react-native/debugger-frontend": "0.83.6", + "@react-native/debugger-shell": "0.83.6", "chrome-launcher": "^0.15.2", "chromium-edge-launcher": "^0.2.0", "connect": "^3.6.5", @@ -3766,836 +3771,448 @@ } }, "node_modules/@react-native/gradle-plugin": { - "version": "0.83.4", - "resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.83.4.tgz", - "integrity": "sha512-AhaSWw2k3eMKqZ21IUdM7rpyTYOpAfsBbIIiom1QQii3QccX0uW2AWTcRhfuWRxqr2faGFaOBYedWl2fzp5hgw==", + "version": "0.83.6", + "resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.83.6.tgz", + "integrity": "sha512-5prXv7WWR1RgZ/kWGZP+mi7/y/IE2ymfOHIZO5Pv14tMOmRAcQSgSYogcRmOiWw5mJs2K0UFeMiQD49ZO9oCug==", "license": "MIT", "engines": { "node": ">= 20.19.4" } }, "node_modules/@react-native/js-polyfills": { - "version": "0.85.2", - "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.85.2.tgz", - "integrity": "sha512-esGEAmKVM40DV/yVmNljCKZTIeUo7qXqc+Hwffkv3TG+b3E24xyFovHrbP98gGxZr2ZsEyx+2sKLdXF5asY5nw==", + "version": "0.83.6", + "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.83.6.tgz", + "integrity": "sha512-VSev0LV2i5X0ibduHBSLqKj0YU2F+waCgjl2uvaGHMGCSV1ZRKNFX/vJFqvLwjvdzLbkAZoFT1Rg7k7jDv44UA==", "license": "MIT", - "optional": true, - "peer": true, "engines": { - "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + "node": ">= 20.19.4" } }, - "node_modules/@react-native/metro-babel-transformer": { - "version": "0.85.2", - "resolved": "https://registry.npmjs.org/@react-native/metro-babel-transformer/-/metro-babel-transformer-0.85.2.tgz", - "integrity": "sha512-lU9XOGahpHvQff30H5lnvh9RYbVwC1zpSHpl84E+7BD2zj0FvW+pD7MBh7CWbmbWmegjtAb+U/2bokXcDVA+jA==", + "node_modules/@react-native/normalize-color": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@react-native/normalize-color/-/normalize-color-2.1.0.tgz", + "integrity": "sha512-Z1jQI2NpdFJCVgpY+8Dq/Bt3d+YUi1928Q+/CZm/oh66fzM0RUl54vvuXlPJKybH4pdCZey1eDTPaLHkMPNgWA==", + "license": "MIT" + }, + "node_modules/@react-native/normalize-colors": { + "version": "0.83.6", + "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.83.6.tgz", + "integrity": "sha512-bTM24b5v4qN3h52oflnv+OujFORn/kVi06WaWhnQQw14/ycilPqIsqsa+DpIBqdBrXxvLa9fXtCRrQtGATZCEw==", + "license": "MIT" + }, + "node_modules/@react-navigation/bottom-tabs": { + "version": "7.15.9", + "resolved": "https://registry.npmjs.org/@react-navigation/bottom-tabs/-/bottom-tabs-7.15.9.tgz", + "integrity": "sha512-Ou28A1aZLj5wiFQ3F93aIsrI4NCwn3IJzkkjNo9KLFXsc0Yks+UqrVaFlffHFLsrbajuGRG/OQpnMA1ljayY5Q==", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "@babel/core": "^7.25.2", - "@react-native/babel-preset": "0.85.2", - "hermes-parser": "0.33.3", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + "@react-navigation/elements": "^2.9.14", + "color": "^4.2.3", + "sf-symbols-typescript": "^2.1.0" }, "peerDependencies": { - "@babel/core": "*" + "@react-navigation/native": "^7.2.2", + "react": ">= 18.2.0", + "react-native": "*", + "react-native-safe-area-context": ">= 4.0.0", + "react-native-screens": ">= 4.0.0" } }, - "node_modules/@react-native/metro-babel-transformer/node_modules/hermes-estree": { - "version": "0.33.3", - "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.33.3.tgz", - "integrity": "sha512-6kzYZHCk8Fy1Uc+t3HGYyJn3OL4aeqKLTyina4UFtWl8I0kSL7OmKThaiX+Uh2f8nGw3mo4Ifxg0M5Zk3/Oeqg==", - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/@react-native/metro-babel-transformer/node_modules/hermes-parser": { - "version": "0.33.3", - "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.33.3.tgz", - "integrity": "sha512-Yg3HgaG4CqgyowtYjX/FsnPAuZdHOqSMtnbpylbptsQ9nwwSKsy6uRWcGO5RK0EqiX12q8HvDWKgeAVajRO5DA==", + "node_modules/@react-navigation/core": { + "version": "7.17.2", + "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-7.17.2.tgz", + "integrity": "sha512-Rt2OZwcgOmjv401uLGAKaRM6xo0fiBce/A7LfRHI1oe5FV+KooWcgAoZ2XOtgKj6UzVMuQWt3b2e6rxo/mDJRA==", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "hermes-estree": "0.33.3" + "@react-navigation/routers": "^7.5.3", + "escape-string-regexp": "^4.0.0", + "fast-deep-equal": "^3.1.3", + "nanoid": "^3.3.11", + "query-string": "^7.1.3", + "react-is": "^19.1.0", + "use-latest-callback": "^0.2.4", + "use-sync-external-store": "^1.5.0" + }, + "peerDependencies": { + "react": ">= 18.2.0" } }, - "node_modules/@react-native/metro-config": { - "version": "0.85.2", - "resolved": "https://registry.npmjs.org/@react-native/metro-config/-/metro-config-0.85.2.tgz", - "integrity": "sha512-YkTIMfTPeyMUrtpQo/7zd3oybVYJCfTp8626PqoakOvEiWi9PxsUpZ8j44a5GFtOIq8Nc6WWVBiFRn/6qdi1uQ==", + "node_modules/@react-navigation/elements": { + "version": "2.9.14", + "resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-2.9.14.tgz", + "integrity": "sha512-lKqzu+su2pI/YIZmR7L7xdOs4UL+rVXKJAMpRMBrwInEy96SjIFst6QDGpE89Dunnu3VjVpjWfByo9f2GWBHDQ==", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "@react-native/js-polyfills": "0.85.2", - "@react-native/metro-babel-transformer": "0.85.2", - "metro-config": "^0.84.0", - "metro-runtime": "^0.84.0" + "color": "^4.2.3", + "use-latest-callback": "^0.2.4", + "use-sync-external-store": "^1.5.0" }, - "engines": { - "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + "peerDependencies": { + "@react-native-masked-view/masked-view": ">= 0.2.0", + "@react-navigation/native": "^7.2.2", + "react": ">= 18.2.0", + "react-native": "*", + "react-native-safe-area-context": ">= 4.0.0" + }, + "peerDependenciesMeta": { + "@react-native-masked-view/masked-view": { + "optional": true + } } }, - "node_modules/@react-native/metro-config/node_modules/hermes-estree": { - "version": "0.35.0", - "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.35.0.tgz", - "integrity": "sha512-xVx5Opwy8Oo1I5yGpVRhCvWL/iV3M+ylksSKVNlxxD90cpDpR/AR1jLYqK8HWihm065a6UI3HeyAmYzwS8NOOg==", + "node_modules/@react-navigation/native": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-7.2.2.tgz", + "integrity": "sha512-kem1Ko2BcbAjmbQIv66dNmr6EtfDut3QU0qjsVhMnLLhktwyXb6FzZYp8gTrUb6AvkAbaJoi+BF5Pl55pAUa5w==", "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/@react-native/metro-config/node_modules/hermes-parser": { - "version": "0.35.0", - "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.35.0.tgz", - "integrity": "sha512-9JLjeHxBx8T4CAsydZR49PNZUaix+WpQJwu9p2010lu+7Kwl6D/7wYFFJxoz+aXkaaClp9Zfg6W6/zVlSJORaA==", - "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "hermes-estree": "0.35.0" + "@react-navigation/core": "^7.17.2", + "escape-string-regexp": "^4.0.0", + "fast-deep-equal": "^3.1.3", + "nanoid": "^3.3.11", + "use-latest-callback": "^0.2.4" + }, + "peerDependencies": { + "react": ">= 18.2.0", + "react-native": "*" } }, - "node_modules/@react-native/metro-config/node_modules/metro": { - "version": "0.84.3", - "resolved": "https://registry.npmjs.org/metro/-/metro-0.84.3.tgz", - "integrity": "sha512-1h3lbVrE6hGf1e/764HfhPGg/bGrWMJDDh7G2rc4gFYZboVuI40BlG/y+UhtbhQDNlO/csMvrcnK0YrTlHUVew==", + "node_modules/@react-navigation/native-stack": { + "version": "7.14.11", + "resolved": "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-7.14.11.tgz", + "integrity": "sha512-1ufBtJ7KbVFlQhXsYSYHqjgkmP30AzJSgW48YjWMQZ3NZGAyYe34w9Wd4KpdebQCfDClPe9maU+8crA/awa6lQ==", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/core": "^7.25.2", - "@babel/generator": "^7.29.1", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.29.0", - "@babel/types": "^7.29.0", - "accepts": "^2.0.0", - "chalk": "^4.0.0", - "ci-info": "^2.0.0", - "connect": "^3.6.5", - "debug": "^4.4.0", - "error-stack-parser": "^2.0.6", - "flow-enums-runtime": "^0.0.6", - "graceful-fs": "^4.2.4", - "hermes-parser": "0.35.0", - "image-size": "^1.0.2", - "invariant": "^2.2.4", - "jest-worker": "^29.7.0", - "jsc-safe-url": "^0.2.2", - "lodash.throttle": "^4.1.1", - "metro-babel-transformer": "0.84.3", - "metro-cache": "0.84.3", - "metro-cache-key": "0.84.3", - "metro-config": "0.84.3", - "metro-core": "0.84.3", - "metro-file-map": "0.84.3", - "metro-resolver": "0.84.3", - "metro-runtime": "0.84.3", - "metro-source-map": "0.84.3", - "metro-symbolicate": "0.84.3", - "metro-transform-plugins": "0.84.3", - "metro-transform-worker": "0.84.3", - "mime-types": "^3.0.1", - "nullthrows": "^1.1.1", - "serialize-error": "^2.1.0", - "source-map": "^0.5.6", - "throat": "^5.0.0", - "ws": "^7.5.10", - "yargs": "^17.6.2" - }, - "bin": { - "metro": "src/cli.js" + "@react-navigation/elements": "^2.9.14", + "color": "^4.2.3", + "sf-symbols-typescript": "^2.1.0", + "warn-once": "^0.1.1" }, - "engines": { - "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + "peerDependencies": { + "@react-navigation/native": "^7.2.2", + "react": ">= 18.2.0", + "react-native": "*", + "react-native-safe-area-context": ">= 4.0.0", + "react-native-screens": ">= 4.0.0" } }, - "node_modules/@react-native/metro-config/node_modules/metro-babel-transformer": { - "version": "0.84.3", - "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.84.3.tgz", - "integrity": "sha512-svAA+yMLpeMiGcz/jKJs4oHpIGEx4nBqNEJ5AGj4CYIg1efvK+A0TjR6tgIuc6tKO5e8JmN/1lglpN2+f3/z/w==", + "node_modules/@react-navigation/routers": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-7.5.3.tgz", + "integrity": "sha512-1tJHg4KKRJuQ1/EvJxatrMef3NZXEPzwUIUZ3n1yJ2t7Q97siwRtbynRpQG9/69ebbtiZ8W3ScOZF/OmhvM4Rg==", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "@babel/core": "^7.25.2", - "flow-enums-runtime": "^0.0.6", - "hermes-parser": "0.35.0", - "metro-cache-key": "0.84.3", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + "nanoid": "^3.3.11" } }, - "node_modules/@react-native/metro-config/node_modules/metro-cache": { - "version": "0.84.3", - "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.84.3.tgz", - "integrity": "sha512-0QElxwLaHqLZf+Xqio8QrjVbuXP/8sJfQBGSPiITlKDVXrVLefuzYVSH9Sj+QL6lrPj2gYZd/iwQh1yZuVKnLA==", + "node_modules/@rnmapbox/maps": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@rnmapbox/maps/-/maps-10.3.0.tgz", + "integrity": "sha512-g5A6hkz6imuA5qy3EGQIbtaofBUbP2YiiyKzj7GJ9ryRXC9gRsnVaJ/8pmAts+0/aIZ5o5w63eboTKjCdO2HrQ==", "license": "MIT", - "optional": true, - "peer": true, + "workspaces": [ + "example" + ], "dependencies": { - "exponential-backoff": "^3.1.1", - "flow-enums-runtime": "^0.0.6", - "https-proxy-agent": "^7.0.5", - "metro-core": "0.84.3" + "@turf/along": "6.5.0", + "@turf/distance": "6.5.0", + "@turf/helpers": "6.5.0", + "@turf/length": "6.5.0", + "@turf/nearest-point-on-line": "6.5.0", + "@types/geojson": "^7946.0.7", + "debounce": "^2.2.0" }, - "engines": { - "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + "peerDependencies": { + "expo": ">=47.0.0", + "mapbox-gl": "^2.9.0", + "react": ">=17.0.0", + "react-dom": ">= 17.0.0", + "react-native": ">=0.79" + }, + "peerDependenciesMeta": { + "expo": { + "optional": true + }, + "mapbox-gl": { + "optional": true + }, + "react-dom": { + "optional": true + } } }, - "node_modules/@react-native/metro-config/node_modules/metro-cache-key": { - "version": "0.84.3", - "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.84.3.tgz", - "integrity": "sha512-TnSL1Fdvrw+2glTdBSRmA5TL8l/i16ECjsrUdf3E5HncA+sNx8KcwDG8r+3ct1UhfYcusJypzZqTN55FZZcwGg==", + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@shopify/flash-list": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@shopify/flash-list/-/flash-list-2.0.2.tgz", + "integrity": "sha512-zhlrhA9eiuEzja4wxVvotgXHtqd3qsYbXkQ3rsBfOgbFA9BVeErpDE/yEwtlIviRGEqpuFj/oU5owD6ByaNX+w==", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "flow-enums-runtime": "^0.0.6" + "tslib": "2.8.1" }, - "engines": { - "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + "peerDependencies": { + "@babel/runtime": "*", + "react": "*", + "react-native": "*" } }, - "node_modules/@react-native/metro-config/node_modules/metro-config": { - "version": "0.84.3", - "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.84.3.tgz", - "integrity": "sha512-JmCzZWOETR+O22q8oPBWyQppx3roU9EbkbGzD8Gf1jukQ4b5T1fTzqqHruu6K4sTiNq5zVQySmKF6bp4kVARew==", - "license": "MIT", - "optional": true, - "peer": true, + "node_modules/@sinclair/typebox": { + "version": "0.27.10", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", + "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "license": "BSD-3-Clause", "dependencies": { - "connect": "^3.6.5", - "flow-enums-runtime": "^0.0.6", - "jest-validate": "^29.7.0", - "metro": "0.84.3", - "metro-cache": "0.84.3", - "metro-core": "0.84.3", - "metro-runtime": "0.84.3", - "yaml": "^2.6.1" - }, - "engines": { - "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + "type-detect": "4.0.8" } }, - "node_modules/@react-native/metro-config/node_modules/metro-core": { - "version": "0.84.3", - "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.84.3.tgz", - "integrity": "sha512-cc0pvAa80ai1nDmqqz0P59a+0ZqCZ/YHU/3jEekZL6spFnYDfX8iDLdn9FR6kX+67rmzKxHNrbrSRFLX2AYocw==", - "license": "MIT", - "optional": true, - "peer": true, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "license": "BSD-3-Clause", "dependencies": { - "flow-enums-runtime": "^0.0.6", - "lodash.throttle": "^4.1.1", - "metro-resolver": "0.84.3" - }, - "engines": { - "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@tanstack/query-core": { + "version": "5.99.2", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.99.2.tgz", + "integrity": "sha512-1HunU0bXVsR1ZJMZbcOPE6VtaBJxsW809RE9xPe4Gz7MlB0GWwQvuTPhMoEmQ/hIzFKJ/DWAuttIe7BOaWx0tA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" } }, - "node_modules/@react-native/metro-config/node_modules/metro-file-map": { - "version": "0.84.3", - "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.84.3.tgz", - "integrity": "sha512-1cL4m4Jv1yRUt9RJExZQLfccscdlMNOcRG6LHLtmJhf3BG9j3MujPVc7CIpKYdFl+KUl+sdjge6oO3+meKCHQA==", + "node_modules/@tanstack/react-query": { + "version": "5.99.2", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.99.2.tgz", + "integrity": "sha512-vM91UEe45QUS9ED6OklsVL15i8qKcRqNwpWzPTVWvRPRSEgDudDgHpvyTjcdlwHcrKNa80T+xXYcchT2noPnZA==", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "debug": "^4.4.0", - "fb-watchman": "^2.0.0", - "flow-enums-runtime": "^0.0.6", - "graceful-fs": "^4.2.4", - "invariant": "^2.2.4", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "nullthrows": "^1.1.1", - "walker": "^1.0.7" + "@tanstack/query-core": "5.99.2" }, - "engines": { - "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" } }, - "node_modules/@react-native/metro-config/node_modules/metro-minify-terser": { - "version": "0.84.3", - "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.84.3.tgz", - "integrity": "sha512-3ofrG2OQyJbO9RNhCfOcl8QU7EE2WrSsnN5dFkuZaJO5+4Imujr9bUXmspeNlXRsOVk0F/rVRbEFH98lFSCkBQ==", + "node_modules/@turf/along": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/along/-/along-6.5.0.tgz", + "integrity": "sha512-LLyWQ0AARqJCmMcIEAXF4GEu8usmd4Kbz3qk1Oy5HoRNpZX47+i5exQtmIWKdqJ1MMhW26fCTXgpsEs5zgJ5gw==", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "flow-enums-runtime": "^0.0.6", - "terser": "^5.15.0" + "@turf/bearing": "^6.5.0", + "@turf/destination": "^6.5.0", + "@turf/distance": "^6.5.0", + "@turf/helpers": "^6.5.0", + "@turf/invariant": "^6.5.0" }, - "engines": { - "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + "funding": { + "url": "https://opencollective.com/turf" } }, - "node_modules/@react-native/metro-config/node_modules/metro-resolver": { - "version": "0.84.3", - "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.84.3.tgz", - "integrity": "sha512-pjEzGDtoM8DTHAIPK/9u9ZxszEiuRohYUVImWvgbnB91V4gqYJpQcoEYUugf2NIm1lrX5HNu0OvNqWmPBnGYjA==", + "node_modules/@turf/bbox": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/@turf/bbox/-/bbox-7.3.5.tgz", + "integrity": "sha512-oG1ya/HtBjAIg4TimbWx+nOYPbY0bCvt82Bq8tm6sBw3qqtbOyRSfDz79Sq90TnH7DXJprJ1qnVGKNtZ6jemfw==", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "flow-enums-runtime": "^0.0.6" + "@turf/helpers": "7.3.5", + "@turf/meta": "7.3.5", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" }, - "engines": { - "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + "funding": { + "url": "https://opencollective.com/turf" } }, - "node_modules/@react-native/metro-config/node_modules/metro-runtime": { - "version": "0.84.3", - "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.84.3.tgz", - "integrity": "sha512-o7HLRfMyVk9N2dUZ9VjQfB6xxUItL9Pi9WcqxURE7MEKOH6wbGt9/E92YdYLluTOtkzYAEVfdC6h6lcxqA+hMQ==", + "node_modules/@turf/bbox/node_modules/@turf/helpers": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-7.3.5.tgz", + "integrity": "sha512-E/NMGV5MwbjjP7AJXBtsanC3yY8N2MQ87IGdIgkB2ji5AtBpwnH4L3gEqpYN4RlCJJWbLbzO91BbKv2waUd0eg==", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "@babel/runtime": "^7.25.0", - "flow-enums-runtime": "^0.0.6" + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" }, - "engines": { - "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + "funding": { + "url": "https://opencollective.com/turf" } }, - "node_modules/@react-native/metro-config/node_modules/metro-source-map": { - "version": "0.84.3", - "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.84.3.tgz", - "integrity": "sha512-jS48CeSzw78M8y6VE0f9uy3lVmfbOS677j2VCxnlmlYmnahcXuC6IhoN9K6LynNvos9517yUadcfgioju38xYQ==", + "node_modules/@turf/bbox/node_modules/@turf/meta": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-7.3.5.tgz", + "integrity": "sha512-r+ohqxoyqeigFB0oFrQx/YEHIkOKqcKpCjvZkvZs7Tkv+IFco5MezAd2zd4rzK+0DfFgDP3KpJc7HqrYjvEjhg==", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "@babel/traverse": "^7.29.0", - "@babel/types": "^7.29.0", - "flow-enums-runtime": "^0.0.6", - "invariant": "^2.2.4", - "metro-symbolicate": "0.84.3", - "nullthrows": "^1.1.1", - "ob1": "0.84.3", - "source-map": "^0.5.6", - "vlq": "^1.0.0" + "@turf/helpers": "7.3.5", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" }, - "engines": { - "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + "funding": { + "url": "https://opencollective.com/turf" } }, - "node_modules/@react-native/metro-config/node_modules/metro-symbolicate": { - "version": "0.84.3", - "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.84.3.tgz", - "integrity": "sha512-J9Tpo8NCycYrozRvBIUyOwGAu4xkawOsAppmTscFiaegK0WvuDGwIM53GbzVSnytCHjVAF0io5GQxpkrKTuc7g==", + "node_modules/@turf/bearing": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/bearing/-/bearing-6.5.0.tgz", + "integrity": "sha512-dxINYhIEMzgDOztyMZc20I7ssYVNEpSv04VbMo5YPQsqa80KO3TFvbuCahMsCAW5z8Tncc8dwBlEFrmRjJG33A==", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "flow-enums-runtime": "^0.0.6", - "invariant": "^2.2.4", - "metro-source-map": "0.84.3", - "nullthrows": "^1.1.1", - "source-map": "^0.5.6", - "vlq": "^1.0.0" - }, - "bin": { - "metro-symbolicate": "src/index.js" + "@turf/helpers": "^6.5.0", + "@turf/invariant": "^6.5.0" }, - "engines": { - "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + "funding": { + "url": "https://opencollective.com/turf" } }, - "node_modules/@react-native/metro-config/node_modules/metro-transform-plugins": { - "version": "0.84.3", - "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.84.3.tgz", - "integrity": "sha512-8S3baq2XhBaafHEH5Q8sJW6tmzsEJk80qKc3RU/nZV1MsnYq94RdjTUR6AyKjQd6Rfsk1BtBxhtiNnk7mgslCg==", + "node_modules/@turf/destination": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/destination/-/destination-6.5.0.tgz", + "integrity": "sha512-4cnWQlNC8d1tItOz9B4pmJdWpXqS0vEvv65bI/Pj/genJnsL7evI0/Xw42RvEGROS481MPiU80xzvwxEvhQiMQ==", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "@babel/core": "^7.25.2", - "@babel/generator": "^7.29.1", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.29.0", - "flow-enums-runtime": "^0.0.6", - "nullthrows": "^1.1.1" + "@turf/helpers": "^6.5.0", + "@turf/invariant": "^6.5.0" }, - "engines": { - "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + "funding": { + "url": "https://opencollective.com/turf" } }, - "node_modules/@react-native/metro-config/node_modules/metro-transform-worker": { - "version": "0.84.3", - "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.84.3.tgz", - "integrity": "sha512-Wjba7PyYktNRsHbPmkx2J2UX32rAzcDXjCu49zPHeF/viJlYJhwRaNePQcHaCRqQ+kmgQT4ThprsnJfDj71ZMA==", + "node_modules/@turf/distance": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/distance/-/distance-6.5.0.tgz", + "integrity": "sha512-xzykSLfoURec5qvQJcfifw/1mJa+5UwByZZ5TZ8iaqjGYN0vomhV9aiSLeYdUGtYRESZ+DYC/OzY+4RclZYgMg==", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "@babel/core": "^7.25.2", - "@babel/generator": "^7.29.1", - "@babel/parser": "^7.29.0", - "@babel/types": "^7.29.0", - "flow-enums-runtime": "^0.0.6", - "metro": "0.84.3", - "metro-babel-transformer": "0.84.3", - "metro-cache": "0.84.3", - "metro-cache-key": "0.84.3", - "metro-minify-terser": "0.84.3", - "metro-source-map": "0.84.3", - "metro-transform-plugins": "0.84.3", - "nullthrows": "^1.1.1" + "@turf/helpers": "^6.5.0", + "@turf/invariant": "^6.5.0" }, - "engines": { - "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + "funding": { + "url": "https://opencollective.com/turf" } }, - "node_modules/@react-native/metro-config/node_modules/ob1": { - "version": "0.84.3", - "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.84.3.tgz", - "integrity": "sha512-J7554Ef8bzmKaDY365Afq6PF+qtdnY/d5PKUQFrsKlZHV/N3OGZewVrvDrQDyX5V5NJjTpcAKtlrFZcDr+HvpQ==", + "node_modules/@turf/helpers": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-6.5.0.tgz", + "integrity": "sha512-VbI1dV5bLFzohYYdgqwikdMVpe7pJ9X3E+dlr425wa2/sMJqYDhTO++ec38/pcPvPE6oD9WEEeU3Xu3gza+VPw==", "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "flow-enums-runtime": "^0.0.6" - }, - "engines": { - "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + "funding": { + "url": "https://opencollective.com/turf" } }, - "node_modules/@react-native/normalize-color": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@react-native/normalize-color/-/normalize-color-2.1.0.tgz", - "integrity": "sha512-Z1jQI2NpdFJCVgpY+8Dq/Bt3d+YUi1928Q+/CZm/oh66fzM0RUl54vvuXlPJKybH4pdCZey1eDTPaLHkMPNgWA==", - "license": "MIT" - }, - "node_modules/@react-native/normalize-colors": { - "version": "0.83.6", - "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.83.6.tgz", - "integrity": "sha512-bTM24b5v4qN3h52oflnv+OujFORn/kVi06WaWhnQQw14/ycilPqIsqsa+DpIBqdBrXxvLa9fXtCRrQtGATZCEw==", - "license": "MIT" - }, - "node_modules/@react-navigation/bottom-tabs": { - "version": "7.15.9", - "resolved": "https://registry.npmjs.org/@react-navigation/bottom-tabs/-/bottom-tabs-7.15.9.tgz", - "integrity": "sha512-Ou28A1aZLj5wiFQ3F93aIsrI4NCwn3IJzkkjNo9KLFXsc0Yks+UqrVaFlffHFLsrbajuGRG/OQpnMA1ljayY5Q==", + "node_modules/@turf/invariant": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-6.5.0.tgz", + "integrity": "sha512-Wv8PRNCtPD31UVbdJE/KVAWKe7l6US+lJItRR/HOEW3eh+U/JwRCSUl/KZ7bmjM/C+zLNoreM2TU6OoLACs4eg==", "license": "MIT", "dependencies": { - "@react-navigation/elements": "^2.9.14", - "color": "^4.2.3", - "sf-symbols-typescript": "^2.1.0" + "@turf/helpers": "^6.5.0" }, - "peerDependencies": { - "@react-navigation/native": "^7.2.2", - "react": ">= 18.2.0", - "react-native": "*", - "react-native-safe-area-context": ">= 4.0.0", - "react-native-screens": ">= 4.0.0" + "funding": { + "url": "https://opencollective.com/turf" } }, - "node_modules/@react-navigation/core": { - "version": "7.17.2", - "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-7.17.2.tgz", - "integrity": "sha512-Rt2OZwcgOmjv401uLGAKaRM6xo0fiBce/A7LfRHI1oe5FV+KooWcgAoZ2XOtgKj6UzVMuQWt3b2e6rxo/mDJRA==", + "node_modules/@turf/length": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/length/-/length-6.5.0.tgz", + "integrity": "sha512-5pL5/pnw52fck3oRsHDcSGrj9HibvtlrZ0QNy2OcW8qBFDNgZ4jtl6U7eATVoyWPKBHszW3dWETW+iLV7UARig==", "license": "MIT", "dependencies": { - "@react-navigation/routers": "^7.5.3", - "escape-string-regexp": "^4.0.0", - "fast-deep-equal": "^3.1.3", - "nanoid": "^3.3.11", - "query-string": "^7.1.3", - "react-is": "^19.1.0", - "use-latest-callback": "^0.2.4", - "use-sync-external-store": "^1.5.0" + "@turf/distance": "^6.5.0", + "@turf/helpers": "^6.5.0", + "@turf/meta": "^6.5.0" }, - "peerDependencies": { - "react": ">= 18.2.0" + "funding": { + "url": "https://opencollective.com/turf" } }, - "node_modules/@react-navigation/elements": { - "version": "2.9.14", - "resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-2.9.14.tgz", - "integrity": "sha512-lKqzu+su2pI/YIZmR7L7xdOs4UL+rVXKJAMpRMBrwInEy96SjIFst6QDGpE89Dunnu3VjVpjWfByo9f2GWBHDQ==", + "node_modules/@turf/line-intersect": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/line-intersect/-/line-intersect-6.5.0.tgz", + "integrity": "sha512-CS6R1tZvVQD390G9Ea4pmpM6mJGPWoL82jD46y0q1KSor9s6HupMIo1kY4Ny+AEYQl9jd21V3Scz20eldpbTVA==", "license": "MIT", "dependencies": { - "color": "^4.2.3", - "use-latest-callback": "^0.2.4", - "use-sync-external-store": "^1.5.0" - }, - "peerDependencies": { - "@react-native-masked-view/masked-view": ">= 0.2.0", - "@react-navigation/native": "^7.2.2", - "react": ">= 18.2.0", - "react-native": "*", - "react-native-safe-area-context": ">= 4.0.0" + "@turf/helpers": "^6.5.0", + "@turf/invariant": "^6.5.0", + "@turf/line-segment": "^6.5.0", + "@turf/meta": "^6.5.0", + "geojson-rbush": "3.x" }, - "peerDependenciesMeta": { - "@react-native-masked-view/masked-view": { - "optional": true - } + "funding": { + "url": "https://opencollective.com/turf" } }, - "node_modules/@react-navigation/native": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-7.2.2.tgz", - "integrity": "sha512-kem1Ko2BcbAjmbQIv66dNmr6EtfDut3QU0qjsVhMnLLhktwyXb6FzZYp8gTrUb6AvkAbaJoi+BF5Pl55pAUa5w==", + "node_modules/@turf/line-segment": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/line-segment/-/line-segment-6.5.0.tgz", + "integrity": "sha512-jI625Ho4jSuJESNq66Mmi290ZJ5pPZiQZruPVpmHkUw257Pew0alMmb6YrqYNnLUuiVVONxAAKXUVeeUGtycfw==", "license": "MIT", "dependencies": { - "@react-navigation/core": "^7.17.2", - "escape-string-regexp": "^4.0.0", - "fast-deep-equal": "^3.1.3", - "nanoid": "^3.3.11", - "use-latest-callback": "^0.2.4" + "@turf/helpers": "^6.5.0", + "@turf/invariant": "^6.5.0", + "@turf/meta": "^6.5.0" }, - "peerDependencies": { - "react": ">= 18.2.0", - "react-native": "*" + "funding": { + "url": "https://opencollective.com/turf" } }, - "node_modules/@react-navigation/native-stack": { - "version": "7.14.11", - "resolved": "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-7.14.11.tgz", - "integrity": "sha512-1ufBtJ7KbVFlQhXsYSYHqjgkmP30AzJSgW48YjWMQZ3NZGAyYe34w9Wd4KpdebQCfDClPe9maU+8crA/awa6lQ==", + "node_modules/@turf/meta": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-6.5.0.tgz", + "integrity": "sha512-RrArvtsV0vdsCBegoBtOalgdSOfkBrTJ07VkpiCnq/491W67hnMWmDu7e6Ztw0C3WldRYTXkg3SumfdzZxLBHA==", "license": "MIT", "dependencies": { - "@react-navigation/elements": "^2.9.14", - "color": "^4.2.3", - "sf-symbols-typescript": "^2.1.0", - "warn-once": "^0.1.1" + "@turf/helpers": "^6.5.0" }, - "peerDependencies": { - "@react-navigation/native": "^7.2.2", - "react": ">= 18.2.0", - "react-native": "*", - "react-native-safe-area-context": ">= 4.0.0", - "react-native-screens": ">= 4.0.0" - } - }, - "node_modules/@react-navigation/routers": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-7.5.3.tgz", - "integrity": "sha512-1tJHg4KKRJuQ1/EvJxatrMef3NZXEPzwUIUZ3n1yJ2t7Q97siwRtbynRpQG9/69ebbtiZ8W3ScOZF/OmhvM4Rg==", - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11" + "funding": { + "url": "https://opencollective.com/turf" } }, - "node_modules/@rnmapbox/maps": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@rnmapbox/maps/-/maps-10.3.0.tgz", - "integrity": "sha512-g5A6hkz6imuA5qy3EGQIbtaofBUbP2YiiyKzj7GJ9ryRXC9gRsnVaJ/8pmAts+0/aIZ5o5w63eboTKjCdO2HrQ==", + "node_modules/@turf/nearest-point-on-line": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/nearest-point-on-line/-/nearest-point-on-line-6.5.0.tgz", + "integrity": "sha512-WthrvddddvmymnC+Vf7BrkHGbDOUu6Z3/6bFYUGv1kxw8tiZ6n83/VG6kHz4poHOfS0RaNflzXSkmCi64fLBlg==", "license": "MIT", - "workspaces": [ - "example" - ], "dependencies": { - "@turf/along": "6.5.0", - "@turf/distance": "6.5.0", - "@turf/helpers": "6.5.0", - "@turf/length": "6.5.0", - "@turf/nearest-point-on-line": "6.5.0", - "@types/geojson": "^7946.0.7", - "debounce": "^2.2.0" - }, - "peerDependencies": { - "expo": ">=47.0.0", - "mapbox-gl": "^2.9.0", - "react": ">=17.0.0", - "react-dom": ">= 17.0.0", - "react-native": ">=0.79" + "@turf/bearing": "^6.5.0", + "@turf/destination": "^6.5.0", + "@turf/distance": "^6.5.0", + "@turf/helpers": "^6.5.0", + "@turf/invariant": "^6.5.0", + "@turf/line-intersect": "^6.5.0", + "@turf/meta": "^6.5.0" }, - "peerDependenciesMeta": { - "expo": { - "optional": true - }, - "mapbox-gl": { - "optional": true - }, - "react-dom": { - "optional": true - } + "funding": { + "url": "https://opencollective.com/turf" } }, - "node_modules/@rtsao/scc": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", - "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", "dev": true, - "license": "MIT" - }, - "node_modules/@shopify/flash-list": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@shopify/flash-list/-/flash-list-2.0.2.tgz", - "integrity": "sha512-zhlrhA9eiuEzja4wxVvotgXHtqd3qsYbXkQ3rsBfOgbFA9BVeErpDE/yEwtlIviRGEqpuFj/oU5owD6ByaNX+w==", "license": "MIT", + "optional": true, "dependencies": { - "tslib": "2.8.1" - }, - "peerDependencies": { - "@babel/runtime": "*", - "react": "*", - "react-native": "*" - } - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.10", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", - "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", - "license": "MIT" - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/@tanstack/query-core": { - "version": "5.99.2", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.99.2.tgz", - "integrity": "sha512-1HunU0bXVsR1ZJMZbcOPE6VtaBJxsW809RE9xPe4Gz7MlB0GWwQvuTPhMoEmQ/hIzFKJ/DWAuttIe7BOaWx0tA==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, - "node_modules/@tanstack/react-query": { - "version": "5.99.2", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.99.2.tgz", - "integrity": "sha512-vM91UEe45QUS9ED6OklsVL15i8qKcRqNwpWzPTVWvRPRSEgDudDgHpvyTjcdlwHcrKNa80T+xXYcchT2noPnZA==", - "license": "MIT", - "dependencies": { - "@tanstack/query-core": "5.99.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "react": "^18 || ^19" - } - }, - "node_modules/@turf/along": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@turf/along/-/along-6.5.0.tgz", - "integrity": "sha512-LLyWQ0AARqJCmMcIEAXF4GEu8usmd4Kbz3qk1Oy5HoRNpZX47+i5exQtmIWKdqJ1MMhW26fCTXgpsEs5zgJ5gw==", - "license": "MIT", - "dependencies": { - "@turf/bearing": "^6.5.0", - "@turf/destination": "^6.5.0", - "@turf/distance": "^6.5.0", - "@turf/helpers": "^6.5.0", - "@turf/invariant": "^6.5.0" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@turf/bbox": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/@turf/bbox/-/bbox-7.3.5.tgz", - "integrity": "sha512-oG1ya/HtBjAIg4TimbWx+nOYPbY0bCvt82Bq8tm6sBw3qqtbOyRSfDz79Sq90TnH7DXJprJ1qnVGKNtZ6jemfw==", - "license": "MIT", - "dependencies": { - "@turf/helpers": "7.3.5", - "@turf/meta": "7.3.5", - "@types/geojson": "^7946.0.10", - "tslib": "^2.8.1" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@turf/bbox/node_modules/@turf/helpers": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-7.3.5.tgz", - "integrity": "sha512-E/NMGV5MwbjjP7AJXBtsanC3yY8N2MQ87IGdIgkB2ji5AtBpwnH4L3gEqpYN4RlCJJWbLbzO91BbKv2waUd0eg==", - "license": "MIT", - "dependencies": { - "@types/geojson": "^7946.0.10", - "tslib": "^2.8.1" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@turf/bbox/node_modules/@turf/meta": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-7.3.5.tgz", - "integrity": "sha512-r+ohqxoyqeigFB0oFrQx/YEHIkOKqcKpCjvZkvZs7Tkv+IFco5MezAd2zd4rzK+0DfFgDP3KpJc7HqrYjvEjhg==", - "license": "MIT", - "dependencies": { - "@turf/helpers": "7.3.5", - "@types/geojson": "^7946.0.10", - "tslib": "^2.8.1" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@turf/bearing": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@turf/bearing/-/bearing-6.5.0.tgz", - "integrity": "sha512-dxINYhIEMzgDOztyMZc20I7ssYVNEpSv04VbMo5YPQsqa80KO3TFvbuCahMsCAW5z8Tncc8dwBlEFrmRjJG33A==", - "license": "MIT", - "dependencies": { - "@turf/helpers": "^6.5.0", - "@turf/invariant": "^6.5.0" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@turf/destination": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@turf/destination/-/destination-6.5.0.tgz", - "integrity": "sha512-4cnWQlNC8d1tItOz9B4pmJdWpXqS0vEvv65bI/Pj/genJnsL7evI0/Xw42RvEGROS481MPiU80xzvwxEvhQiMQ==", - "license": "MIT", - "dependencies": { - "@turf/helpers": "^6.5.0", - "@turf/invariant": "^6.5.0" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@turf/distance": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@turf/distance/-/distance-6.5.0.tgz", - "integrity": "sha512-xzykSLfoURec5qvQJcfifw/1mJa+5UwByZZ5TZ8iaqjGYN0vomhV9aiSLeYdUGtYRESZ+DYC/OzY+4RclZYgMg==", - "license": "MIT", - "dependencies": { - "@turf/helpers": "^6.5.0", - "@turf/invariant": "^6.5.0" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@turf/helpers": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-6.5.0.tgz", - "integrity": "sha512-VbI1dV5bLFzohYYdgqwikdMVpe7pJ9X3E+dlr425wa2/sMJqYDhTO++ec38/pcPvPE6oD9WEEeU3Xu3gza+VPw==", - "license": "MIT", - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@turf/invariant": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-6.5.0.tgz", - "integrity": "sha512-Wv8PRNCtPD31UVbdJE/KVAWKe7l6US+lJItRR/HOEW3eh+U/JwRCSUl/KZ7bmjM/C+zLNoreM2TU6OoLACs4eg==", - "license": "MIT", - "dependencies": { - "@turf/helpers": "^6.5.0" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@turf/length": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@turf/length/-/length-6.5.0.tgz", - "integrity": "sha512-5pL5/pnw52fck3oRsHDcSGrj9HibvtlrZ0QNy2OcW8qBFDNgZ4jtl6U7eATVoyWPKBHszW3dWETW+iLV7UARig==", - "license": "MIT", - "dependencies": { - "@turf/distance": "^6.5.0", - "@turf/helpers": "^6.5.0", - "@turf/meta": "^6.5.0" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@turf/line-intersect": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@turf/line-intersect/-/line-intersect-6.5.0.tgz", - "integrity": "sha512-CS6R1tZvVQD390G9Ea4pmpM6mJGPWoL82jD46y0q1KSor9s6HupMIo1kY4Ny+AEYQl9jd21V3Scz20eldpbTVA==", - "license": "MIT", - "dependencies": { - "@turf/helpers": "^6.5.0", - "@turf/invariant": "^6.5.0", - "@turf/line-segment": "^6.5.0", - "@turf/meta": "^6.5.0", - "geojson-rbush": "3.x" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@turf/line-segment": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@turf/line-segment/-/line-segment-6.5.0.tgz", - "integrity": "sha512-jI625Ho4jSuJESNq66Mmi290ZJ5pPZiQZruPVpmHkUw257Pew0alMmb6YrqYNnLUuiVVONxAAKXUVeeUGtycfw==", - "license": "MIT", - "dependencies": { - "@turf/helpers": "^6.5.0", - "@turf/invariant": "^6.5.0", - "@turf/meta": "^6.5.0" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@turf/meta": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-6.5.0.tgz", - "integrity": "sha512-RrArvtsV0vdsCBegoBtOalgdSOfkBrTJ07VkpiCnq/491W67hnMWmDu7e6Ztw0C3WldRYTXkg3SumfdzZxLBHA==", - "license": "MIT", - "dependencies": { - "@turf/helpers": "^6.5.0" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@turf/nearest-point-on-line": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@turf/nearest-point-on-line/-/nearest-point-on-line-6.5.0.tgz", - "integrity": "sha512-WthrvddddvmymnC+Vf7BrkHGbDOUu6Z3/6bFYUGv1kxw8tiZ6n83/VG6kHz4poHOfS0RaNflzXSkmCi64fLBlg==", - "license": "MIT", - "dependencies": { - "@turf/bearing": "^6.5.0", - "@turf/destination": "^6.5.0", - "@turf/distance": "^6.5.0", - "@turf/helpers": "^6.5.0", - "@turf/invariant": "^6.5.0", - "@turf/line-intersect": "^6.5.0", - "@turf/meta": "^6.5.0" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", - "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" + "tslib": "^2.4.0" } }, "node_modules/@types/babel__core": { @@ -4741,10 +4358,20 @@ "csstype": "^3.2.2" } }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "node_modules/@types/sharp": { + "version": "0.31.1", + "resolved": "https://registry.npmjs.org/@types/sharp/-/sharp-0.31.1.tgz", + "integrity": "sha512-5nWwamN9ZFHXaYEincMSuza8nNfOof8nmO+mcI+Agx1uMUk4/pQnNIcix+9rLPXzKrm1pS34+6WRDbDV0Jn7ag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "license": "MIT" }, "node_modules/@types/suncalc": { @@ -5573,12 +5200,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array-timsort": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", - "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==", - "license": "MIT" - }, "node_modules/array.prototype.findlast": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", @@ -5736,382 +5357,148 @@ "resolved": "https://registry.npmjs.org/await-lock/-/await-lock-2.2.2.tgz", "integrity": "sha512-aDczADvlvTGajTDjcjpJMqRkOF6Qdz3YbPZm/PyW6tKPkx2hlYBzxMhEywM/tU72HrVZjgl5VCdRuMlA7pZ8Gw==", "license": "MIT" - }, - "node_modules/babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "license": "MIT", - "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "license": "BSD-3-Clause", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "license": "MIT", - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.17", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz", - "integrity": "sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==", - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-define-polyfill-provider": "^0.6.8", - "semver": "^6.3.1" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz", - "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==", - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.5", - "core-js-compat": "^3.43.0" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.8.tgz", - "integrity": "sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==", - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.8" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-react-compiler": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-react-compiler/-/babel-plugin-react-compiler-1.0.0.tgz", - "integrity": "sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.26.0" - } - }, - "node_modules/babel-plugin-react-native-web": { - "version": "0.21.2", - "resolved": "https://registry.npmjs.org/babel-plugin-react-native-web/-/babel-plugin-react-native-web-0.21.2.tgz", - "integrity": "sha512-SPD0J6qjJn8231i0HZhlAGH6NORe+QvRSQM2mwQEzJ2Fb3E4ruWTiiicPlHjmeWShDXLcvoorOCXjeR7k/lyWA==", - "license": "MIT" - }, - "node_modules/babel-plugin-syntax-hermes-parser": { - "version": "0.33.3", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.33.3.tgz", - "integrity": "sha512-/Z9xYdaJ1lC0pT9do6TqCqhOSLfZ5Ot8D5za1p+feEfWYupCOfGbhhEXN9r2ZgJtDNUNRw/Z+T2CvAGKBqtqWA==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "hermes-parser": "0.33.3" - } - }, - "node_modules/babel-plugin-syntax-hermes-parser/node_modules/hermes-estree": { - "version": "0.33.3", - "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.33.3.tgz", - "integrity": "sha512-6kzYZHCk8Fy1Uc+t3HGYyJn3OL4aeqKLTyina4UFtWl8I0kSL7OmKThaiX+Uh2f8nGw3mo4Ifxg0M5Zk3/Oeqg==", - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/babel-plugin-syntax-hermes-parser/node_modules/hermes-parser": { - "version": "0.33.3", - "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.33.3.tgz", - "integrity": "sha512-Yg3HgaG4CqgyowtYjX/FsnPAuZdHOqSMtnbpylbptsQ9nwwSKsy6uRWcGO5RK0EqiX12q8HvDWKgeAVajRO5DA==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "hermes-estree": "0.33.3" - } - }, - "node_modules/babel-plugin-transform-flow-enums": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-enums/-/babel-plugin-transform-flow-enums-0.0.2.tgz", - "integrity": "sha512-g4aaCrDDOsWjbm0PUUeVnkcVd6AKJsVc/MbnPhEotEpkeJQP6b8nzewohQi7+QS8UyPehOhGWn0nOwjvWpmMvQ==", - "license": "MIT", - "dependencies": { - "@babel/plugin-syntax-flow": "^7.12.1" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", - "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", - "license": "MIT", - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5" - }, - "peerDependencies": { - "@babel/core": "^7.0.0 || ^8.0.0-0" - } - }, - "node_modules/babel-preset-expo": { - "version": "55.0.18", - "resolved": "https://registry.npmjs.org/babel-preset-expo/-/babel-preset-expo-55.0.18.tgz", - "integrity": "sha512-zmDwKxCFBTe4e/jQXuITRUZlbl8HTZOhsUlwcHGjwEUB0lKQfRdaSYXZckQ+jMOBC34MrOl3Cs7/6F6vNbj5Pw==", - "license": "MIT", - "dependencies": { - "@babel/generator": "^7.20.5", - "@babel/helper-module-imports": "^7.25.9", - "@babel/plugin-proposal-decorators": "^7.12.9", - "@babel/plugin-proposal-export-default-from": "^7.24.7", - "@babel/plugin-syntax-export-default-from": "^7.24.7", - "@babel/plugin-transform-class-static-block": "^7.27.1", - "@babel/plugin-transform-export-namespace-from": "^7.25.9", - "@babel/plugin-transform-flow-strip-types": "^7.25.2", - "@babel/plugin-transform-modules-commonjs": "^7.24.8", - "@babel/plugin-transform-object-rest-spread": "^7.24.7", - "@babel/plugin-transform-parameters": "^7.24.7", - "@babel/plugin-transform-private-methods": "^7.24.7", - "@babel/plugin-transform-private-property-in-object": "^7.24.7", - "@babel/plugin-transform-runtime": "^7.24.7", - "@babel/preset-react": "^7.22.15", - "@babel/preset-typescript": "^7.23.0", - "@react-native/babel-preset": "0.83.6", - "babel-plugin-react-compiler": "^1.0.0", - "babel-plugin-react-native-web": "~0.21.0", - "babel-plugin-syntax-hermes-parser": "^0.32.0", - "babel-plugin-transform-flow-enums": "^0.0.2", - "debug": "^4.3.4", - "resolve-from": "^5.0.0" - }, - "peerDependencies": { - "@babel/runtime": "^7.20.0", - "expo": "*", - "expo-widgets": "^55.0.14", - "react-refresh": ">=0.14.0 <1.0.0" - }, - "peerDependenciesMeta": { - "@babel/runtime": { - "optional": true - }, - "expo": { - "optional": true - }, - "expo-widgets": { - "optional": true - } - } - }, - "node_modules/babel-preset-expo/node_modules/@react-native/babel-plugin-codegen": { - "version": "0.83.6", - "resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.83.6.tgz", - "integrity": "sha512-qfRXsHGeucT5c6mK+8Q7v4Ly3zmygfVmFlEtkiq7q07W1OTreld6nib4rJ/DBEeNiKBoBTuHjWliYGNuDjLFQA==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.25.3", - "@react-native/codegen": "0.83.6" - }, - "engines": { - "node": ">= 20.19.4" - } - }, - "node_modules/babel-preset-expo/node_modules/@react-native/babel-preset": { - "version": "0.83.6", - "resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.83.6.tgz", - "integrity": "sha512-4/fXFDUvGOObETZq4+SUFkafld6OGgQWut5cQiqVghlhCB5z/p2lVhPgEUr/aTxTzeS3AmN+ztC+GpYPQ7tsTw==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.25.2", - "@babel/plugin-proposal-export-default-from": "^7.24.7", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-default-from": "^7.24.7", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-transform-arrow-functions": "^7.24.7", - "@babel/plugin-transform-async-generator-functions": "^7.25.4", - "@babel/plugin-transform-async-to-generator": "^7.24.7", - "@babel/plugin-transform-block-scoping": "^7.25.0", - "@babel/plugin-transform-class-properties": "^7.25.4", - "@babel/plugin-transform-classes": "^7.25.4", - "@babel/plugin-transform-computed-properties": "^7.24.7", - "@babel/plugin-transform-destructuring": "^7.24.8", - "@babel/plugin-transform-flow-strip-types": "^7.25.2", - "@babel/plugin-transform-for-of": "^7.24.7", - "@babel/plugin-transform-function-name": "^7.25.1", - "@babel/plugin-transform-literals": "^7.25.2", - "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", - "@babel/plugin-transform-modules-commonjs": "^7.24.8", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", - "@babel/plugin-transform-numeric-separator": "^7.24.7", - "@babel/plugin-transform-object-rest-spread": "^7.24.7", - "@babel/plugin-transform-optional-catch-binding": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.8", - "@babel/plugin-transform-parameters": "^7.24.7", - "@babel/plugin-transform-private-methods": "^7.24.7", - "@babel/plugin-transform-private-property-in-object": "^7.24.7", - "@babel/plugin-transform-react-display-name": "^7.24.7", - "@babel/plugin-transform-react-jsx": "^7.25.2", - "@babel/plugin-transform-react-jsx-self": "^7.24.7", - "@babel/plugin-transform-react-jsx-source": "^7.24.7", - "@babel/plugin-transform-regenerator": "^7.24.7", - "@babel/plugin-transform-runtime": "^7.24.7", - "@babel/plugin-transform-shorthand-properties": "^7.24.7", - "@babel/plugin-transform-spread": "^7.24.7", - "@babel/plugin-transform-sticky-regex": "^7.24.7", - "@babel/plugin-transform-typescript": "^7.25.2", - "@babel/plugin-transform-unicode-regex": "^7.24.7", - "@babel/template": "^7.25.0", - "@react-native/babel-plugin-codegen": "0.83.6", - "babel-plugin-syntax-hermes-parser": "0.32.0", - "babel-plugin-transform-flow-enums": "^0.0.2", - "react-refresh": "^0.14.0" + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "license": "MIT", + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" }, "engines": { - "node": ">= 20.19.4" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, "peerDependencies": { - "@babel/core": "*" + "@babel/core": "^7.8.0" } }, - "node_modules/babel-preset-expo/node_modules/@react-native/babel-preset/node_modules/babel-plugin-syntax-hermes-parser": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.32.0.tgz", - "integrity": "sha512-m5HthL++AbyeEA2FcdwOLfVFvWYECOBObLHNqdR8ceY4TsEdn4LdX2oTvbB2QJSSElE2AWA/b2MXZ/PF/CqLZg==", + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", "license": "MIT", "dependencies": { - "hermes-parser": "0.32.0" + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/babel-preset-expo/node_modules/@react-native/babel-preset/node_modules/hermes-parser": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.32.0.tgz", - "integrity": "sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw==", + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.17", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz", + "integrity": "sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==", "license": "MIT", "dependencies": { - "hermes-estree": "0.32.0" + "@babel/compat-data": "^7.28.6", + "@babel/helper-define-polyfill-provider": "^0.6.8", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/babel-preset-expo/node_modules/@react-native/codegen": { - "version": "0.83.6", - "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.83.6.tgz", - "integrity": "sha512-doB/Pq6Cf6IjF3wlQXTIiZOnsX9X8mEEk+CdGfyuCwZjWrf7IB8KaZEXXckJmfUcIwvJ9u/a72ZoTTCIoxAc9A==", + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz", + "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==", "license": "MIT", "dependencies": { - "@babel/core": "^7.25.2", - "@babel/parser": "^7.25.3", - "glob": "^7.1.1", - "hermes-parser": "0.32.0", - "invariant": "^2.2.4", - "nullthrows": "^1.1.1", - "yargs": "^17.6.2" + "@babel/helper-define-polyfill-provider": "^0.6.5", + "core-js-compat": "^3.43.0" }, - "engines": { - "node": ">= 20.19.4" + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.8.tgz", + "integrity": "sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==", + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.8" }, "peerDependencies": { - "@babel/core": "*" + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/babel-preset-expo/node_modules/@react-native/codegen/node_modules/hermes-parser": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.32.0.tgz", - "integrity": "sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw==", + "node_modules/babel-plugin-react-compiler": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-react-compiler/-/babel-plugin-react-compiler-1.0.0.tgz", + "integrity": "sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw==", "license": "MIT", "dependencies": { - "hermes-estree": "0.32.0" + "@babel/types": "^7.26.0" } }, - "node_modules/babel-preset-expo/node_modules/babel-plugin-syntax-hermes-parser": { - "version": "0.32.1", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.32.1.tgz", - "integrity": "sha512-HgErPZTghW76Rkq9uqn5ESeiD97FbqpZ1V170T1RG2RDp+7pJVQV2pQJs7y5YzN0/gcT6GM5ci9apRnIwuyPdQ==", + "node_modules/babel-plugin-react-native-web": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/babel-plugin-react-native-web/-/babel-plugin-react-native-web-0.21.2.tgz", + "integrity": "sha512-SPD0J6qjJn8231i0HZhlAGH6NORe+QvRSQM2mwQEzJ2Fb3E4ruWTiiicPlHjmeWShDXLcvoorOCXjeR7k/lyWA==", + "license": "MIT" + }, + "node_modules/babel-plugin-transform-flow-enums": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-enums/-/babel-plugin-transform-flow-enums-0.0.2.tgz", + "integrity": "sha512-g4aaCrDDOsWjbm0PUUeVnkcVd6AKJsVc/MbnPhEotEpkeJQP6b8nzewohQi7+QS8UyPehOhGWn0nOwjvWpmMvQ==", "license": "MIT", "dependencies": { - "hermes-parser": "0.32.1" + "@babel/plugin-syntax-flow": "^7.12.1" } }, - "node_modules/babel-preset-expo/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob 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 exorbitant rates) by contacting i@izs.me", - "license": "ISC", + "node_modules/babel-preset-current-node-syntax": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", + "license": "MIT", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" } }, - "node_modules/babel-preset-expo/node_modules/hermes-estree": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.32.0.tgz", - "integrity": "sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ==", - "license": "MIT" - }, "node_modules/babel-preset-jest": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", @@ -6616,19 +6003,6 @@ "node": ">= 10" } }, - "node_modules/comment-json": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.6.2.tgz", - "integrity": "sha512-R2rze/hDX30uul4NZoIZ76ImSJLFxn/1/ZxtKC1L77y2X1k+yYu1joKbAtMA2Fg3hZrTOiw0I5mwVMo0cf250w==", - "license": "MIT", - "dependencies": { - "array-timsort": "^1.0.3", - "esprima": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/compressible": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", @@ -7067,9 +6441,9 @@ "license": "MIT" }, "node_modules/dnssd-advertise": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/dnssd-advertise/-/dnssd-advertise-1.1.4.tgz", - "integrity": "sha512-AmGyK9WpNf06WeP5TjHZq/wNzP76OuEeaiTlKr9E/EEelYLczywUKoqRz+DPRq/ErssjT4lU+/W7wzJW+7K/ZA==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/dnssd-advertise/-/dnssd-advertise-1.1.6.tgz", + "integrity": "sha512-Ndrrf6BMPalkQPd/zubL+4YghH2J9NspapQ09uDXwYbvOPkP0oaqf5CkcwJ0b50kS2O3ul6yVu+jz+RY62Cejg==", "license": "MIT" }, "node_modules/doctrine": { @@ -7567,16 +6941,16 @@ } }, "node_modules/eslint-config-expo": { - "version": "55.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-expo/-/eslint-config-expo-55.0.0.tgz", - "integrity": "sha512-YvhaKrp1g7pR/qjdI12E5nw9y0DJZWgYr815vyW8wskGLsFvxATY3mtKL8zm3ZYzWj3Bvc37tRIS661TEkrv9A==", + "version": "55.0.1", + "resolved": "https://registry.npmjs.org/eslint-config-expo/-/eslint-config-expo-55.0.1.tgz", + "integrity": "sha512-sfVHQ0GKZofMA0zUu5lah7PJ2LdcQwExGK+BLM9YvVPMRf6kezFYCpC6atT1ddX3L9oeCxS6FygDXbvCwjocNg==", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/eslint-plugin": "^8.18.2", "@typescript-eslint/parser": "^8.18.2", "eslint-import-resolver-typescript": "^3.6.3", - "eslint-plugin-expo": "^1.0.0", + "eslint-plugin-expo": "^1.0.3", "eslint-plugin-import": "^2.30.0", "eslint-plugin-react": "^7.37.3", "eslint-plugin-react-hooks": "^5.1.0", @@ -7685,9 +7059,9 @@ } }, "node_modules/eslint-plugin-expo": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-expo/-/eslint-plugin-expo-1.0.0.tgz", - "integrity": "sha512-qLtunR+cNFtC+jwYCBia5c/PJurMjSLMOV78KrEOyQK02ohZapU4dCFFnS2hfrJuw0zxfsjVkjqg3QBqi933QA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-expo/-/eslint-plugin-expo-1.0.3.tgz", + "integrity": "sha512-C1v9NPvpDET36+7Klpp/+53Jl+VzOfpbDxpKtL/pAPhCDwTX0kW6Swo425PT0uc4AMT5jpQbB7hSKFjKOGMl4A==", "dev": true, "license": "MIT", "dependencies": { @@ -7918,34 +7292,34 @@ } }, "node_modules/expo": { - "version": "55.0.17", - "resolved": "https://registry.npmjs.org/expo/-/expo-55.0.17.tgz", - "integrity": "sha512-yVF2phiPw5XgOCedC/oQaL3j0XbwzsBLst3JiAF8bi9aFlxLOVvuDEM8BDg3E09XGSLaGCAclY4q5L+sFerXlQ==", + "version": "55.0.26", + "resolved": "https://registry.npmjs.org/expo/-/expo-55.0.26.tgz", + "integrity": "sha512-MuVW6Uzd/Jh6E37ICOYAiTOm9nflNMUNzf6wH5ld/IXFyuF2Lo86a8fCSMgHcvTGsSjRsJ5Uxhf+WHZcvGPfrg==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.20.0", - "@expo/cli": "55.0.26", - "@expo/config": "~55.0.15", - "@expo/config-plugins": "~55.0.8", - "@expo/devtools": "55.0.2", - "@expo/fingerprint": "0.16.6", - "@expo/local-build-cache-provider": "55.0.11", - "@expo/log-box": "55.0.11", - "@expo/metro": "~55.1.0", - "@expo/metro-config": "55.0.17", + "@expo/cli": "55.0.32", + "@expo/config": "~55.0.17", + "@expo/config-plugins": "~55.0.10", + "@expo/devtools": "55.0.3", + "@expo/fingerprint": "0.16.7", + "@expo/local-build-cache-provider": "55.0.13", + "@expo/log-box": "55.0.12", + "@expo/metro": "~55.1.1", + "@expo/metro-config": "55.0.23", "@expo/vector-icons": "^15.0.2", "@ungap/structured-clone": "^1.3.0", - "babel-preset-expo": "~55.0.18", - "expo-asset": "~55.0.16", - "expo-constants": "~55.0.15", - "expo-file-system": "~55.0.17", - "expo-font": "~55.0.6", - "expo-keep-awake": "~55.0.6", - "expo-modules-autolinking": "55.0.18", - "expo-modules-core": "55.0.23", + "babel-preset-expo": "~55.0.22", + "expo-asset": "~55.0.17", + "expo-constants": "~55.0.16", + "expo-file-system": "~55.0.22", + "expo-font": "~55.0.8", + "expo-keep-awake": "~55.0.8", + "expo-modules-autolinking": "55.0.24", + "expo-modules-core": "55.0.25", "pretty-format": "^29.7.0", "react-refresh": "^0.14.2", - "whatwg-url-minimum": "^0.1.1" + "whatwg-url-minimum": "^0.1.2" }, "bin": { "expo": "bin/cli", @@ -7971,21 +7345,6 @@ } } }, - "node_modules/expo-asset": { - "version": "55.0.16", - "resolved": "https://registry.npmjs.org/expo-asset/-/expo-asset-55.0.16.tgz", - "integrity": "sha512-5IJyfJtYqvKGg04NKGQWiCIoK/fULDL9m15mXPPyfabD1jsToVj2hnWmo1r2SWNNmMwtQxi6jTpcGwVo2nLDxg==", - "license": "MIT", - "dependencies": { - "@expo/image-utils": "^0.8.13", - "expo-constants": "~55.0.15" - }, - "peerDependencies": { - "expo": "*", - "react": "*", - "react-native": "*" - } - }, "node_modules/expo-blur": { "version": "55.0.14", "resolved": "https://registry.npmjs.org/expo-blur/-/expo-blur-55.0.14.tgz", @@ -7998,12 +7357,12 @@ } }, "node_modules/expo-constants": { - "version": "55.0.15", - "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-55.0.15.tgz", - "integrity": "sha512-w394fcZLJjeKN+9ZnJzL/HiarE1nwZFDa+3S9frevh6Ur+MAAs9QDrcXhDrV8T3xqRzzYaqsP6Z8TFZ4efWN1A==", + "version": "55.0.16", + "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-55.0.16.tgz", + "integrity": "sha512-Z15/No94UHoogD+pulxjudGAeOHTEIWZgb/vnX48Wx5D+apWTeCbnKxQZZtGQlosvduYL5kaic2/W8U+NHfBQQ==", "license": "MIT", "dependencies": { - "@expo/env": "~2.1.1" + "@expo/env": "~2.1.2" }, "peerDependencies": { "expo": "*", @@ -8011,15 +7370,15 @@ } }, "node_modules/expo-dev-client": { - "version": "55.0.28", - "resolved": "https://registry.npmjs.org/expo-dev-client/-/expo-dev-client-55.0.28.tgz", - "integrity": "sha512-QZK6Ylx8Jg7lhOOHCxwC10g+i34ggMBAqV497JXFqla1tuuYiEw1poNJS5pD/60ZLe8kyy5PYPB4E9ezDHA9yQ==", + "version": "55.0.35", + "resolved": "https://registry.npmjs.org/expo-dev-client/-/expo-dev-client-55.0.35.tgz", + "integrity": "sha512-DN50x9gqWYAfnJpxgiJm3zK2bFvDhxJ5JjFq0wFot7o4knZ7H3BVwiL6zZMHG29g6gfxdgpzGG69WPiSR/Ipgg==", "license": "MIT", "dependencies": { - "expo-dev-launcher": "55.0.29", - "expo-dev-menu": "55.0.24", + "expo-dev-launcher": "55.0.36", + "expo-dev-menu": "55.0.30", "expo-dev-menu-interface": "55.0.2", - "expo-manifests": "~55.0.16", + "expo-manifests": "~55.0.17", "expo-updates-interface": "~55.1.6" }, "peerDependencies": { @@ -8027,23 +7386,23 @@ } }, "node_modules/expo-dev-launcher": { - "version": "55.0.29", - "resolved": "https://registry.npmjs.org/expo-dev-launcher/-/expo-dev-launcher-55.0.29.tgz", - "integrity": "sha512-Rusz6VfVUAXPArkQhnxC5yY70RCfGNZv+06qCGIkm2boQ3wOiSUwJic8oIt7kW6yD2rkpm24q/7F/6r5joPfng==", + "version": "55.0.36", + "resolved": "https://registry.npmjs.org/expo-dev-launcher/-/expo-dev-launcher-55.0.36.tgz", + "integrity": "sha512-Dn2om4J71aavWqi1jLzK3QlGZjDiFv7nIBZkQyzy2zW62IOD9kLwOOvHHj07Ra/6n9cqFEpNYzwpPkR7KHuYZA==", "license": "MIT", "dependencies": { - "@expo/schema-utils": "^55.0.3", - "expo-dev-menu": "55.0.24", - "expo-manifests": "~55.0.16" + "@expo/schema-utils": "^55.0.4", + "expo-dev-menu": "55.0.30", + "expo-manifests": "~55.0.17" }, "peerDependencies": { "expo": "*" } }, "node_modules/expo-dev-menu": { - "version": "55.0.24", - "resolved": "https://registry.npmjs.org/expo-dev-menu/-/expo-dev-menu-55.0.24.tgz", - "integrity": "sha512-/J93rADODlKpmaN9uywTd/RMywPDeUo/bAnrZNxlHrFUuO1VCGqYLhacITg2zebU8hucaou8pa8zVsTQaUCv6w==", + "version": "55.0.30", + "resolved": "https://registry.npmjs.org/expo-dev-menu/-/expo-dev-menu-55.0.30.tgz", + "integrity": "sha512-uwDI4cEPzpRemf06Ts5O41azJcz8BBcE6QOkNaTX8JlzdJ05eq9jWxmbA1WhoSoE5C+NFo8njHSvmHqUqTpOng==", "license": "MIT", "dependencies": { "expo-dev-menu-interface": "55.0.2" @@ -8077,9 +7436,9 @@ "license": "MIT" }, "node_modules/expo-file-system": { - "version": "55.0.17", - "resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-55.0.17.tgz", - "integrity": "sha512-d27K1cagUOt2BwxwPka9KW8Znu5kN1tnairozCzzCRZviZFtWnBxwFuJ3KU6MAbav/9UhSMkp5Ve/oZ+SR0UgQ==", + "version": "55.0.22", + "resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-55.0.22.tgz", + "integrity": "sha512-T5Rfv3vqcFyhVrl/tEEeglc/J8LJbcZQgC3TMT5jxzIgUgWmIgJEgncGYqB/YNXFgUTL2LiuCvqrU51Dzp83NQ==", "license": "MIT", "peerDependencies": { "expo": "*", @@ -8087,9 +7446,9 @@ } }, "node_modules/expo-font": { - "version": "55.0.6", - "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-55.0.6.tgz", - "integrity": "sha512-x9czUA3UQWjIwa0ZUEs/eWJNqB4mAue/m4ltESlNPLZhHL0nWWqIfsyHmklTLFH7mVfcHSJvew6k+pR2FE1zVw==", + "version": "55.0.8", + "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-55.0.8.tgz", + "integrity": "sha512-WyP75pnKqhLNktYwDn3xKAUNt5rLihRDv8XWGhhz6VEhVqypixpT86NA3uGtiDTlM3gGjhrYCY7o7ypXgCUOZg==", "license": "MIT", "dependencies": { "fontfaceobserver": "^2.1.0" @@ -8101,9 +7460,9 @@ } }, "node_modules/expo-glass-effect": { - "version": "55.0.10", - "resolved": "https://registry.npmjs.org/expo-glass-effect/-/expo-glass-effect-55.0.10.tgz", - "integrity": "sha512-5kL/jATvgJWdrqPdxixrECJqD2l8cfQ4ALr1DK7qi9XkyI97ejXvUjB2VsfEePNy3Fg+/VwzA3n3L7Nv3tAPkw==", + "version": "55.0.11", + "resolved": "https://registry.npmjs.org/expo-glass-effect/-/expo-glass-effect-55.0.11.tgz", + "integrity": "sha512-wqq7GUOqSkfoFJzreZvBG0jzjsq5c582m3glhWSjcmIuByxXXWp6j6GY6hyFuYKzpOXhbuvusVxGCQi0yWnp3g==", "license": "MIT", "peerDependencies": { "expo": "*", @@ -8121,9 +7480,9 @@ } }, "node_modules/expo-image": { - "version": "55.0.9", - "resolved": "https://registry.npmjs.org/expo-image/-/expo-image-55.0.9.tgz", - "integrity": "sha512-+NVgWv+tr7a6EpBEaIIVVp+XfruRA2JL5xOxvd6ajvFGdH0rOhagwX1m1piAII6w7sh6uAnBr8X+fDZsav7B2w==", + "version": "55.0.11", + "resolved": "https://registry.npmjs.org/expo-image/-/expo-image-55.0.11.tgz", + "integrity": "sha512-PVIBYQJW/h1f6Zb9xnoWlgfqyOPVm2yb6eo6ZogaKbvMrhb/Q/fiERbagi4oqmR6IPljWPEpkXXQyFBUh7TjpQ==", "license": "MIT", "dependencies": { "sf-symbols-typescript": "^2.2.0" @@ -8146,23 +7505,13 @@ "integrity": "sha512-QJMOZOPOG7CTnKcrdVaiummn2va1MCO56z++eyWkDv3GBRODldM6MFMDf/jTREWthFc2Nxo6TuyWRrEV9S6n/Q==", "license": "MIT" }, - "node_modules/expo-keep-awake": { - "version": "55.0.6", - "resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-55.0.6.tgz", - "integrity": "sha512-acJjeHqkNxMVckEcJhGQeIksqqsarscSHJtT559bNgyiM4r14dViQ66su7bb6qDVeBt0K7z3glXI1dHVck1Zgg==", - "license": "MIT", - "peerDependencies": { - "expo": "*", - "react": "*" - } - }, "node_modules/expo-linking": { - "version": "55.0.14", - "resolved": "https://registry.npmjs.org/expo-linking/-/expo-linking-55.0.14.tgz", - "integrity": "sha512-ZSqOvJyEquf04M5/ZpQo2diK9QRnNrzgqZo7p8gzxaPPHxP6IyUJnmcd12qT+dTxnRTVmUpxFQVHHWbvwPNIwQ==", + "version": "55.0.15", + "resolved": "https://registry.npmjs.org/expo-linking/-/expo-linking-55.0.15.tgz", + "integrity": "sha512-/RQh2vkNqV8Bim9Owm/evVqn2fqTvCDYHkpYPoSKbLAdydSGdHC2xZNw7Odl4wu1i1/3L4Xz//LKd3NsPWYWBQ==", "license": "MIT", "dependencies": { - "expo-constants": "~55.0.15", + "expo-constants": "~55.0.16", "invariant": "^2.2.4" }, "peerDependencies": { @@ -8170,22 +7519,35 @@ "react-native": "*" } }, + "node_modules/expo-localization": { + "version": "55.0.15", + "resolved": "https://registry.npmjs.org/expo-localization/-/expo-localization-55.0.15.tgz", + "integrity": "sha512-+HD55LeeIWyVRLvpQ909Am89XS16dUBkbB4/ruCJXS9oWv1K8W+FoXuOPTpmdvwHfC9cxt0loiwPWUiw2fdgbg==", + "license": "MIT", + "dependencies": { + "rtl-detect": "^1.0.2" + }, + "peerDependencies": { + "expo": "*", + "react": "*" + } + }, "node_modules/expo-location": { - "version": "55.1.8", - "resolved": "https://registry.npmjs.org/expo-location/-/expo-location-55.1.8.tgz", - "integrity": "sha512-mEExFf84nmWLwi14GFfUsFLrCm10gbcqFn9EPXpuruQ28YMtJWgCD+jJtESYPQkYF44N21fVok3T28fLuCqydA==", + "version": "55.1.10", + "resolved": "https://registry.npmjs.org/expo-location/-/expo-location-55.1.10.tgz", + "integrity": "sha512-MkcFucsZ567Bn8ChElVTYVbOs2QXn27IKaBrVKogw7ZcbooImdj3L/UR6E7s3LkgF33YubKynAp9Opvixdwl7g==", "license": "MIT", "dependencies": { - "@expo/image-utils": "^0.8.13" + "@expo/image-utils": "^0.8.14" }, "peerDependencies": { "expo": "*" } }, "node_modules/expo-manifests": { - "version": "55.0.16", - "resolved": "https://registry.npmjs.org/expo-manifests/-/expo-manifests-55.0.16.tgz", - "integrity": "sha512-BR9BPcNsSnCKlQ/d7ECywr+2T54+bTSr26HjRjSua949o4mO/iPIrLjK0lOAa1oIczju6a6oUFckZD2OljxP0g==", + "version": "55.0.17", + "resolved": "https://registry.npmjs.org/expo-manifests/-/expo-manifests-55.0.17.tgz", + "integrity": "sha512-vKZvFivX3usVJKfBODKQcFHso0g38zlGbRGqGAppz+il0zKvG6umpJ47OZbzLod7iJpjd+ZDD2AGuOxacixonA==", "license": "MIT", "dependencies": { "expo-json-utils": "~55.0.2" @@ -8195,12 +7557,12 @@ } }, "node_modules/expo-modules-autolinking": { - "version": "55.0.18", - "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-55.0.18.tgz", - "integrity": "sha512-olGTCWYkwVPj/momcgnF+z8MTzurGNFjopqPztQ4F53UkGPJnOFEuaM2/z4KbZtKbwHqeBv34OA5hxZP8uLdaQ==", + "version": "55.0.24", + "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-55.0.24.tgz", + "integrity": "sha512-A0OyMbTPZqibYrwqj98HFYTNSvl4NSS4Zt+R5A8qiAx3nM0mc81e6Iqw7Wl4J8M/t36lJ+cT3WuVTz5Oszj6Hw==", "license": "MIT", "dependencies": { - "@expo/require-utils": "^55.0.4", + "@expo/require-utils": "^55.0.5", "@expo/spawn-async": "^1.7.2", "chalk": "^4.1.0", "commander": "^7.2.0" @@ -8210,13 +7572,13 @@ } }, "node_modules/expo-router": { - "version": "55.0.13", - "resolved": "https://registry.npmjs.org/expo-router/-/expo-router-55.0.13.tgz", - "integrity": "sha512-cIBR5RmQtbr+b535mlbMhmm7lweVZXFtjzJOgJTutoxIApRztl816kFRFNesnVyqQ0LZrEU0a6vqa3i0wdlRQw==", + "version": "55.0.16", + "resolved": "https://registry.npmjs.org/expo-router/-/expo-router-55.0.16.tgz", + "integrity": "sha512-xVwWsDz3Ar2+3hRpMMrZMYFzkJak322vCA5/XCP7WOL0hEXnWhgQGhv5IEYZyz/TXZbl2IYD6/1MnH9mBhjwKQ==", "license": "MIT", "dependencies": { - "@expo/metro-runtime": "^55.0.10", - "@expo/schema-utils": "^55.0.3", + "@expo/metro-runtime": "^55.0.11", + "@expo/schema-utils": "^55.0.4", "@radix-ui/react-slot": "^1.2.0", "@radix-ui/react-tabs": "^1.1.12", "@react-navigation/bottom-tabs": "^7.15.5", @@ -8225,10 +7587,10 @@ "client-only": "^0.0.1", "debug": "^4.3.4", "escape-string-regexp": "^4.0.0", - "expo-glass-effect": "^55.0.10", - "expo-image": "^55.0.9", - "expo-server": "^55.0.8", - "expo-symbols": "^55.0.7", + "expo-glass-effect": "^55.0.11", + "expo-image": "^55.0.11", + "expo-server": "^55.0.11", + "expo-symbols": "^55.0.9", "fast-deep-equal": "^3.1.3", "invariant": "^2.2.4", "nanoid": "^3.3.8", @@ -8243,13 +7605,13 @@ "vaul": "^1.1.2" }, "peerDependencies": { - "@expo/log-box": "55.0.11", - "@expo/metro-runtime": "^55.0.10", + "@expo/log-box": "55.0.12", + "@expo/metro-runtime": "^55.0.11", "@react-navigation/drawer": "^7.9.4", "@testing-library/react-native": ">= 13.2.0", "expo": "*", - "expo-constants": "^55.0.15", - "expo-linking": "^55.0.14", + "expo-constants": "^55.0.16", + "expo-linking": "^55.0.15", "react": "*", "react-dom": "*", "react-native": "*", @@ -8297,30 +7659,30 @@ } }, "node_modules/expo-server": { - "version": "55.0.8", - "resolved": "https://registry.npmjs.org/expo-server/-/expo-server-55.0.8.tgz", - "integrity": "sha512-AoV5TKuO4biSzrhe/OVLyInfTT0pV9/OOc/g/oVq5vmCjL8SaSYTkES8PLt+67Tm7VqX+Dn0+kSx1nQcjEKaPw==", + "version": "55.0.11", + "resolved": "https://registry.npmjs.org/expo-server/-/expo-server-55.0.11.tgz", + "integrity": "sha512-AxRdHqcv0H1g4s923vu+5n1Nrhne23bjXbP+Vl7+Lwfpe7MG9PuU1IS95IJK6a+7BVV1mRN6QlZvs8Yv7EEXNQ==", "license": "MIT", "engines": { "node": ">=20.16.0" } }, "node_modules/expo-splash-screen": { - "version": "55.0.19", - "resolved": "https://registry.npmjs.org/expo-splash-screen/-/expo-splash-screen-55.0.19.tgz", - "integrity": "sha512-l8BWI/inLJW46Ojz5NgwvaM8LftrdXeFfZBUXhAoZxg44Qo2xKY76s0S1h3WIxWXT4sRKwK8YQzGr4k+zHubxQ==", + "version": "55.0.21", + "resolved": "https://registry.npmjs.org/expo-splash-screen/-/expo-splash-screen-55.0.21.tgz", + "integrity": "sha512-hFGEap69ggCckbHIdDXMe5rqfBR9TwcnY5gBhyaACUxU64w827T6prOQcIvLmAdv00kp3Gqt7hgE+mNn37EF+A==", "license": "MIT", "dependencies": { - "@expo/prebuild-config": "^55.0.16" + "@expo/prebuild-config": "^55.0.18" }, "peerDependencies": { "expo": "*" } }, "node_modules/expo-sqlite": { - "version": "55.0.15", - "resolved": "https://registry.npmjs.org/expo-sqlite/-/expo-sqlite-55.0.15.tgz", - "integrity": "sha512-vxE5fs6l953QSIyievQ8TuSstj62eC7zUREjNzbUOwRWaHGGnhnlPJM1HLoTIv+oIt3+b1m7k2fmcDGkpK5t3w==", + "version": "55.0.16", + "resolved": "https://registry.npmjs.org/expo-sqlite/-/expo-sqlite-55.0.16.tgz", + "integrity": "sha512-v6EIL4ygqWt/+ZfI76jIIv+IIaU8PnWPNjkmIN95vEQgh0FrWqzwssqe5ffQmm79kIfqIPTtAgTdl8MuZv88gg==", "license": "MIT", "dependencies": { "await-lock": "^2.2.2" @@ -8332,9 +7694,9 @@ } }, "node_modules/expo-status-bar": { - "version": "55.0.5", - "resolved": "https://registry.npmjs.org/expo-status-bar/-/expo-status-bar-55.0.5.tgz", - "integrity": "sha512-qb0c3rJO2b7CC0gUVGi1JYp92oLenWdYGyk8l4YQs6U+uaXUTPv6aaFa3KkT2HON10re3AxxPNJci8rsz6kPxg==", + "version": "55.0.6", + "resolved": "https://registry.npmjs.org/expo-status-bar/-/expo-status-bar-55.0.6.tgz", + "integrity": "sha512-ijOUptfdiqYt7rObZ6jrPQ8sE5YN/8MxKCIJx0b7TY4nGkSJxhPIxeoW4GXcXCA8mTQ9PiOHH/ThLZgRVZvUlQ==", "license": "MIT", "dependencies": { "react-native-is-edge-to-edge": "^1.2.1" @@ -8351,9 +7713,9 @@ "license": "MIT" }, "node_modules/expo-symbols": { - "version": "55.0.7", - "resolved": "https://registry.npmjs.org/expo-symbols/-/expo-symbols-55.0.7.tgz", - "integrity": "sha512-y4ALLbncSGQzhFLw1PaIBbO39xzaw3ie249HmK6zK/WLJYfw4Z/9UU4iPKO3KCE4FyCKIzd+yRsvzvlri23YrQ==", + "version": "55.0.9", + "resolved": "https://registry.npmjs.org/expo-symbols/-/expo-symbols-55.0.9.tgz", + "integrity": "sha512-F85C/8ExQjd2gYjasLVKMT8wPj+1+19TVTqg4jAeVjVZklqiQtLO72io9Ji1xAjYNgmDeUI0diVHlFMMTC4Ekg==", "license": "MIT", "dependencies": { "@expo-google-fonts/material-symbols": "^0.4.1", @@ -8367,9 +7729,9 @@ } }, "node_modules/expo-system-ui": { - "version": "55.0.16", - "resolved": "https://registry.npmjs.org/expo-system-ui/-/expo-system-ui-55.0.16.tgz", - "integrity": "sha512-LwFBpFzy7L4j0ZqHZaxNU4tewQXkH37N4afXu6ZrkyKsH9q5V3jOT1way/N+Hylgyx5+jGpzvae9OcphS/+iDQ==", + "version": "55.0.18", + "resolved": "https://registry.npmjs.org/expo-system-ui/-/expo-system-ui-55.0.18.tgz", + "integrity": "sha512-Fbc0HJgqMpABeA/gI7NJFnSXwUeLrEMjjXq8Nl+4gTXyacIK2iOOrzCkvq41rKBBde0CR6kVnB1DXj0j9ZYnjg==", "license": "MIT", "dependencies": { "@react-native/normalize-colors": "0.83.6", @@ -8387,19 +7749,19 @@ } }, "node_modules/expo-updates": { - "version": "55.0.21", - "resolved": "https://registry.npmjs.org/expo-updates/-/expo-updates-55.0.21.tgz", - "integrity": "sha512-wpWQAqNeBw1LLjqSK85/P9aHB+2R0nuuFPHb8ZRPRMJLhRUIk7IF0FaOdEy2NbiRJvrnGfRW3SK4NVQqrT8ULQ==", + "version": "55.0.24", + "resolved": "https://registry.npmjs.org/expo-updates/-/expo-updates-55.0.24.tgz", + "integrity": "sha512-aqbsRT5GyKG8++RndIb4+jFUknsPgqWImzYUG20PiPjwPlQ25MSfz5+r1IAI8YfvGuLRIIRt8yDQ2Ob+RV+fyg==", "license": "MIT", "dependencies": { "@expo/code-signing-certificates": "^0.0.6", - "@expo/plist": "^0.5.2", + "@expo/plist": "^0.5.4", "@expo/spawn-async": "^1.7.2", "arg": "^4.1.0", "chalk": "^4.1.2", "debug": "^4.3.4", "expo-eas-client": "~55.0.5", - "expo-manifests": "~55.0.16", + "expo-manifests": "~55.0.17", "expo-structured-headers": "~55.0.2", "expo-updates-interface": "~55.1.6", "getenv": "^2.0.0", @@ -8432,9 +7794,9 @@ "license": "MIT" }, "node_modules/expo-web-browser": { - "version": "55.0.14", - "resolved": "https://registry.npmjs.org/expo-web-browser/-/expo-web-browser-55.0.14.tgz", - "integrity": "sha512-bTDkBSQBnrlnYcM7Aak72AOvJuvdgA3M8p//Lazrm0Nfa77T9cRXzQ6KhLrB08V39n1+00d1dvuTWznJslkmdg==", + "version": "55.0.16", + "resolved": "https://registry.npmjs.org/expo-web-browser/-/expo-web-browser-55.0.16.tgz", + "integrity": "sha512-eeGs3439ewO/Q56Pzg3qbAVZSE0oH/R7XW9VCXI59k0m78ZIYbBtPT4PMFL/+sBgRkXm546Lq/DFcJQPTOfXJg==", "license": "MIT", "peerDependencies": { "expo": "*", @@ -8442,28 +7804,28 @@ } }, "node_modules/expo/node_modules/@expo/cli": { - "version": "55.0.26", - "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-55.0.26.tgz", - "integrity": "sha512-Ud9gpeGMF5RIL42LXvCw3k3mWK8rf/P2wu+Yrzz9Do1kcFKZeT9Vy2D/xukjdr/Xw+ELba87ThOot17GsPiWjw==", + "version": "55.0.32", + "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-55.0.32.tgz", + "integrity": "sha512-fq+/yUYBVw5ZudT4igNyJ3WaF17R39iS7EZlrkfHkLI7Y1kmUlivabwKviLoAfepJOKjKODKpViti9EPfmG3SQ==", "license": "MIT", "dependencies": { "@expo/code-signing-certificates": "^0.0.6", - "@expo/config": "~55.0.15", - "@expo/config-plugins": "~55.0.8", + "@expo/config": "~55.0.17", + "@expo/config-plugins": "~55.0.10", "@expo/devcert": "^1.2.1", - "@expo/env": "~2.1.1", - "@expo/image-utils": "^0.8.13", - "@expo/json-file": "^10.0.13", - "@expo/log-box": "55.0.11", - "@expo/metro": "~55.1.0", - "@expo/metro-config": "~55.0.17", - "@expo/osascript": "^2.4.2", - "@expo/package-manager": "^1.10.4", - "@expo/plist": "^0.5.2", - "@expo/prebuild-config": "^55.0.16", - "@expo/require-utils": "^55.0.4", - "@expo/router-server": "^55.0.15", - "@expo/schema-utils": "^55.0.3", + "@expo/env": "~2.1.2", + "@expo/image-utils": "^0.8.14", + "@expo/json-file": "^10.0.15", + "@expo/log-box": "55.0.12", + "@expo/metro": "~55.1.1", + "@expo/metro-config": "~55.0.23", + "@expo/osascript": "^2.4.4", + "@expo/package-manager": "^1.10.5", + "@expo/plist": "^0.5.4", + "@expo/prebuild-config": "^55.0.18", + "@expo/require-utils": "^55.0.5", + "@expo/router-server": "^55.0.18", + "@expo/schema-utils": "^55.0.4", "@expo/spawn-async": "^1.7.2", "@expo/ws-tunnel": "^1.0.1", "@expo/xcpretty": "^4.4.0", @@ -8479,7 +7841,7 @@ "connect": "^3.7.0", "debug": "^4.3.4", "dnssd-advertise": "^1.1.4", - "expo-server": "^55.0.8", + "expo-server": "^55.0.11", "fetch-nodeshim": "^0.4.10", "getenv": "^2.0.0", "glob": "^13.0.0", @@ -8523,20 +7885,20 @@ } }, "node_modules/expo/node_modules/@expo/cli/node_modules/@expo/router-server": { - "version": "55.0.15", - "resolved": "https://registry.npmjs.org/@expo/router-server/-/router-server-55.0.15.tgz", - "integrity": "sha512-6LksYO4Pg13qroL138KfUebt/x/EO07zVhdyT/nTgcxnpn6CS4ecTl3DciSKhxbaH+0BVLdANkxYeGdp43TMwQ==", + "version": "55.0.18", + "resolved": "https://registry.npmjs.org/@expo/router-server/-/router-server-55.0.18.tgz", + "integrity": "sha512-W0VsvIiR48OvdlAOUlag4qspGYT/DV4srfYowlbYxwZh5Qw0MjiZAID4Zt7F0qynGZZxx8OZPpFhIX7XsqtRmg==", "license": "MIT", "dependencies": { "debug": "^4.3.4" }, "peerDependencies": { - "@expo/metro-runtime": "^55.0.10", + "@expo/metro-runtime": "^55.0.11", "expo": "*", - "expo-constants": "^55.0.15", - "expo-font": "^55.0.6", + "expo-constants": "^55.0.16", + "expo-font": "^55.0.8", "expo-router": "*", - "expo-server": "^55.0.8", + "expo-server": "^55.0.11", "react": "*", "react-dom": "*", "react-server-dom-webpack": "~19.0.1 || ~19.1.2 || ~19.2.1" @@ -8557,69 +7919,155 @@ } }, "node_modules/expo/node_modules/@expo/cli/node_modules/ws": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", - "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", "license": "MIT", "engines": { "node": ">=10.0.0" }, "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/expo/node_modules/@expo/metro-config": { + "version": "55.0.23", + "resolved": "https://registry.npmjs.org/@expo/metro-config/-/metro-config-55.0.23.tgz", + "integrity": "sha512-Mkw3Ss/1LFlafH3iie3r9E13yKMyJgZqGTEkGviGf6LYp51eY5fR8ATbXrNsH69wVc2z+ty4lT/8lEA18YJv7g==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.20.0", + "@babel/core": "^7.20.0", + "@babel/generator": "^7.20.5", + "@expo/config": "~55.0.17", + "@expo/env": "~2.1.2", + "@expo/json-file": "~10.0.15", + "@expo/metro": "~55.1.1", + "@expo/spawn-async": "^1.7.2", + "browserslist": "^4.25.0", + "chalk": "^4.1.0", + "debug": "^4.3.2", + "getenv": "^2.0.0", + "glob": "^13.0.0", + "hermes-parser": "^0.32.0", + "jsc-safe-url": "^0.2.4", + "lightningcss": "^1.30.1", + "picomatch": "^4.0.3", + "postcss": "^8.5.14", + "resolve-from": "^5.0.0" + }, + "peerDependencies": { + "expo": "*" }, "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { + "expo": { "optional": true } } }, - "node_modules/expo/node_modules/@react-native/debugger-frontend": { + "node_modules/expo/node_modules/@react-native/babel-plugin-codegen": { "version": "0.83.6", - "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.83.6.tgz", - "integrity": "sha512-TyWXEpAjVundrc87fPWg91piOUg75+X9iutcfDe7cO3NrAEYCsl7Z09rKHuiAGkxfG9/rFD13dPsYIixUFkSFA==", - "license": "BSD-3-Clause", + "resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.83.6.tgz", + "integrity": "sha512-qfRXsHGeucT5c6mK+8Q7v4Ly3zmygfVmFlEtkiq7q07W1OTreld6nib4rJ/DBEeNiKBoBTuHjWliYGNuDjLFQA==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.3", + "@react-native/codegen": "0.83.6" + }, "engines": { "node": ">= 20.19.4" } }, - "node_modules/expo/node_modules/@react-native/debugger-shell": { + "node_modules/expo/node_modules/@react-native/babel-preset": { "version": "0.83.6", - "resolved": "https://registry.npmjs.org/@react-native/debugger-shell/-/debugger-shell-0.83.6.tgz", - "integrity": "sha512-684TJMBCU0l0ZjJWzrnK0HH+ERaM9KLyxyArE1k7BrP+gVl4X9GO0Pi94RoInOxvW/nyV65sOU6Ip1F3ygS0cg==", + "resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.83.6.tgz", + "integrity": "sha512-4/fXFDUvGOObETZq4+SUFkafld6OGgQWut5cQiqVghlhCB5z/p2lVhPgEUr/aTxTzeS3AmN+ztC+GpYPQ7tsTw==", "license": "MIT", "dependencies": { - "cross-spawn": "^7.0.6", - "fb-dotslash": "0.5.8" + "@babel/core": "^7.25.2", + "@babel/plugin-proposal-export-default-from": "^7.24.7", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-default-from": "^7.24.7", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-transform-arrow-functions": "^7.24.7", + "@babel/plugin-transform-async-generator-functions": "^7.25.4", + "@babel/plugin-transform-async-to-generator": "^7.24.7", + "@babel/plugin-transform-block-scoping": "^7.25.0", + "@babel/plugin-transform-class-properties": "^7.25.4", + "@babel/plugin-transform-classes": "^7.25.4", + "@babel/plugin-transform-computed-properties": "^7.24.7", + "@babel/plugin-transform-destructuring": "^7.24.8", + "@babel/plugin-transform-flow-strip-types": "^7.25.2", + "@babel/plugin-transform-for-of": "^7.24.7", + "@babel/plugin-transform-function-name": "^7.25.1", + "@babel/plugin-transform-literals": "^7.25.2", + "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", + "@babel/plugin-transform-modules-commonjs": "^7.24.8", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", + "@babel/plugin-transform-numeric-separator": "^7.24.7", + "@babel/plugin-transform-object-rest-spread": "^7.24.7", + "@babel/plugin-transform-optional-catch-binding": "^7.24.7", + "@babel/plugin-transform-optional-chaining": "^7.24.8", + "@babel/plugin-transform-parameters": "^7.24.7", + "@babel/plugin-transform-private-methods": "^7.24.7", + "@babel/plugin-transform-private-property-in-object": "^7.24.7", + "@babel/plugin-transform-react-display-name": "^7.24.7", + "@babel/plugin-transform-react-jsx": "^7.25.2", + "@babel/plugin-transform-react-jsx-self": "^7.24.7", + "@babel/plugin-transform-react-jsx-source": "^7.24.7", + "@babel/plugin-transform-regenerator": "^7.24.7", + "@babel/plugin-transform-runtime": "^7.24.7", + "@babel/plugin-transform-shorthand-properties": "^7.24.7", + "@babel/plugin-transform-spread": "^7.24.7", + "@babel/plugin-transform-sticky-regex": "^7.24.7", + "@babel/plugin-transform-typescript": "^7.25.2", + "@babel/plugin-transform-unicode-regex": "^7.24.7", + "@babel/template": "^7.25.0", + "@react-native/babel-plugin-codegen": "0.83.6", + "babel-plugin-syntax-hermes-parser": "0.32.0", + "babel-plugin-transform-flow-enums": "^0.0.2", + "react-refresh": "^0.14.0" }, "engines": { "node": ">= 20.19.4" + }, + "peerDependencies": { + "@babel/core": "*" } }, - "node_modules/expo/node_modules/@react-native/dev-middleware": { - "version": "0.83.6", - "resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.83.6.tgz", - "integrity": "sha512-22xoddLTelpcVnF385SNH2hdP7X2av5pu7yRl/WnM5jBznbcl0+M9Ce94cj+WVeomsoUF/vlfuB0Ooy+RMlRiA==", + "node_modules/expo/node_modules/@react-native/babel-preset/node_modules/babel-plugin-syntax-hermes-parser": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.32.0.tgz", + "integrity": "sha512-m5HthL++AbyeEA2FcdwOLfVFvWYECOBObLHNqdR8ceY4TsEdn4LdX2oTvbB2QJSSElE2AWA/b2MXZ/PF/CqLZg==", "license": "MIT", "dependencies": { - "@isaacs/ttlcache": "^1.4.1", - "@react-native/debugger-frontend": "0.83.6", - "@react-native/debugger-shell": "0.83.6", - "chrome-launcher": "^0.15.2", - "chromium-edge-launcher": "^0.2.0", - "connect": "^3.6.5", - "debug": "^4.4.0", - "invariant": "^2.2.4", - "nullthrows": "^1.1.1", - "open": "^7.0.3", - "serve-static": "^1.16.2", - "ws": "^7.5.10" - }, - "engines": { - "node": ">= 20.19.4" + "hermes-parser": "0.32.0" + } + }, + "node_modules/expo/node_modules/@react-native/babel-preset/node_modules/hermes-estree": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.32.0.tgz", + "integrity": "sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ==", + "license": "MIT" + }, + "node_modules/expo/node_modules/@react-native/babel-preset/node_modules/hermes-parser": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.32.0.tgz", + "integrity": "sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw==", + "license": "MIT", + "dependencies": { + "hermes-estree": "0.32.0" } }, "node_modules/expo/node_modules/accepts": { @@ -8635,6 +8083,63 @@ "node": ">= 0.6" } }, + "node_modules/expo/node_modules/babel-plugin-syntax-hermes-parser": { + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.32.1.tgz", + "integrity": "sha512-HgErPZTghW76Rkq9uqn5ESeiD97FbqpZ1V170T1RG2RDp+7pJVQV2pQJs7y5YzN0/gcT6GM5ci9apRnIwuyPdQ==", + "license": "MIT", + "dependencies": { + "hermes-parser": "0.32.1" + } + }, + "node_modules/expo/node_modules/babel-preset-expo": { + "version": "55.0.22", + "resolved": "https://registry.npmjs.org/babel-preset-expo/-/babel-preset-expo-55.0.22.tgz", + "integrity": "sha512-Se6kPnvCNN13jJVIa6JJvlmImVoVRzu9stagAbivCPcfrq2VNrsEiYpJZ1+H32kXinKW/y797/wctGuxPy0APw==", + "license": "MIT", + "dependencies": { + "@babel/generator": "^7.20.5", + "@babel/helper-module-imports": "^7.25.9", + "@babel/plugin-proposal-decorators": "^7.12.9", + "@babel/plugin-proposal-export-default-from": "^7.24.7", + "@babel/plugin-syntax-export-default-from": "^7.24.7", + "@babel/plugin-transform-class-static-block": "^7.27.1", + "@babel/plugin-transform-export-namespace-from": "^7.25.9", + "@babel/plugin-transform-flow-strip-types": "^7.25.2", + "@babel/plugin-transform-modules-commonjs": "^7.24.8", + "@babel/plugin-transform-object-rest-spread": "^7.24.7", + "@babel/plugin-transform-parameters": "^7.24.7", + "@babel/plugin-transform-private-methods": "^7.24.7", + "@babel/plugin-transform-private-property-in-object": "^7.24.7", + "@babel/plugin-transform-runtime": "^7.24.7", + "@babel/preset-react": "^7.22.15", + "@babel/preset-typescript": "^7.23.0", + "@react-native/babel-preset": "0.83.6", + "babel-plugin-react-compiler": "^1.0.0", + "babel-plugin-react-native-web": "~0.21.0", + "babel-plugin-syntax-hermes-parser": "^0.32.0", + "babel-plugin-transform-flow-enums": "^0.0.2", + "debug": "^4.3.4", + "resolve-from": "^5.0.0" + }, + "peerDependencies": { + "@babel/runtime": "^7.20.0", + "expo": "*", + "expo-widgets": "^55.0.19", + "react-refresh": ">=0.14.0 <1.0.0" + }, + "peerDependenciesMeta": { + "@babel/runtime": { + "optional": true + }, + "expo": { + "optional": true + }, + "expo-widgets": { + "optional": true + } + } + }, "node_modules/expo/node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", @@ -8650,10 +8155,35 @@ "node": ">=8" } }, + "node_modules/expo/node_modules/expo-asset": { + "version": "55.0.17", + "resolved": "https://registry.npmjs.org/expo-asset/-/expo-asset-55.0.17.tgz", + "integrity": "sha512-pK9HHJuFqjE8kDUcbMFsZj3Cz8WdXpvZHZmYl7ouFQp59P83BvHln6VnqPDGlO+/4929G0Lm8ZUzbONuNRhi9w==", + "license": "MIT", + "dependencies": { + "@expo/image-utils": "^0.8.14", + "expo-constants": "~55.0.16" + }, + "peerDependencies": { + "expo": "*", + "react": "*", + "react-native": "*" + } + }, + "node_modules/expo/node_modules/expo-keep-awake": { + "version": "55.0.8", + "resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-55.0.8.tgz", + "integrity": "sha512-PfIpMfM+STOBwkR5XOE+yVtER86c44MD+W8QD8JxuO0sT9pF7Y1SJYakWlpvX8xsGA+bjKLxftm9403s9kQhKA==", + "license": "MIT", + "peerDependencies": { + "expo": "*", + "react": "*" + } + }, "node_modules/expo/node_modules/expo-modules-core": { - "version": "55.0.23", - "resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-55.0.23.tgz", - "integrity": "sha512-IGWT5N9MoV4zgWyrv686bElnKhzhE7E6pSazhaBNh3vgViAah5nnAz2o5h5YoUMR2B+ZTdHumRbGHN6gHLgwPA==", + "version": "55.0.25", + "resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-55.0.25.tgz", + "integrity": "sha512-yXpfg7aHLbuqoXocK34Vua6Aey5SCyqLygAsXAMbul9P8vfBjLpaOPiTJ5cLVF7Drfq8ownqVJO6qpGEtZ6GOw==", "license": "MIT", "dependencies": { "invariant": "^2.2.4" @@ -8669,6 +8199,21 @@ } } }, + "node_modules/expo/node_modules/hermes-estree": { + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.32.1.tgz", + "integrity": "sha512-ne5hkuDxheNBAikDjqvCZCwihnz0vVu9YsBzAEO1puiyFR4F1+PAz/SiPHSsNTuOveCYGRMX8Xbx4LOubeC0Qg==", + "license": "MIT" + }, + "node_modules/expo/node_modules/hermes-parser": { + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.32.1.tgz", + "integrity": "sha512-175dz634X/W5AiwrpLdoMl/MOb17poLHyIqgyExlE8D9zQ1OPnoORnGMB5ltRKnpvQzBjMYvT2rN/sHeIfZW5Q==", + "license": "MIT", + "dependencies": { + "hermes-estree": "0.32.1" + } + }, "node_modules/expo/node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -8699,37 +8244,10 @@ "node": ">= 0.6" } }, - "node_modules/expo/node_modules/react-native-worklets": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/react-native-worklets/-/react-native-worklets-0.8.1.tgz", - "integrity": "sha512-oWP/lStsAHU6oYCaWDXrda/wOHVdhusQJz1e6x9gPnXdFf4ndNDAOtWCmk2zGrAnlapfyA3rM6PCQq94mPg9cw==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@babel/plugin-transform-arrow-functions": "^7.27.1", - "@babel/plugin-transform-class-properties": "^7.27.1", - "@babel/plugin-transform-classes": "^7.28.4", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", - "@babel/plugin-transform-optional-chaining": "^7.27.1", - "@babel/plugin-transform-shorthand-properties": "^7.27.1", - "@babel/plugin-transform-template-literals": "^7.27.1", - "@babel/plugin-transform-unicode-regex": "^7.27.1", - "@babel/preset-typescript": "^7.27.1", - "convert-source-map": "^2.0.0", - "semver": "^7.7.3" - }, - "peerDependencies": { - "@babel/core": "*", - "@react-native/metro-config": "*", - "react": "*", - "react-native": "0.81 - 0.85" - } - }, "node_modules/expo/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", + "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -9433,18 +8951,18 @@ "license": "MIT" }, "node_modules/hermes-estree": { - "version": "0.32.1", - "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.32.1.tgz", - "integrity": "sha512-ne5hkuDxheNBAikDjqvCZCwihnz0vVu9YsBzAEO1puiyFR4F1+PAz/SiPHSsNTuOveCYGRMX8Xbx4LOubeC0Qg==", + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.32.0.tgz", + "integrity": "sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ==", "license": "MIT" }, "node_modules/hermes-parser": { - "version": "0.32.1", - "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.32.1.tgz", - "integrity": "sha512-175dz634X/W5AiwrpLdoMl/MOb17poLHyIqgyExlE8D9zQ1OPnoORnGMB5ltRKnpvQzBjMYvT2rN/sHeIfZW5Q==", + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.32.0.tgz", + "integrity": "sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw==", "license": "MIT", "dependencies": { - "hermes-estree": "0.32.1" + "hermes-estree": "0.32.0" } }, "node_modules/hoist-non-react-statics": { @@ -10665,6 +10183,9 @@ "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -10685,6 +10206,9 @@ "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -10705,6 +10229,9 @@ "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -10725,6 +10252,9 @@ "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -11006,9 +10536,9 @@ } }, "node_modules/metro": { - "version": "0.83.6", - "resolved": "https://registry.npmjs.org/metro/-/metro-0.83.6.tgz", - "integrity": "sha512-pbdndsAZ2F/ceopDdhVbttpa/hfLzXPJ/husc+QvQ33R0D9UXJKzTn5+OzOXx4bpQNtAKF2bY88cCI3Zl44xDQ==", + "version": "0.83.7", + "resolved": "https://registry.npmjs.org/metro/-/metro-0.83.7.tgz", + "integrity": "sha512-SPaPEyvTsTmd0LpT7RaZciQyDw2i/JB7+iY9L5VfBo72+psescFxBqpI1TL9dnL+pmnfkU+l/J1mEEGLeF65EQ==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.29.0", @@ -11019,7 +10549,6 @@ "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "accepts": "^2.0.0", - "chalk": "^4.0.0", "ci-info": "^2.0.0", "connect": "^3.6.5", "debug": "^4.4.0", @@ -11032,18 +10561,18 @@ "jest-worker": "^29.7.0", "jsc-safe-url": "^0.2.2", "lodash.throttle": "^4.1.1", - "metro-babel-transformer": "0.83.6", - "metro-cache": "0.83.6", - "metro-cache-key": "0.83.6", - "metro-config": "0.83.6", - "metro-core": "0.83.6", - "metro-file-map": "0.83.6", - "metro-resolver": "0.83.6", - "metro-runtime": "0.83.6", - "metro-source-map": "0.83.6", - "metro-symbolicate": "0.83.6", - "metro-transform-plugins": "0.83.6", - "metro-transform-worker": "0.83.6", + "metro-babel-transformer": "0.83.7", + "metro-cache": "0.83.7", + "metro-cache-key": "0.83.7", + "metro-config": "0.83.7", + "metro-core": "0.83.7", + "metro-file-map": "0.83.7", + "metro-resolver": "0.83.7", + "metro-runtime": "0.83.7", + "metro-source-map": "0.83.7", + "metro-symbolicate": "0.83.7", + "metro-transform-plugins": "0.83.7", + "metro-transform-worker": "0.83.7", "mime-types": "^3.0.1", "nullthrows": "^1.1.1", "serialize-error": "^2.1.0", @@ -11060,15 +10589,15 @@ } }, "node_modules/metro-babel-transformer": { - "version": "0.83.6", - "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.83.6.tgz", - "integrity": "sha512-1AnuazBpzY3meRMr04WUw14kRBkV0W3Ez+AA75FAeNpRyWNN5S3M3PHLUbZw7IXq7ZeOzceyRsHStaFrnWd+8w==", + "version": "0.83.7", + "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.83.7.tgz", + "integrity": "sha512-sBqBkt6kNut/88bv+Ucvm4yqdPetbvAEsHzi3MAgJEifOSYYzX5Z5Kgw3TFOrwf/mHJTOBG2ONlaMHoyfP15TA==", "license": "MIT", "dependencies": { "@babel/core": "^7.25.2", "flow-enums-runtime": "^0.0.6", "hermes-parser": "0.35.0", - "metro-cache-key": "0.83.6", + "metro-cache-key": "0.83.7", "nullthrows": "^1.1.1" }, "engines": { @@ -11091,24 +10620,24 @@ } }, "node_modules/metro-cache": { - "version": "0.83.6", - "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.83.6.tgz", - "integrity": "sha512-DpvZE32feNkqfZkI4Fic7YI/Kw8QP9wdl1rC4YKPrA77wQbI9vXbxjmfkCT/EGwBTFOPKqvIXo+H3BNe93YyiQ==", + "version": "0.83.7", + "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.83.7.tgz", + "integrity": "sha512-E9SRePXQ1Zvlj79VcOk57q7VC7rMHMFQ+jhmPHBiq+dJ0bJB5BL87lWZF6oh5X76Cci5tpDuQNaDwwuSCToEeg==", "license": "MIT", "dependencies": { "exponential-backoff": "^3.1.1", "flow-enums-runtime": "^0.0.6", "https-proxy-agent": "^7.0.5", - "metro-core": "0.83.6" + "metro-core": "0.83.7" }, "engines": { "node": ">=20.19.4" } }, "node_modules/metro-cache-key": { - "version": "0.83.6", - "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.83.6.tgz", - "integrity": "sha512-5gdK4PVpgNOHi7xCGrgesNP1AuOA2TiPqpcirGXZi4RLLzX1VMowpkgTVtBfpQQCqWoosQF9yrSo9/KDQg1eBg==", + "version": "0.83.7", + "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.83.7.tgz", + "integrity": "sha512-W1c2Nmx8MiJTJt+eWhMO08z9VKi3kZOaz99IYGdqeqDgY9j+yZjXl62rUav4Di0heZfh4/n2s722PqRL1OODeg==", "license": "MIT", "dependencies": { "flow-enums-runtime": "^0.0.6" @@ -11118,18 +10647,18 @@ } }, "node_modules/metro-config": { - "version": "0.83.6", - "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.83.6.tgz", - "integrity": "sha512-G5622400uNtnAMlppEA5zkFAZltEf7DSGhOu09BkisCxOlVMWfdosD/oPyh4f2YVQsc1MBYyp4w6OzbExTYarg==", + "version": "0.83.7", + "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.83.7.tgz", + "integrity": "sha512-83mjWFbFOt2GeJ6pFIum5mSnc1uTsZJAtD8o4ej0s4NVsYsA7fB+pHvTfHhFrpeMONaobu2riKavkPei05Er/Q==", "license": "MIT", "dependencies": { "connect": "^3.6.5", "flow-enums-runtime": "^0.0.6", "jest-validate": "^29.7.0", - "metro": "0.83.6", - "metro-cache": "0.83.6", - "metro-core": "0.83.6", - "metro-runtime": "0.83.6", + "metro": "0.83.7", + "metro-cache": "0.83.7", + "metro-core": "0.83.7", + "metro-runtime": "0.83.7", "yaml": "^2.6.1" }, "engines": { @@ -11137,23 +10666,23 @@ } }, "node_modules/metro-core": { - "version": "0.83.6", - "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.83.6.tgz", - "integrity": "sha512-l+yQ2fuIgR//wszUlMrrAa9+Z+kbKazd0QOh0VQY7jC4ghb7yZBBSla/UMYRBZZ6fPg9IM+wD3+h+37a5f9etw==", + "version": "0.83.7", + "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.83.7.tgz", + "integrity": "sha512-6yn3w1wnltT6RQl7p7YES2l95ArC+mWrOssEiH8p5/DDrJS65/szf9LsC9JrBv8c5DdvSY3V3f0GRYg0Ox7hCg==", "license": "MIT", "dependencies": { "flow-enums-runtime": "^0.0.6", "lodash.throttle": "^4.1.1", - "metro-resolver": "0.83.6" + "metro-resolver": "0.83.7" }, "engines": { "node": ">=20.19.4" } }, "node_modules/metro-file-map": { - "version": "0.83.6", - "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.83.6.tgz", - "integrity": "sha512-Jg3oN604C7GWbQwFAUXt8KsbMXeKfsxbZ5HFy4XFM3ggTS+ja9QgUmq9B613kgXv3G4M6rwiI6cvh9TRly4x3w==", + "version": "0.83.7", + "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.83.7.tgz", + "integrity": "sha512-+j0F1m+FQYVAQ6syf+mwhIPV5GoFQrkInX8bppuc50IzNsZbMrp8R5H/Sx/K2daQ3YEa9F/XwkeZT8gzJfgeCw==", "license": "MIT", "dependencies": { "debug": "^4.4.0", @@ -11171,9 +10700,9 @@ } }, "node_modules/metro-minify-terser": { - "version": "0.83.6", - "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.83.6.tgz", - "integrity": "sha512-Vx3/Ne9Q+EIEDLfKzZUOtn/rxSNa/QjlYxc42nvK4Mg8mB6XUgd3LXX5ZZVq7lzQgehgEqLrbgShJPGfeF8PnQ==", + "version": "0.83.7", + "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.83.7.tgz", + "integrity": "sha512-MfJar2IS4tBRuLb9svwb0Gu5l9BsH+pcRm8eGcEi/wy8MzZinfinh5dFLt2nWkocnulIgtGB5NkFDdbXqMXKhQ==", "license": "MIT", "dependencies": { "flow-enums-runtime": "^0.0.6", @@ -11184,9 +10713,9 @@ } }, "node_modules/metro-resolver": { - "version": "0.83.6", - "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.83.6.tgz", - "integrity": "sha512-lAwR/FsT1uJ5iCt4AIsN3boKfJ88aN8bjvDT5FwBS0tKeKw4/sbdSTWlFxc7W/MUTN5RekJ3nQkJRIWsvs28tA==", + "version": "0.83.7", + "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.83.7.tgz", + "integrity": "sha512-WSJIENlMcoSsuz66IfBHOkgfp3KJt2UW2TnEHPf1b8pIG2eEXNOVmo2+03A0H17WY2XGXWgxL0CG7FAopqgB1A==", "license": "MIT", "dependencies": { "flow-enums-runtime": "^0.0.6" @@ -11196,9 +10725,9 @@ } }, "node_modules/metro-runtime": { - "version": "0.83.6", - "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.83.6.tgz", - "integrity": "sha512-WQPua1G2VgYbwRn6vSKxOhTX7CFbSf/JdUu6Nd8bZnPXckOf7HQ2y51NXNQHoEsiuawathrkzL8pBhv+zgZFmg==", + "version": "0.83.7", + "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.83.7.tgz", + "integrity": "sha512-9GKkJURaB2iyYoEExKnedzAHzxmKtSi+k0tsZUvMoU27tBZJElchYt7JH/Ai/XzYAI9lCAaV7u5HZSI8J5Z+wQ==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.25.0", @@ -11209,18 +10738,18 @@ } }, "node_modules/metro-source-map": { - "version": "0.83.6", - "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.83.6.tgz", - "integrity": "sha512-AqJbOMMpeyyM4iNI91pchqDIszzNuuHApEhg6OABqZ+9mjLEqzcIEQ/fboZ7x74fNU5DBd2K36FdUQYPqlGClA==", + "version": "0.83.7", + "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.83.7.tgz", + "integrity": "sha512-JgA1h7oc1a1jydBe1GhVFsUoMYo3wLPk7oRA32rjlDsq+sP2JLt9x2p2lWbNSxTm/u8NV4VRid3hvEJgcX8tKw==", "license": "MIT", "dependencies": { "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", - "metro-symbolicate": "0.83.6", + "metro-symbolicate": "0.83.7", "nullthrows": "^1.1.1", - "ob1": "0.83.6", + "ob1": "0.83.7", "source-map": "^0.5.6", "vlq": "^1.0.0" }, @@ -11229,14 +10758,14 @@ } }, "node_modules/metro-symbolicate": { - "version": "0.83.6", - "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.83.6.tgz", - "integrity": "sha512-4nvkmv9T7ozhprlPwk/+xm0SVPsxly5kYyMHdNaOlFemFz4df9BanvD46Ac6OISu/4Idinzfk2KVb++6OfzPAQ==", + "version": "0.83.7", + "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.83.7.tgz", + "integrity": "sha512-g4suyxw20WOHWI680c+Kq4wC/NF+Hx5pRH9afrMp+sMTxqLeKcPR1Xf4wMhsjlbvx7LbIREdke6q928jEjvJWw==", "license": "MIT", "dependencies": { "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", - "metro-source-map": "0.83.6", + "metro-source-map": "0.83.7", "nullthrows": "^1.1.1", "source-map": "^0.5.6", "vlq": "^1.0.0" @@ -11249,9 +10778,9 @@ } }, "node_modules/metro-transform-plugins": { - "version": "0.83.6", - "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.83.6.tgz", - "integrity": "sha512-V+zoY2Ul0v0BW6IokJkTud3raXmDdbdwkUQ/5eiSoy0jKuKMhrDjdH+H5buCS5iiJdNbykOn69Eip+Sqymkodg==", + "version": "0.83.7", + "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.83.7.tgz", + "integrity": "sha512-Ss0FpBiZDjX2kwhukMDl5sNdYK8T/06IPqxNE4H6PTlRlfs9q11cef13c/xESY/Pm4VCkp1yJUZO3kXzvMxQFA==", "license": "MIT", "dependencies": { "@babel/core": "^7.25.2", @@ -11266,9 +10795,9 @@ } }, "node_modules/metro-transform-worker": { - "version": "0.83.6", - "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.83.6.tgz", - "integrity": "sha512-G5kDJ/P0ZTIf57t3iyAd5qIXbj2Wb1j7WtIDh82uTFQHe2Mq2SO9aXG9j1wI+kxZlIe58Z22XEXIKMl89z0ibQ==", + "version": "0.83.7", + "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.83.7.tgz", + "integrity": "sha512-UegCo7ygB2fT64mRK2nbAjQVJ1zSwIIHy8d96jJv2nKZFDaViYBiughEdu5HM/Ceq0WN3LZrZk3zhl9aoiLYFw==", "license": "MIT", "dependencies": { "@babel/core": "^7.25.2", @@ -11276,13 +10805,13 @@ "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "flow-enums-runtime": "^0.0.6", - "metro": "0.83.6", - "metro-babel-transformer": "0.83.6", - "metro-cache": "0.83.6", - "metro-cache-key": "0.83.6", - "metro-minify-terser": "0.83.6", - "metro-source-map": "0.83.6", - "metro-transform-plugins": "0.83.6", + "metro": "0.83.7", + "metro-babel-transformer": "0.83.7", + "metro-cache": "0.83.7", + "metro-cache-key": "0.83.7", + "metro-minify-terser": "0.83.7", + "metro-source-map": "0.83.7", + "metro-transform-plugins": "0.83.7", "nullthrows": "^1.1.1" }, "engines": { @@ -11442,9 +10971,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", "funding": [ { "type": "github", @@ -11472,24 +11001,7 @@ "node": "^12.20.0 || ^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://opencollective.com/napi-postinstall" - } - }, - "node_modules/nativewind": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/nativewind/-/nativewind-4.2.3.tgz", - "integrity": "sha512-HglF1v6A8CqBFpXWs0d3yf4qQGurrreLuyE8FTRI/VDH8b0npZa2SDG5tviTkLiBg0s5j09mQALZOjxuocgMLA==", - "license": "MIT", - "dependencies": { - "comment-json": "^4.2.5", - "debug": "^4.3.7", - "react-native-css-interop": "0.2.3" - }, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "tailwindcss": ">3.3.0" + "url": "https://opencollective.com/napi-postinstall" } }, "node_modules/natural-compare": { @@ -11593,9 +11105,9 @@ } }, "node_modules/npm-package-arg/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", + "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -11623,9 +11135,9 @@ "license": "MIT" }, "node_modules/ob1": { - "version": "0.83.6", - "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.83.6.tgz", - "integrity": "sha512-m/xZYkwcjo6UqLMrUICEB3iHk7Bjt3RSR7KXMi6Y1MO/kGkPhoRmfUDF6KAan3rLAZ7ABRqnQyKUTwaqZgUV4w==", + "version": "0.83.7", + "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.83.7.tgz", + "integrity": "sha512-9M5kpuOLyTPogMtZiQUIxdAZxl7Dxs6tVBbJErSumsqGMuhVSoUbkfeZ3XNPpLpwBBtqY5QDUzGwggLHX3slQg==", "license": "MIT", "dependencies": { "flow-enums-runtime": "^0.0.6" @@ -12171,9 +11683,9 @@ } }, "node_modules/postcss": { - "version": "8.4.49", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", - "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", "funding": [ { "type": "opencollective", @@ -12190,7 +11702,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", + "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -12583,19 +12095,19 @@ "license": "MIT" }, "node_modules/react-native": { - "version": "0.83.4", - "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.83.4.tgz", - "integrity": "sha512-H5Wco3UJyY6zZsjoBayY8RM9uiAEQ3FeG4G2NAt+lr9DO43QeqPlVe9xxxYEukMkEmeIhNjR70F6bhXuWArOMQ==", + "version": "0.83.6", + "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.83.6.tgz", + "integrity": "sha512-H513+8VzviNFXOdPnStRzX9S3/jiJGg++QZ1zd+ROyAvBEKqFqKUPHH0d82y3QyRPct5qKjdOa7J6vNehCvXYA==", "license": "MIT", "dependencies": { "@jest/create-cache-key-function": "^29.7.0", - "@react-native/assets-registry": "0.83.4", - "@react-native/codegen": "0.83.4", - "@react-native/community-cli-plugin": "0.83.4", - "@react-native/gradle-plugin": "0.83.4", - "@react-native/js-polyfills": "0.83.4", - "@react-native/normalize-colors": "0.83.4", - "@react-native/virtualized-lists": "0.83.4", + "@react-native/assets-registry": "0.83.6", + "@react-native/codegen": "0.83.6", + "@react-native/community-cli-plugin": "0.83.6", + "@react-native/gradle-plugin": "0.83.6", + "@react-native/js-polyfills": "0.83.6", + "@react-native/normalize-colors": "0.83.6", + "@react-native/virtualized-lists": "0.83.6", "abort-controller": "^3.0.0", "anser": "^1.4.9", "ansi-regex": "^5.0.0", @@ -12609,8 +12121,8 @@ "invariant": "^2.2.4", "jest-environment-node": "^29.7.0", "memoize-one": "^5.0.0", - "metro-runtime": "^0.83.3", - "metro-source-map": "^0.83.3", + "metro-runtime": "^0.83.6", + "metro-source-map": "^0.83.6", "nullthrows": "^1.1.1", "pretty-format": "^29.7.0", "promise": "^8.3.0", @@ -12640,289 +12152,6 @@ } } }, - "node_modules/react-native-css-interop": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/react-native-css-interop/-/react-native-css-interop-0.2.3.tgz", - "integrity": "sha512-wc+JI7iUfdFBqnE18HhMTtD0q9vkhuMczToA87UdHGWwMyxdT5sCcNy+i4KInPCE855IY0Ic8kLQqecAIBWz7w==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.22.15", - "@babel/traverse": "^7.23.0", - "@babel/types": "^7.23.0", - "debug": "^4.3.7", - "lightningcss": "~1.27.0", - "semver": "^7.6.3" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "react": ">=18", - "react-native": "*", - "react-native-reanimated": ">=3.6.2", - "tailwindcss": "~3" - }, - "peerDependenciesMeta": { - "react-native-safe-area-context": { - "optional": true - }, - "react-native-svg": { - "optional": true - } - } - }, - "node_modules/react-native-css-interop/node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", - "license": "Apache-2.0", - "bin": { - "detect-libc": "bin/detect-libc.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/react-native-css-interop/node_modules/lightningcss": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.27.0.tgz", - "integrity": "sha512-8f7aNmS1+etYSLHht0fQApPc2kNO8qGRutifN5rVIc6Xo6ABsEbqOr758UwI7ALVbTt4x1fllKt0PYgzD9S3yQ==", - "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^1.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-darwin-arm64": "1.27.0", - "lightningcss-darwin-x64": "1.27.0", - "lightningcss-freebsd-x64": "1.27.0", - "lightningcss-linux-arm-gnueabihf": "1.27.0", - "lightningcss-linux-arm64-gnu": "1.27.0", - "lightningcss-linux-arm64-musl": "1.27.0", - "lightningcss-linux-x64-gnu": "1.27.0", - "lightningcss-linux-x64-musl": "1.27.0", - "lightningcss-win32-arm64-msvc": "1.27.0", - "lightningcss-win32-x64-msvc": "1.27.0" - } - }, - "node_modules/react-native-css-interop/node_modules/lightningcss-darwin-arm64": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.27.0.tgz", - "integrity": "sha512-Gl/lqIXY+d+ySmMbgDf0pgaWSqrWYxVHoc88q+Vhf2YNzZ8DwoRzGt5NZDVqqIW5ScpSnmmjcgXP87Dn2ylSSQ==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/react-native-css-interop/node_modules/lightningcss-darwin-x64": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.27.0.tgz", - "integrity": "sha512-0+mZa54IlcNAoQS9E0+niovhyjjQWEMrwW0p2sSdLRhLDc8LMQ/b67z7+B5q4VmjYCMSfnFi3djAAQFIDuj/Tg==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/react-native-css-interop/node_modules/lightningcss-freebsd-x64": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.27.0.tgz", - "integrity": "sha512-n1sEf85fePoU2aDN2PzYjoI8gbBqnmLGEhKq7q0DKLj0UTVmOTwDC7PtLcy/zFxzASTSBlVQYJUhwIStQMIpRA==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/react-native-css-interop/node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.27.0.tgz", - "integrity": "sha512-MUMRmtdRkOkd5z3h986HOuNBD1c2lq2BSQA1Jg88d9I7bmPGx08bwGcnB75dvr17CwxjxD6XPi3Qh8ArmKFqCA==", - "cpu": [ - "arm" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/react-native-css-interop/node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.27.0.tgz", - "integrity": "sha512-cPsxo1QEWq2sfKkSq2Bq5feQDHdUEwgtA9KaB27J5AX22+l4l0ptgjMZZtYtUnteBofjee+0oW1wQ1guv04a7A==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/react-native-css-interop/node_modules/lightningcss-linux-arm64-musl": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.27.0.tgz", - "integrity": "sha512-rCGBm2ax7kQ9pBSeITfCW9XSVF69VX+fm5DIpvDZQl4NnQoMQyRwhZQm9pd59m8leZ1IesRqWk2v/DntMo26lg==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/react-native-css-interop/node_modules/lightningcss-linux-x64-gnu": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.27.0.tgz", - "integrity": "sha512-Dk/jovSI7qqhJDiUibvaikNKI2x6kWPN79AQiD/E/KeQWMjdGe9kw51RAgoWFDi0coP4jinaH14Nrt/J8z3U4A==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/react-native-css-interop/node_modules/lightningcss-linux-x64-musl": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.27.0.tgz", - "integrity": "sha512-QKjTxXm8A9s6v9Tg3Fk0gscCQA1t/HMoF7Woy1u68wCk5kS4fR+q3vXa1p3++REW784cRAtkYKrPy6JKibrEZA==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/react-native-css-interop/node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.27.0.tgz", - "integrity": "sha512-/wXegPS1hnhkeG4OXQKEMQeJd48RDC3qdh+OA8pCuOPCyvnm/yEayrJdJVqzBsqpy1aJklRCVxscpFur80o6iQ==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/react-native-css-interop/node_modules/lightningcss-win32-x64-msvc": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.27.0.tgz", - "integrity": "sha512-/OJLj94Zm/waZShL8nB5jsNj3CfNATLCTyFxZyouilfTmSoLDX7VlVAmhPHoZWVFp4vdmoiEbPEYC8HID3m6yw==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/react-native-css-interop/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/react-native-gesture-handler": { "version": "2.30.1", "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.30.1.tgz", @@ -13091,9 +12320,9 @@ } }, "node_modules/react-native-worklets": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/react-native-worklets/-/react-native-worklets-0.7.2.tgz", - "integrity": "sha512-DuLu1kMV/Uyl9pQHp3hehAlThoLw7Yk2FwRTpzASOmI+cd4845FWn3m2bk9MnjUw8FBRIyhwLqYm2AJaXDXsog==", + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/react-native-worklets/-/react-native-worklets-0.7.4.tgz", + "integrity": "sha512-NYOdM1MwBb3n+AtMqy1tFy3Mn8DliQtd8sbzAVRf9Gc+uvQ0zRfxN7dS8ZzoyX7t6cyQL5THuGhlnX+iFlQTag==", "license": "MIT", "dependencies": { "@babel/plugin-transform-arrow-functions": "7.27.1", @@ -13212,46 +12441,10 @@ "node": ">=10" } }, - "node_modules/react-native/node_modules/@react-native/codegen": { - "version": "0.83.4", - "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.83.4.tgz", - "integrity": "sha512-CJ7XutzIqJPz3Lp/5TOiRWlU/JAjTboMT1BHNLSXjYHXwTmgHM3iGEbpCOtBMjWvsojRTJyRO/G3ghInIIXEYg==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.25.2", - "@babel/parser": "^7.25.3", - "glob": "^7.1.1", - "hermes-parser": "0.32.0", - "invariant": "^2.2.4", - "nullthrows": "^1.1.1", - "yargs": "^17.6.2" - }, - "engines": { - "node": ">= 20.19.4" - }, - "peerDependencies": { - "@babel/core": "*" - } - }, - "node_modules/react-native/node_modules/@react-native/js-polyfills": { - "version": "0.83.4", - "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.83.4.tgz", - "integrity": "sha512-wYUdv0rt4MjhKhQloO1AnGDXhZQOFZHDxm86dEtEA0WcsCdVrFdRULFM+rKUC/QQtJW2rS6WBqtBusgtrsDADg==", - "license": "MIT", - "engines": { - "node": ">= 20.19.4" - } - }, - "node_modules/react-native/node_modules/@react-native/normalize-colors": { - "version": "0.83.4", - "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.83.4.tgz", - "integrity": "sha512-9ezxaHjxqTkTOLg62SGg7YhFaE+fxa/jlrWP0nwf7eGFHlGOiTAaRR2KUfiN3K05e+EMbEhgcH/c7bgaXeGyJw==", - "license": "MIT" - }, "node_modules/react-native/node_modules/@react-native/virtualized-lists": { - "version": "0.83.4", - "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.83.4.tgz", - "integrity": "sha512-vNF/8kokMW8JEjG4n+j7veLTjHRRABlt4CaTS6+wtqzvWxCJHNIC8fhCqrDPn9fIn8sNePd8DyiFVX5L9TBBRA==", + "version": "0.83.6", + "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.83.6.tgz", + "integrity": "sha512-gNSFXeb4P7qHtauLvl+zESroULIyX6Ltpvau3dhwy/QmfanBv0KUcrIU/7aVXxtWcXgp+54oWJyu2LIrsZ9+LQ==", "license": "MIT", "dependencies": { "invariant": "^2.2.4", @@ -13310,21 +12503,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/react-native/node_modules/hermes-estree": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.32.0.tgz", - "integrity": "sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ==", - "license": "MIT" - }, - "node_modules/react-native/node_modules/hermes-parser": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.32.0.tgz", - "integrity": "sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw==", - "license": "MIT", - "dependencies": { - "hermes-estree": "0.32.0" - } - }, "node_modules/react-native/node_modules/semver": { "version": "7.7.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", @@ -13669,6 +12847,12 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rtl-detect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/rtl-detect/-/rtl-detect-1.1.2.tgz", + "integrity": "sha512-PGMBq03+TTG/p/cRB7HCLKJ1MgDIi07+QU1faSjiYRfmY5UsAttV9Hs08jDAHVwcOwmVLcSJkpwyfXszVjWfIQ==", + "license": "BSD-3-Clause" + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -15270,9 +14454,9 @@ } }, "node_modules/whatwg-url-minimum": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/whatwg-url-minimum/-/whatwg-url-minimum-0.1.1.tgz", - "integrity": "sha512-u2FNVjFVFZhdjb502KzXy1gKn1mEisQRJssmSJT8CPhZdZa0AP6VCbWlXERKyGu0l09t0k50FiDiralpGhBxgA==", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/whatwg-url-minimum/-/whatwg-url-minimum-0.1.2.tgz", + "integrity": "sha512-XPEm0XFQWNVG292lII1PrRRJl3sItrs7CettZ4ncYxuDVpLyy+NwlGyut2hXI0JswcJUxeCH+CyOJK0ZzAXD6A==", "license": "MIT" }, "node_modules/which": { diff --git a/package.json b/package.json index a86ac3c..781c3c6 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "update-avicommons": "tsx ./scripts/update-avicommons.ts" }, "dependencies": { - "@expo/metro-runtime": "~55.0.6", + "@expo/metro-runtime": "~55.0.11", "@expo/react-native-action-sheet": "^4.1.1", "@expo/vector-icons": "^15.0.2", "@lodev09/react-native-true-sheet": "^3.9.2", @@ -27,31 +27,31 @@ "@tanstack/react-query": "^5.87.1", "@types/lodash": "^4.17.20", "dayjs": "^1.11.19", - "expo": "~55.0.15", + "expo": "~55.0.26", "expo-blur": "~55.0.14", - "expo-constants": "~55.0.9", - "expo-dev-client": "~55.0.27", + "expo-constants": "~55.0.16", + "expo-dev-client": "~55.0.35", "expo-document-picker": "~55.0.13", - "expo-file-system": "~55.0.11", - "expo-font": "~55.0.4", - "expo-glass-effect": "~55.0.8", + "expo-file-system": "~55.0.22", + "expo-font": "~55.0.8", + "expo-glass-effect": "~55.0.11", "expo-haptics": "~55.0.14", - "expo-image": "~55.0.6", - "expo-linking": "~55.0.13", - "expo-location": "~55.1.8", - "expo-router": "~55.0.12", - "expo-splash-screen": "~55.0.18", - "expo-sqlite": "~55.0.15", - "expo-status-bar": "~55.0.4", - "expo-symbols": "~55.0.5", - "expo-system-ui": "~55.0.15", - "expo-updates": "~55.0.20", - "expo-web-browser": "~55.0.14", + "expo-image": "~55.0.11", + "expo-linking": "~55.0.15", + "expo-localization": "~55.0.15", + "expo-location": "~55.1.10", + "expo-router": "~55.0.16", + "expo-splash-screen": "~55.0.21", + "expo-sqlite": "~55.0.16", + "expo-status-bar": "~55.0.6", + "expo-symbols": "~55.0.9", + "expo-system-ui": "~55.0.18", + "expo-updates": "~55.0.24", + "expo-web-browser": "~55.0.16", "lodash": "^4.17.21", - "nativewind": "^4.1.23", "react": "19.2.0", "react-dom": "19.2.0", - "react-native": "0.83.4", + "react-native": "0.83.6", "react-native-gesture-handler": "~2.30.0", "react-native-popover-view": "^6.1.0", "react-native-reanimated": "4.2.1", @@ -61,7 +61,7 @@ "react-native-toast-message": "^2.3.3", "react-native-web": "^0.21.0", "react-native-webview": "13.16.0", - "react-native-worklets": "0.7.2", + "react-native-worklets": "0.7.4", "suncalc": "^1.9.0", "tailwindcss": "^3.4.17", "twrnc": "^4.9.1", @@ -69,10 +69,11 @@ }, "devDependencies": { "@types/react": "~19.2.10", + "@types/sharp": "^0.31.1", "@types/suncalc": "^1.9.2", "dotenv-cli": "^11.0.0", "eslint": "^9.25.0", - "eslint-config-expo": "~55.0.0", + "eslint-config-expo": "~55.0.1", "tsx": "^4.21.0", "typescript": "~5.9.2" }, diff --git a/stores/filtersStore.ts b/stores/filtersStore.ts index 3458e52..48a5892 100644 --- a/stores/filtersStore.ts +++ b/stores/filtersStore.ts @@ -1,13 +1,23 @@ import AsyncStorage from "@react-native-async-storage/async-storage"; import { create } from "zustand"; import { createJSONStorage, persist } from "zustand/middleware"; +import { + normalizeMinTargets, + normalizeMinTargetFrequency, +} from "@/lib/targetRichHotspots"; type FiltersState = { showSavedOnly: boolean; + targetRichEnabled: boolean; + minTargets: number; + minTargetFrequency: number; }; type FiltersActions = { setShowSavedOnly: (value: boolean) => void; + setTargetRichEnabled: (value: boolean) => void; + setMinTargets: (value: number) => void; + setMinTargetFrequency: (value: number) => void; resetFilters: () => void; }; @@ -15,8 +25,21 @@ export const useFiltersStore = create()( persist( (set) => ({ showSavedOnly: false, + targetRichEnabled: false, + minTargets: 5, + minTargetFrequency: 50, setShowSavedOnly: (value) => set({ showSavedOnly: value }), - resetFilters: () => set({ showSavedOnly: false }), + setTargetRichEnabled: (value) => set({ targetRichEnabled: value }), + setMinTargets: (value) => set({ minTargets: normalizeMinTargets(value) }), + setMinTargetFrequency: (value) => + set({ minTargetFrequency: normalizeMinTargetFrequency(value) }), + resetFilters: () => + set({ + showSavedOnly: false, + targetRichEnabled: false, + minTargets: 5, + minTargetFrequency: 50, + }), }), { name: "filters-storage", diff --git a/stores/mapStore.ts b/stores/mapStore.ts index 1900876..a74f143 100644 --- a/stores/mapStore.ts +++ b/stores/mapStore.ts @@ -1,3 +1,4 @@ +import { Bounds } from "@/lib/types"; import { create } from "zustand"; type MapLayerType = "default" | "satellite"; @@ -5,6 +6,12 @@ type MapLayerType = "default" | "satellite"; type MapStore = { currentLayer: MapLayerType; setCurrentLayer: (layer: MapLayerType) => void; + bounds: Bounds | null; + setBounds: (bounds: Bounds | null) => void; + inViewCount: number | null; + setInViewCount: (count: number | null) => void; + displayedCount: number | null; + setDisplayedCount: (count: number | null) => void; hotspotId: string | null; setHotspotId: (id: string | null) => void; placeId: string | null; @@ -28,6 +35,12 @@ type MapStore = { export const useMapStore = create((set) => ({ currentLayer: "default", setCurrentLayer: (layer) => set({ currentLayer: layer }), + bounds: null, + setBounds: (bounds) => set({ bounds }), + inViewCount: null, + setInViewCount: (count) => set({ inViewCount: count }), + displayedCount: null, + setDisplayedCount: (count) => set({ displayedCount: count }), hotspotId: null, setHotspotId: (id) => set({ hotspotId: id }), placeId: null, diff --git a/stores/settingsStore.ts b/stores/settingsStore.ts index 3dc7f4c..d103435 100644 --- a/stores/settingsStore.ts +++ b/stores/settingsStore.ts @@ -1,4 +1,5 @@ import AsyncStorage from "@react-native-async-storage/async-storage"; +import { getLocales } from "expo-localization"; import { create } from "zustand"; import { createJSONStorage, persist } from "zustand/middleware"; @@ -10,6 +11,15 @@ export type LifeListEntry = { isManual?: boolean; }; +export type DistanceUnits = "metric" | "imperial"; + +// Default distance units from the device's region. measurementSystem is "us" | "uk" | "metric" | null; +// both the US and UK use miles for road distances, everything else is metric. +function getDeviceDistanceUnits(): DistanceUnits { + const system = getLocales()[0]?.measurementSystem; + return system === "us" || system === "uk" ? "imperial" : "metric"; +} + type SettingsState = { version: number; directionsProvider: string | null; @@ -18,6 +28,7 @@ type SettingsState = { disableSunTimes: boolean; showAllSpecies: boolean; targetMonths: number[]; + distanceUnits: DistanceUnits; }; type SettingsActions = { @@ -27,6 +38,7 @@ type SettingsActions = { setDisableSunTimes: (value: boolean) => void; setShowAllSpecies: (value: boolean) => void; setTargetMonths: (months: number[]) => void; + setDistanceUnits: (units: DistanceUnits) => void; }; type SettingsStore = SettingsState & SettingsActions; @@ -50,6 +62,17 @@ const migrations: Migration[] = [ return state; }, }, + { + version: 2, + migrate: async (state) => { + // Seed distanceUnits from the device region for users upgrading from a build that + // predates the setting, and persist it so it no longer relies on the rehydrate-merge default. + if (state.distanceUnits == null) { + return { ...state, distanceUnits: getDeviceDistanceUnits() }; + } + return state; + }, + }, ]; const LATEST_VERSION = migrations[migrations.length - 1]?.version ?? 0; @@ -77,12 +100,16 @@ export const useSettingsStore = create()( disableSunTimes: false, showAllSpecies: false, targetMonths: [], + // Seeded from the device region; persisted values from earlier installs fall back to this default + // via the shallow rehydrate merge, so existing users also pick up their locale's units. + distanceUnits: getDeviceDistanceUnits(), setDirectionsProvider: (provider) => set({ directionsProvider: provider || null }), setLifelist: (lifelist) => set({ lifelist }), setLifelistExclusions: (exclusions) => set({ lifelistExclusions: exclusions }), setDisableSunTimes: (value) => set({ disableSunTimes: value }), setShowAllSpecies: (value) => set({ showAllSpecies: value }), setTargetMonths: (months) => set({ targetMonths: months }), + setDistanceUnits: (units) => set({ distanceUnits: units }), }), { name: "settings",