diff --git a/.github/workflows/companion-build.yml b/.github/workflows/companion-build.yml new file mode 100644 index 0000000000..db8f2606e8 --- /dev/null +++ b/.github/workflows/companion-build.yml @@ -0,0 +1,37 @@ +name: Companion Builds + +on: + workflow_call: + +permissions: + contents: read + +env: + EXPO_PUBLIC_CAL_API_KEY: ${{ secrets.CI_EXPO_PUBLIC_CAL_API_KEY || vars.CI_EXPO_PUBLIC_CAL_API_KEY || vars.EXPO_PUBLIC_CAL_API_KEY }} + +jobs: + build: + name: Build Companion App & Extension + runs-on: buildjet-4vcpu-ubuntu-2204 + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/dangerous-git-checkout + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + cache-dependency-path: companion/package-lock.json + - name: Install dependencies + working-directory: companion + run: npm ci + - name: Build Expo web app + working-directory: companion + env: + EXPO_NO_TELEMETRY: 1 + CI: true + run: npx expo export --platform web + - name: Build browser extension + working-directory: companion + run: npm run ext:build diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 3ae424169b..dba3b0eba0 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -146,6 +146,13 @@ jobs: uses: ./.github/workflows/docs-build.yml secrets: inherit + build-companion: + name: Companion builds + needs: [changes, check-label, deps] + if: ${{ needs.check-label.outputs.run-e2e == 'true' && needs.changes.outputs.has-files-requiring-all-checks == 'true' }} + uses: ./.github/workflows/companion-build.yml + secrets: inherit + build: name: Production builds needs: [changes, check-label, deps] @@ -244,6 +251,7 @@ jobs: build-api-v2, build-atoms, build-docs, + build-companion, e2e, e2e-api-v2, e2e-embed, diff --git a/companion/.env.example b/companion/.env.example new file mode 100644 index 0000000000..36237cbab0 --- /dev/null +++ b/companion/.env.example @@ -0,0 +1,5 @@ +# Cal.com OAuth Configuration +EXPO_PUBLIC_CALCOM_OAUTH_CLIENT_ID=your_oauth_client_id_here + +EXPO_PUBLIC_CALCOM_OAUTH_REDIRECT_URI=your_oauth_redirect_uri_here + diff --git a/companion/.gitignore b/companion/.gitignore new file mode 100644 index 0000000000..b0af0a9220 --- /dev/null +++ b/companion/.gitignore @@ -0,0 +1,67 @@ +# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files + +# dependencies +node_modules/ +.yarn/ +.pnp.* + +# Expo +.expo/ +dist/ +web-build/ +expo-env.d.ts + +# Native +.kotlin/ +*.orig.* +*.jks +*.p8 +*.p12 +*.key +*.mobileprovision + +# Metro +.metro-health-check* + +# debug +npm-debug.* +yarn-debug.* +yarn-error.* +*.log + +# macOS +.DS_Store +*.pem + +# local env files +.env*.local +.env + +# typescript +*.tsbuildinfo + +# generated native folders +/ios +/android + +# WXT Chrome Extension build outputs +.output/ +.wxt/ +dev/ + +# Extension builds (generated at build time) +*.zip + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# Testing +coverage/ +.nyc_output/ + +# Misc +*.code-workspace diff --git a/companion/.husky/pre-commit b/companion/.husky/pre-commit new file mode 100755 index 0000000000..0951c031f8 --- /dev/null +++ b/companion/.husky/pre-commit @@ -0,0 +1,6 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +cd companion +npx lint-staged + diff --git a/companion/.prettierrc.js b/companion/.prettierrc.js new file mode 100644 index 0000000000..359fced2da --- /dev/null +++ b/companion/.prettierrc.js @@ -0,0 +1,10 @@ +module.exports = { + semi: true, + trailingComma: "es5", + singleQuote: false, + printWidth: 100, + tabWidth: 2, + useTabs: false, + arrowParens: "always", + plugins: ["prettier-plugin-tailwindcss"], +}; diff --git a/companion/app.json b/companion/app.json new file mode 100644 index 0000000000..f7407ae093 --- /dev/null +++ b/companion/app.json @@ -0,0 +1,32 @@ +{ + "expo": { + "name": "expo-wxt-app", + "slug": "expo-wxt-app", + "scheme": "expo-wxt-app", + "version": "1.0.0", + "orientation": "portrait", + "icon": "./assets/icon.png", + "userInterfaceStyle": "light", + "newArchEnabled": true, + "splash": { + "image": "./assets/splash-icon.png", + "resizeMode": "contain", + "backgroundColor": "#ffffff" + }, + "ios": { + "supportsTablet": true, + "deploymentTarget": "18.0" + }, + "android": { + "adaptiveIcon": { + "foregroundImage": "./assets/adaptive-icon.png", + "backgroundColor": "#ffffff" + }, + "edgeToEdgeEnabled": true + }, + "web": { + "favicon": "./assets/favicon.png", + "bundler": "metro" + } + } +} diff --git a/companion/app/(tabs)/_layout.tsx b/companion/app/(tabs)/_layout.tsx new file mode 100644 index 0000000000..8cf25bfa9b --- /dev/null +++ b/companion/app/(tabs)/_layout.tsx @@ -0,0 +1,100 @@ +import { Ionicons } from "@expo/vector-icons"; +import { isLiquidGlassAvailable } from "expo-glass-effect"; +import { Tabs } from "expo-router"; +import { NativeTabs, Icon, Label } from "expo-router/unstable-native-tabs"; +import { Platform } from "react-native"; + +export default function TabLayout() { + // Check if liquid glass effect is available + const supportsLiquidGlass = isLiquidGlassAvailable(); + + // Use NativeTabs for iOS 15+ (liquid glass support), fallback to standard Tabs + if (supportsLiquidGlass) { + return ( + + + + + + + + + + + + + + + + + + + + + + ); + } + + // Fallback to standard Expo Router Tabs for older iOS and Android + return ( + + ( + + ), + }} + /> + + ( + + ), + }} + /> + + ( + + ), + }} + /> + + ( + + ), + }} + /> + + ); +} diff --git a/companion/app/(tabs)/availability.tsx b/companion/app/(tabs)/availability.tsx new file mode 100644 index 0000000000..3d5c2d276e --- /dev/null +++ b/companion/app/(tabs)/availability.tsx @@ -0,0 +1,685 @@ +import { Ionicons } from "@expo/vector-icons"; +import { useRouter, useFocusEffect } from "expo-router"; +import React, { useState, useEffect, useCallback } from "react"; +import { + View, + Text, + FlatList, + ActivityIndicator, + RefreshControl, + TouchableOpacity, + ActionSheetIOS, + Alert, + Platform, + Modal, + TextInput, + KeyboardAvoidingView, +} from "react-native"; + +import { CalComAPIService, Schedule } from "../../services/calcom"; +import { Header } from "../../components/Header"; +import { FullScreenModal } from "../../components/FullScreenModal"; + +export default function Availability() { + const router = useRouter(); + const [schedules, setSchedules] = useState([]); + const [filteredSchedules, setFilteredSchedules] = useState([]); + const [searchQuery, setSearchQuery] = useState(""); + const [loading, setLoading] = useState(true); + const [refreshing, setRefreshing] = useState(false); + const [error, setError] = useState(null); + const [showCreateModal, setShowCreateModal] = useState(false); + const [newScheduleName, setNewScheduleName] = useState(""); + const [creating, setCreating] = useState(false); + const [showActionsModal, setShowActionsModal] = useState(false); + const [selectedSchedule, setSelectedSchedule] = useState(null); + const [showDeleteModal, setShowDeleteModal] = useState(false); + const [deleting, setDeleting] = useState(false); + + const fetchSchedules = async () => { + try { + setError(null); + + // Fetch all schedules + const allSchedules = await CalComAPIService.getSchedules(); + + // Sort schedules: default first, then by name + const sortedSchedules = allSchedules.sort((a, b) => { + // Default schedule first + if (a.isDefault && !b.isDefault) return -1; + if (!a.isDefault && b.isDefault) return 1; + // Then sort by name alphabetically + return a.name.localeCompare(b.name); + }); + + setSchedules(sortedSchedules); + setFilteredSchedules(sortedSchedules); + } catch (err) { + setError("Failed to load availability. Please check your API key and try again."); + } finally { + setLoading(false); + setRefreshing(false); + } + }; + + useEffect(() => { + fetchSchedules(); + }, []); + + // Refresh schedules when screen comes into focus + useFocusEffect( + useCallback(() => { + // Only refresh if not currently loading (to avoid duplicate calls) + if (!loading && !refreshing) { + fetchSchedules(); + } + }, [loading, refreshing]) + ); + + const onRefresh = () => { + setRefreshing(true); + fetchSchedules(); + }; + + const handleSearch = (query: string) => { + setSearchQuery(query); + if (query.trim() === "") { + setFilteredSchedules(schedules); + } else { + const filtered = schedules.filter((schedule) => + schedule.name.toLowerCase().includes(query.toLowerCase()) + ); + setFilteredSchedules(filtered); + } + }; + + const handleScheduleLongPress = (schedule: Schedule) => { + if (Platform.OS !== "ios") { + // Fallback for non-iOS platforms (Android Alert supports max 3 buttons) + const options = []; + if (!schedule.isDefault) { + options.push({ text: "⭐ Set as default", onPress: () => handleSetAsDefault(schedule) }); + } + options.push( + { text: "📋 Duplicate", onPress: () => handleDuplicate(schedule) }, + { text: "🗑️ Delete", style: "destructive", onPress: () => handleDelete(schedule) } + ); + // Android Alert automatically adds cancel, so we don't need to include it explicitly + Alert.alert(schedule.name, "", options); + return; + } + + const options = ["Cancel"]; + if (!schedule.isDefault) { + options.push("⭐ Set as default"); + } + options.push("📋 Duplicate", "🗑️ Delete"); + + const destructiveButtonIndex = options.length - 1; // Delete button + const cancelButtonIndex = 0; + + ActionSheetIOS.showActionSheetWithOptions( + { + options, + destructiveButtonIndex, + cancelButtonIndex, + title: schedule.name, + }, + (buttonIndex) => { + if (buttonIndex === cancelButtonIndex) { + return; + } + + if (!schedule.isDefault) { + // Options: ["Cancel", "⭐ Set as default", "📋 Duplicate", "🗑️ Delete"] + if (buttonIndex === 1) { + handleSetAsDefault(schedule); + } else if (buttonIndex === 2) { + handleDuplicate(schedule); + } else if (buttonIndex === 3) { + handleDelete(schedule); + } + } else { + // Options: ["Cancel", "📋 Duplicate", "🗑️ Delete"] + if (buttonIndex === 1) { + handleDuplicate(schedule); + } else if (buttonIndex === 2) { + handleDelete(schedule); + } + } + } + ); + }; + + const handleSetAsDefault = async (schedule: Schedule) => { + try { + await CalComAPIService.updateSchedule(schedule.id, { isDefault: true }); + await fetchSchedules(); + } catch (err) { + Alert.alert("Error", "Failed to set schedule as default. Please try again."); + } + }; + + const handleDuplicate = async (schedule: Schedule) => { + try { + await CalComAPIService.duplicateSchedule(schedule.id); + await fetchSchedules(); + } catch (err) { + Alert.alert("Error", "Failed to duplicate schedule. Please try again."); + } + }; + + const handleDelete = (schedule: Schedule) => { + if (Platform.OS === "web") { + // Use custom modal for web + setShowDeleteModal(true); + } else { + // Use native Alert for iOS/Android + Alert.alert("Delete Schedule", `Are you sure you want to delete "${schedule.name}"?`, [ + { text: "Cancel", style: "cancel" }, + { + text: "Delete", + style: "destructive", + onPress: async () => { + try { + await CalComAPIService.deleteSchedule(schedule.id); + await fetchSchedules(); + } catch (err) { + Alert.alert("Error", "Failed to delete schedule. Please try again."); + } + }, + }, + ]); + } + }; + + const confirmDelete = async () => { + if (!selectedSchedule) return; + + try { + setDeleting(true); + await CalComAPIService.deleteSchedule(selectedSchedule.id); + setShowDeleteModal(false); + setSelectedSchedule(null); + await fetchSchedules(); + } catch (err) { + Alert.alert("Error", "Failed to delete schedule. Please try again."); + setDeleting(false); + } + }; + + const handleSchedulePress = (schedule: Schedule) => { + router.push({ + pathname: "/availability-detail", + params: { id: schedule.id.toString() }, + }); + }; + + const handleCreateNew = () => { + setNewScheduleName(""); + setShowCreateModal(true); + }; + + const handleCreateSchedule = async () => { + if (!newScheduleName.trim()) { + Alert.alert("Error", "Please enter a schedule name"); + return; + } + + try { + setCreating(true); + + // Get user's timezone (default to America/New_York if not available) + let userTimezone = "America/New_York"; + try { + const userProfile = await CalComAPIService.getUserProfile(); + if (userProfile.timeZone) { + userTimezone = userProfile.timeZone; + } + } catch (error) { + console.log("Could not get user timezone, using default"); + } + + // Create schedule with Monday-Friday 9 AM - 5 PM default + const newSchedule = await CalComAPIService.createSchedule({ + name: newScheduleName.trim(), + timeZone: userTimezone, + isDefault: false, + availability: [ + { + days: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], + startTime: "09:00", + endTime: "17:00", + }, + ], + }); + + setShowCreateModal(false); + setNewScheduleName(""); + + // Navigate to edit the newly created schedule + router.push({ + pathname: "/availability-detail", + params: { + id: newSchedule.id.toString(), + }, + }); + + // Refresh the list + fetchSchedules(); + } catch (error) { + console.error("Failed to create schedule:", error); + Alert.alert("Error", "Failed to create schedule. Please try again."); + } finally { + setCreating(false); + } + }; + + const renderSchedule = ({ item: schedule, index }: { item: Schedule; index: number }) => { + return ( + handleSchedulePress(schedule)} + onLongPress={() => handleScheduleLongPress(schedule)} + style={{ paddingHorizontal: 16, paddingVertical: 16 }} + > + + + + {schedule.name} + {schedule.isDefault && ( + + Default + + )} + + + {schedule.availability && schedule.availability.length > 0 ? ( + + {schedule.availability.map((slot, slotIndex) => ( + 0 ? "mt-2" : ""} + > + + {slot.days.join(", ")} {slot.startTime} - {slot.endTime} + + + ))} + + ) : ( + No availability set + )} + + + + {schedule.timeZone} + + + + {/* Three dots button - vertically centered on the right */} + { + e.stopPropagation(); + setSelectedSchedule(schedule); + setShowActionsModal(true); + }} + > + + + + + ); + }; + + if (loading) { + return ( + +
+ + + Loading availability... + + + ); + } + + if (error) { + return ( + +
+ + + + Unable to load availability + + {error} + + Retry + + + + ); + } + + if (schedules.length === 0 && !loading) { + return ( + +
+ + + + + New + + + + + No schedules found + + Create your availability schedule in Cal.com + + + + ); + } + + if (filteredSchedules.length === 0 && searchQuery.trim() !== "") { + return ( + +
+ + + + + New + + + + + No results found + + Try searching with different keywords + + + + ); + } + + return ( + +
+ + + + + New + + + + + item.id.toString()} + renderItem={renderSchedule} + contentContainerStyle={{ paddingBottom: 90 }} + refreshControl={} + showsVerticalScrollIndicator={false} + /> + + + + {/* Create Schedule Modal */} + setShowCreateModal(false)} + > + setShowCreateModal(false)} + > + e.stopPropagation()} + style={{ + shadowColor: "#000", + shadowOffset: { width: 0, height: 20 }, + shadowOpacity: 0.25, + shadowRadius: 25, + elevation: 24, + }} + > + {/* Header */} + + Add a new schedule + + + {/* Content */} + + + Name + + + + + {/* Footer */} + + + { + setShowCreateModal(false); + setNewScheduleName(""); + }} + disabled={creating} + > + Close + + + Continue + + + + + + + + {/* Schedule Actions Modal */} + setShowActionsModal(false)} + > + setShowActionsModal(false)} + > + e.stopPropagation()} + > + {/* Header */} + + + Schedule Actions + + + + {/* Actions List */} + + {/* Set as Default - only show if not already default */} + {selectedSchedule && !selectedSchedule.isDefault && ( + <> + { + setShowActionsModal(false); + if (selectedSchedule) { + handleSetAsDefault(selectedSchedule); + } + }} + className="flex-row items-center p-2 hover:bg-gray-50 md:p-4" + > + + Set as Default + + + + + )} + + {/* Duplicate */} + { + setShowActionsModal(false); + if (selectedSchedule) { + setTimeout(() => { + handleDuplicate(selectedSchedule); + }, 100); + } + }} + className="flex-row items-center p-2 hover:bg-gray-50 md:p-4" + > + + Duplicate + + + {/* Separator */} + + + {/* Delete */} + { + setShowActionsModal(false); + if (selectedSchedule) { + // Small delay to ensure modal closes before alert shows + setTimeout(() => { + handleDelete(selectedSchedule); + }, 100); + } + }} + className="flex-row items-center p-2 hover:bg-gray-50 md:p-4" + > + + Delete + + + + {/* Cancel button */} + + setShowActionsModal(false)} + > + Cancel + + + + + + + {/* Delete Confirmation Modal */} + !deleting && setShowDeleteModal(false)} + > + + + {/* Icon */} + + + + + + + {/* Title */} + + Delete Schedule + + + {/* Description */} + + Are you sure you want to delete "{selectedSchedule?.name}"? This action cannot be + undone. + + + {/* Buttons */} + + setShowDeleteModal(false)} + disabled={deleting} + > + Cancel + + + Delete + + + + + + + ); +} diff --git a/companion/app/(tabs)/bookings.tsx b/companion/app/(tabs)/bookings.tsx new file mode 100644 index 0000000000..d4b2dfc934 --- /dev/null +++ b/companion/app/(tabs)/bookings.tsx @@ -0,0 +1,1254 @@ +import { Ionicons } from "@expo/vector-icons"; +import SegmentedControl from "@react-native-segmented-control/segmented-control"; +import { useRouter } from "expo-router"; +import React, { useState, useEffect } from "react"; +import { + View, + Text, + FlatList, + ActivityIndicator, + Alert, + TouchableOpacity, + RefreshControl, + Platform, + TextInput, + ActionSheetIOS, + Linking, + Modal, + ScrollView, +} from "react-native"; +import type { NativeSyntheticEvent } from "react-native"; + +import { CalComAPIService, Booking, EventType } from "../../services/calcom"; +import { Header } from "../../components/Header"; +import { FullScreenModal } from "../../components/FullScreenModal"; + +type BookingFilter = "upcoming" | "unconfirmed" | "past" | "cancelled"; + +export default function Bookings() { + const router = useRouter(); + const [bookings, setBookings] = useState([]); + const [filteredBookings, setFilteredBookings] = useState([]); + const [searchQuery, setSearchQuery] = useState(""); + const [loading, setLoading] = useState(true); + const [refreshing, setRefreshing] = useState(false); + const [error, setError] = useState(null); + const [activeFilter, setActiveFilter] = useState("upcoming"); + const [showFilterModal, setShowFilterModal] = useState(false); + const [eventTypes, setEventTypes] = useState([]); + const [selectedEventTypeId, setSelectedEventTypeId] = useState(null); + const [selectedEventTypeLabel, setSelectedEventTypeLabel] = useState(null); + const [eventTypesLoading, setEventTypesLoading] = useState(false); + const [showBookingActionsModal, setShowBookingActionsModal] = useState(false); + const [selectedBooking, setSelectedBooking] = useState(null); + + const filterOptions: { key: BookingFilter; label: string }[] = [ + { key: "upcoming", label: "Upcoming" }, + { key: "unconfirmed", label: "Unconfirmed" }, + { key: "past", label: "Past" }, + { key: "cancelled", label: "Cancelled" }, + ]; + + const filterLabels = filterOptions.map((option) => option.label); + const activeIndex = filterOptions.findIndex((option) => option.key === activeFilter); + + const getFiltersForActiveTab = () => { + switch (activeFilter) { + case "upcoming": + return { + status: ["upcoming"], + limit: 50, + }; + case "unconfirmed": + return { + status: ["unconfirmed"], + limit: 50, + }; + case "past": + return { + status: ["past"], + limit: 100, + }; + case "cancelled": + return { + status: ["cancelled"], + limit: 100, + }; + default: + return { + status: ["upcoming"], + limit: 50, + }; + } + }; + + const fetchBookings = async () => { + try { + setError(null); + + // First, test the raw bookings API call (only on first load) + if (loading) { + await CalComAPIService.testRawBookingsAPI(); + } + + const filters = getFiltersForActiveTab(); + + const data = await CalComAPIService.getBookings(filters); + + // Log individual bookings to see what we're getting + if (Array.isArray(data) && data.length > 0) { + } else { + } + + if (Array.isArray(data)) { + let filteredBookings = data; + const now = new Date(); + + // Log all bookings before filtering + + // Server already filters by status correctly, so we only need to sort + // The server's logic: + // - "upcoming": endTime >= now AND status not in ["cancelled", "rejected"] + // - "unconfirmed": endTime >= now AND status = "pending" + // - "past": endTime <= now AND status not in ["cancelled", "rejected"] + // - "cancelled": status in ["cancelled", "rejected"] + switch (activeFilter) { + case "upcoming": + // Server already filtered, just sort by start time + filteredBookings = data.sort( + (a, b) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime() + ); + break; + case "unconfirmed": + // Server already filtered, just sort by start time + filteredBookings = data.sort( + (a, b) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime() + ); + break; + case "past": + // Server already filtered, sort by start time descending (latest first) + filteredBookings = data.sort( + (a, b) => new Date(b.startTime).getTime() - new Date(a.startTime).getTime() + ); + break; + case "cancelled": + // Server already filtered, sort by start time descending (latest first) + filteredBookings = data.sort( + (a, b) => new Date(b.startTime).getTime() - new Date(a.startTime).getTime() + ); + break; + } + + setBookings(filteredBookings); + applyFilters(filteredBookings, searchQuery, selectedEventTypeId); + } else { + setBookings([]); + setFilteredBookings([]); + } + } catch (err) { + console.error("🎯 BookingsScreen: Error fetching bookings:", err); + setError("Failed to load bookings. Please check your API key and try again."); + } finally { + setLoading(false); + setRefreshing(false); + } + }; + + useEffect(() => { + fetchBookings(); + }, []); + + useEffect(() => { + setSearchQuery(""); // Clear search when filter changes + setSelectedEventTypeId(null); // Clear event type filter when status filter changes + setSelectedEventTypeLabel(null); + if (!loading) { + setLoading(true); + fetchBookings(); + } + }, [activeFilter]); + + useEffect(() => {}, [loading, error, bookings]); + + const onRefresh = () => { + setRefreshing(true); + fetchBookings(); + }; + + const applyFilters = ( + bookingsToFilter: Booking[], + searchText: string, + eventTypeId: number | null + ) => { + let filtered = bookingsToFilter; + + // Apply event type filter + if (eventTypeId !== null) { + filtered = filtered.filter((booking) => booking.eventTypeId === eventTypeId); + } + + // Apply search filter + if (searchText.trim() !== "") { + filtered = filtered.filter( + (booking) => + // Search in booking title + booking.title?.toLowerCase().includes(searchText.toLowerCase()) || + // Search in booking description + booking.description?.toLowerCase().includes(searchText.toLowerCase()) || + // Search in event type title + booking.eventType?.title?.toLowerCase().includes(searchText.toLowerCase()) || + // Search in attendee names + (booking.attendees && + booking.attendees.some((attendee) => + attendee.name?.toLowerCase().includes(searchText.toLowerCase()) + )) || + // Search in attendee emails + (booking.attendees && + booking.attendees.some((attendee) => + attendee.email?.toLowerCase().includes(searchText.toLowerCase()) + )) || + // Search in location + booking.location?.toLowerCase().includes(searchText.toLowerCase()) || + // Search in user name + booking.user?.name?.toLowerCase().includes(searchText.toLowerCase()) || + // Search in user email + booking.user?.email?.toLowerCase().includes(searchText.toLowerCase()) + ); + } + + setFilteredBookings(filtered); + }; + + const handleSearch = (query: string) => { + setSearchQuery(query); + applyFilters(bookings, query, selectedEventTypeId); + }; + + const fetchEventTypes = async () => { + try { + setEventTypesLoading(true); + const types = await CalComAPIService.getEventTypes(); + setEventTypes(types); + } catch (err) { + console.error("Error fetching event types:", err); + setError("Failed to load event types"); + } finally { + setEventTypesLoading(false); + } + }; + + const handleFilterButtonPress = () => { + setShowFilterModal(true); + if (eventTypes.length === 0) { + fetchEventTypes(); + } + }; + + const clearEventTypeFilter = () => { + setSelectedEventTypeId(null); + setSelectedEventTypeLabel(null); + applyFilters(bookings, searchQuery, null); + }; + + const handleEventTypeSelect = (eventTypeId: number | null, label?: string | null) => { + if (eventTypeId === null) { + clearEventTypeFilter(); + } else { + setSelectedEventTypeId(eventTypeId); + setSelectedEventTypeLabel(label || null); + applyFilters(bookings, searchQuery, eventTypeId); + } + setShowFilterModal(false); + }; + + const handleFilterChange = (filter: BookingFilter) => { + setActiveFilter(filter); + }; + + const handleSegmentChange = (event: NativeSyntheticEvent<{ selectedSegmentIndex: number }>) => { + const { selectedSegmentIndex } = event.nativeEvent; + const selectedFilter = filterOptions[selectedSegmentIndex]; + if (selectedFilter) { + handleFilterChange(selectedFilter.key); + } + }; + + const getEmptyStateContent = () => { + switch (activeFilter) { + case "upcoming": + return { + icon: "calendar-clear-outline", + title: "No upcoming bookings", + text: "Your upcoming appointments will appear here", + }; + case "unconfirmed": + return { + icon: "hourglass-outline", + title: "No unconfirmed bookings", + text: "Bookings awaiting confirmation will appear here", + }; + case "past": + return { + icon: "archive-outline", + title: "No past bookings", + text: "Your completed appointments will appear here", + }; + case "cancelled": + return { + icon: "close-circle-outline", + title: "No cancelled bookings", + text: "Cancelled or rejected bookings will appear here", + }; + default: + return { + icon: "calendar-clear-outline", + title: "No bookings found", + text: "Your bookings will appear here", + }; + } + }; + + const renderSegmentedControl = () => { + return ( + <> + + + + + + + + + Filter + + + + + + + {selectedEventTypeId !== null && ( + + + Filtered by {selectedEventTypeLabel || "event type"} + + + Clear filter + + + )} + + + ); + }; + + const handleBookingPress = (booking: Booking) => { + router.push({ + pathname: "/booking-detail", + params: { uid: booking.uid }, + }); + }; + + const handleBookingPressOld = (booking: Booking) => { + if (Platform.OS !== "ios") { + // Fallback for non-iOS platforms + const attendeesList = booking.attendees?.map((att) => att.name).join(", ") || "No attendees"; + const startTime = booking.start || booking.startTime || ""; + const endTime = booking.end || booking.endTime || ""; + + const actions = getBookingActions(booking); + const alertActions = actions.map((action) => ({ + text: action.title, + style: (action.destructive ? "destructive" : "default") as + | "destructive" + | "default" + | "cancel", + onPress: action.onPress, + })); + alertActions.unshift({ text: "Cancel", style: "cancel" as const, onPress: () => {} }); + + Alert.alert( + booking.title, + `${booking.description ? `${booking.description}\n\n` : ""}Time: ${formatDateTime( + startTime + )} - ${formatTime(endTime)}\nAttendees: ${attendeesList}\nStatus: ${booking.status}${ + booking.location ? `\nLocation: ${booking.location}` : "" + }`, + alertActions + ); + return; + } + + const actions = getBookingActions(booking); + const options = ["Cancel", ...actions.map((action) => action.title)]; + const destructiveButtonIndex = actions.findIndex((action) => action.destructive); + + ActionSheetIOS.showActionSheetWithOptions( + { + options, + destructiveButtonIndex: + destructiveButtonIndex >= 0 ? destructiveButtonIndex + 1 : undefined, + cancelButtonIndex: 0, + title: booking.title, + message: getBookingDetailsMessage(booking), + }, + (buttonIndex) => { + if (buttonIndex === 0) return; // Cancel + + const actionIndex = buttonIndex - 1; + if (actions[actionIndex]) { + actions[actionIndex].onPress(); + } + } + ); + }; + + const getBookingDetailsMessage = (booking: Booking): string => { + const attendeesList = booking.attendees?.map((att) => att.name).join(", ") || "No attendees"; + const startTime = booking.start || booking.startTime || ""; + const endTime = booking.end || booking.endTime || ""; + + return `${booking.description ? `${booking.description}\n\n` : ""}Time: ${formatDateTime( + startTime + )} - ${formatTime(endTime)}\nAttendees: ${attendeesList}\nStatus: ${formatStatusText(booking.status)}${ + booking.location ? `\nLocation: ${booking.location}` : "" + }`; + }; + + const getBookingActions = (booking: Booking) => { + const baseActions = [ + { + title: "Open Location", + onPress: () => handleOpenLocation(booking), + destructive: false, + }, + ]; + + const specificActions = (() => { + switch (activeFilter) { + case "upcoming": + return [ + { + title: "Request Reschedule", + onPress: () => handleRescheduleBooking(booking), + destructive: false, + }, + { + title: "Report booking", + onPress: () => handleReportBooking(booking), + destructive: false, + }, + { + title: "Cancel event", + onPress: () => handleCancelEvent(booking), + destructive: true, + }, + ]; + case "unconfirmed": + return [ + { + title: "Confirm booking", + onPress: () => handleConfirmBooking(booking), + destructive: false, + }, + { + title: "Reject booking", + onPress: () => handleRejectBooking(booking), + destructive: true, + }, + ]; + case "past": + return [ + { + title: "Report booking", + onPress: () => handleReportBooking(booking), + destructive: false, + }, + ]; + case "cancelled": + return [ + { + title: "Report booking", + onPress: () => handleReportBooking(booking), + destructive: false, + }, + ]; + default: + return []; + } + })(); + + return [...baseActions, ...specificActions]; + }; + + const handleOpenLocation = async (booking: Booking) => { + if (!booking.location) { + Alert.alert("No Location", "This booking doesn't have a location set."); + return; + } + + try { + // Check if location is a URL (starts with http:// or https://) + if (booking.location.match(/^https?:\/\//)) { + const supported = await Linking.canOpenURL(booking.location); + if (supported) { + await Linking.openURL(booking.location); + } else { + Alert.alert("Error", "Cannot open this URL on your device."); + } + } else { + // If it's not a URL, try to open it as a location in maps + const mapsUrl = + Platform.OS === "ios" + ? `maps://maps.apple.com/?q=${encodeURIComponent(booking.location)}` + : `geo:0,0?q=${encodeURIComponent(booking.location)}`; + + const supported = await Linking.canOpenURL(mapsUrl); + if (supported) { + await Linking.openURL(mapsUrl); + } else { + // Fallback to Google Maps web + const googleMapsUrl = `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent( + booking.location + )}`; + await Linking.openURL(googleMapsUrl); + } + } + } catch (error) { + Alert.alert("Error", "Failed to open location. Please try again."); + } + }; + + const handleRescheduleBooking = (booking: Booking) => { + Alert.alert("Reschedule Booking", "Reschedule functionality coming soon"); + }; + + const handleRequestReschedule = (booking: Booking) => { + Alert.alert("Request Reschedule", "Request reschedule functionality coming soon"); + }; + + const handleReportBooking = (booking: Booking) => { + Alert.alert("Report Booking", "Report functionality coming soon"); + }; + + const handleCancelEvent = (booking: Booking) => { + Alert.alert("Cancel Event", `Are you sure you want to cancel "${booking.title}"?`, [ + { text: "Cancel", style: "cancel" }, + { + text: "Cancel Event", + style: "destructive", + onPress: () => { + // Prompt for cancellation reason + Alert.prompt( + "Cancellation Reason", + "Please provide a reason for cancelling this booking:", + [ + { text: "Cancel", style: "cancel" }, + { + text: "Cancel Event", + style: "destructive", + onPress: async (reason) => { + try { + const cancellationReason = reason?.trim() || "Event cancelled by host"; + await CalComAPIService.cancelBooking(booking.uid, cancellationReason); + + // Remove the cancelled booking from local state or refresh the list + if (activeFilter === "upcoming") { + // For upcoming bookings, remove from list since it's now cancelled + const updatedBookings = bookings.filter((b) => b.uid !== booking.uid); + setBookings(updatedBookings); + setFilteredBookings(updatedBookings); + } else { + // For other filters, refresh to get updated data + await fetchBookings(); + } + + Alert.alert("Success", "Event cancelled successfully"); + } catch (error) { + console.error("Failed to cancel booking:", error); + Alert.alert("Error", "Failed to cancel event. Please try again."); + } + }, + }, + ], + "plain-text", + "", + "default" + ); + }, + }, + ]); + }; + + const handleConfirmBooking = (booking: Booking) => { + Alert.alert("Confirm Booking", "Confirm functionality coming soon"); + }; + + const handleRejectBooking = (booking: Booking) => { + Alert.alert("Reject Booking", `Are you sure you want to reject "${booking.title}"?`, [ + { text: "Cancel", style: "cancel" }, + { + text: "Reject", + style: "destructive", + onPress: () => { + Alert.alert("Reject Booking", "Reject functionality coming soon"); + }, + }, + ]); + }; + + const formatDateTime = (dateString: string) => { + const date = new Date(dateString); + return date.toLocaleDateString("en-US", { + weekday: "short", + month: "short", + day: "numeric", + hour: "numeric", + minute: "2-digit", + }); + }; + + const formatTime = (dateString: string) => { + if (!dateString) { + return ""; + } + try { + const date = new Date(dateString); + if (isNaN(date.getTime())) { + console.warn("Invalid date string:", dateString); + return ""; + } + return date.toLocaleTimeString("en-US", { + hour: "numeric", + minute: "2-digit", + hour12: true, + }); + } catch (error) { + console.error("Error formatting time:", error, dateString); + return ""; + } + }; + + const formatDate = (dateString: string, isUpcoming: boolean) => { + if (!dateString) { + return ""; + } + try { + const date = new Date(dateString); + if (isNaN(date.getTime())) { + console.warn("Invalid date string:", dateString); + return ""; + } + const bookingYear = date.getFullYear(); + const currentYear = new Date().getFullYear(); + const isDifferentYear = bookingYear !== currentYear; + + if (isUpcoming) { + // Format: "ddd, D MMM" or "ddd, D MMM YYYY" if different year + const weekday = date.toLocaleDateString("en-US", { weekday: "short" }); + const day = date.getDate(); + const month = date.toLocaleDateString("en-US", { month: "short" }); + if (isDifferentYear) { + return `${weekday}, ${day} ${month} ${bookingYear}`; + } + return `${weekday}, ${day} ${month}`; + } else { + // Format: "D MMMM YYYY" for past bookings + return date.toLocaleDateString("en-US", { + day: "numeric", + month: "long", + year: "numeric", + }); + } + } catch (error) { + console.error("Error formatting date:", error, dateString); + return ""; + } + }; + + const getStatusColor = (status: string) => { + // API v2 2024-08-13 returns status in lowercase, so normalize to uppercase for comparison + const normalizedStatus = status.toUpperCase(); + switch (normalizedStatus) { + case "ACCEPTED": + return "#34C759"; + case "PENDING": + return "#FF9500"; + case "CANCELLED": + return "#FF3B30"; + case "REJECTED": + return "#FF3B30"; + default: + return "#666"; + } + }; + + const formatStatusText = (status: string) => { + // Capitalize first letter for display + return status.charAt(0).toUpperCase() + status.slice(1).toLowerCase(); + }; + + const formatMonthYear = (dateString: string): string => { + if (!dateString) return ""; + try { + const date = new Date(dateString); + if (isNaN(date.getTime())) { + return ""; + } + const now = new Date(); + const isCurrentMonth = + date.getFullYear() === now.getFullYear() && date.getMonth() === now.getMonth(); + + if (isCurrentMonth) { + return "This month"; + } + + return date.toLocaleDateString("en-US", { + month: "long", + year: "numeric", + }); + } catch (error) { + return ""; + } + }; + + const getMonthYearKey = (dateString: string): string => { + if (!dateString) return ""; + try { + const date = new Date(dateString); + if (isNaN(date.getTime())) { + return ""; + } + return `${date.getFullYear()}-${date.getMonth()}`; + } catch (error) { + return ""; + } + }; + + type ListItem = + | { type: "monthHeader"; monthYear: string; key: string } + | { type: "booking"; booking: Booking; key: string }; + + const groupBookingsByMonth = (bookings: Booking[]): ListItem[] => { + const grouped: ListItem[] = []; + let currentMonthYear: string | null = null; + + bookings.forEach((booking) => { + const startTime = booking.start || booking.startTime || ""; + if (!startTime) return; + + const monthYearKey = getMonthYearKey(startTime); + const monthYear = formatMonthYear(startTime); + + if (monthYearKey !== currentMonthYear) { + currentMonthYear = monthYearKey; + grouped.push({ + type: "monthHeader", + monthYear, + key: `month-${monthYearKey}`, + }); + } + + grouped.push({ + type: "booking", + booking, + key: booking.id.toString(), + }); + }); + + return grouped; + }; + + const renderBooking = ({ item }: { item: Booking }) => { + const startTime = item.start || item.startTime || ""; + const endTime = item.end || item.endTime || ""; + const isUpcoming = new Date(endTime) >= new Date(); + const isPending = item.status?.toUpperCase() === "PENDING"; + const isCancelled = item.status?.toUpperCase() === "CANCELLED"; + const isRejected = item.status?.toUpperCase() === "REJECTED"; + + return ( + + handleBookingPress(item)} + onLongPress={() => { + setSelectedBooking(item); + setShowBookingActionsModal(true); + }} + style={{ paddingHorizontal: 16, paddingTop: 16, paddingBottom: 12 }} + > + {/* Time and Date Row */} + + + {formatDate(startTime, isUpcoming)} + + + {formatTime(startTime)} - {formatTime(endTime)} + + + + {/* Badges Row */} + + {isPending && ( + + Unconfirmed + + )} + + + {/* Title */} + + {item.title} + + + {/* Description */} + {item.description && ( + + "{item.description}" + + )} + + {/* Host and Attendees */} + {((item.hosts && item.hosts.length > 0) || + item.user || + (item.attendees && item.attendees.length > 0)) && ( + + {/* Host */} + {(item.hosts && item.hosts.length > 0) || item.user ? ( + <> + {item.hosts && item.hosts.length > 0 + ? item.hosts[0].name || item.hosts[0].email + : item.user?.name || item.user?.email} + + ) : null} + + {/* Separator */} + {((item.hosts && item.hosts.length > 0) || item.user) && + item.attendees && + item.attendees.length > 0 && and } + + {/* Attendees */} + {item.attendees && item.attendees.length > 0 && ( + <> + {item.attendees.length === 1 + ? item.attendees[0].name || item.attendees[0].email + : item.attendees + .slice(0, 2) + .map((att) => att.name || att.email) + .join(", ") + + (item.attendees.length > 2 ? ` and ${item.attendees.length - 2} more` : "")} + + )} + + )} + + + {/* Three dots button - below content, aligned to the right */} + + { + e.stopPropagation(); + setSelectedBooking(item); + setShowBookingActionsModal(true); + }} + > + + + + + ); + }; + + const renderListItem = ({ item }: { item: ListItem }) => { + if (item.type === "monthHeader") { + return ( + + {item.monthYear} + + ); + } + return renderBooking({ item: item.booking }); + }; + + if (loading) { + return ( + +
+ {renderSegmentedControl()} + + + Loading {activeFilter} bookings... + + + ); + } + + if (error) { + return ( + +
+ {renderSegmentedControl()} + + + + Unable to load bookings + + {error} + + Retry + + + + ); + } + + if (bookings.length === 0 && !loading) { + const emptyState = getEmptyStateContent(); + return ( + +
+ {renderSegmentedControl()} + + + + {emptyState.title} + + {emptyState.text} + + + ); + } + + if (filteredBookings.length === 0 && searchQuery.trim() !== "" && !loading) { + return ( + +
+ {renderSegmentedControl()} + + + No results found + + Try searching with different keywords + + + + ); + } + + return ( + +
+ {renderSegmentedControl()} + + + item.key} + renderItem={renderListItem} + contentContainerStyle={{ paddingBottom: 90 }} + refreshControl={} + showsVerticalScrollIndicator={false} + /> + + + + {/* Filter Modal */} + setShowFilterModal(false)} + > + setShowFilterModal(false)} + > + e.stopPropagation()} + className="w-[85%] max-w-[350px] rounded-2xl bg-white p-5" + > + + Filter by Event Type + + + {eventTypesLoading ? ( + + + Loading event types... + + ) : ( + + {eventTypes.map((eventType) => ( + handleEventTypeSelect(eventType.id, eventType.title)} + > + + {eventType.title} + + {selectedEventTypeId === eventType.id && ( + + )} + + ))} + + {eventTypes.length === 0 && ( + + No event types found + + )} + + )} + + + + + {/* Booking Actions Modal */} + setShowBookingActionsModal(false)} + > + setShowBookingActionsModal(false)} + > + e.stopPropagation()} + > + {/* Header */} + + + Booking Actions + + + + {/* Actions List */} + + {/* View Booking */} + { + setShowBookingActionsModal(false); + if (selectedBooking) { + handleBookingPress(selectedBooking); + } + }} + className="flex-row items-center p-2 hover:bg-gray-50 md:p-4" + > + + View Booking + + + {/* Separator */} + + + {/* Edit event label */} + + Edit event + + + {/* Request Reschedule */} + {activeFilter === "upcoming" && ( + { + setShowBookingActionsModal(false); + if (selectedBooking) { + handleRescheduleBooking(selectedBooking); + } + }} + className="flex-row items-center p-2 hover:bg-gray-50 md:p-4" + > + + Send Reschedule Request + + )} + + {/* Edit Location */} + {activeFilter === "upcoming" && ( + { + setShowBookingActionsModal(false); + Alert.alert("Edit Location", "Edit location functionality coming soon"); + }} + className="flex-row items-center p-2 hover:bg-gray-50 md:p-4" + > + + Edit Location + + )} + + {/* Add Guests */} + {activeFilter === "upcoming" && ( + { + setShowBookingActionsModal(false); + Alert.alert("Add Guests", "Add guests functionality coming soon"); + }} + className="flex-row items-center p-2 hover:bg-gray-50 md:p-4" + > + + Add Guests + + )} + + {/* Open Location */} + {selectedBooking?.location && ( + <> + + { + setShowBookingActionsModal(false); + if (selectedBooking) { + handleOpenLocation(selectedBooking); + } + }} + className="flex-row items-center p-2 hover:bg-gray-50 md:p-4" + > + + Open Location + + + )} + + {/* Separator */} + {(activeFilter === "past" || activeFilter === "upcoming") && ( + <> + + + {/* After event label */} + {activeFilter === "past" && ( + + After event + + )} + + {/* Mark as No-Show */} + {activeFilter === "past" && ( + { + setShowBookingActionsModal(false); + Alert.alert("Mark as No-Show", "Mark as no-show functionality coming soon"); + }} + className="flex-row items-center p-2 hover:bg-gray-50 md:p-4" + > + + Mark as No-Show + + )} + + )} + + {/* Confirm booking (for unconfirmed) */} + {activeFilter === "unconfirmed" && ( + <> + + { + setShowBookingActionsModal(false); + if (selectedBooking) { + handleConfirmBooking(selectedBooking); + } + }} + className="flex-row items-center p-2 hover:bg-gray-50 md:p-4" + > + + Confirm booking + + + )} + + {/* Separator */} + + + {/* Report Booking */} + { + setShowBookingActionsModal(false); + if (selectedBooking) { + handleReportBooking(selectedBooking); + } + }} + className="flex-row items-center p-2 hover:bg-gray-50 md:p-4" + > + + Report Booking + + + {/* Separator */} + + + {/* Cancel/Reject Booking */} + {(activeFilter === "upcoming" || activeFilter === "unconfirmed") && ( + { + setShowBookingActionsModal(false); + if (selectedBooking) { + if (activeFilter === "unconfirmed") { + handleRejectBooking(selectedBooking); + } else { + handleCancelEvent(selectedBooking); + } + } + }} + className="flex-row items-center p-2 hover:bg-gray-50 md:p-4" + > + + + {activeFilter === "unconfirmed" ? "Reject booking" : "Cancel event"} + + + )} + + + {/* Cancel button */} + + setShowBookingActionsModal(false)} + > + Cancel + + + + + + + ); +} diff --git a/companion/app/(tabs)/event-types.tsx b/companion/app/(tabs)/event-types.tsx new file mode 100644 index 0000000000..7b195fd909 --- /dev/null +++ b/companion/app/(tabs)/event-types.tsx @@ -0,0 +1,1065 @@ +import { Ionicons } from "@expo/vector-icons"; +import { useRouter } from "expo-router"; +import React, { useState, useEffect, useRef } from "react"; +import { + View, + Text, + FlatList, + ScrollView, + ActivityIndicator, + TouchableOpacity, + RefreshControl, + TextInput, + ActionSheetIOS, + Share, + Alert, + Clipboard, + Platform, + Modal, + KeyboardAvoidingView, + Linking, +} from "react-native"; +import Svg, { Path } from "react-native-svg"; + +import { CalComAPIService, EventType } from "../../services/calcom"; +import { Header } from "../../components/Header"; +import { Tooltip } from "../../components/Tooltip"; +import { FullScreenModal } from "../../components/FullScreenModal"; +import { slugify } from "../../utils/slugify"; + +export default function EventTypes() { + console.log("EventTypes component rendering"); + const router = useRouter(); + const [eventTypes, setEventTypes] = useState([]); + const [filteredEventTypes, setFilteredEventTypes] = useState([]); + const [searchQuery, setSearchQuery] = useState(""); + const [loading, setLoading] = useState(true); + const [refreshing, setRefreshing] = useState(false); + const [error, setError] = useState(null); + const isMountedRef = useRef(true); + + // Modal state for creating new event type + const [showCreateModal, setShowCreateModal] = useState(false); + const [creating, setCreating] = useState(false); + const [newEventTitle, setNewEventTitle] = useState(""); + const [newEventSlug, setNewEventSlug] = useState(""); + const [newEventDescription, setNewEventDescription] = useState(""); + const [newEventDuration, setNewEventDuration] = useState("15"); + const [username, setUsername] = useState(""); + const [isSlugManuallyEdited, setIsSlugManuallyEdited] = useState(false); + + // Modal state for web platform action sheet + const [showActionModal, setShowActionModal] = useState(false); + const [selectedEventType, setSelectedEventType] = useState(null); + + // Modal state for New button menu + const [showNewModal, setShowNewModal] = useState(false); + + // Modal state for delete confirmation + const [showDeleteModal, setShowDeleteModal] = useState(false); + const [eventTypeToDelete, setEventTypeToDelete] = useState(null); + const [isDeleting, setIsDeleting] = useState(false); + + // Toast state for web platform + const [showToast, setShowToast] = useState(false); + const [toastMessage, setToastMessage] = useState(""); + const [copiedEventTypeId, setCopiedEventTypeId] = useState(null); + + // Function to show toast + const showToastMessage = (message: string, eventTypeId?: number) => { + setToastMessage(message); + setShowToast(true); + if (eventTypeId) { + setCopiedEventTypeId(eventTypeId); + } + setTimeout(() => { + setShowToast(false); + setCopiedEventTypeId(null); + }, 2000); + }; + + useEffect(() => { + isMountedRef.current = true; + return () => { + isMountedRef.current = false; + }; + }, []); + + // Fetch username on mount + useEffect(() => { + const fetchUsername = async () => { + try { + const fetchedUsername = await CalComAPIService.getUsername(); + setUsername(fetchedUsername); + } catch (error) { + console.error("Failed to fetch username:", error); + // Keep default username if fetch fails + } + }; + fetchUsername(); + }, []); + + const fetchEventTypes = async () => { + try { + setError(null); + + const data = await CalComAPIService.getEventTypes(); + + if (isMountedRef.current) { + if (Array.isArray(data)) { + setEventTypes(data); + setFilteredEventTypes(data); + } else { + setEventTypes([]); + setFilteredEventTypes([]); + } + } + } catch (err) { + console.error("🎯 EventTypesScreen: Error fetching event types:", err); + if (isMountedRef.current) { + setError("Failed to load event types. Please check your API key and try again."); + } + } finally { + if (isMountedRef.current) { + setLoading(false); + setRefreshing(false); + } + } + }; + + useEffect(() => { + fetchEventTypes(); + }, []); + + const onRefresh = () => { + setRefreshing(true); + fetchEventTypes(); + }; + + const handleSearch = (query: string) => { + setSearchQuery(query); + if (query.trim() === "") { + setFilteredEventTypes(eventTypes); + } else { + const filtered = eventTypes.filter( + (eventType) => + eventType.title.toLowerCase().includes(query.toLowerCase()) || + (eventType.description && + eventType.description.toLowerCase().includes(query.toLowerCase())) + ); + setFilteredEventTypes(filtered); + } + }; + + const formatDuration = (minutes: number | undefined) => { + if (!minutes || minutes <= 0) { + return "0m"; + } + if (minutes < 60) { + return `${minutes}m`; + } + const hours = Math.floor(minutes / 60); + const remainingMinutes = minutes % 60; + return remainingMinutes > 0 ? `${hours}h ${remainingMinutes}m` : `${hours}h`; + }; + + const getDuration = (eventType: EventType): number => { + // Prefer lengthInMinutes (API field), fallback to length for backwards compatibility + return eventType.lengthInMinutes ?? eventType.length ?? 0; + }; + + const normalizeMarkdown = (text: string): string => { + if (!text) return ""; + + return ( + text + // Remove HTML tags including
,
,

, etc. + .replace(/<[^>]*>/g, " ") + // Convert HTML entities + .replace(/&/g, "&") + .replace(/</g, "<") + .replace(/>/g, ">") + .replace(/"/g, '"') + .replace(/'/g, "'") + .replace(/ /g, " ") + // Convert markdown links [text](url) to just text + .replace(/\[([^\]]+)\]\([^)]+\)/g, "$1") + // Remove bold/italic markers **text** or *text* + .replace(/\*\*([^*]+)\*\*/g, "$1") + .replace(/\*([^*]+)\*/g, "$1") + // Remove inline code `text` + .replace(/`([^`]+)`/g, "$1") + // Remove strikethrough ~~text~~ + .replace(/~~([^~]+)~~/g, "$1") + // Remove heading markers # ## ### + .replace(/^#{1,6}\s+/gm, "") + // Remove blockquote markers > + .replace(/^>\s+/gm, "") + // Remove list markers - * + + .replace(/^[\s]*[-*+]\s+/gm, "") + // Remove numbered list markers 1. 2. etc + .replace(/^[\s]*\d+\.\s+/gm, "") + // Normalize multiple whitespace/newlines to single space + .replace(/\s+/g, " ") + // Trim whitespace + .trim() + ); + }; + + const handleEventTypePress = (eventType: EventType) => { + handleEdit(eventType); + }; + + const handleEventTypeLongPress = (eventType: EventType) => { + if (Platform.OS === "web") { + // Show custom modal for web platform + setSelectedEventType(eventType); + setShowActionModal(true); + return; + } + + if (Platform.OS !== "ios") { + // Fallback for non-iOS platforms (Android) + Alert.alert(eventType.title, eventType.description || "", [ + { text: "Cancel", style: "cancel" }, + { text: "Edit", onPress: () => handleEdit(eventType) }, + { text: "Duplicate", onPress: () => handleDuplicate(eventType) }, + { text: "Delete", style: "destructive", onPress: () => handleDelete(eventType) }, + ]); + return; + } + + ActionSheetIOS.showActionSheetWithOptions( + { + options: ["Cancel", "Edit", "Duplicate", "Delete"], + destructiveButtonIndex: 3, // Delete button + cancelButtonIndex: 0, + title: eventType.title, + message: eventType.description ? normalizeMarkdown(eventType.description) : undefined, + }, + (buttonIndex) => { + switch (buttonIndex) { + case 1: // Edit + handleEdit(eventType); + break; + case 2: // Duplicate + handleDuplicate(eventType); + break; + case 3: // Delete + handleDelete(eventType); + break; + default: + // Cancel - do nothing + break; + } + } + ); + }; + + const handleCopyLink = async (eventType: EventType) => { + try { + const link = await CalComAPIService.buildEventTypeLink(eventType.slug); + Clipboard.setString(link); + + if (Platform.OS === "web") { + showToastMessage("Link copied!", eventType.id); + } else { + Alert.alert("Link Copied", "Event type link copied!"); + } + } catch (error) { + if (Platform.OS === "web") { + showToastMessage("Failed to copy link"); + } else { + Alert.alert("Error", "Failed to copy link. Please try again."); + } + } + }; + + const handleShare = async (eventType: EventType) => { + try { + const link = await CalComAPIService.buildEventTypeLink(eventType.slug); + await Share.share({ + message: `Book a meeting: ${eventType.title}`, + url: link, + }); + } catch (error) { + Alert.alert("Error", "Failed to share link. Please try again."); + } + }; + + const handleEdit = (eventType: EventType) => { + const duration = getDuration(eventType); + router.push({ + pathname: "/event-type-detail", + params: { + id: eventType.id.toString(), + title: eventType.title, + description: eventType.description || "", + duration: duration.toString(), + price: eventType.price?.toString() || "", + currency: eventType.currency || "", + slug: eventType.slug || "", + }, + }); + }; + + const handleDelete = (eventType: EventType) => { + setEventTypeToDelete(eventType); + setShowDeleteModal(true); + }; + + const confirmDelete = async () => { + if (!eventTypeToDelete) return; + + setIsDeleting(true); + try { + await CalComAPIService.deleteEventType(eventTypeToDelete.id); + + // Only update state if component is still mounted + if (isMountedRef.current) { + // Remove the deleted event type from local state + const updatedEventTypes = eventTypes.filter((et) => et.id !== eventTypeToDelete.id); + setEventTypes(updatedEventTypes); + setFilteredEventTypes(updatedEventTypes); + + // Close modal and reset state + setShowDeleteModal(false); + setEventTypeToDelete(null); + + if (Platform.OS === "web") { + showToastMessage("Event type deleted successfully"); + } else { + Alert.alert("Success", "Event type deleted successfully"); + } + } + } catch (error) { + console.error("Failed to delete event type:", error); + if (isMountedRef.current) { + if (Platform.OS === "web") { + showToastMessage("Failed to delete event type"); + } else { + Alert.alert("Error", "Failed to delete event type. Please try again."); + } + } + } finally { + setIsDeleting(false); + } + }; + + const handleDuplicate = async (eventType: EventType) => { + try { + // Generate a new title and slug for the duplicate + const newTitle = `${eventType.title} (copy)`; + let newSlug = `${eventType.slug}-copy`; + + // Check if slug already exists and append a number if needed + let counter = 1; + while (eventTypes.some((et) => et.slug === newSlug)) { + newSlug = `${eventType.slug}-copy-${counter}`; + counter++; + } + + const duration = getDuration(eventType); + + // Create the duplicate event type + const duplicatedEventType = await CalComAPIService.createEventType({ + title: newTitle, + slug: newSlug, + lengthInMinutes: duration, + description: eventType.description || undefined, + }); + + // Refresh the list + await fetchEventTypes(); + + if (Platform.OS === "web") { + showToastMessage("Event type duplicated successfully"); + } else { + Alert.alert("Success", "Event type duplicated successfully"); + } + + // Navigate to edit the newly created duplicate + router.push({ + pathname: "/event-type-detail", + params: { + id: duplicatedEventType.id.toString(), + title: duplicatedEventType.title, + description: duplicatedEventType.description || "", + duration: ( + duplicatedEventType.lengthInMinutes || + duplicatedEventType.length || + duration + ).toString(), + slug: duplicatedEventType.slug || "", + }, + }); + } catch (error) { + console.error("Failed to duplicate event type:", error); + if (Platform.OS === "web") { + showToastMessage("Failed to duplicate event type"); + } else { + Alert.alert("Error", "Failed to duplicate event type. Please try again."); + } + } + }; + + const handlePreview = async (eventType: EventType) => { + try { + const link = await CalComAPIService.buildEventTypeLink(eventType.slug); + // Open in browser + if (Platform.OS === "web") { + window.open(link, "_blank"); + } else { + // For mobile, use Linking + await Linking.openURL(link); + } + } catch (error) { + console.error("Failed to open preview:", error); + if (Platform.OS === "web") { + showToastMessage("Failed to open preview"); + } else { + Alert.alert("Error", "Failed to open preview. Please try again."); + } + } + }; + + const handleCreateNew = () => { + handleOpenCreateModal(); + }; + + const handleOpenCreateModal = () => { + setShowCreateModal(true); + }; + + const handleCloseCreateModal = () => { + setShowCreateModal(false); + setNewEventTitle(""); + setNewEventSlug(""); + setNewEventDescription(""); + setNewEventDuration("15"); + setIsSlugManuallyEdited(false); + }; + + const handleCreateEventType = async () => { + if (!newEventTitle.trim()) { + Alert.alert("Error", "Please enter a title for your event type"); + return; + } + + if (!newEventSlug.trim()) { + Alert.alert("Error", "Please enter a URL for your event type"); + return; + } + + const duration = parseInt(newEventDuration); + if (isNaN(duration) || duration <= 0) { + Alert.alert("Error", "Please enter a valid duration"); + return; + } + + setCreating(true); + try { + // Create the event type with the form data + const newEventType = await CalComAPIService.createEventType({ + title: newEventTitle.trim(), + slug: newEventSlug.trim(), + lengthInMinutes: duration, + description: newEventDescription.trim() || undefined, + }); + + // Close modal and reset form + handleCloseCreateModal(); + + // Refresh the list + await fetchEventTypes(); + + // Navigate to edit the newly created event type + router.push({ + pathname: "/event-type-detail", + params: { + id: newEventType.id.toString(), + title: newEventType.title, + description: newEventType.description || "", + duration: (newEventType.lengthInMinutes || newEventType.length || 15).toString(), + slug: newEventType.slug || "", + }, + }); + } catch (error) { + console.error("Failed to create event type:", error); + Alert.alert("Error", "Failed to create event type. Please try again."); + } finally { + setCreating(false); + } + }; + + const renderEventType = ({ item, index }: { item: EventType; index: number }) => { + const duration = getDuration(item); + const isLast = index === filteredEventTypes.length - 1; + + return ( + handleEventTypePress(item)} + onLongPress={() => handleEventTypeLongPress(item)} + style={{ paddingHorizontal: 16, paddingVertical: 16 }} + > + + + + {item.title} + + {item.description && ( + + {normalizeMarkdown(item.description)} + + )} + + + + {formatDuration(duration)} + + + {(item.price != null && item.price > 0) || item.requiresConfirmation ? ( + + {item.price != null && item.price > 0 && ( + + {item.currency || "$"} + {item.price} + + )} + {item.requiresConfirmation && ( + + Requires Confirmation + + )} + + ) : null} + + + + handlePreview(item)} + > + + + + + handleCopyLink(item)} + > + {copiedEventTypeId === item.id ? ( + + ) : ( + + + + + )} + + + + handleEventTypeLongPress(item)} + > + + + + + + + ); + }; + + if (loading) { + return ( + +

+ + + Loading event types... + + + ); + } + + if (error) { + return ( + +
+ + + + Unable to load event types + + {error} + + Retry + + + + ); + } + + if (eventTypes.length === 0) { + return ( + +
+ + + + + + No event types found + + Create your first event type in Cal.com + + + + ); + } + + if (filteredEventTypes.length === 0 && searchQuery.trim() !== "") { + return ( + +
+ + + + + + No results found + + Try searching with different keywords + + + + ); + } + + return ( + +
+ + + + + New + + + } + showsVerticalScrollIndicator={false} + > + + + {filteredEventTypes.map((item, index) => ( + {renderEventType({ item, index })} + ))} + + + + + {/* Create Event Type Modal */} + + + e.stopPropagation()} + style={{ + shadowColor: "#000", + shadowOffset: { width: 0, height: 20 }, + shadowOpacity: 0.25, + shadowRadius: 25, + elevation: 24, + }} + > + {/* Header */} + + + Add a new event type + + + Set up event types to offer different types of meetings. + + + + {/* Content */} + + {/* Title */} + + Title + { + setNewEventTitle(text); + // Auto-generate slug from title if user hasn't manually edited it + if (!isSlugManuallyEdited) { + setNewEventSlug(slugify(text, true)); + } + }} + autoFocus + autoCapitalize="words" + returnKeyType="next" + /> + + + {/* URL */} + + URL + + https://cal.com/{username}/ + { + setIsSlugManuallyEdited(true); + setNewEventSlug(slugify(text, true)); + }} + autoCapitalize="none" + autoCorrect={false} + returnKeyType="next" + /> + + + + {/* Description */} + + Description + + + + {/* Duration */} + + Duration + + + minutes + + + + + {/* Footer */} + + + + Close + + + Continue + + + + + + + + {/* Action Modal for Web Platform */} + { + setShowActionModal(false); + setSelectedEventType(null); + }} + > + { + setShowActionModal(false); + setSelectedEventType(null); + }} + > + e.stopPropagation()} + > + {selectedEventType && ( + <> + + + {selectedEventType.title} + + {selectedEventType.description && ( + + {normalizeMarkdown(selectedEventType.description)} + + )} + + + + { + setShowActionModal(false); + const eventType = selectedEventType; + setSelectedEventType(null); + if (eventType) handleEdit(eventType); + }} + > + + Edit + + + { + setShowActionModal(false); + const eventType = selectedEventType; + setSelectedEventType(null); + if (eventType) handleDuplicate(eventType); + }} + > + + Duplicate + + + {/* Separator before delete button */} + + + { + setShowActionModal(false); + const eventType = selectedEventType; + setSelectedEventType(null); + if (eventType) handleDelete(eventType); + }} + > + + Delete + + + + + { + setShowActionModal(false); + setSelectedEventType(null); + }} + > + Cancel + + + + )} + + + + + {/* New Menu Modal for Web Platform */} + setShowNewModal(false)} + > + setShowNewModal(false)} + > + e.stopPropagation()} + > + {/* Header */} + + New + Choose what to create + + + {/* Options List */} + + {/* New Event Type */} + { + setShowNewModal(false); + // Small delay to ensure the "New" modal closes before opening create modal + setTimeout(() => { + handleOpenCreateModal(); + }, 100); + }} + className="flex-row items-center p-2 hover:bg-gray-50 md:p-4" + > + + New Event Type + + + + {/* Cancel button */} + + setShowNewModal(false)} + > + Cancel + + + + + + + {/* Delete Confirmation Modal */} + { + if (!isDeleting) { + setShowDeleteModal(false); + setEventTypeToDelete(null); + } + }} + > + + + {/* Header with icon and title */} + + + {/* Danger icon */} + + + + + {/* Title and description */} + + + Delete Event Type + + + {eventTypeToDelete && ( + <> + This will permanently delete the "{eventTypeToDelete.title}" event type. + This action cannot be undone. + + )} + + + + + + {/* Footer with buttons */} + + + Delete + + + { + setShowDeleteModal(false); + setEventTypeToDelete(null); + }} + disabled={isDeleting} + > + Cancel + + + + + + + {/* Toast for Web Platform */} + {showToast && ( + + + {toastMessage} + + + )} + + ); +} diff --git a/companion/app/(tabs)/more.tsx b/companion/app/(tabs)/more.tsx new file mode 100644 index 0000000000..d33ca5250b --- /dev/null +++ b/companion/app/(tabs)/more.tsx @@ -0,0 +1,114 @@ +import React from "react"; +import { View, Text, TouchableOpacity, ScrollView, Linking, Alert } from "react-native"; +import { Ionicons } from "@expo/vector-icons"; +import { useRouter } from "expo-router"; +import { Header } from "../../components/Header"; +import { LogoutButton } from "../../components/LogoutButton"; + +interface MoreMenuItem { + name: string; + icon: keyof typeof Ionicons.glyphMap; + isExternal?: boolean; + href?: string; + onPress?: () => void; +} + +export default function More() { + const router = useRouter(); + + const openExternalLink = async (url: string, fallbackMessage: string) => { + try { + const supported = await Linking.canOpenURL(url); + if (supported) { + await Linking.openURL(url); + } else { + Alert.alert("Error", `Cannot open ${fallbackMessage} on your device.`); + } + } catch (error) { + console.error(`Failed to open ${url}:`, error); + Alert.alert("Error", `Failed to open ${fallbackMessage}. Please try again.`); + } + }; + + const menuItems: MoreMenuItem[] = [ + { + name: "Profile", + icon: "person-outline", + isExternal: true, + onPress: () => + openExternalLink("https://app.cal.com/settings/my-account/profile", "Profile page"), + }, + { + name: "Apps", + icon: "grid-outline", + isExternal: true, + onPress: () => openExternalLink("https://app.cal.com/apps", "Apps page"), + }, + { + name: "Routing", + icon: "git-branch-outline", + isExternal: true, + onPress: () => openExternalLink("https://app.cal.com/routing", "Routing page"), + }, + { + name: "Workflows", + icon: "flash-outline", + isExternal: true, + onPress: () => openExternalLink("https://app.cal.com/workflows", "Workflows page"), + }, + { + name: "Insights", + icon: "bar-chart-outline", + isExternal: true, + onPress: () => openExternalLink("https://app.cal.com/insights", "Insights page"), + }, + { + name: "Support", + icon: "help-circle-outline", + isExternal: true, + onPress: () => openExternalLink("https://go.cal.com/support", "Support"), + }, + ]; + + return ( + +
+ + + {menuItems.map((item, index) => ( + router.push(item.href!) : item.onPress} + className={`flex-row items-center justify-between bg-white px-5 py-5 active:bg-[#F8F9FA] ${ + index < menuItems.length - 1 ? "border-b border-[#E5E5EA]" : "" + }`} + > + + + {item.name} + + {item.isExternal ? ( + + ) : ( + + )} + + ))} + + + {/* Authentication Info and Logout */} + + + + + + We view the companion as an extension of the web application. If you are performing any + complicated actions, please refer back to the web application. + + + + ); +} diff --git a/companion/app/_layout.tsx b/companion/app/_layout.tsx new file mode 100644 index 0000000000..da89a2764f --- /dev/null +++ b/companion/app/_layout.tsx @@ -0,0 +1,48 @@ +import { Stack } from "expo-router"; +import { Platform, View, StatusBar } from "react-native"; +import { AuthProvider, useAuth } from "../contexts/AuthContext"; +import LoginScreen from "../components/LoginScreen"; +import "../global.css"; + +function RootLayoutContent() { + const { isAuthenticated } = useAuth(); + + const content = isAuthenticated ? ( + + + + ) : ( + + ); + + const containerClass = + Platform.OS === "web" + ? "w-[400px] flex-1 flex-col self-end border-l border-gray-200 bg-white" + : "bg-white"; + + const containerStyle = + Platform.OS === "web" + ? { + width: 400, + flex: 1, + display: "flex", + flexDirection: "column", + pointerEvents: "auto", + } + : { flex: 1 }; + + return ( + + + {content} + + ); +} + +export default function RootLayout() { + return ( + + + + ); +} diff --git a/companion/app/availability-detail.tsx b/companion/app/availability-detail.tsx new file mode 100644 index 0000000000..4e0f33ab8e --- /dev/null +++ b/companion/app/availability-detail.tsx @@ -0,0 +1,994 @@ +import { Ionicons } from "@expo/vector-icons"; +import { GlassView } from "expo-glass-effect"; +import { useRouter, useLocalSearchParams, Stack } from "expo-router"; +import React, { useState, useEffect } from "react"; +import { + View, + Text, + ScrollView, + TouchableOpacity, + TextInput, + Alert, + ActivityIndicator, + Switch, +} from "react-native"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; + +import { CalComAPIService, Schedule } from "../services/calcom"; +import { ScheduleAvailability } from "../services/types"; +import { FullScreenModal } from "../components/FullScreenModal"; + +const DAYS = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]; +const DAY_ABBREVIATIONS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; + +// Convert 24-hour time to 12-hour format with AM/PM +const formatTime12Hour = (time24: string): string => { + const [hours, minutes] = time24.split(":"); + const hour = parseInt(hours, 10); + const min = minutes || "00"; + const period = hour >= 12 ? "PM" : "AM"; + const hour12 = hour === 0 ? 12 : hour > 12 ? hour - 12 : hour; + // Pad single-digit hours with leading zero for consistent width + const hour12Padded = hour12.toString().padStart(2, "0"); + return `${hour12Padded}:${min} ${period}`; +}; + +// Format availability for display +const formatAvailabilityDisplay = ( + availability: Record +): string[] => { + // Group slots by time range + const timeRangeMap: Record = {}; + + Object.keys(availability).forEach((dayIndexStr) => { + const dayIndex = Number(dayIndexStr); + const slots = availability[dayIndex]; + if (slots && slots.length > 0) { + slots.forEach((slot) => { + const timeKey = `${slot.startTime}-${slot.endTime}`; + if (!timeRangeMap[timeKey]) { + timeRangeMap[timeKey] = []; + } + timeRangeMap[timeKey].push(dayIndex); + }); + } + }); + + // Format each time range group + const formatted: string[] = []; + Object.keys(timeRangeMap).forEach((timeKey) => { + const days = timeRangeMap[timeKey].sort((a, b) => a - b); + const [startTime, endTime] = timeKey.split("-"); + const dayNames = days.map((day) => DAY_ABBREVIATIONS[day]).join(", "); + const timeRange = `${formatTime12Hour(startTime)} - ${formatTime12Hour(endTime)}`; + formatted.push(`${dayNames}, ${timeRange}`); + }); + + return formatted; +}; + +// Generate time options (15-minute intervals) +const generateTimeOptions = () => { + const options: string[] = []; + for (let hour = 0; hour < 24; hour++) { + for (let minute = 0; minute < 60; minute += 15) { + const h = hour.toString().padStart(2, "0"); + const m = minute.toString().padStart(2, "0"); + options.push(`${h}:${m}`); + } + } + return options; +}; + +const TIME_OPTIONS = generateTimeOptions(); + +export default function AvailabilityDetail() { + const router = useRouter(); + const { id } = useLocalSearchParams<{ id: string }>(); + const insets = useSafeAreaInsets(); + + const [loading, setLoading] = useState(true); + const [saving, setSaving] = useState(false); + const [schedule, setSchedule] = useState(null); + const [scheduleName, setScheduleName] = useState(""); + const [timeZone, setTimeZone] = useState(""); + const [isDefault, setIsDefault] = useState(false); + const [availability, setAvailability] = useState>({}); + const [showTimezoneModal, setShowTimezoneModal] = useState(false); + const [showTimePicker, setShowTimePicker] = useState<{ + dayIndex: number; + slotIndex: number; + type: "start" | "end"; + } | null>(null); + const [overrides, setOverrides] = useState< + Array<{ + date: string; // Format: "2024-05-20" + startTime: string; // Format: "12:00" or "00:00" for unavailable + endTime: string; // Format: "14:00" or "00:00" for unavailable + }> + >([]); + const [showOverrideModal, setShowOverrideModal] = useState(false); + const [editingOverride, setEditingOverride] = useState(null); + const [overrideDate, setOverrideDate] = useState(""); + const [overrideStartTime, setOverrideStartTime] = useState("09:00"); + const [overrideEndTime, setOverrideEndTime] = useState("17:00"); + const [isUnavailable, setIsUnavailable] = useState(false); + const [showOverrideTimePicker, setShowOverrideTimePicker] = useState<{ + type: "start" | "end"; + } | null>(null); + + // Common timezones + const timezones = [ + "America/New_York", + "America/Chicago", + "America/Denver", + "America/Los_Angeles", + "Europe/London", + "Europe/Paris", + "Europe/Berlin", + "Asia/Tokyo", + "Asia/Shanghai", + "Asia/Kolkata", + "Australia/Sydney", + "UTC", + ]; + + useEffect(() => { + if (id) { + fetchSchedule(); + } + }, [id]); + + const fetchSchedule = async () => { + try { + setLoading(true); + const scheduleData = await CalComAPIService.getScheduleById(Number(id)); + + if (scheduleData) { + setSchedule(scheduleData); + setScheduleName(scheduleData.name || ""); + setTimeZone(scheduleData.timeZone || "UTC"); + setIsDefault(scheduleData.isDefault || false); + + // Convert availability array to day-indexed object + const availabilityMap: Record = {}; + + // Map day names to numbers + const dayNameToNumber: Record = { + Sunday: 0, + Monday: 1, + Tuesday: 2, + Wednesday: 3, + Thursday: 4, + Friday: 5, + Saturday: 6, + }; + + if (scheduleData.availability && Array.isArray(scheduleData.availability)) { + scheduleData.availability.forEach((slot) => { + // Handle both string day names and number day formats + let days: number[] = []; + if (Array.isArray(slot.days)) { + days = slot.days + .map((day) => { + // If it's a day name string (e.g., "Sunday", "Monday") + if (typeof day === "string" && dayNameToNumber[day] !== undefined) { + return dayNameToNumber[day]; + } + // If it's a number string (e.g., "0", "1") + if (typeof day === "string") { + const parsed = parseInt(day, 10); + if (!isNaN(parsed) && parsed >= 0 && parsed <= 6) { + return parsed; + } + } + // If it's already a number + if (typeof day === "number" && day >= 0 && day <= 6) { + return day; + } + return null; + }) + .filter((day): day is number => day !== null); + } + + days.forEach((day) => { + if (!availabilityMap[day]) { + availabilityMap[day] = []; + } + availabilityMap[day].push({ + days: [day.toString()], + startTime: slot.startTime || "09:00:00", + endTime: slot.endTime || "17:00:00", + }); + }); + }); + } + + setAvailability(availabilityMap); + + // Load overrides if they exist + if (scheduleData.overrides && Array.isArray(scheduleData.overrides)) { + const formattedOverrides = scheduleData.overrides.map((override) => ({ + date: override.date || "", + startTime: override.startTime || "00:00", + endTime: override.endTime || "00:00", + })); + setOverrides(formattedOverrides); + } else { + setOverrides([]); + } + } + } catch (error) { + console.error("Error fetching schedule:", error); + Alert.alert("Error", "Failed to load schedule. Please try again."); + router.back(); + } finally { + setLoading(false); + } + }; + + const toggleDay = (dayIndex: number) => { + setAvailability((prev) => { + const newAvailability = { ...prev }; + if (newAvailability[dayIndex] && newAvailability[dayIndex].length > 0) { + // Disable day - remove availability + delete newAvailability[dayIndex]; + } else { + // Enable day - add default time slot (9 AM - 5 PM) + newAvailability[dayIndex] = [ + { + days: [dayIndex.toString()], + startTime: "09:00:00", + endTime: "17:00:00", + }, + ]; + } + return newAvailability; + }); + }; + + const addTimeSlot = (dayIndex: number) => { + setAvailability((prev) => { + const newAvailability = { ...prev }; + if (!newAvailability[dayIndex]) { + newAvailability[dayIndex] = []; + } + newAvailability[dayIndex].push({ + days: [dayIndex.toString()], + startTime: "09:00:00", + endTime: "17:00:00", + }); + return newAvailability; + }); + }; + + const removeTimeSlot = (dayIndex: number, slotIndex: number) => { + setAvailability((prev) => { + const newAvailability = { ...prev }; + if (newAvailability[dayIndex]) { + newAvailability[dayIndex] = newAvailability[dayIndex].filter((_, i) => i !== slotIndex); + if (newAvailability[dayIndex].length === 0) { + delete newAvailability[dayIndex]; + } + } + return newAvailability; + }); + }; + + const updateTimeSlot = ( + dayIndex: number, + slotIndex: number, + type: "start" | "end", + time: string + ) => { + setAvailability((prev) => { + const newAvailability = { ...prev }; + if (newAvailability[dayIndex] && newAvailability[dayIndex][slotIndex]) { + const slot = { ...newAvailability[dayIndex][slotIndex] }; + if (type === "start") { + slot.startTime = `${time}:00`; + } else { + slot.endTime = `${time}:00`; + } + newAvailability[dayIndex][slotIndex] = slot; + } + return newAvailability; + }); + }; + + const handleSave = async () => { + try { + setSaving(true); + + // Convert availability object back to array format with day names + const availabilityArray: Array<{ + days: string[]; // Day names like "Monday", "Tuesday" + startTime: string; // Format: "09:00" + endTime: string; // Format: "10:00" + }> = []; + + Object.keys(availability).forEach((dayIndexStr) => { + const dayIndex = Number(dayIndexStr); + const slots = availability[dayIndex]; + if (slots && slots.length > 0) { + slots.forEach((slot) => { + // Convert day number to day name + const dayName = DAYS[dayIndex]; + + // Format time from "09:00:00" to "09:00" (remove seconds) + const formatTimeForAPI = (time: string): string => { + return time.substring(0, 5); // Take only HH:MM + }; + + availabilityArray.push({ + days: [dayName], + startTime: formatTimeForAPI(slot.startTime), + endTime: formatTimeForAPI(slot.endTime), + }); + }); + } + }); + + // Format overrides for API (ensure times are in HH:MM format) + const formattedOverrides = overrides.map((override) => ({ + date: override.date, + startTime: override.startTime.substring(0, 5), // Ensure HH:MM format + endTime: override.endTime.substring(0, 5), // Ensure HH:MM format + })); + + await CalComAPIService.updateSchedule(Number(id), { + name: scheduleName, + timeZone, + availability: availabilityArray, + isDefault: isDefault, + overrides: formattedOverrides, + }); + + Alert.alert("Success", "Schedule updated successfully", [ + { text: "OK", onPress: () => router.back() }, + ]); + } catch (error) { + Alert.alert("Error", "Failed to update schedule. Please try again."); + } finally { + setSaving(false); + } + }; + + const handleSetAsDefault = async () => { + try { + await CalComAPIService.updateSchedule(Number(id), { + isDefault: true, + }); + setIsDefault(true); + Alert.alert("Success", "Schedule set as default successfully"); + } catch (error) { + Alert.alert("Error", "Failed to set schedule as default. Please try again."); + } + }; + + const handleDelete = () => { + Alert.alert("Delete Schedule", `Are you sure you want to delete "${scheduleName}"?`, [ + { text: "Cancel", style: "cancel" }, + { + text: "Delete", + style: "destructive", + onPress: async () => { + try { + await CalComAPIService.deleteSchedule(Number(id)); + Alert.alert("Success", "Schedule deleted successfully", [ + { text: "OK", onPress: () => router.back() }, + ]); + } catch (error) { + Alert.alert("Error", "Failed to delete schedule. Please try again."); + } + }, + }, + ]); + }; + + const handleAddOverride = () => { + setEditingOverride(null); + setOverrideDate(""); + setOverrideStartTime("09:00"); + setOverrideEndTime("17:00"); + setIsUnavailable(false); + setShowOverrideModal(true); + }; + + const handleEditOverride = (index: number) => { + const override = overrides[index]; + setEditingOverride(index); + setOverrideDate(override.date); + setOverrideStartTime(override.startTime); + setOverrideEndTime(override.endTime); + setIsUnavailable(override.startTime === "00:00" && override.endTime === "00:00"); + setShowOverrideModal(true); + }; + + const handleDeleteOverride = (index: number) => { + Alert.alert("Delete Override", "Are you sure you want to delete this date override?", [ + { text: "Cancel", style: "cancel" }, + { + text: "Delete", + style: "destructive", + onPress: () => { + setOverrides(overrides.filter((_, i) => i !== index)); + }, + }, + ]); + }; + + const handleSaveOverride = () => { + if (!overrideDate) { + Alert.alert("Error", "Please select a date"); + return; + } + + const newOverride = { + date: overrideDate, + startTime: isUnavailable ? "00:00" : overrideStartTime, + endTime: isUnavailable ? "00:00" : overrideEndTime, + }; + + if (editingOverride !== null) { + // Update existing override + const updated = [...overrides]; + updated[editingOverride] = newOverride; + setOverrides(updated); + } else { + // Add new override + setOverrides([...overrides, newOverride]); + } + + setShowOverrideModal(false); + }; + + const formatDateForDisplay = (dateStr: string): string => { + if (!dateStr) return ""; + const date = new Date(dateStr + "T00:00:00"); + return date.toLocaleDateString("en-US", { + month: "short", + day: "numeric", + year: "numeric", + }); + }; + + if (loading) { + return ( + + + Loading schedule... + + ); + } + + return ( + <> + ( + Edit Availability + ), + headerLeft: () => ( + router.back()} className="ml-4"> + + + ), + headerRight: () => ( + + Save + + ), + }} + /> + + + + {/* Schedule Name and Working Hours Display */} + + {scheduleName} + {Object.keys(availability).length > 0 ? ( + + {formatAvailabilityDisplay(availability).map((line, index) => ( + + {line} + + ))} + + ) : ( + No availability set + )} + + + {/* Availability Schedule */} + + Availability + {DAYS.map((day, dayIndex) => { + const daySlots = availability[dayIndex] || []; + const isEnabled = daySlots.length > 0; + const firstSlot = daySlots[0]; + + return ( + + + + toggleDay(dayIndex)} + trackColor={{ false: "#E5E5EA", true: "#34C759" }} + thumbColor="#fff" + style={{ transform: [{ scaleX: 0.8 }, { scaleY: 0.8 }] }} + /> + + {DAY_ABBREVIATIONS[dayIndex]} + + {isEnabled && firstSlot && ( + + + setShowTimePicker({ dayIndex, slotIndex: 0, type: "start" }) + } + className="rounded-lg border border-[#E5E5EA] bg-white px-2 py-1" + style={{ width: 85 }} + > + + {formatTime12Hour(firstSlot.startTime)} + + + - + + setShowTimePicker({ dayIndex, slotIndex: 0, type: "end" }) + } + className="rounded-lg border border-[#E5E5EA] bg-white px-2 py-1" + style={{ width: 85 }} + > + + {formatTime12Hour(firstSlot.endTime)} + + + + )} + + {isEnabled && ( + addTimeSlot(dayIndex)} className="p-1"> + + + )} + + + {isEnabled && daySlots.length > 1 && ( + + {daySlots.slice(1).map((slot, slotIndex) => ( + + + setShowTimePicker({ + dayIndex, + slotIndex: slotIndex + 1, + type: "start", + }) + } + className="rounded-lg border border-[#E5E5EA] bg-white px-2 py-1" + style={{ width: 85 }} + > + + {formatTime12Hour(slot.startTime)} + + + - + + setShowTimePicker({ + dayIndex, + slotIndex: slotIndex + 1, + type: "end", + }) + } + className="rounded-lg border border-[#E5E5EA] bg-white px-2 py-1" + style={{ width: 85 }} + > + + {formatTime12Hour(slot.endTime)} + + + removeTimeSlot(dayIndex, slotIndex + 1)} + className="p-1" + > + + + + ))} + + )} + + ); + })} + + + {/* Timezone */} + + Timezone + setShowTimezoneModal(true)} + className="flex-row items-center justify-between rounded-lg border border-[#E5E5EA] bg-[#F8F9FA] px-3 py-3" + > + {timeZone} + + + + + {/* Date Overrides */} + + Date overrides + + Add dates when your availability changes from your daily hours. + + + {overrides.length > 0 && ( + + {overrides.map((override, index) => ( + + + + {formatDateForDisplay(override.date)} + + {override.startTime === "00:00" && override.endTime === "00:00" ? ( + Unavailable (All day) + ) : ( + + {formatTime12Hour(override.startTime + ":00")} -{" "} + {formatTime12Hour(override.endTime + ":00")} + + )} + + + handleEditOverride(index)} className="p-2"> + + + handleDeleteOverride(index)} + className="p-2" + > + + + + + ))} + + )} + + + + Add an override + + + + + + {/* Bottom Action Bar */} + + + + Set as Default + + + + + + + + + + + + + + + {/* Timezone Modal */} + setShowTimezoneModal(false)} + > + setShowTimezoneModal(false)} + > + e.stopPropagation()} + > + + Select Timezone + setShowTimezoneModal(false)}> + + + + + {timezones.map((tz) => ( + { + setTimeZone(tz); + setShowTimezoneModal(false); + }} + className="border-b border-[#E5E5EA] py-3" + > + {tz} + + ))} + + + + + + {/* Time Picker Modal */} + setShowTimePicker(null)} + > + setShowTimePicker(null)} + > + e.stopPropagation()} + > + + + Select {showTimePicker?.type === "start" ? "Start" : "End"} Time + + setShowTimePicker(null)}> + + + + + {TIME_OPTIONS.map((time) => { + const currentTime = + showTimePicker?.type === "start" + ? availability[showTimePicker.dayIndex]?.[ + showTimePicker.slotIndex + ]?.startTime.substring(0, 5) + : availability[showTimePicker?.dayIndex || 0]?.[ + showTimePicker?.slotIndex || 0 + ]?.endTime.substring(0, 5); + const isSelected = currentTime === time; + + return ( + { + if (showTimePicker) { + updateTimeSlot( + showTimePicker.dayIndex, + showTimePicker.slotIndex, + showTimePicker.type, + time + ); + setShowTimePicker(null); + } + }} + className={`border-b border-[#E5E5EA] py-3 ${isSelected ? "bg-[#E3F2FD]" : ""}`} + > + + {time} + + + ); + })} + + + + + + {/* Override Modal */} + setShowOverrideModal(false)} + > + setShowOverrideModal(false)} + > + e.stopPropagation()} + > + + + {editingOverride !== null ? "Edit Override" : "Add Override"} + + setShowOverrideModal(false)}> + + + + + + + {/* Date Picker */} + + + Select the dates to override + + + Enter date in YYYY-MM-DD format + + + {/* Unavailable Toggle */} + + + Mark unavailable (All day) + + + + + {/* Time Picker (only if not unavailable) */} + {!isUnavailable && ( + + + Which hours are you free? + + + setShowOverrideTimePicker({ type: "start" })} + className="flex-1 rounded-lg border border-[#E5E5EA] bg-white px-3 py-3" + > + + {formatTime12Hour(overrideStartTime + ":00")} + + + - + setShowOverrideTimePicker({ type: "end" })} + className="flex-1 rounded-lg border border-[#E5E5EA] bg-white px-3 py-3" + > + + {formatTime12Hour(overrideEndTime + ":00")} + + + + + )} + + {/* Save Button */} + + + Save Override + + + + + + + + + {/* Override Time Picker Modal */} + setShowOverrideTimePicker(null)} + > + setShowOverrideTimePicker(null)} + > + e.stopPropagation()} + > + + + Select {showOverrideTimePicker?.type === "start" ? "Start" : "End"} Time + + setShowOverrideTimePicker(null)}> + + + + + {TIME_OPTIONS.map((time) => { + const currentTime = + showOverrideTimePicker?.type === "start" ? overrideStartTime : overrideEndTime; + const isSelected = currentTime === time; + return ( + { + if (showOverrideTimePicker?.type === "start") { + setOverrideStartTime(time); + } else { + setOverrideEndTime(time); + } + setShowOverrideTimePicker(null); + }} + className={`border-b border-[#E5E5EA] py-3 ${isSelected ? "bg-[#F0F9FF]" : ""}`} + > + + {formatTime12Hour(time + ":00")} + + + ); + })} + + + + + + ); +} diff --git a/companion/app/booking-detail.tsx b/companion/app/booking-detail.tsx new file mode 100644 index 0000000000..821978e77e --- /dev/null +++ b/companion/app/booking-detail.tsx @@ -0,0 +1,747 @@ +import { Ionicons } from "@expo/vector-icons"; +import { useRouter, useLocalSearchParams, Stack } from "expo-router"; +import React, { useState, useEffect } from "react"; +import { + View, + Text, + ScrollView, + TouchableOpacity, + Alert, + ActivityIndicator, + Linking, + TextInput, + Platform, +} from "react-native"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { CalComAPIService, Booking } from "../services/calcom"; +import { SvgImage } from "../components/SvgImage"; +import { FullScreenModal } from "../components/FullScreenModal"; +import { getAppIconUrl } from "../utils/getAppIconUrl"; +import { getDefaultLocationIconUrl, defaultLocations } from "../utils/defaultLocations"; + +// Format date: "Tuesday, November 25, 2025" +const formatDateFull = (dateString: string): string => { + if (!dateString) return ""; + try { + const date = new Date(dateString); + return date.toLocaleDateString("en-US", { + weekday: "long", + month: "long", + day: "numeric", + year: "numeric", + }); + } catch (error) { + return ""; + } +}; + +// Format time: "9:40pm - 10:00pm" +const formatTime12Hour = (dateString: string): string => { + if (!dateString) return ""; + try { + const date = new Date(dateString); + const hours = date.getHours(); + const minutes = date.getMinutes(); + const period = hours >= 12 ? "pm" : "am"; + const hour12 = hours === 0 ? 12 : hours > 12 ? hours - 12 : hours; + const minStr = minutes.toString().padStart(2, "0"); + return `${hour12}:${minStr}${period}`; + } catch (error) { + return ""; + } +}; + +// Get timezone from date string +const getTimezone = (dateString: string): string => { + if (!dateString) return ""; + try { + const date = new Date(dateString); + const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; + return timeZone; + } catch (error) { + return ""; + } +}; + +// Get initials from a name (e.g., "Keith Williams" -> "KW", "Dhairyashil Shinde" -> "DS") +const getInitials = (name: string): string => { + if (!name) return ""; + const parts = name.trim().split(/\s+/); + if (parts.length === 0) return ""; + if (parts.length === 1) { + return parts[0].charAt(0).toUpperCase(); + } + // Get first letter of first name and first letter of last name + return (parts[0].charAt(0) + parts[parts.length - 1].charAt(0)).toUpperCase(); +}; + +// Get location provider info +const getLocationProvider = (location: string | undefined, metadata?: Record) => { + // Check metadata for videoCallUrl first + const videoCallUrl = metadata?.videoCallUrl; + const locationToCheck = videoCallUrl || location; + + if (!locationToCheck) return null; + + // Check if it's a video call URL + if (typeof locationToCheck === "string" && locationToCheck.startsWith("http")) { + // Try to detect provider from URL + if (locationToCheck.includes("cal.com/video") || locationToCheck.includes("cal-video")) { + const iconUrl = getAppIconUrl("daily_video", "cal-video"); + return { + label: "Cal Video", + iconUrl: iconUrl || "https://app.cal.com/app-store/dailyvideo/icon.svg", + url: locationToCheck, + }; + } + // Check for other video providers by URL pattern + const videoProviders = [ + { pattern: /zoom\.us/, label: "Zoom", type: "zoom_video", appId: "zoom" }, + { + pattern: /meet\.google\.com/, + label: "Google Meet", + type: "google_video", + appId: "google-meet", + }, + { + pattern: /teams\.microsoft\.com/, + label: "Microsoft Teams", + type: "office365_video", + appId: "msteams", + }, + ]; + + for (const provider of videoProviders) { + if (provider.pattern.test(locationToCheck)) { + const iconUrl = getAppIconUrl(provider.type, provider.appId); + return { + label: provider.label, + iconUrl: iconUrl, + url: locationToCheck, + }; + } + } + + // Generic link meeting + const linkIconUrl = getDefaultLocationIconUrl("link") || "https://app.cal.com/link.svg"; + return { + label: "Link Meeting", + iconUrl: linkIconUrl, + url: locationToCheck, + }; + } + + // Check if it's an integration location (e.g., "integrations:zoom", "integrations:cal-video") + if (typeof locationToCheck === "string" && locationToCheck.startsWith("integrations:")) { + const appId = locationToCheck.replace("integrations:", ""); + const iconUrl = getAppIconUrl("", appId); + + if (iconUrl) { + // Format appId to display name (e.g., "cal-video" -> "Cal Video") + const formatAppIdToDisplayName = (id: string): string => { + return id + .split("-") + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(" "); + }; + + return { + label: formatAppIdToDisplayName(appId), + iconUrl: iconUrl, + url: null, + }; + } + } + + // Check if it's a default location type + const defaultLocation = defaultLocations.find((loc) => loc.type === locationToCheck); + if (defaultLocation) { + return { + label: defaultLocation.label, + iconUrl: defaultLocation.iconUrl, + url: null, + }; + } + + // Fallback: return as plain text location + return { + label: locationToCheck, + iconUrl: null, + url: null, + }; +}; + +export default function BookingDetail() { + const router = useRouter(); + const { uid } = useLocalSearchParams<{ uid: string }>(); + const insets = useSafeAreaInsets(); + + const [loading, setLoading] = useState(true); + const [booking, setBooking] = useState(null); + const [error, setError] = useState(null); + const [showActionsModal, setShowActionsModal] = useState(false); + const [showRescheduleModal, setShowRescheduleModal] = useState(false); + const [rescheduleReason, setRescheduleReason] = useState(""); + const [rescheduling, setRescheduling] = useState(false); + + useEffect(() => { + if (uid) { + fetchBooking(); + } + }, [uid]); + + const fetchBooking = async () => { + try { + setLoading(true); + setError(null); + const bookingData = await CalComAPIService.getBookingByUid(uid); + console.log("Booking data received:", JSON.stringify(bookingData, null, 2)); + console.log("User:", bookingData.user); + console.log("Hosts:", bookingData.hosts); + console.log("Attendees:", bookingData.attendees); + console.log("Recurring fields:", { + recurringEventId: bookingData.recurringEventId, + recurringBookingUid: (bookingData as any).recurringBookingUid, + metadata: (bookingData as any).metadata, + }); + setBooking(bookingData); + } catch (err) { + console.error("Error fetching booking:", err); + setError("Failed to load booking. Please try again."); + Alert.alert("Error", "Failed to load booking. Please try again.", [ + { text: "OK", onPress: () => router.back() }, + ]); + } finally { + setLoading(false); + } + }; + + const handleJoinMeeting = () => { + if (!booking?.location) return; + + const provider = getLocationProvider(booking.location); + if (provider?.url) { + Linking.openURL(provider.url); + } + }; + + const handleReschedule = async () => { + if (!booking) return; + + setRescheduling(true); + try { + // For now, we'll use the current start time + // In a full implementation, you'd show a date/time picker + await CalComAPIService.rescheduleBooking(booking.uid, { + start: booking.startTime, + reschedulingReason: rescheduleReason.trim() || undefined, + }); + + Alert.alert("Success", "Reschedule request sent successfully"); + setShowRescheduleModal(false); + setRescheduleReason(""); + + // Refresh booking data + await fetchBooking(); + } catch (error) { + console.error("Failed to reschedule booking:", error); + Alert.alert("Error", "Failed to send reschedule request. Please try again."); + } finally { + setRescheduling(false); + } + }; + + if (loading) { + return ( + + + Loading booking... + + ); + } + + if (error || !booking) { + return ( + + + + {error || "Booking not found"} + + router.back()} + > + Go Back + + + ); + } + + const startTime = booking.start || booking.startTime || ""; + const endTime = booking.end || booking.endTime || ""; + const dateFormatted = formatDateFull(startTime); + const timeFormatted = `${formatTime12Hour(startTime)} - ${formatTime12Hour(endTime)}`; + const timezone = getTimezone(startTime); + const locationProvider = getLocationProvider(booking.location, booking.responses); + + return ( + <> + ( + router.back()} + className="items-center justify-center rounded-full" + style={{ width: 40, height: 40, backgroundColor: "#F0F0F0" }} + > + + + ), + headerRight: () => ( + setShowActionsModal(true)} + className="items-center justify-center rounded-full" + style={{ width: 40, height: 40, backgroundColor: "#F0F0F0" }} + > + + + ), + }} + /> + + + {/* Title */} + + {booking.title} + + {dateFormatted} {timeFormatted} ({timezone}) + + + + {/* Who Section */} + + Who + {/* Show host from user field or hosts array */} + {(booking.user || (booking.hosts && booking.hosts.length > 0)) && ( + + {booking.user ? ( + + + + {getInitials(booking.user.name)} + + + + + + {booking.user.name} + + + host + + + {booking.user.email} + + + ) : booking.hosts && booking.hosts.length > 0 ? ( + booking.hosts.map((host, hostIndex) => ( + 0 ? "mt-4" : ""}`} + > + + + {getInitials(host.name || "Host")} + + + + + + {host.name || "Host"} + + + host + + + {host.email && {host.email}} + + + )) + ) : null} + + )} + {booking.attendees && booking.attendees.length > 0 && ( + + {booking.attendees.map((attendee, index) => ( + 0 ? "mt-4" : ""}`}> + + + {getInitials(attendee.name)} + + + + + {attendee.name} + + {attendee.email} + + + ))} + + )} + + + {/* Where Section */} + {locationProvider && ( + + Where + {locationProvider.url ? ( + + {locationProvider.iconUrl && ( + + )} + {locationProvider.label}: + + {locationProvider.url} + + + ) : ( + + {locationProvider.iconUrl && ( + + )} + {locationProvider.label} + + )} + + )} + + {/* Recurring Event Section */} + {(booking.recurringEventId || booking.recurringBookingUid) && ( + + Recurring Event + Every 2 weeks for 6 occurrences + + )} + + {/* Description Section */} + {booking.description && ( + + Description + {booking.description} + + )} + + {/* Join Meeting Button */} + {locationProvider?.url && ( + + {locationProvider.iconUrl && ( + + )} + + Join {locationProvider.label} + + + )} + + + + {/* Booking Actions Modal */} + setShowActionsModal(false)} + > + setShowActionsModal(false)} + > + e.stopPropagation()} + > + {/* Header */} + + + Booking Actions + + + {/* Actions List */} + + {/* View Booking */} + { + setShowActionsModal(false); + // TODO: Navigate to booking view page + console.log("View booking"); + }} + className="flex-row items-center p-2 hover:bg-gray-50 md:p-4" + > + + View Booking + + + {/* Separator */} + + + {/* Edit event label */} + + Edit event + + + {/* Request Reschedule */} + { + setShowActionsModal(false); + setShowRescheduleModal(true); + }} + className="flex-row items-center p-2 hover:bg-gray-50 md:p-4" + > + + Send Reschedule Request + + + {/* Edit Location */} + { + setShowActionsModal(false); + // TODO: Open edit location dialog + console.log("Edit location"); + }} + className="flex-row items-center p-2 hover:bg-gray-50 md:p-4" + > + + Edit Location + + + {/* Add Guests */} + { + setShowActionsModal(false); + // TODO: Open add guests dialog + console.log("Add guests"); + }} + className="flex-row items-center p-2 hover:bg-gray-50 md:p-4" + > + + Add Guests + + + {/* Separator */} + + + {/* After event label */} + + After event + + + {/* View Recordings */} + {locationProvider?.url && ( + { + setShowActionsModal(false); + // TODO: Open view recordings dialog + console.log("View recordings"); + }} + className="flex-row items-center p-2 hover:bg-gray-50 md:p-4" + > + + View Recordings + + )} + + {/* Meeting Session Details */} + {locationProvider?.url && ( + { + setShowActionsModal(false); + // TODO: Open session details dialog + console.log("Meeting session details"); + }} + className="flex-row items-center p-2 hover:bg-gray-50 md:p-4" + > + + Meeting Session Details + + )} + + {/* Mark as No-Show */} + { + setShowActionsModal(false); + // TODO: Mark as no-show + console.log("Mark as no-show"); + }} + className="flex-row items-center p-2 hover:bg-gray-50 md:p-4" + > + + Mark as No-Show + + + {/* Separator */} + + + {/* Report Booking */} + { + setShowActionsModal(false); + // TODO: Open report booking dialog + console.log("Report booking"); + }} + className="flex-row items-center p-2 hover:bg-gray-50 md:p-4" + > + + Report Booking + + + {/* Separator */} + + + {/* Cancel Booking */} + { + setShowActionsModal(false); + Alert.alert("Cancel Booking", "Are you sure you want to cancel this booking?", [ + { text: "No", style: "cancel" }, + { + text: "Yes, Cancel", + style: "destructive", + onPress: () => { + // TODO: Cancel booking + console.log("Cancel booking"); + }, + }, + ]); + }} + className="flex-row items-center p-2 hover:bg-gray-50 md:p-4" + > + + Cancel Booking + + + + {/* Cancel button */} + + setShowActionsModal(false)} + > + Cancel + + + + + + + {/* Reschedule Modal */} + setShowRescheduleModal(false)} + > + setShowRescheduleModal(false)} + > + e.stopPropagation()} + style={{ + shadowColor: "#000", + shadowOffset: { width: 0, height: 20 }, + shadowOpacity: 0.25, + shadowRadius: 25, + elevation: 24, + }} + > + {/* Header */} + + + + + + + + Reschedule request + + + Send a reschedule request to the organizer of this booking. + + + + + + {/* Content */} + + + Reason for reschedule request + (Optional) + + + + + {/* Footer */} + + + { + setShowRescheduleModal(false); + setRescheduleReason(""); + }} + disabled={rescheduling} + > + Cancel + + + Reschedule request + + + + + + + + ); +} diff --git a/companion/app/event-type-detail.tsx b/companion/app/event-type-detail.tsx new file mode 100644 index 0000000000..3991df5b37 --- /dev/null +++ b/companion/app/event-type-detail.tsx @@ -0,0 +1,2188 @@ +import { Ionicons } from "@expo/vector-icons"; +import { GlassView } from "expo-glass-effect"; +import { useRouter, useLocalSearchParams, Stack } from "expo-router"; +import React, { useState, useEffect } from "react"; +import { + View, + Text, + ScrollView, + TouchableOpacity, + TextInput, + Switch, + Modal, + Linking, + Alert, + Clipboard, + Animated, + Image, + Platform, +} from "react-native"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; + +import { CalComAPIService, Schedule, ConferencingOption, EventType } from "../services/calcom"; +import { getAppIconUrl } from "../utils/getAppIconUrl"; +import { + defaultLocations, + getDefaultLocationIconUrl, + isDefaultLocation, + DefaultLocationType, +} from "../utils/defaultLocations"; +import { SvgImage } from "../components/SvgImage"; +import { + parseBufferTime, + parseMinimumNotice, + parseFrequencyUnit, + parseSlotInterval, +} from "../utils/parsers/event-type-parsers"; +import { slugify } from "../utils/slugify"; +import { BasicsTab } from "./event-type-detail/tabs/BasicsTab"; +import { AvailabilityTab } from "./event-type-detail/tabs/AvailabilityTab"; +import { LimitsTab } from "./event-type-detail/tabs/LimitsTab"; +import { AdvancedTab } from "./event-type-detail/tabs/AdvancedTab"; +import { RecurringTab } from "./event-type-detail/tabs/RecurringTab"; + +const tabs = [ + { id: "basics", label: "Basics", icon: "link" }, + { id: "availability", label: "Availability", icon: "calendar" }, + { id: "limits", label: "Limits", icon: "time" }, + { id: "advanced", label: "Advanced", icon: "settings" }, + { id: "recurring", label: "Recurring", icon: "refresh" }, + { id: "apps", label: "Apps", icon: "grid" }, + { id: "workflows", label: "Workflows", icon: "flash" }, + { id: "webhooks", label: "Webhooks", icon: "code" }, +]; + +export default function EventTypeDetail() { + const router = useRouter(); + const { id, title, description, duration, price, currency, slug } = useLocalSearchParams<{ + id: string; + title: string; + description?: string; + duration: string; + price?: string; + currency?: string; + slug?: string; + }>(); + + const insets = useSafeAreaInsets(); + const [activeTab, setActiveTab] = useState("basics"); + + // Form state + const [eventTitle, setEventTitle] = useState(title || ""); + const [eventDescription, setEventDescription] = useState(description || ""); + const [eventSlug, setEventSlug] = useState(slug || ""); + const [eventDuration, setEventDuration] = useState(duration || "30"); + const [username, setUsername] = useState("username"); + const [allowMultipleDurations, setAllowMultipleDurations] = useState(false); + const [selectedLocation, setSelectedLocation] = useState(""); + const [showLocationDropdown, setShowLocationDropdown] = useState(false); + const [locationAddress, setLocationAddress] = useState(""); + const [locationLink, setLocationLink] = useState(""); + const [locationPhone, setLocationPhone] = useState(""); + const [selectedDurations, setSelectedDurations] = useState([]); + const [defaultDuration, setDefaultDuration] = useState(""); + const [showDurationDropdown, setShowDurationDropdown] = useState(false); + const [showDefaultDurationDropdown, setShowDefaultDurationDropdown] = useState(false); + const [schedules, setSchedules] = useState([]); + const [selectedSchedule, setSelectedSchedule] = useState(null); + const [selectedScheduleDetails, setSelectedScheduleDetails] = useState(null); + const [showScheduleDropdown, setShowScheduleDropdown] = useState(false); + const [schedulesLoading, setSchedulesLoading] = useState(false); + const [scheduleDetailsLoading, setScheduleDetailsLoading] = useState(false); + const [isHidden, setIsHidden] = useState(false); + const [selectedTimezone, setSelectedTimezone] = useState(""); + const [showTimezoneDropdown, setShowTimezoneDropdown] = useState(false); + const [conferencingOptions, setConferencingOptions] = useState([]); + const [conferencingLoading, setConferencingLoading] = useState(false); + const [eventTypeData, setEventTypeData] = useState(null); + const [saving, setSaving] = useState(false); + const [beforeEventBuffer, setBeforeEventBuffer] = useState("No buffer time"); + const [afterEventBuffer, setAfterEventBuffer] = useState("No buffer time"); + const [showBeforeBufferDropdown, setShowBeforeBufferDropdown] = useState(false); + const [showAfterBufferDropdown, setShowAfterBufferDropdown] = useState(false); + const [minimumNoticeValue, setMinimumNoticeValue] = useState("1"); + const [minimumNoticeUnit, setMinimumNoticeUnit] = useState("Hours"); + const [showMinimumNoticeUnitDropdown, setShowMinimumNoticeUnitDropdown] = useState(false); + const [limitBookingFrequency, setLimitBookingFrequency] = useState(false); + const [frequencyLimits, setFrequencyLimits] = useState([{ id: 1, value: "1", unit: "Per day" }]); + const [showFrequencyUnitDropdown, setShowFrequencyUnitDropdown] = useState(null); + const [frequencyAnimationValue] = useState(new Animated.Value(0)); + const [limitTotalDuration, setLimitTotalDuration] = useState(false); + const [durationLimits, setDurationLimits] = useState([{ id: 1, value: "60", unit: "Per day" }]); + const [showDurationUnitDropdown, setShowDurationUnitDropdown] = useState(null); + const [durationAnimationValue] = useState(new Animated.Value(0)); + const [slotInterval, setSlotInterval] = useState("Default"); + const [showSlotIntervalDropdown, setShowSlotIntervalDropdown] = useState(false); + const [onlyShowFirstAvailableSlot, setOnlyShowFirstAvailableSlot] = useState(false); + const [maxActiveBookingsPerBooker, setMaxActiveBookingsPerBooker] = useState(false); + const [maxActiveBookingsValue, setMaxActiveBookingsValue] = useState("1"); + const [limitFutureBookings, setLimitFutureBookings] = useState(false); + const [futureBookingType, setFutureBookingType] = useState<"rolling" | "range">("rolling"); + const [rollingDays, setRollingDays] = useState("30"); + const [rollingCalendarDays, setRollingCalendarDays] = useState(false); + const [rangeStartDate, setRangeStartDate] = useState(""); + const [rangeEndDate, setRangeEndDate] = useState(""); + const [offsetStartTimes, setOffsetStartTimes] = useState(false); + const [offsetStartValue, setOffsetStartValue] = useState("0"); + + // Advanced tab state + const [calendarEventName, setCalendarEventName] = useState(""); + const [addToCalendarEmail, setAddToCalendarEmail] = useState(""); + const [selectedLayouts, setSelectedLayouts] = useState(["MONTH_VIEW"]); + const [defaultLayout, setDefaultLayout] = useState("MONTH_VIEW"); + const [requiresConfirmation, setRequiresConfirmation] = useState(false); + const [disableCancelling, setDisableCancelling] = useState(false); + const [disableRescheduling, setDisableRescheduling] = useState(false); + const [sendCalVideoTranscription, setSendCalVideoTranscription] = useState(false); + const [autoTranslate, setAutoTranslate] = useState(false); + const [requiresBookerEmailVerification, setRequiresBookerEmailVerification] = useState(false); + const [hideCalendarNotes, setHideCalendarNotes] = useState(false); + const [hideCalendarEventDetails, setHideCalendarEventDetails] = useState(false); + const [successRedirectUrl, setSuccessRedirectUrl] = useState(""); + const [forwardParamsSuccessRedirect, setForwardParamsSuccessRedirect] = useState(false); + const [hideOrganizerEmail, setHideOrganizerEmail] = useState(false); + const [lockTimezone, setLockTimezone] = useState(false); + const [allowReschedulingPastEvents, setAllowReschedulingPastEvents] = useState(false); + const [allowBookingThroughRescheduleLink, setAllowBookingThroughRescheduleLink] = useState(false); + const [customReplyToEmail, setCustomReplyToEmail] = useState(""); + const [eventTypeColorLight, setEventTypeColorLight] = useState("#292929"); + const [eventTypeColorDark, setEventTypeColorDark] = useState("#FAFAFA"); + + // Recurring tab state + const [recurringEnabled, setRecurringEnabled] = useState(false); + const [recurringInterval, setRecurringInterval] = useState("1"); + const [recurringFrequency, setRecurringFrequency] = useState< + "daily" | "weekly" | "monthly" | "yearly" + >("weekly"); + const [recurringOccurrences, setRecurringOccurrences] = useState("12"); + + const bufferTimeOptions = [ + "No buffer time", + "5 Minutes", + "10 Minutes", + "15 Minutes", + "20 Minutes", + "30 Minutes", + "45 Minutes", + "60 Minutes", + "90 Minutes", + "120 Minutes", + ]; + const timeUnitOptions = ["Minutes", "Hours", "Days"]; + const frequencyUnitOptions = ["Per day", "Per Month", "Per year"]; + const durationUnitOptions = ["Per day", "Per week", "Per month"]; + const slotIntervalOptions = [ + "Default", + "5 Minutes", + "10 Minutes", + "15 Minutes", + "20 Minutes", + "30 Minutes", + "45 Minutes", + "60 Minutes", + "75 Minutes", + "90 Minutes", + "105 Minutes", + "120 Minutes", + ]; + const availableDurations = [ + "5 mins", + "10 mins", + "15 mins", + "20 mins", + "25 mins", + "30 mins", + "45 mins", + "50 mins", + "60 mins", + "75 mins", + "80 mins", + "90 mins", + "120 mins", + "150 mins", + "180 mins", + "240 mins", + "300 mins", + "360 mins", + "420 mins", + "480 mins", + ]; + + const formatDuration = (minutes: string) => { + const mins = parseInt(minutes) || 0; + if (mins < 60) return `${mins}m`; + const hours = Math.floor(mins / 60); + const remainingMins = mins % 60; + return remainingMins > 0 ? `${hours}h ${remainingMins}m` : `${hours}h`; + }; + + const truncateTitle = (text: string, maxLength: number = 20) => { + return text.length > maxLength ? `${text.substring(0, maxLength)}...` : text; + }; + + const formatAppIdToDisplayName = (appId: string): string => { + // Convert appId like "google-meet" to "Google Meet" + return appId + .split("-") + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(" "); + }; + + const displayNameToLocationValue = ( + displayName: string + ): { + type: string; + integration?: string; + address?: string; + link?: string; + phone?: string; + public?: boolean; + } | null => { + // First check if it's a default location + const defaultLocation = defaultLocations.find((loc) => loc.label === displayName); + if (defaultLocation) { + // Map internal location types to API location types + switch (defaultLocation.type) { + case "attendeeInPerson": + return { type: "attendeeAddress" }; + case "inPerson": + // For organizer address, we need to preserve existing address if available + // or use empty string (will be filled by organizer later) + return { type: "address", address: "", public: true }; + case "link": + // For link meeting, we need to preserve existing link if available + // or use empty string (will be filled by organizer later) + return { type: "link", link: "", public: true }; + case "phone": + return { type: "attendeePhone" }; + case "userPhone": + // For organizer phone, we need to preserve existing phone if available + // or use empty string (will be filled by organizer later) + return { type: "phone", phone: "", public: true }; + case "somewhereElse": + return { type: "attendeeDefined" }; + default: + return { type: defaultLocation.type }; + } + } + + // Otherwise, find matching conferencing option + const option = conferencingOptions.find( + (opt) => formatAppIdToDisplayName(opt.appId) === displayName + ); + if (option) { + return { type: "integration", integration: option.appId, public: true }; + } + + return null; + }; + + const displayNameToAppId = (displayName: string): string | null => { + // Legacy function for backward compatibility - only for conferencing apps + const option = conferencingOptions.find( + (opt) => formatAppIdToDisplayName(opt.appId) === displayName + ); + return option ? option.appId : null; + }; + + type LocationOption = { label: string; iconUrl: string | null; value: string }; + type LocationGroup = { category: string; options: LocationOption[] }; + + const getLocationOptions = (): LocationGroup[] => { + // Group conferencing apps under "conferencing" category + const conferencingAppOptions: LocationOption[] = conferencingOptions.map((option) => { + const iconUrl = getAppIconUrl(option.type, option.appId); + return { + label: formatAppIdToDisplayName(option.appId), + iconUrl: iconUrl, + value: `integrations:${option.appId}`, + }; + }); + + // Group default locations by their category + const defaultLocationOptions: LocationOption[] = defaultLocations.map((location) => ({ + label: location.label, + iconUrl: location.iconUrl, + value: location.type, + })); + + // Group options by category + const grouped: Record = {}; + + // Add conferencing apps + if (conferencingAppOptions.length > 0) { + grouped["conferencing"] = conferencingAppOptions; + } + + // Add default locations by category + defaultLocations.forEach((location) => { + const category = location.category; + if (!grouped[category]) { + grouped[category] = []; + } + const option = defaultLocationOptions.find((opt) => opt.value === location.type); + if (option) { + grouped[category].push(option); + } + }); + + // Convert to array format with category labels + const categoryLabels: Record = { + conferencing: "Conferencing", + "in person": "In Person", + phone: "Phone", + other: "Other", + }; + + const result: LocationGroup[] = []; + for (const category in grouped) { + if (grouped[category].length > 0) { + result.push({ + category: categoryLabels[category] || category, + options: grouped[category], + }); + } + } + + console.log( + "Total location groups:", + result.length, + result.map((g) => `${g.category}: ${g.options.length}`) + ); + return result; + }; + + const getSelectedLocationIconUrl = (): string | null => { + if (!selectedLocation) return null; + + // First, check if it's a default location + const defaultLocation = defaultLocations.find((loc) => loc.label === selectedLocation); + if (defaultLocation) { + return defaultLocation.iconUrl; + } + + // Try to find in conferencing options + const option = conferencingOptions.find( + (opt) => formatAppIdToDisplayName(opt.appId) === selectedLocation + ); + if (option) { + return getAppIconUrl(option.type, option.appId); + } + + // Fallback: Handle Cal Video directly (it might not be in conferencing options as it's a global app) + // Check if selectedLocation matches Cal Video display names + const calVideoNames = ["Cal Video", "Cal-Video", "cal-video"]; + if ( + calVideoNames.includes(selectedLocation) || + selectedLocation.toLowerCase().includes("cal video") + ) { + return getAppIconUrl("daily_video", "cal-video"); + } + + // Fallback: Try to reverse the formatAppIdToDisplayName to get appId + // Convert "Cal Video" back to "cal-video" and try to get icon + const reverseAppId = selectedLocation.toLowerCase().replace(/\s+/g, "-"); + const fallbackIconUrl = getAppIconUrl("", reverseAppId); + if (fallbackIconUrl) { + return fallbackIconUrl; + } + + return null; + }; + + const toggleDurationSelection = (duration: string) => { + const newSelected = selectedDurations.includes(duration) + ? selectedDurations.filter((d) => d !== duration) + : [...selectedDurations, duration]; + + setSelectedDurations(newSelected); + + // Reset default duration if it's no longer in selected durations + if (!newSelected.includes(defaultDuration)) { + setDefaultDuration(""); + } + }; + + const toggleBookingFrequency = (value: boolean) => { + setLimitBookingFrequency(value); + + Animated.timing(frequencyAnimationValue, { + toValue: value ? 1 : 0, + duration: 300, + useNativeDriver: false, + }).start(); + }; + + const addFrequencyLimit = () => { + const newId = Math.max(...frequencyLimits.map((limit) => limit.id)) + 1; + setFrequencyLimits([...frequencyLimits, { id: newId, value: "1", unit: "Per day" }]); + }; + + const removeFrequencyLimit = (id: number) => { + setFrequencyLimits(frequencyLimits.filter((limit) => limit.id !== id)); + }; + + const updateFrequencyLimit = (id: number, field: "value" | "unit", newValue: string) => { + setFrequencyLimits( + frequencyLimits.map((limit) => (limit.id === id ? { ...limit, [field]: newValue } : limit)) + ); + }; + + const toggleTotalDuration = (value: boolean) => { + setLimitTotalDuration(value); + + Animated.timing(durationAnimationValue, { + toValue: value ? 1 : 0, + duration: 300, + useNativeDriver: false, + }).start(); + }; + + const addDurationLimit = () => { + const newId = Math.max(...durationLimits.map((limit) => limit.id)) + 1; + setDurationLimits([...durationLimits, { id: newId, value: "60", unit: "Per day" }]); + }; + + const removeDurationLimit = (id: number) => { + setDurationLimits(durationLimits.filter((limit) => limit.id !== id)); + }; + + const updateDurationLimit = (id: number, field: "value" | "unit", newValue: string) => { + setDurationLimits( + durationLimits.map((limit) => (limit.id === id ? { ...limit, [field]: newValue } : limit)) + ); + }; + + const fetchSchedules = async () => { + try { + setSchedulesLoading(true); + const schedulesData = await CalComAPIService.getSchedules(); + setSchedules(schedulesData); + + // Set default schedule if one exists + const defaultSchedule = schedulesData.find((schedule) => schedule.isDefault); + if (defaultSchedule) { + setSelectedSchedule(defaultSchedule); + await fetchScheduleDetails(defaultSchedule.id); + } + } catch (error) { + console.error("Failed to fetch schedules:", error); + } finally { + setSchedulesLoading(false); + } + }; + + const fetchScheduleDetails = async (scheduleId: number) => { + try { + setScheduleDetailsLoading(true); + console.log("Fetching schedule details for ID:", scheduleId); + const scheduleDetails = await CalComAPIService.getScheduleById(scheduleId); + console.log("Raw schedule details response:", scheduleDetails); + setSelectedScheduleDetails(scheduleDetails); + if (scheduleDetails.timeZone) { + setSelectedTimezone(scheduleDetails.timeZone); + } + } catch (error) { + console.error("Failed to fetch schedule details:", error); + setSelectedScheduleDetails(null); + } finally { + setScheduleDetailsLoading(false); + } + }; + + const fetchConferencingOptions = async () => { + try { + setConferencingLoading(true); + const options = await CalComAPIService.getConferencingOptions(); + console.log("Fetched conferencing options:", JSON.stringify(options, null, 2)); + setConferencingOptions(options); + } catch (error) { + console.error("Failed to fetch conferencing options:", error); + } finally { + setConferencingLoading(false); + } + }; + + const fetchEventTypeData = async () => { + if (!id) return; + + try { + const eventType = await CalComAPIService.getEventTypeById(parseInt(id)); + if (eventType) { + setEventTypeData(eventType); + + // Load basic fields + if (eventType.title) setEventTitle(eventType.title); + if (eventType.slug) setEventSlug(eventType.slug); + if (eventType.description) setEventDescription(eventType.description); + if (eventType.lengthInMinutes) setEventDuration(eventType.lengthInMinutes.toString()); + if (eventType.hidden !== undefined) setIsHidden(eventType.hidden); + + // Load buffer times + if (eventType.beforeEventBuffer) { + setBeforeEventBuffer(`${eventType.beforeEventBuffer} Minutes`); + } + if (eventType.afterEventBuffer) { + setAfterEventBuffer(`${eventType.afterEventBuffer} Minutes`); + } + + // Load minimum booking notice + if (eventType.minimumBookingNotice) { + const minutes = eventType.minimumBookingNotice; + if (minutes >= 1440) { + // Days + setMinimumNoticeValue((minutes / 1440).toString()); + setMinimumNoticeUnit("Days"); + } else if (minutes >= 60) { + // Hours + setMinimumNoticeValue((minutes / 60).toString()); + setMinimumNoticeUnit("Hours"); + } else { + // Minutes + setMinimumNoticeValue(minutes.toString()); + setMinimumNoticeUnit("Minutes"); + } + } + + // Load slot interval + if (eventType.slotInterval) { + setSlotInterval(`${eventType.slotInterval} Minutes`); + } + + // Load booking frequency limits + if (eventType.bookingLimitsCount && !("disabled" in eventType.bookingLimitsCount)) { + setLimitBookingFrequency(true); + const limits = []; + let idCounter = 1; + if (eventType.bookingLimitsCount.day) { + limits.push({ + id: idCounter++, + value: eventType.bookingLimitsCount.day.toString(), + unit: "Per day", + }); + } + if (eventType.bookingLimitsCount.week) { + limits.push({ + id: idCounter++, + value: eventType.bookingLimitsCount.week.toString(), + unit: "Per week", + }); + } + if (eventType.bookingLimitsCount.month) { + limits.push({ + id: idCounter++, + value: eventType.bookingLimitsCount.month.toString(), + unit: "Per month", + }); + } + if (eventType.bookingLimitsCount.year) { + limits.push({ + id: idCounter++, + value: eventType.bookingLimitsCount.year.toString(), + unit: "Per year", + }); + } + if (limits.length > 0) { + setFrequencyLimits(limits); + } + } + + // Load duration limits + if (eventType.bookingLimitsDuration && !("disabled" in eventType.bookingLimitsDuration)) { + setLimitTotalDuration(true); + const limits = []; + let idCounter = 1; + if (eventType.bookingLimitsDuration.day) { + limits.push({ + id: idCounter++, + value: eventType.bookingLimitsDuration.day.toString(), + unit: "Per day", + }); + } + if (eventType.bookingLimitsDuration.week) { + limits.push({ + id: idCounter++, + value: eventType.bookingLimitsDuration.week.toString(), + unit: "Per week", + }); + } + if (eventType.bookingLimitsDuration.month) { + limits.push({ + id: idCounter++, + value: eventType.bookingLimitsDuration.month.toString(), + unit: "Per month", + }); + } + if (eventType.bookingLimitsDuration.year) { + limits.push({ + id: idCounter++, + value: eventType.bookingLimitsDuration.year.toString(), + unit: "Per year", + }); + } + if (limits.length > 0) { + setDurationLimits(limits); + } + } + + // Load only show first slot + if (eventType.onlyShowFirstAvailableSlot !== undefined) { + setOnlyShowFirstAvailableSlot(eventType.onlyShowFirstAvailableSlot); + } + + // Load max active bookings + if ( + eventType.bookerActiveBookingsLimit && + !("disabled" in eventType.bookerActiveBookingsLimit) + ) { + setMaxActiveBookingsPerBooker(true); + setMaxActiveBookingsValue(eventType.bookerActiveBookingsLimit.count.toString()); + } + + // Load booking window (future bookings limit) + if (eventType.bookingWindow && !("disabled" in eventType.bookingWindow)) { + setLimitFutureBookings(true); + if (eventType.bookingWindow.type === "range") { + setFutureBookingType("range"); + if ( + Array.isArray(eventType.bookingWindow.value) && + eventType.bookingWindow.value.length === 2 + ) { + setRangeStartDate(eventType.bookingWindow.value[0]); + setRangeEndDate(eventType.bookingWindow.value[1]); + } + } else { + setFutureBookingType("rolling"); + if (typeof eventType.bookingWindow.value === "number") { + setRollingDays(eventType.bookingWindow.value.toString()); + } + setRollingCalendarDays(eventType.bookingWindow.type === "calendarDays"); + } + } + + // Load Advanced tab fields + if (eventType.metadata) { + if (eventType.metadata.calendarEventName) { + setCalendarEventName(eventType.metadata.calendarEventName); + } + if (eventType.metadata.addToCalendarEmail) { + setAddToCalendarEmail(eventType.metadata.addToCalendarEmail); + } + if (eventType.metadata.customReplyToEmail) { + setCustomReplyToEmail(eventType.metadata.customReplyToEmail); + } + if (eventType.metadata.disableCancelling) { + setDisableCancelling(true); + } + if (eventType.metadata.disableRescheduling) { + setDisableRescheduling(true); + } + if (eventType.metadata.sendCalVideoTranscription) { + setSendCalVideoTranscription(true); + } + if (eventType.metadata.autoTranslate) { + setAutoTranslate(true); + } + if (eventType.metadata.hideCalendarEventDetails) { + setHideCalendarEventDetails(true); + } + if (eventType.metadata.hideOrganizerEmail) { + setHideOrganizerEmail(true); + } + if (eventType.metadata.allowReschedulingPastEvents) { + setAllowReschedulingPastEvents(true); + } + if (eventType.metadata.allowBookingThroughRescheduleLink) { + setAllowBookingThroughRescheduleLink(true); + } + } + + // Load booker layouts + if (eventType.bookerLayouts) { + if ( + eventType.bookerLayouts.enabledLayouts && + Array.isArray(eventType.bookerLayouts.enabledLayouts) + ) { + setSelectedLayouts(eventType.bookerLayouts.enabledLayouts); + } + if (eventType.bookerLayouts.defaultLayout) { + setDefaultLayout(eventType.bookerLayouts.defaultLayout); + } + } + + // Load confirmation settings + if (eventType.requiresConfirmation !== undefined) { + setRequiresConfirmation(eventType.requiresConfirmation); + } + + // Load other boolean fields + if (eventType.requiresBookerEmailVerification !== undefined) { + setRequiresBookerEmailVerification(eventType.requiresBookerEmailVerification); + } + if (eventType.hideCalendarNotes !== undefined) { + setHideCalendarNotes(eventType.hideCalendarNotes); + } + if (eventType.lockTimeZoneToggleOnBookingPage !== undefined) { + setLockTimezone(eventType.lockTimeZoneToggleOnBookingPage); + } + + // Load redirect URL + if (eventType.successRedirectUrl) { + setSuccessRedirectUrl(eventType.successRedirectUrl); + if (eventType.forwardParamsSuccessRedirect !== undefined) { + setForwardParamsSuccessRedirect(eventType.forwardParamsSuccessRedirect); + } + } + + // Load event type colors + if (eventType.eventTypeColor) { + if (eventType.eventTypeColor.lightEventTypeColor) { + setEventTypeColorLight(eventType.eventTypeColor.lightEventTypeColor); + } + if (eventType.eventTypeColor.darkEventTypeColor) { + setEventTypeColorDark(eventType.eventTypeColor.darkEventTypeColor); + } + } + + // Load recurring event settings + if (eventType.recurrence && !("disabled" in eventType.recurrence)) { + setRecurringEnabled(true); + setRecurringInterval(eventType.recurrence.interval.toString()); + setRecurringFrequency(eventType.recurrence.frequency); + setRecurringOccurrences(eventType.recurrence.occurrences.toString()); + } + + // Extract location from event type + if (eventType.locations && eventType.locations.length > 0) { + const firstLocation = eventType.locations[0]; + + // Handle conferencing apps (with integration field) + if (firstLocation.integration) { + // Format the integration name (e.g., "google-meet" -> "Google Meet") + const formattedLocation = formatAppIdToDisplayName(firstLocation.integration); + setSelectedLocation(formattedLocation); + } + // Handle default locations (with type field) + else if (firstLocation.type) { + // Map API location types to internal location types + // Need to distinguish between organizer and attendee types based on presence of fields + let internalType = firstLocation.type; + + // Check if it's an organizer type (has address, link, or phone field) + if ( + firstLocation.type === "address" || + (firstLocation.type === "phone" && firstLocation.phone) + ) { + // Organizer types + if (firstLocation.type === "address") { + internalType = "inPerson"; // Organizer address + } else if (firstLocation.type === "phone" && firstLocation.phone) { + internalType = "userPhone"; // Organizer phone + } + } else if (firstLocation.type === "link") { + internalType = "link"; // Link meeting + } else { + // Attendee types - map API types to internal types + const apiToInternalTypeMap: Record = { + attendeeAddress: "attendeeInPerson", + attendeePhone: "phone", + attendeeDefined: "somewhereElse", + }; + + if (apiToInternalTypeMap[firstLocation.type]) { + internalType = apiToInternalTypeMap[firstLocation.type]; + } + } + + const defaultLocation = defaultLocations.find((loc) => loc.type === internalType); + if (defaultLocation) { + setSelectedLocation(defaultLocation.label); + + // Populate location input values if they exist + if (firstLocation.address) { + setLocationAddress(firstLocation.address); + } + if (firstLocation.link) { + setLocationLink(firstLocation.link); + } + if (firstLocation.phone) { + setLocationPhone(firstLocation.phone); + } + } else { + // Fallback: try to format the type as display name + setSelectedLocation(firstLocation.type); + } + } + } + } + } catch (error) { + console.error("Failed to fetch event type data:", error); + } + }; + + useEffect(() => { + if (activeTab === "availability") { + fetchSchedules(); + } + if (activeTab === "basics") { + fetchConferencingOptions(); + } + }, [activeTab]); + + useEffect(() => { + // Fetch event type data and conferencing options on initial load + fetchEventTypeData(); + fetchConferencingOptions(); + }, [id]); + + useEffect(() => { + const fetchUsername = async () => { + try { + const userUsername = await CalComAPIService.getUsername(); + setUsername(userUsername); + } catch (error) { + console.error("Failed to fetch username:", error); + } + }; + fetchUsername(); + }, []); + + const formatTime = (time: string) => { + try { + // Handle different time formats that might come from the API + let date: Date; + + if (time.includes(":")) { + // Format like "09:00" or "09:00:00" + const [hours, minutes] = time.split(":").map(Number); + date = new Date(); + date.setHours(hours, minutes || 0, 0, 0); + } else { + // Other formats + date = new Date(time); + } + + return date.toLocaleTimeString("en-US", { + hour: "numeric", + minute: "2-digit", + hour12: true, + }); + } catch (error) { + return time; // Return original if parsing fails + } + }; + + const getDaySchedule = () => { + if (!selectedScheduleDetails) { + console.log("No selectedScheduleDetails"); + return []; + } + + console.log("selectedScheduleDetails:", selectedScheduleDetails); + console.log("availability:", selectedScheduleDetails.availability); + + const daysOfWeek = [ + "Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + ]; + + const daySchedule = daysOfWeek.map((day) => { + // Try different possible day formats + const dayLower = day.toLowerCase(); + const dayUpper = day.toUpperCase(); + const dayShort = day.substring(0, 3).toLowerCase(); // mon, tue, etc. + const dayShortUpper = day.substring(0, 3).toUpperCase(); + + const availability = selectedScheduleDetails.availability?.find((avail) => { + if (!avail.days || !Array.isArray(avail.days)) return false; + + return avail.days.some( + (d) => + d === dayLower || + d === dayUpper || + d === day || + d === dayShort || + d === dayShortUpper || + d.toLowerCase() === dayLower + ); + }); + + console.log(`${day}: availability found:`, availability); + + return { + day, + available: !!availability, + startTime: availability?.startTime, + endTime: availability?.endTime, + }; + }); + + return daySchedule; + }; + + const handlePreview = async () => { + try { + const eventTypeSlug = eventSlug || "preview"; + const link = await CalComAPIService.buildEventTypeLink(eventTypeSlug); + + const supported = await Linking.canOpenURL(link); + if (supported) { + await Linking.openURL(link); + } else { + Alert.alert("Error", "Cannot open this URL on your device."); + } + } catch (error) { + console.error("Failed to generate preview link:", error); + Alert.alert("Error", "Failed to generate preview link. Please try again."); + } + }; + + const handleCopyLink = async () => { + try { + const eventTypeSlug = eventSlug || "event-link"; + const link = await CalComAPIService.buildEventTypeLink(eventTypeSlug); + + Clipboard.setString(link); + Alert.alert("Success", "Link copied!"); + } catch (error) { + console.error("Failed to copy link:", error); + Alert.alert("Error", "Failed to copy link. Please try again."); + } + }; + + const handleDelete = () => { + Alert.alert("Delete Event Type", `Are you sure you want to delete "${eventTitle}"?`, [ + { text: "Cancel", style: "cancel" }, + { + text: "Delete", + style: "destructive", + onPress: async () => { + try { + console.log("Attempting to delete event type with ID:", id); + const eventTypeId = parseInt(id); + + if (isNaN(eventTypeId)) { + throw new Error("Invalid event type ID"); + } + + await CalComAPIService.deleteEventType(eventTypeId); + console.log("Event type deleted successfully"); + + Alert.alert("Success", "Event type deleted successfully", [ + { + text: "OK", + onPress: () => { + console.log("Navigating back after successful deletion"); + router.back(); + }, + }, + ]); + } catch (error) { + console.error("Failed to delete event type:", error); + const errorMessage = error instanceof Error ? error.message : "Unknown error occurred"; + Alert.alert("Error", `Failed to delete event type: ${errorMessage}`); + } + }, + }, + ]); + }; + + const handleSave = async () => { + if (!id) { + Alert.alert("Error", "Event type ID is missing"); + return; + } + + // Validate required fields + if (!eventTitle || !eventSlug) { + Alert.alert("Error", "Title and slug are required"); + return; + } + + const durationNum = parseInt(eventDuration); + if (isNaN(durationNum) || durationNum <= 0) { + Alert.alert("Error", "Duration must be a positive number"); + return; + } + + try { + setSaving(true); + + // Build location payload if a location is selected + let locationsPayload: + | Array<{ + type: string; + integration?: string; + address?: string; + link?: string; + phone?: string; + public?: boolean; + }> + | undefined; + + if (selectedLocation) { + const locationValue = displayNameToLocationValue(selectedLocation); + if (!locationValue) { + Alert.alert("Error", "Invalid location selected"); + setSaving(false); + return; + } + + // Get existing location data to preserve values (address, link, phone) + const existingLocation = eventTypeData?.locations?.[0]; + + // Build location payload based on type + const locationPayload: { + type: string; + integration?: string; + address?: string; + link?: string; + phone?: string; + public?: boolean; + } = { + type: locationValue.type, + }; + + // Add type-specific fields based on location type + if (locationValue.type === "integration") { + locationPayload.integration = locationValue.integration; + locationPayload.public = locationValue.public ?? true; + } else if (locationValue.type === "address") { + locationPayload.address = locationAddress || existingLocation?.address || ""; + locationPayload.public = true; + } else if (locationValue.type === "link") { + locationPayload.link = locationLink || existingLocation?.link || ""; + locationPayload.public = true; + } else if (locationValue.type === "phone") { + locationPayload.phone = locationPhone || existingLocation?.phone || ""; + locationPayload.public = true; + } + + locationsPayload = [locationPayload]; + } + + // Build the payload with all fields + const payload: any = { + title: eventTitle, + slug: eventSlug, + lengthInMinutes: durationNum, + }; + + // Add optional fields if they have values + if (eventDescription) { + payload.description = eventDescription; + } + + if (locationsPayload) { + payload.locations = locationsPayload; + } + + if (selectedSchedule) { + payload.scheduleId = selectedSchedule.id; + } + + if (isHidden !== undefined) { + payload.hidden = isHidden; + } + + // Add buffer times if set + if (beforeEventBuffer && beforeEventBuffer !== "No buffer time") { + const bufferMinutes = parseBufferTime(beforeEventBuffer); + if (bufferMinutes > 0) { + payload.beforeEventBuffer = bufferMinutes; + } + } + + if (afterEventBuffer && afterEventBuffer !== "No buffer time") { + const bufferMinutes = parseBufferTime(afterEventBuffer); + if (bufferMinutes > 0) { + payload.afterEventBuffer = bufferMinutes; + } + } + + // Add minimum booking notice + if (minimumNoticeValue && minimumNoticeUnit) { + const noticeMinutes = parseMinimumNotice(minimumNoticeValue, minimumNoticeUnit); + if (noticeMinutes > 0) { + payload.minimumBookingNotice = noticeMinutes; + } + } + + // Add booking limits if enabled + if (limitBookingFrequency && frequencyLimits.length > 0) { + const limitsCount: any = {}; + frequencyLimits.forEach((limit) => { + const unit = parseFrequencyUnit(limit.unit); + if (unit) { + limitsCount[unit] = parseInt(limit.value) || 1; + } + }); + if (Object.keys(limitsCount).length > 0) { + payload.bookingLimitsCount = limitsCount; + } + } + + if (limitTotalDuration && durationLimits.length > 0) { + const limitsDuration: any = {}; + durationLimits.forEach((limit) => { + const unit = parseFrequencyUnit(limit.unit); + if (unit) { + limitsDuration[unit] = parseInt(limit.value) || 60; + } + }); + if (Object.keys(limitsDuration).length > 0) { + payload.bookingLimitsDuration = limitsDuration; + } + } + + // Add slot interval if not default + if (slotInterval && slotInterval !== "Default") { + const intervalMinutes = parseSlotInterval(slotInterval); + if (intervalMinutes > 0) { + payload.slotInterval = intervalMinutes; + } + } + + // Add other boolean flags + if (onlyShowFirstAvailableSlot) { + payload.onlyShowFirstAvailableSlot = true; + } + + if (maxActiveBookingsPerBooker && maxActiveBookingsValue) { + const count = parseInt(maxActiveBookingsValue); + if (count > 0) { + payload.bookerActiveBookingsLimit = { count }; + } + } + + // Add booking window (future bookings limit) + if (limitFutureBookings) { + if (futureBookingType === "range") { + payload.bookingWindow = { + type: "range", + value: [rangeStartDate, rangeEndDate], + }; + } else { + payload.bookingWindow = { + type: rollingCalendarDays ? "calendarDays" : "businessDays", + value: parseInt(rollingDays), + rolling: true, + }; + } + } else { + payload.bookingWindow = { disabled: true }; + } + + // Add Advanced tab fields + if (calendarEventName) { + payload.metadata = payload.metadata || {}; + payload.metadata.calendarEventName = calendarEventName; + } + + if (addToCalendarEmail) { + payload.metadata = payload.metadata || {}; + payload.metadata.addToCalendarEmail = addToCalendarEmail; + } + + // Booker layouts + if (selectedLayouts.length > 0) { + payload.bookerLayouts = { + enabledLayouts: selectedLayouts, + defaultLayout: defaultLayout, + }; + } + + // Confirmation policy + payload.requiresConfirmation = requiresConfirmation; + + // Boolean flags - always set the actual boolean value so toggling off works + payload.metadata = { + ...(payload.metadata || {}), + disableCancelling, + disableRescheduling, + sendCalVideoTranscription, + autoTranslate, + hideCalendarEventDetails, + hideOrganizerEmail, + allowReschedulingPastEvents, + allowBookingThroughRescheduleLink, + }; + payload.requiresBookerEmailVerification = requiresBookerEmailVerification; + payload.hideCalendarNotes = hideCalendarNotes; + payload.lockTimeZoneToggleOnBookingPage = lockTimezone; + + // Redirect URL + if (successRedirectUrl) { + payload.successRedirectUrl = successRedirectUrl; + if (forwardParamsSuccessRedirect) { + payload.forwardParamsSuccessRedirect = true; + } + } + + // Custom reply-to email + if (customReplyToEmail) { + payload.metadata = payload.metadata || {}; + payload.metadata.customReplyToEmail = customReplyToEmail; + } + + // Event type colors + if (eventTypeColorLight || eventTypeColorDark) { + payload.eventTypeColor = { + lightEventTypeColor: eventTypeColorLight, + darkEventTypeColor: eventTypeColorDark, + }; + } + + // Recurring event + if (recurringEnabled) { + payload.recurrence = { + interval: parseInt(recurringInterval) || 1, + occurrences: parseInt(recurringOccurrences) || 12, + frequency: recurringFrequency, + }; + } else { + payload.recurrence = { disabled: true }; + } + + // Detect create vs update mode + const isCreateMode = id === "new"; + + if (isCreateMode) { + // Create new event type + const newEventType = await CalComAPIService.createEventType(payload); + Alert.alert("Success", "Event type created successfully", [ + { + text: "OK", + onPress: () => router.back(), + }, + ]); + } else { + // Update existing event type + await CalComAPIService.updateEventType(parseInt(id), payload); + Alert.alert("Success", "Event type updated successfully"); + // Refresh event type data + await fetchEventTypeData(); + } + } catch (error) { + console.error("Failed to save event type:", error); + const action = id === "new" ? "create" : "update"; + Alert.alert("Error", `Failed to ${action} event type. Please try again.`); + } finally { + setSaving(false); + } + }; + + return ( + <> + + + {/* Glass Header */} + + + router.back()} + > + + + + + {id === "new" ? "Create Event Type" : truncateTitle(title)} + + + + + {id === "new" ? "Create" : "Save"} + + + + + + {/* Tabs */} + + + {tabs.map((tab) => ( + setActiveTab(tab.id)} + > + + + + {tab.label} + + + + ))} + + + + {/* Content */} + + {activeTab === "basics" && ( + + )} + + {/* Duration Multi-Select Modal */} + setShowDurationDropdown(false)} + > + setShowDurationDropdown(false)} + > + + + Select Available Durations + + + {availableDurations.map((duration) => ( + toggleDurationSelection(duration)} + > + + {duration} + + {selectedDurations.includes(duration) && ( + + )} + + ))} + + setShowDurationDropdown(false)} + > + Done + + + + + + {/* Default Duration Dropdown Modal */} + setShowDefaultDurationDropdown(false)} + > + setShowDefaultDurationDropdown(false)} + > + + + Select Default Duration + + {selectedDurations.map((duration) => ( + { + setDefaultDuration(duration); + setShowDefaultDurationDropdown(false); + }} + > + + {duration} + + {defaultDuration === duration && ( + + )} + + ))} + + + + + {/* Schedule Dropdown Modal */} + setShowScheduleDropdown(false)} + > + setShowScheduleDropdown(false)} + > + + + Select Schedule + + {schedules.map((schedule) => ( + { + setSelectedSchedule(schedule); + setShowScheduleDropdown(false); + fetchScheduleDetails(schedule.id); + }} + > + + + {schedule.name} + + {schedule.isDefault && ( + + Default + + )} + + {selectedSchedule?.id === schedule.id && ( + + )} + + ))} + + + + + {/* Timezone Dropdown Modal */} + setShowTimezoneDropdown(false)} + > + setShowTimezoneDropdown(false)} + > + + + Select Timezone + + + {[ + "America/New_York", + "America/Chicago", + "America/Denver", + "America/Los_Angeles", + "Europe/London", + "Europe/Paris", + "Europe/Berlin", + "Asia/Tokyo", + "Asia/Shanghai", + "Asia/Kolkata", + "Australia/Sydney", + "UTC", + ].map((tz) => ( + { + setSelectedTimezone(tz); + setShowTimezoneDropdown(false); + }} + > + + {tz} + + {(selectedTimezone === tz || + (selectedScheduleDetails?.timeZone === tz && !selectedTimezone)) && ( + + )} + + ))} + + + + + + {/* Location Dropdown Modal */} + setShowLocationDropdown(false)} + > + setShowLocationDropdown(false)} + > + + {conferencingLoading ? ( + + Loading locations... + + ) : ( + (() => { + const groups = getLocationOptions(); + if (groups.length === 0) { + return ( + + No locations available + + ); + } + return ( + + {groups.map((group) => ( + + {/* Section Header */} + + + {group.category} + + + {/* Section Options */} + {group.options.map((option) => ( + { + const newLocation = defaultLocations.find( + (loc) => loc.label === option.label + ); + setSelectedLocation(option.label); + setShowLocationDropdown(false); + // Clear input values that are not needed for the new location + if (!newLocation || newLocation.type !== "inPerson") { + setLocationAddress(""); + } + if (!newLocation || newLocation.type !== "link") { + setLocationLink(""); + } + if (!newLocation || newLocation.type !== "userPhone") { + setLocationPhone(""); + } + }} + > + + {option.iconUrl ? ( + + ) : ( + + + ? + + + )} + + {option.label} + + + {selectedLocation === option.label && ( + + )} + + ))} + + ))} + + ); + })() + )} + + + + + {/* Before Event Buffer Dropdown Modal */} + setShowBeforeBufferDropdown(false)} + > + setShowBeforeBufferDropdown(false)} + > + + + Before event buffer + + {bufferTimeOptions.map((option) => ( + { + setBeforeEventBuffer(option); + setShowBeforeBufferDropdown(false); + }} + > + + {option} + + {beforeEventBuffer === option && ( + + )} + + ))} + + + + + {/* After Event Buffer Dropdown Modal */} + setShowAfterBufferDropdown(false)} + > + setShowAfterBufferDropdown(false)} + > + + + After event buffer + + {bufferTimeOptions.map((option) => ( + { + setAfterEventBuffer(option); + setShowAfterBufferDropdown(false); + }} + > + + {option} + + {afterEventBuffer === option && ( + + )} + + ))} + + + + + {/* Minimum Notice Unit Dropdown Modal */} + setShowMinimumNoticeUnitDropdown(false)} + > + setShowMinimumNoticeUnitDropdown(false)} + > + + + Time unit + + {timeUnitOptions.map((option) => ( + { + setMinimumNoticeUnit(option); + setShowMinimumNoticeUnitDropdown(false); + }} + > + + {option} + + {minimumNoticeUnit === option && ( + + )} + + ))} + + + + + {/* Frequency Unit Dropdown Modal */} + setShowFrequencyUnitDropdown(null)} + > + setShowFrequencyUnitDropdown(null)} + > + + + Frequency unit + + {frequencyUnitOptions.map((option) => { + const selectedLimit = frequencyLimits.find( + (limit) => limit.id === showFrequencyUnitDropdown + ); + const isSelected = selectedLimit?.unit === option; + return ( + { + if (showFrequencyUnitDropdown) { + updateFrequencyLimit(showFrequencyUnitDropdown, "unit", option); + } + setShowFrequencyUnitDropdown(null); + }} + > + + {option} + + {isSelected && } + + ); + })} + + + + + {/* Duration Unit Dropdown Modal */} + setShowDurationUnitDropdown(null)} + > + setShowDurationUnitDropdown(null)} + > + + + Duration unit + + {durationUnitOptions.map((option) => { + const selectedLimit = durationLimits.find( + (limit) => limit.id === showDurationUnitDropdown + ); + const isSelected = selectedLimit?.unit === option; + return ( + { + if (showDurationUnitDropdown) { + updateDurationLimit(showDurationUnitDropdown, "unit", option); + } + setShowDurationUnitDropdown(null); + }} + > + + {option} + + {isSelected && } + + ); + })} + + + + + {/* Slot Interval Dropdown Modal */} + setShowSlotIntervalDropdown(false)} + > + setShowSlotIntervalDropdown(false)} + > + + + Slot interval + + {slotIntervalOptions.map((option) => ( + { + setSlotInterval(option); + setShowSlotIntervalDropdown(false); + }} + > + + {option} + + {slotInterval === option && ( + + )} + + ))} + + + + + {activeTab === "availability" && ( + + )} + + {activeTab === "limits" && ( + + )} + + {activeTab === "advanced" && ( + + )} + + {activeTab === "recurring" && ( + + )} + + {activeTab === "apps" && ( + + Connected Apps + + Manage app integrations for this event type. + + + )} + + {activeTab === "workflows" && ( + + Workflows + + Configure automated workflows and actions. + + + )} + + {activeTab === "webhooks" && ( + + Webhooks + + Set up webhook endpoints for event notifications. + + + )} + + + {/* Bottom Action Bar */} + + + + Hidden + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/companion/app/event-type-detail/constants.ts b/companion/app/event-type-detail/constants.ts new file mode 100644 index 0000000000..dff9194f1e --- /dev/null +++ b/companion/app/event-type-detail/constants.ts @@ -0,0 +1,79 @@ +// Constants for Event Type Detail screen + +export const bufferTimeOptions = [ + "No buffer time", + "5 Minutes", + "10 Minutes", + "15 Minutes", + "20 Minutes", + "30 Minutes", + "45 Minutes", + "60 Minutes", + "90 Minutes", + "120 Minutes", +]; + +export const timeUnitOptions = ["Minutes", "Hours", "Days"]; + +export const frequencyUnitOptions = ["Per day", "Per Month", "Per year"]; + +export const durationUnitOptions = ["Per day", "Per week", "Per month"]; + +export const slotIntervalOptions = [ + "Default", + "5 Minutes", + "10 Minutes", + "15 Minutes", + "20 Minutes", + "30 Minutes", + "45 Minutes", + "60 Minutes", + "75 Minutes", + "90 Minutes", + "105 Minutes", + "120 Minutes", +]; + +export const availableDurations = [ + "5 mins", + "10 mins", + "15 mins", + "20 mins", + "25 mins", + "30 mins", + "45 mins", + "50 mins", + "60 mins", + "75 mins", + "80 mins", + "90 mins", + "120 mins", + "150 mins", + "180 mins", + "240 mins", + "300 mins", + "360 mins", + "420 mins", + "480 mins", +]; + +export const defaultLocations = [ + { label: "Attendee Address", type: "attendeeInPerson" as const }, + { label: "Organizer Address", type: "inPerson" as const }, + { label: "Link Meeting", type: "link" as const }, + { label: "Attendee Phone Number", type: "phone" as const }, + { label: "Organizer Phone Number", type: "userPhone" as const }, +]; + +export const tabs = [ + { id: "basics", label: "Basics" }, + { id: "availability", label: "Availability" }, + { id: "limits", label: "Limits" }, + { id: "advanced", label: "Advanced" }, + { id: "recurring", label: "Recurring" }, + { id: "apps", label: "Apps" }, + { id: "webhooks", label: "Webhooks" }, + { id: "private-links", label: "Private Links" }, + { id: "seats", label: "Seats" }, + { id: "colors", label: "Colors" }, +]; diff --git a/companion/app/event-type-detail/tabs/AdvancedTab.tsx b/companion/app/event-type-detail/tabs/AdvancedTab.tsx new file mode 100644 index 0000000000..754d1ddf70 --- /dev/null +++ b/companion/app/event-type-detail/tabs/AdvancedTab.tsx @@ -0,0 +1,521 @@ +import React from "react"; +import { View, Text, TextInput, TouchableOpacity, Switch, Alert } from "react-native"; +import { Ionicons } from "@expo/vector-icons"; + +interface AdvancedTabProps { + // Calendar event name + calendarEventName: string; + setCalendarEventName: (value: string) => void; + + // Add to calendar email + addToCalendarEmail: string; + setAddToCalendarEmail: (value: string) => void; + + // Layout + selectedLayouts: string[]; + setSelectedLayouts: (layouts: string[]) => void; + defaultLayout: string; + setDefaultLayout: (layout: string) => void; + + // Confirmation settings + requiresConfirmation: boolean; + setRequiresConfirmation: (value: boolean) => void; + disableCancelling: boolean; + setDisableCancelling: (value: boolean) => void; + disableRescheduling: boolean; + setDisableRescheduling: (value: boolean) => void; + + // Additional settings + sendCalVideoTranscription: boolean; + setSendCalVideoTranscription: (value: boolean) => void; + autoTranslate: boolean; + setAutoTranslate: (value: boolean) => void; + requiresBookerEmailVerification: boolean; + setRequiresBookerEmailVerification: (value: boolean) => void; + hideCalendarNotes: boolean; + setHideCalendarNotes: (value: boolean) => void; + hideCalendarEventDetails: boolean; + setHideCalendarEventDetails: (value: boolean) => void; + hideOrganizerEmail: boolean; + setHideOrganizerEmail: (value: boolean) => void; + lockTimezone: boolean; + setLockTimezone: (value: boolean) => void; + allowReschedulingPastEvents: boolean; + setAllowReschedulingPastEvents: (value: boolean) => void; + allowBookingThroughRescheduleLink: boolean; + setAllowBookingThroughRescheduleLink: (value: boolean) => void; + + // Redirect on booking + successRedirectUrl: string; + setSuccessRedirectUrl: (value: string) => void; + forwardParamsSuccessRedirect: boolean; + setForwardParamsSuccessRedirect: (value: boolean) => void; + + // Custom reply-to email + customReplyToEmail: string; + setCustomReplyToEmail: (value: string) => void; + + // Event type colors + eventTypeColorLight: string; + setEventTypeColorLight: (value: string) => void; + eventTypeColorDark: string; + setEventTypeColorDark: (value: string) => void; +} + +export function AdvancedTab(props: AdvancedTabProps) { + const layoutOptions = [ + { id: "MONTH_VIEW", label: "Month", icon: "calendar-outline" }, + { id: "WEEK_VIEW", label: "Weekly", icon: "calendar-outline" }, + { id: "COLUMN_VIEW", label: "Column", icon: "list-outline" }, + ]; + + return ( + + {/* Calendar Event Name Card */} + + Calendar event name + + + Use variables like {"{"}Scheduler{"}"} for booker name, {"{"}Organizer{"}"} for your name + + + + {/* Add to Calendar Card */} + + Add to calendar + + We'll display this email address as the organizer, and send confirmation emails here. + + + + + {/* Layout Card */} + + Layout + + You can select multiple and your bookers can switch views. + + + {/* Layout Options */} + + {layoutOptions.map((layout) => ( + { + if (props.selectedLayouts.includes(layout.id)) { + // Don't allow deselecting if it's the only one + if (props.selectedLayouts.length > 1) { + props.setSelectedLayouts(props.selectedLayouts.filter((l) => l !== layout.id)); + // If removing the default, set a new default + if (props.defaultLayout === layout.id) { + const remaining = props.selectedLayouts.filter((l) => l !== layout.id); + props.setDefaultLayout(remaining[0]); + } + } + } else { + props.setSelectedLayouts([...props.selectedLayouts, layout.id]); + } + }} + > + + + {layout.label} + + {props.selectedLayouts.includes(layout.id) && ( + + )} + + ))} + + + {/* Default View */} + {props.selectedLayouts.length > 1 && ( + + Default view + + {props.selectedLayouts.map((layoutId) => { + const layout = layoutOptions.find((l) => l.id === layoutId); + if (!layout) return null; + return ( + props.setDefaultLayout(layout.id)} + > + {layout.label} + {props.defaultLayout === layout.id && ( + + )} + + ); + })} + + + )} + + + {/* Booking Questions Card */} + + Booking questions + + Customize the questions asked on the booking page. + + + Alert.alert("Coming Soon", "Booking questions customization will be available soon.") + } + > + + Manage booking questions + + + + {/* Requires confirmation */} + + + + Requires confirmation + + The booking needs to be manually confirmed before it is pushed to your calendar + + + + + + + {/* Disable Cancelling */} + + + + Disable Cancelling + + Guests can no longer cancel the event with calendar invite or email + + + + + + + {/* Disable Rescheduling */} + + + + Disable Rescheduling + + Guests can no longer reschedule the event with calendar invite or email + + + + + + + {/* Send Cal Video Transcription Emails */} + + + + + Send Cal Video Transcription Emails + + + Send emails with the transcription of the Cal Video after the meeting ends + + + + + + + {/* Auto translate */} + + + + + Auto translate title and description + + + Automatically translate titles and descriptions to the visitor's browser language + using AI + + + + + + + {/* Requires booker email verification */} + + + + + Requires booker email verification + + + To ensure booker's email verification before scheduling events + + + + + + + {/* Hide notes in calendar */} + + + + Hide notes in calendar + + For privacy reasons, additional inputs and notes will be hidden in the calendar entry + + + + + + + {/* Hide calendar event details */} + + + + + Hide calendar event details on shared calendars + + + When a calendar is shared, events are visible but details are hidden from those + without write access + + + + + + + {/* Hide organizer's email */} + + + + Hide organizer's email + + Hide organizer's email address from the booking screen, email notifications, and + calendar events + + + + + + + {/* Lock timezone */} + + + + + Lock timezone on booking page + + + To lock the timezone on booking page, useful for in-person events + + + + + + + {/* Allow rescheduling past events */} + + + + + Allow rescheduling past events + + + Enabling this option allows for past events to be rescheduled + + + + + + + {/* Allow booking through reschedule link */} + + + + + Allow booking through reschedule link + + + When enabled, users will be able to create a new booking when trying to reschedule a + cancelled booking + + + + + + + {/* Redirect on booking Card */} + + Redirect on booking + + Redirect to a custom URL after a successful booking + + + + Forward query parameters + + + + + {/* Custom Reply-To Email Card */} + + Custom 'Reply-To' email + + Use a different email address as the replyTo for confirmation emails instead of the + organizer's email + + + + + {/* Event Type Color Card */} + + Event type color + + This is only used for event type & booking differentiation within the app. It is not + displayed to bookers. + + + + Light theme color + + + + + + + Dark theme color + + + + + + + + + ); +} diff --git a/companion/app/event-type-detail/tabs/AvailabilityTab.tsx b/companion/app/event-type-detail/tabs/AvailabilityTab.tsx new file mode 100644 index 0000000000..6096989fcf --- /dev/null +++ b/companion/app/event-type-detail/tabs/AvailabilityTab.tsx @@ -0,0 +1,110 @@ +import React from "react"; +import { View, Text, TouchableOpacity } from "react-native"; +import { Ionicons } from "@expo/vector-icons"; +import { Schedule } from "../../../services/calcom"; + +interface DaySchedule { + day: string; + available: boolean; + startTime?: string; + endTime?: string; +} + +interface AvailabilityTabProps { + selectedSchedule: Schedule | null; + setShowScheduleDropdown: (show: boolean) => void; + schedulesLoading: boolean; + scheduleDetailsLoading: boolean; + selectedScheduleDetails: any; + getDaySchedule: () => DaySchedule[]; + formatTime: (time: string) => string; + selectedTimezone: string; +} + +export function AvailabilityTab(props: AvailabilityTabProps) { + return ( + + + Availability + props.setShowScheduleDropdown(true)} + disabled={props.schedulesLoading} + > + + {props.schedulesLoading + ? "Loading schedules..." + : props.selectedSchedule + ? props.selectedSchedule.name + : "Select schedule"} + + + + + + {props.selectedSchedule && ( + <> + + {props.scheduleDetailsLoading ? ( + + Loading schedule details... + + ) : props.selectedScheduleDetails ? ( + props.getDaySchedule().map((dayInfo, index) => ( + + + {dayInfo.day} + + + {dayInfo.available && dayInfo.startTime && dayInfo.endTime + ? `${props.formatTime(dayInfo.startTime)} - ${props.formatTime(dayInfo.endTime)}` + : "Unavailable"} + + + )) + ) : ( + + + Failed to load schedule details + + + )} + + + + Timezone + + + {props.selectedTimezone || props.selectedScheduleDetails?.timeZone || "No timezone"} + + + + + )} + + ); +} diff --git a/companion/app/event-type-detail/tabs/BasicsTab.tsx b/companion/app/event-type-detail/tabs/BasicsTab.tsx new file mode 100644 index 0000000000..939486c06d --- /dev/null +++ b/companion/app/event-type-detail/tabs/BasicsTab.tsx @@ -0,0 +1,257 @@ +import React from "react"; +import { View, Text, TextInput, TouchableOpacity, Switch } from "react-native"; +import { Ionicons } from "@expo/vector-icons"; +import { SvgImage } from "../../../components/SvgImage"; +import { defaultLocations, DefaultLocationType } from "../../../utils/defaultLocations"; +import { slugify } from "../../../utils/slugify"; + +interface BasicsTabProps { + // Title, description, URL + eventTitle: string; + setEventTitle: (value: string) => void; + eventDescription: string; + setEventDescription: (value: string) => void; + username: string; + eventSlug: string; + setEventSlug: (value: string) => void; + + // Duration + allowMultipleDurations: boolean; + setAllowMultipleDurations: (value: boolean) => void; + eventDuration: string; + setEventDuration: (value: string) => void; + selectedDurations: string[]; + setShowDurationDropdown: (show: boolean) => void; + defaultDuration: string; + setShowDefaultDurationDropdown: (show: boolean) => void; + + // Location + selectedLocation: string; + setShowLocationDropdown: (show: boolean) => void; + conferencingLoading: boolean; + getSelectedLocationIconUrl: () => string | null; + locationAddress: string; + setLocationAddress: (value: string) => void; + locationLink: string; + setLocationLink: (value: string) => void; + locationPhone: string; + setLocationPhone: (value: string) => void; +} + +export function BasicsTab(props: BasicsTabProps) { + return ( + + {/* Title and Description Card */} + + + Title + + + + + Description + + + + + URL + + + cal.com/{props.username}/ + + props.setEventSlug(slugify(text, true))} + placeholder="event-slug" + placeholderTextColor="#8E8E93" + /> + + + + + {/* Duration Card */} + + {!props.allowMultipleDurations && ( + + Duration + + + Minutes + + + )} + + {props.allowMultipleDurations && ( + <> + + + Available durations + + props.setShowDurationDropdown(true)} + > + + {props.selectedDurations.length > 0 + ? `${props.selectedDurations.length} duration${ + props.selectedDurations.length > 1 ? "s" : "" + } selected` + : "Select durations"} + + + + + + {props.selectedDurations.length > 0 && ( + + Default duration + props.setShowDefaultDurationDropdown(true)} + > + + {props.defaultDuration || "Select default duration"} + + + + + )} + + )} + + + Allow multiple durations + + + + + {/* Location Card */} + + + Location + props.setShowLocationDropdown(true)} + disabled={props.conferencingLoading} + > + + {!props.conferencingLoading && + props.selectedLocation && + props.getSelectedLocationIconUrl() && ( + + )} + + {props.conferencingLoading + ? "Loading locations..." + : props.selectedLocation || "Select location"} + + + + + + {/* Location Input Fields - shown conditionally based on selected location type */} + {(() => { + const currentLocation = defaultLocations.find( + (loc) => loc.label === props.selectedLocation + ); + if (!currentLocation || !currentLocation.organizerInputType) { + return null; + } + + if (currentLocation.organizerInputType === "text") { + // Text input for address or link + const isAddress = currentLocation.type === "inPerson"; + const isLink = currentLocation.type === "link"; + + return ( + + + {currentLocation.organizerInputLabel || + (isAddress ? "Address" : "Meeting Link")} + + { + if (isAddress) { + props.setLocationAddress(text); + } else { + props.setLocationLink(text); + } + }} + autoCapitalize="none" + autoCorrect={false} + keyboardType={isLink ? "url" : "default"} + /> + {currentLocation.messageForOrganizer && ( + + {currentLocation.messageForOrganizer} + + )} + + ); + } else if (currentLocation.organizerInputType === "phone") { + // Phone input + return ( + + + {currentLocation.organizerInputLabel || "Phone Number"} + + + {currentLocation.messageForOrganizer && ( + + {currentLocation.messageForOrganizer} + + )} + + ); + } + return null; + })()} + + + + ); +} diff --git a/companion/app/event-type-detail/tabs/LimitsTab.tsx b/companion/app/event-type-detail/tabs/LimitsTab.tsx new file mode 100644 index 0000000000..5eea809a17 --- /dev/null +++ b/companion/app/event-type-detail/tabs/LimitsTab.tsx @@ -0,0 +1,500 @@ +import React from "react"; +import { View, Text, TextInput, TouchableOpacity, Switch, Animated } from "react-native"; +import { Ionicons } from "@expo/vector-icons"; +import { + bufferTimeOptions, + timeUnitOptions, + frequencyUnitOptions, + durationUnitOptions, + slotIntervalOptions, +} from "../constants"; + +interface FrequencyLimit { + id: number; + value: string; + unit: string; +} + +interface DurationLimit { + id: number; + value: string; + unit: string; +} + +interface LimitsTabProps { + // Buffer times + beforeEventBuffer: string; + setShowBeforeBufferDropdown: (show: boolean) => void; + afterEventBuffer: string; + setShowAfterBufferDropdown: (show: boolean) => void; + + // Minimum notice + minimumNoticeValue: string; + setMinimumNoticeValue: (value: string) => void; + minimumNoticeUnit: string; + setShowMinimumNoticeUnitDropdown: (show: boolean) => void; + + // Slot interval + slotInterval: string; + setShowSlotIntervalDropdown: (show: boolean) => void; + + // Booking frequency + limitBookingFrequency: boolean; + toggleBookingFrequency: (value: boolean) => void; + frequencyAnimationValue: Animated.Value; + frequencyLimits: FrequencyLimit[]; + updateFrequencyLimit: (id: number, field: string, value: string) => void; + setShowFrequencyUnitDropdown: (id: number) => void; + removeFrequencyLimit: (id: number) => void; + addFrequencyLimit: () => void; + + // Total duration + limitTotalDuration: boolean; + toggleTotalDuration: (value: boolean) => void; + durationAnimationValue: Animated.Value; + durationLimits: DurationLimit[]; + updateDurationLimit: (id: number, field: string, value: string) => void; + setShowDurationUnitDropdown: (id: number) => void; + removeDurationLimit: (id: number) => void; + addDurationLimit: () => void; + + // Only show first slot + onlyShowFirstAvailableSlot: boolean; + setOnlyShowFirstAvailableSlot: (value: boolean) => void; + + // Max active bookings + maxActiveBookingsPerBooker: boolean; + setMaxActiveBookingsPerBooker: (value: boolean) => void; + maxActiveBookingsValue: string; + setMaxActiveBookingsValue: (value: string) => void; + + // Future bookings + limitFutureBookings: boolean; + setLimitFutureBookings: (value: boolean) => void; + futureBookingType: "rolling" | "range"; + setFutureBookingType: (type: "rolling" | "range") => void; + rollingDays: string; + setRollingDays: (value: string) => void; + rollingCalendarDays: boolean; + setRollingCalendarDays: (value: boolean) => void; + rangeStartDate: string; + setRangeStartDate: (value: string) => void; + rangeEndDate: string; + setRangeEndDate: (value: string) => void; +} + +export function LimitsTab(props: LimitsTabProps) { + return ( + + {/* Buffer Time, Minimum Notice, and Slot Interval Card */} + + + Before event + props.setShowBeforeBufferDropdown(true)} + > + {props.beforeEventBuffer} + + + + + + After event + props.setShowAfterBufferDropdown(true)} + > + {props.afterEventBuffer} + + + + + + Minimum Notice + + { + const numericValue = text.replace(/[^0-9]/g, ""); + const num = parseInt(numericValue) || 0; + if (num >= 0) { + props.setMinimumNoticeValue(numericValue || "0"); + } + }} + placeholder="1" + placeholderTextColor="#8E8E93" + keyboardType="numeric" + /> + props.setShowMinimumNoticeUnitDropdown(true)} + > + {props.minimumNoticeUnit} + + + + + + + Time-slot intervals + props.setShowSlotIntervalDropdown(true)} + > + + {props.slotInterval === "Default" ? "Use event length (default)" : props.slotInterval} + + + + + + + {/* Booking Frequency Limit Card */} + + + + Limit booking frequency + + Limit how many times this event can be booked. + + + + + + + {props.limitBookingFrequency && ( + <> + {props.frequencyLimits.map((limit, index) => ( + + { + const numericValue = text.replace(/[^0-9]/g, ""); + const num = parseInt(numericValue) || 0; + if (num >= 0) { + props.updateFrequencyLimit(limit.id, "value", numericValue || "0"); + } + }} + placeholder="1" + placeholderTextColor="#8E8E93" + keyboardType="numeric" + /> + props.setShowFrequencyUnitDropdown(limit.id)} + > + {limit.unit} + + + {props.frequencyLimits.length > 1 && ( + props.removeFrequencyLimit(limit.id)} + > + + + )} + + ))} + + + Add Limit + + + )} + + + + {/* Total Booking Duration Limit Card */} + + + + + Limit total booking duration + + + Limit total amount of time that this event can be booked. + + + + + + + {props.limitTotalDuration && ( + <> + {props.durationLimits.map((limit, index) => ( + + + { + const numericValue = text.replace(/[^0-9]/g, ""); + const num = parseInt(numericValue) || 0; + if (num >= 0) { + props.updateDurationLimit(limit.id, "value", numericValue || "0"); + } + }} + placeholder="60" + placeholderTextColor="#8E8E93" + keyboardType="numeric" + /> + Minutes + + props.setShowDurationUnitDropdown(limit.id)} + > + {limit.unit} + + + {props.durationLimits.length > 1 && ( + props.removeDurationLimit(limit.id)} + > + + + )} + + ))} + + + Add Limit + + + )} + + + + {/* Only Show First Available Slot Card */} + + + + + Only show the first slot of each day as available + + + This will limit your availability for this event type to one slot per day, scheduled + at the earliest available time. + + + + + + + {/* Max Active Bookings Per Booker Card */} + + + + + Limit number of upcoming bookings per booker + + + Limit the number of active bookings a booker can make for this event type. + + + + + {props.maxActiveBookingsPerBooker && ( + + { + const numericValue = text.replace(/[^0-9]/g, ""); + const num = parseInt(numericValue) || 0; + if (num >= 0) { + props.setMaxActiveBookingsValue(numericValue || "1"); + } + }} + placeholder="1" + placeholderTextColor="#8E8E93" + keyboardType="numeric" + /> + + )} + + + {/* Limit Future Bookings Card */} + + + + Limit future bookings + + Limit how far in the future this event can be booked. + + + + + {props.limitFutureBookings && ( + + + props.setFutureBookingType("rolling")} + > + + Rolling + + + props.setFutureBookingType("range")} + > + + Date Range + + + + {props.futureBookingType === "rolling" && ( + + + { + const numericValue = text.replace(/[^0-9]/g, ""); + const num = parseInt(numericValue) || 0; + if (num >= 0) { + props.setRollingDays(numericValue || "30"); + } + }} + placeholder="30" + placeholderTextColor="#8E8E93" + keyboardType="numeric" + /> + props.setRollingCalendarDays(!props.rollingCalendarDays)} + > + + {props.rollingCalendarDays ? "Calendar days" : "Business days"} + + + + days into the future + + )} + {props.futureBookingType === "range" && ( + + + Start date + + + + End date + + + + )} + + )} + + + ); +} diff --git a/companion/app/event-type-detail/tabs/RecurringTab.tsx b/companion/app/event-type-detail/tabs/RecurringTab.tsx new file mode 100644 index 0000000000..d779fdef14 --- /dev/null +++ b/companion/app/event-type-detail/tabs/RecurringTab.tsx @@ -0,0 +1,137 @@ +import React from "react"; +import { View, Text, TextInput, TouchableOpacity, Switch, Alert } from "react-native"; +import { Ionicons } from "@expo/vector-icons"; + +interface RecurringTabProps { + recurringEnabled: boolean; + setRecurringEnabled: (value: boolean) => void; + recurringInterval: string; + setRecurringInterval: (value: string) => void; + recurringFrequency: "daily" | "weekly" | "monthly" | "yearly"; + setRecurringFrequency: (value: "daily" | "weekly" | "monthly" | "yearly") => void; + recurringOccurrences: string; + setRecurringOccurrences: (value: string) => void; +} + +export function RecurringTab({ + recurringEnabled, + setRecurringEnabled, + recurringInterval, + setRecurringInterval, + recurringFrequency, + setRecurringFrequency, + recurringOccurrences, + setRecurringOccurrences, +}: RecurringTabProps) { + return ( + + {/* Recurring Event Toggle Card */} + + + + Recurring event + + Set up this event type to repeat at regular intervals + + + + + + + {/* Recurring Configuration Card - shown when enabled */} + {recurringEnabled && ( + + Recurrence pattern + + {/* Repeats Every */} + + Repeats every + + { + const numericValue = text.replace(/[^0-9]/g, ""); + // Don't allow empty or 0 values; fall back to 1 so users can keep editing + if (numericValue === "" || numericValue === "0") { + setRecurringInterval("1"); + return; + } + const num = parseInt(numericValue); + if (num >= 1 && num <= 20) { + setRecurringInterval(numericValue); + } + }} + placeholder="1" + placeholderTextColor="#8E8E93" + keyboardType="numeric" + /> + { + Alert.alert("Select Frequency", "Choose how often this event repeats", [ + { + text: "Daily", + onPress: () => setRecurringFrequency("daily"), + }, + { + text: "Weekly", + onPress: () => setRecurringFrequency("weekly"), + }, + { + text: "Monthly", + onPress: () => setRecurringFrequency("monthly"), + }, + { + text: "Yearly", + onPress: () => setRecurringFrequency("yearly"), + }, + { text: "Cancel", style: "cancel" }, + ]); + }} + > + {recurringFrequency} + + + + + + {/* Maximum Occurrences */} + + Maximum number of events + + { + const numericValue = text.replace(/[^0-9]/g, ""); + // Don't allow empty or 0 values; fall back to 1 so users can keep editing + if (numericValue === "" || numericValue === "0") { + setRecurringOccurrences("1"); + return; + } + const num = parseInt(numericValue); + if (num >= 1) { + setRecurringOccurrences(numericValue); + } + }} + placeholder="12" + placeholderTextColor="#8E8E93" + keyboardType="numeric" + /> + occurrences + + + The booking will create {recurringOccurrences} events that repeat {recurringFrequency} + + + + )} + + ); +} diff --git a/companion/app/event-type-detail/utils.ts b/companion/app/event-type-detail/utils.ts new file mode 100644 index 0000000000..c6682935f6 --- /dev/null +++ b/companion/app/event-type-detail/utils.ts @@ -0,0 +1,58 @@ +// Utility functions for Event Type Detail + +export const formatDuration = (minutes: string) => { + const mins = parseInt(minutes) || 0; + if (mins < 60) return `${mins}m`; + const hours = Math.floor(mins / 60); + const remainingMins = mins % 60; + return remainingMins > 0 ? `${hours}h ${remainingMins}m` : `${hours}h`; +}; + +export const truncateTitle = (text: string, maxLength: number = 20) => { + return text.length > maxLength ? `${text.substring(0, maxLength)}...` : text; +}; + +export const formatAppIdToDisplayName = (appId: string): string => { + // Convert appId like "google-meet" to "Google Meet" + return appId + .split("-") + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(" "); +}; + +export const displayNameToLocationValue = ( + displayName: string, + defaultLocations: Array<{ label: string; type: string }> +): { + type: string; + integration?: string; + address?: string; + link?: string; + phone?: string; + public?: boolean; +} | null => { + // First check if it's a default location + const defaultLocation = defaultLocations.find((loc) => loc.label === displayName); + if (defaultLocation) { + // Map internal location types to API location types + switch (defaultLocation.type) { + case "attendeeInPerson": + return { type: "attendeeAddress" }; + case "inPerson": + return { type: "address", address: "", public: true }; + case "link": + return { type: "link", link: "", public: true }; + case "phone": + return { type: "attendeePhone" }; + case "userPhone": + return { type: "phone", phone: "", public: true }; + default: + return null; + } + } + + // Check if it's a conferencing app (formatted display name) + // e.g., "Google Meet", "Zoom", etc. + const appId = displayName.toLowerCase().replace(/\s+/g, "-"); + return { type: "integration", integration: appId }; +}; diff --git a/companion/app/index.tsx b/companion/app/index.tsx new file mode 100644 index 0000000000..b8113aefc7 --- /dev/null +++ b/companion/app/index.tsx @@ -0,0 +1,5 @@ +import { Redirect } from "expo-router"; + +export default function Index() { + return ; +} diff --git a/companion/app/oauth/callback.tsx b/companion/app/oauth/callback.tsx new file mode 100644 index 0000000000..b98a1a8681 --- /dev/null +++ b/companion/app/oauth/callback.tsx @@ -0,0 +1,97 @@ +import React, { useEffect } from "react"; +import { View, Text, ActivityIndicator } from "react-native"; +import { useRouter, useLocalSearchParams } from "expo-router"; + +export default function OAuthCallback() { + const router = useRouter(); + const params = useLocalSearchParams(); + + useEffect(() => { + // Extract code, state, and error parameters from URL + const code = params.code as string; + const state = params.state as string; + const error = params.error as string; + const errorDescription = params.error_description as string; + + // Handle OAuth error response + if (error) { + const errorMessage = errorDescription || error || "OAuth authorization failed"; + console.error("OAuth error:", error, errorDescription); + + if (typeof window !== "undefined") { + if (window.opener) { + window.opener.postMessage( + { + type: "OAUTH_ERROR", + error: errorMessage, + errorCode: error, + }, + "*" + ); + window.close(); + } else { + // Store error for main app to pick up + if (state) { + window.localStorage.setItem(`oauth_callback_error_${state}`, errorMessage); + window.localStorage.setItem(`oauth_callback_error_code_${state}`, error); + } + router.replace("/(tabs)"); + } + } + return; + } + + if (code && state) { + // Store the parameters temporarily using state-specific keys to prevent race conditions + // The OAuth service should be able to pick these up + if (typeof window !== "undefined") { + window.localStorage.setItem(`oauth_callback_code_${state}`, code); + window.localStorage.setItem(`oauth_callback_state_${state}`, state); + + // Close the OAuth popup/tab if it exists + if (window.opener) { + window.opener.postMessage( + { + type: "OAUTH_SUCCESS", + code, + state, + }, + "*" + ); + window.close(); + } else { + // Redirect to main app + router.replace("/(tabs)"); + } + } + } else { + console.error("No authorization code or state in OAuth callback"); + + // Handle error case + if (typeof window !== "undefined") { + if (window.opener) { + window.opener.postMessage( + { + type: "OAUTH_ERROR", + error: "No authorization code received", + }, + "*" + ); + window.close(); + } else { + router.replace("/(tabs)"); + } + } + } + }, [params, router]); + + return ( + + + Processing OAuth callback... + + Please wait while we complete the authentication. + + + ); +} diff --git a/companion/assets/adaptive-icon.png b/companion/assets/adaptive-icon.png new file mode 100644 index 0000000000..03d6f6b6c6 Binary files /dev/null and b/companion/assets/adaptive-icon.png differ diff --git a/companion/assets/favicon.png b/companion/assets/favicon.png new file mode 100644 index 0000000000..e75f697b18 Binary files /dev/null and b/companion/assets/favicon.png differ diff --git a/companion/assets/icon.png b/companion/assets/icon.png new file mode 100644 index 0000000000..a0b1526fc7 Binary files /dev/null and b/companion/assets/icon.png differ diff --git a/companion/assets/logo.svg b/companion/assets/logo.svg new file mode 100644 index 0000000000..bccfe813ef --- /dev/null +++ b/companion/assets/logo.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/companion/assets/splash-icon.png b/companion/assets/splash-icon.png new file mode 100644 index 0000000000..03d6f6b6c6 Binary files /dev/null and b/companion/assets/splash-icon.png differ diff --git a/companion/babel.config.js b/companion/babel.config.js new file mode 100644 index 0000000000..18767383ab --- /dev/null +++ b/companion/babel.config.js @@ -0,0 +1,6 @@ +module.exports = function (api) { + api.cache(true); + return { + presets: [["babel-preset-expo", { jsxImportSource: "nativewind" }], "nativewind/babel"], + }; +}; diff --git a/companion/components/AuthTokenProvider.tsx b/companion/components/AuthTokenProvider.tsx new file mode 100644 index 0000000000..b6d6f0f261 --- /dev/null +++ b/companion/components/AuthTokenProvider.tsx @@ -0,0 +1,21 @@ +import { useEffect } from "react"; +import { useAuth } from "../contexts/AuthContext"; +import { setAuthToken, setWebSession } from "../services/calcom"; + +export function AuthTokenProvider({ children }: { children: React.ReactNode }) { + const { accessToken, isAuthenticated, isWebSession } = useAuth(); + + useEffect(() => { + if (isAuthenticated) { + if (accessToken) { + setAuthToken(accessToken); + } + setWebSession(isWebSession); + } else { + setAuthToken(null); + setWebSession(false); + } + }, [isAuthenticated, accessToken, isWebSession]); + + return <>{children}; +} diff --git a/companion/components/CalComLogo.tsx b/companion/components/CalComLogo.tsx new file mode 100644 index 0000000000..d6df1b423d --- /dev/null +++ b/companion/components/CalComLogo.tsx @@ -0,0 +1,40 @@ +import React from "react"; +import Svg, { Path } from "react-native-svg"; + +interface CalComLogoProps { + width?: number; + height?: number; + color?: string; +} + +export function CalComLogo({ width = 101, height = 22, color = "#292929" }: CalComLogoProps) { + return ( + + + + + + + + + + ); +} diff --git a/companion/components/FullScreenModal.tsx b/companion/components/FullScreenModal.tsx new file mode 100644 index 0000000000..bb4ce8281e --- /dev/null +++ b/companion/components/FullScreenModal.tsx @@ -0,0 +1,65 @@ +import React, { useEffect } from "react"; +import { Modal, ModalProps, Platform, View } from "react-native"; + +interface FullScreenModalProps extends ModalProps { + visible: boolean; + onRequestClose: () => void; + children: React.ReactNode; +} + +/** + * A modal wrapper that expands the browser extension iframe to full screen + * when running in the web platform (browser extension). + * + * For mobile platforms, it behaves like a regular Modal. + */ +export function FullScreenModal({ + visible, + onRequestClose, + children, + ...modalProps +}: FullScreenModalProps) { + useEffect(() => { + // Only send expand/collapse messages on web platform (browser extension) + if (Platform.OS !== "web") { + return; + } + + if (visible) { + // Expand the iframe to full width when modal opens + window.parent.postMessage({ type: "cal-companion-expand" }, "*"); + } else { + // Collapse the iframe back to 400px when modal closes + window.parent.postMessage({ type: "cal-companion-collapse" }, "*"); + } + + // Cleanup: collapse on unmount if still visible + return () => { + if (visible && Platform.OS === "web") { + window.parent.postMessage({ type: "cal-companion-collapse" }, "*"); + } + }; + }, [visible]); + + // For non-web platforms, just use the regular Modal + if (Platform.OS !== "web") { + return ( + + {children} + + ); + } + + // For web platform (browser extension), use the modal with full-screen behavior + return ( + + {children} + + ); +} diff --git a/companion/components/Header.tsx b/companion/components/Header.tsx new file mode 100644 index 0000000000..237e8d67e9 --- /dev/null +++ b/companion/components/Header.tsx @@ -0,0 +1,300 @@ +import { Ionicons } from "@expo/vector-icons"; +import { useRouter } from "expo-router"; +import React, { useState, useEffect } from "react"; +import { + View, + Text, + TouchableOpacity, + Image, + ActivityIndicator, + Modal, + Alert, + Linking, + Platform, + ActionSheetIOS, +} from "react-native"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { CalComAPIService, UserProfile } from "../services/calcom"; +import { CalComLogo } from "./CalComLogo"; +import { FullScreenModal } from "./FullScreenModal"; + +export function Header() { + const router = useRouter(); + const insets = useSafeAreaInsets(); + const [userProfile, setUserProfile] = useState(null); + const [loading, setLoading] = useState(true); + const [showProfileModal, setShowProfileModal] = useState(false); + + useEffect(() => { + fetchUserProfile(); + }, []); + + const fetchUserProfile = async () => { + try { + const profile = await CalComAPIService.getUserProfile(); + setUserProfile(profile); + } catch (error) { + console.error("Failed to fetch user profile:", error); + } finally { + setLoading(false); + } + }; + + const handleProfile = () => { + if (Platform.OS === "ios") { + const options = ["Cancel", "My Profile", "My Settings", "Out of Office", "Help", "Sign Out"]; + + ActionSheetIOS.showActionSheetWithOptions( + { + options, + destructiveButtonIndex: 6, // Sign Out + cancelButtonIndex: 0, + title: userProfile?.name || "Profile Menu", + }, + (buttonIndex) => { + switch (buttonIndex) { + case 1: // My Profile + handleMenuOption("profile"); + break; + case 2: // My Settings + handleMenuOption("settings"); + break; + case 3: // Out of Office + handleMenuOption("outOfOffice"); + break; + case 4: // Support + handleMenuOption("help"); + break; + case 5: // Sign Out + handleMenuOption("signOut"); + break; + } + } + ); + } else { + setShowProfileModal(true); + } + }; + + const openExternalLink = async (url: string, fallbackMessage: string) => { + try { + const supported = await Linking.canOpenURL(url); + if (supported) { + await Linking.openURL(url); + } else { + Alert.alert("Error", `Cannot open ${fallbackMessage} on your device.`); + } + } catch (error) { + console.error(`Failed to open ${url}:`, error); + Alert.alert("Error", `Failed to open ${fallbackMessage}. Please try again.`); + } + }; + + const handleMenuOption = (option: string) => { + if (Platform.OS !== "ios") { + setShowProfileModal(false); + } + switch (option) { + case "profile": + openExternalLink("https://app.cal.com/settings/my-account/profile", "Profile page"); + break; + case "settings": + openExternalLink("https://app.cal.com/settings/my-account", "Settings page"); + break; + case "outOfOffice": + openExternalLink( + "https://app.cal.com/settings/my-account/out-of-office", + "Out of Office page" + ); + break; + case "roadmap": + openExternalLink("https://cal.com/roadmap", "Roadmap"); + break; + case "help": + openExternalLink("https://cal.com/help", "Help page"); + break; + case "signOut": + Alert.alert("Sign Out", "Are you sure you want to sign out?", [ + { text: "Cancel", style: "cancel" }, + { + text: "Sign Out", + style: "destructive", + onPress: () => { + // TODO: Implement sign out + console.log("Sign Out pressed"); + }, + }, + ]); + break; + } + }; + + return ( + <> + + {/* Left: Cal.com Logo */} + + + + + {/* Right: Icons */} + + {/* Profile Picture */} + + {loading ? ( + + ) : userProfile?.avatarUrl ? ( + + ) : ( + + + + )} + + + + + {/* Profile Menu Modal */} + setShowProfileModal(false)} + > + setShowProfileModal(false)} + > + e.stopPropagation()} + > + {/* Header with profile info */} + + + + {userProfile?.avatarUrl ? ( + + ) : ( + + {userProfile?.name + ? userProfile.name.charAt(0).toUpperCase() + : userProfile?.email?.charAt(0).toUpperCase() || "?"} + + )} + + + + {userProfile?.name || "User"} + + {userProfile?.email && ( + {userProfile.email} + )} + + + + + {/* Menu Items */} + + handleMenuOption("profile")} + > + + + My Profile + + + + + handleMenuOption("settings")} + > + + + My Settings + + + + + handleMenuOption("outOfOffice")} + > + + + Out of Office + + + + + + + handleMenuOption("roadmap")} + > + + + Roadmap + + + + + handleMenuOption("help")} + > + + + Help + + + + + + + handleMenuOption("signOut")} + > + + Sign Out + + + + {/* Cancel button */} + + setShowProfileModal(false)} + > + Cancel + + + + + + + ); +} diff --git a/companion/components/LoginScreen.tsx b/companion/components/LoginScreen.tsx new file mode 100644 index 0000000000..d20ea5e0f2 --- /dev/null +++ b/companion/components/LoginScreen.tsx @@ -0,0 +1,48 @@ +import React from "react"; +import { View, Text, TouchableOpacity, Alert, ActivityIndicator } from "react-native"; +import { useAuth } from "../contexts/AuthContext"; + +export function LoginScreen() { + const { loginWithOAuth, loading } = useAuth(); + + const handleOAuthLogin = async () => { + try { + await loginWithOAuth(); + } catch (error) { + console.error("OAuth login error:", error); + Alert.alert( + "Login Failed", + error instanceof Error + ? error.message + : "Failed to login with OAuth. Please check your configuration and try again.", + [{ text: "OK" }] + ); + } + }; + + return ( + + + + Welcome to Cal.com Companion + + + Sign in to manage your bookings and event types + + + + {loading ? : null} + + {loading ? "Signing in..." : "Sign in with Cal.com"} + + + + ); +} + +export default LoginScreen; diff --git a/companion/components/LogoutButton.tsx b/companion/components/LogoutButton.tsx new file mode 100644 index 0000000000..14defc4fa3 --- /dev/null +++ b/companion/components/LogoutButton.tsx @@ -0,0 +1,76 @@ +import React, { useState } from "react"; +import { TouchableOpacity, Text, Alert, Platform } from "react-native"; +import { useAuth } from "../contexts/AuthContext"; +import { LogoutConfirmModal } from "./LogoutConfirmModal"; + +interface LogoutButtonProps { + className?: string; + variant?: "default" | "destructive" | "ghost"; +} + +export function LogoutButton({ className = "" }: LogoutButtonProps) { + const { logout } = useAuth(); + const [showConfirmModal, setShowConfirmModal] = useState(false); + + const handleLogoutPress = () => { + if (Platform.OS === "web") { + setShowConfirmModal(true); + } else { + // Mobile: use Alert.alert + Alert.alert("Sign Out", "Are you sure you want to sign out?", [ + { + text: "Cancel", + style: "cancel", + }, + { + text: "Sign Out", + style: "destructive", + onPress: performLogout, + }, + ]); + } + }; + + const performLogout = async () => { + try { + await logout(); + } catch (error) { + console.error("Logout error:", error); + if (Platform.OS === "web") { + window.alert("Failed to sign out. Please try again."); + } else { + Alert.alert("Error", "Failed to sign out. Please try again."); + } + } + }; + + const handleConfirmLogout = () => { + setShowConfirmModal(false); + performLogout(); + }; + + const handleCancelLogout = () => { + setShowConfirmModal(false); + }; + + return ( + <> + + Sign Out + + + + + ); +} + +export default LogoutButton; diff --git a/companion/components/LogoutConfirmModal.tsx b/companion/components/LogoutConfirmModal.tsx new file mode 100644 index 0000000000..fc83ca65bb --- /dev/null +++ b/companion/components/LogoutConfirmModal.tsx @@ -0,0 +1,42 @@ +import React from "react"; +import { View, Text, TouchableOpacity, Platform } from "react-native"; +import { FullScreenModal } from "./FullScreenModal"; + +interface LogoutConfirmModalProps { + visible: boolean; + onConfirm: () => void; + onCancel: () => void; +} + +export function LogoutConfirmModal({ visible, onConfirm, onCancel }: LogoutConfirmModalProps) { + return ( + + + + Sign Out + Are you sure you want to sign out? + + + + Cancel + + + + Sign Out + + + + + + ); +} diff --git a/companion/components/SvgImage.tsx b/companion/components/SvgImage.tsx new file mode 100644 index 0000000000..4848f1fd25 --- /dev/null +++ b/companion/components/SvgImage.tsx @@ -0,0 +1,51 @@ +import React, { useState } from "react"; +import { View, Image } from "react-native"; +import { SvgUri } from "react-native-svg"; + +interface SvgImageProps { + uri: string; + width: number; + height: number; + style?: { marginRight?: number }; +} + +/** + * Component that can display both SVG and regular images + * Falls back to regular Image if SVG fails + */ +export const SvgImage: React.FC = ({ uri, width, height, style }) => { + const [useFallback, setUseFallback] = useState(false); + // Extract pathname (ignore query strings and hash) to detect SVG correctly + // Handles URLs like: icon.svg?variant=dark or icon.svg#hash + const pathname = uri.split("?")[0].split("#")[0]; + const isSvg = pathname.toLowerCase().endsWith(".svg"); + + if (!isSvg || useFallback) { + // Use regular Image for non-SVG or if SVG failed + return ( + + console.log("Image load error:", uri)} + /> + + ); + } + + // Use SvgUri for SVG files + return ( + + { + console.log("SVG load error, falling back to Image:", error); + setUseFallback(true); + }} + /> + + ); +}; diff --git a/companion/components/Tooltip.tsx b/companion/components/Tooltip.tsx new file mode 100644 index 0000000000..08757504c2 --- /dev/null +++ b/companion/components/Tooltip.tsx @@ -0,0 +1,75 @@ +import React, { useState, useRef, useEffect } from "react"; +import { View, Text, Platform } from "react-native"; + +interface TooltipProps { + text: string; + children: React.ReactElement; +} + +export function Tooltip({ text, children }: TooltipProps) { + const [showTooltip, setShowTooltip] = useState(false); + const [position, setPosition] = useState({ top: 0, left: 0 }); + const containerRef = useRef(null); + + // Only show tooltips on web platform + if (Platform.OS !== "web") { + return children; + } + + const handleMouseEnter = (e: any) => { + if (containerRef.current) { + const rect = containerRef.current.getBoundingClientRect(); + setPosition({ + top: rect.top - 40, + left: rect.left + rect.width / 2 + 20, + }); + setShowTooltip(true); + } + }; + + const handleMouseLeave = () => { + setShowTooltip(false); + }; + + return ( + + {children} + {showTooltip && ( + + + {text} + + + )} + + ); +} diff --git a/companion/contexts/AuthContext.tsx b/companion/contexts/AuthContext.tsx new file mode 100644 index 0000000000..b93645ffcf --- /dev/null +++ b/companion/contexts/AuthContext.tsx @@ -0,0 +1,367 @@ +/// + +import React, { createContext, useContext, useState, useEffect, ReactNode } from "react"; +import * as SecureStore from "expo-secure-store"; +import { Platform } from "react-native"; +import { WebAuthService } from "../services/webAuth"; +import { CalComAPIService } from "../services/calcom"; +import { + createCalComOAuthService, + OAuthTokens, + CalComOAuthService, +} from "../services/oauthService"; + +interface AuthContextType { + isAuthenticated: boolean; + accessToken: string | null; + refreshToken: string | null; + userInfo: any; + isWebSession: boolean; + loginFromWebSession: (userInfo: any) => Promise; + loginWithOAuth: () => Promise; + logout: () => Promise; + loading: boolean; +} + +const AuthContext = createContext(undefined); + +const ACCESS_TOKEN_KEY = "cal_access_token"; +const REFRESH_TOKEN_KEY = "cal_refresh_token"; +const OAUTH_TOKENS_KEY = "cal_oauth_tokens"; +const AUTH_TYPE_KEY = "cal_auth_type"; + +type AuthType = "oauth" | "web_session"; + +interface AuthProviderProps { + children: ReactNode; +} + +// Check if chrome.storage is available (browser extension context) +const isChromeStorageAvailable = (): boolean => { + return ( + Platform.OS === "web" && + typeof chrome !== "undefined" && + chrome.storage !== undefined && + chrome.storage.local !== undefined + ); +}; + +// Unified storage helper to abstract web/mobile/extension differences +const storage = { + get: async (key: string): Promise => { + // Use chrome.storage in browser extension context (most secure) + if (isChromeStorageAvailable()) { + return new Promise((resolve) => { + chrome.storage.local.get([key], (result) => { + resolve(result[key] || null); + }); + }); + } + // Fall back to localStorage for regular web apps + if (Platform.OS === "web") { + return localStorage.getItem(key); + } + // Use SecureStore for mobile + return await SecureStore.getItemAsync(key); + }, + set: async (key: string, value: string): Promise => { + // Use chrome.storage in browser extension context (most secure) + if (isChromeStorageAvailable()) { + return new Promise((resolve, reject) => { + chrome.storage.local.set({ [key]: value }, () => { + if (chrome.runtime.lastError) { + reject(new Error(chrome.runtime.lastError.message)); + } else { + resolve(); + } + }); + }); + } + // Fall back to localStorage for regular web apps + if (Platform.OS === "web") { + localStorage.setItem(key, value); + return; + } + // Use SecureStore for mobile + await SecureStore.setItemAsync(key, value); + }, + remove: async (key: string): Promise => { + // Use chrome.storage in browser extension context (most secure) + if (isChromeStorageAvailable()) { + return new Promise((resolve, reject) => { + chrome.storage.local.remove(key, () => { + if (chrome.runtime.lastError) { + reject(new Error(chrome.runtime.lastError.message)); + } else { + resolve(); + } + }); + }); + } + // Fall back to localStorage for regular web apps + if (Platform.OS === "web") { + localStorage.removeItem(key); + return; + } + // Use SecureStore for mobile + await SecureStore.deleteItemAsync(key); + }, + removeAll: async (keys: string[]): Promise => { + // Use chrome.storage in browser extension context (most secure) + if (isChromeStorageAvailable()) { + return new Promise((resolve, reject) => { + chrome.storage.local.remove(keys, () => { + if (chrome.runtime.lastError) { + reject(new Error(chrome.runtime.lastError.message)); + } else { + resolve(); + } + }); + }); + } + // Fall back to localStorage for regular web apps + if (Platform.OS === "web") { + keys.forEach((key) => localStorage.removeItem(key)); + return; + } + // Use SecureStore for mobile + await Promise.all(keys.map((key) => SecureStore.deleteItemAsync(key))); + }, +}; + +export function AuthProvider({ children }: AuthProviderProps) { + const [isAuthenticated, setIsAuthenticated] = useState(false); + const [accessToken, setAccessToken] = useState(null); + const [refreshToken, setRefreshToken] = useState(null); + const [userInfo, setUserInfo] = useState(null); + const [isWebSession, setIsWebSession] = useState(false); + const [loading, setLoading] = useState(true); + const [oauthService] = useState(() => { + try { + return createCalComOAuthService(); + } catch (error) { + console.warn("Failed to initialize OAuth service:", error); + return null; + } + }); + + // Setup refresh token function for OAuth + const setupRefreshTokenFunction = (service: CalComOAuthService) => { + CalComAPIService.setRefreshTokenFunction(async (refreshToken: string) => { + const newTokens = await service.refreshAccessToken(refreshToken); + return { + accessToken: newTokens.accessToken, + refreshToken: newTokens.refreshToken, + }; + }); + }; + + // Common post-login setup: configure API service and fetch user profile + const setupAfterLogin = async (token: string, refreshToken?: string) => { + CalComAPIService.setAccessToken(token, refreshToken); + + try { + await CalComAPIService.getUserProfile(); + } catch (profileError) { + console.error("Failed to fetch user profile:", profileError); + // Don't fail login if profile fetch fails + } + }; + + // Save OAuth tokens to storage + const saveOAuthTokens = async (tokens: OAuthTokens) => { + await storage.set(OAUTH_TOKENS_KEY, JSON.stringify(tokens)); + await storage.set(AUTH_TYPE_KEY, "oauth"); + }; + + // Clear all auth data from storage + const clearAuth = async () => { + await storage.removeAll([ACCESS_TOKEN_KEY, REFRESH_TOKEN_KEY, OAUTH_TOKENS_KEY, AUTH_TYPE_KEY]); + }; + + // Reset all auth state + const resetAuthState = () => { + setAccessToken(null); + setRefreshToken(null); + setUserInfo(null); + setIsAuthenticated(false); + setIsWebSession(false); + CalComAPIService.clearAuth(); + CalComAPIService.clearUserProfile(); + }; + + useEffect(() => { + checkAuthState(); + + // Set up token refresh callback + const handleTokenRefresh = async (newAccessToken: string, newRefreshToken?: string) => { + try { + const tokens: OAuthTokens = { + accessToken: newAccessToken, + refreshToken: newRefreshToken || refreshToken || undefined, + tokenType: "Bearer", + }; + + await saveOAuthTokens(tokens); + setAccessToken(newAccessToken); + if (newRefreshToken) { + setRefreshToken(newRefreshToken); + } + + CalComAPIService.setAccessToken( + newAccessToken, + newRefreshToken || refreshToken || undefined + ); + } catch (error) { + console.error("Failed to handle token refresh:", error); + await logout(); + } + }; + + CalComAPIService.setTokenRefreshCallback(handleTokenRefresh); + + return () => { + CalComAPIService.setTokenRefreshCallback(() => Promise.resolve()); + }; + }, [refreshToken]); + + // Handle OAuth authentication + const handleOAuthAuth = async (storedTokens: OAuthTokens) => { + if (!oauthService) return; + + // Refresh token if expired + let tokens = storedTokens; + if (oauthService.isTokenExpired(storedTokens) && storedTokens.refreshToken) { + try { + console.log("Access token expired, refreshing..."); + tokens = await oauthService.refreshAccessToken(storedTokens.refreshToken); + await saveOAuthTokens(tokens); + } catch (refreshError) { + console.error("Failed to refresh token:", refreshError); + await clearAuth(); + return; + } + } + + // Set state + setAccessToken(tokens.accessToken); + setRefreshToken(tokens.refreshToken || null); + setIsAuthenticated(true); + setIsWebSession(false); + + // Setup API service and refresh function + await setupAfterLogin(tokens.accessToken, tokens.refreshToken); + if (tokens.refreshToken) { + setupRefreshTokenFunction(oauthService); + } + }; + + // Handle web session authentication + const handleWebSessionAuth = () => { + setIsWebSession(true); + }; + + const checkAuthState = async () => { + try { + const authType = (await storage.get(AUTH_TYPE_KEY)) as AuthType | null; + const storedOAuthTokens = await storage.get(OAUTH_TOKENS_KEY); + const storedTokens = storedOAuthTokens ? JSON.parse(storedOAuthTokens) : null; + + if (authType === "oauth" && storedTokens && oauthService) { + await handleOAuthAuth(storedTokens); + } else if (authType === "web_session") { + handleWebSessionAuth(); + } + } catch (error) { + console.error("Failed to check auth state:", error); + } finally { + setLoading(false); + } + }; + + const loginFromWebSession = async (sessionUserInfo: any) => { + try { + setUserInfo(sessionUserInfo); + setIsAuthenticated(true); + setIsWebSession(true); + await storage.set(AUTH_TYPE_KEY, "web_session"); + + // Try to get tokens from web session + const tokens = await WebAuthService.getTokensFromWebSession(); + if (tokens.accessToken) { + setAccessToken(tokens.accessToken); + setRefreshToken(tokens.refreshToken || null); + await setupAfterLogin(tokens.accessToken, tokens.refreshToken); + } + } catch (error) { + console.error("Failed to login from web session:", error); + throw error; + } + }; + + const loginWithOAuth = async (): Promise => { + if (!oauthService) { + throw new Error("OAuth service not available. Please check your configuration."); + } + + try { + setLoading(true); + + const tokens = await oauthService.startAuthorizationFlow(); + + // Save tokens + await saveOAuthTokens(tokens); + + // Update state + setAccessToken(tokens.accessToken); + setRefreshToken(tokens.refreshToken || null); + setIsAuthenticated(true); + setIsWebSession(false); + + // Setup API service and refresh function + await setupAfterLogin(tokens.accessToken, tokens.refreshToken); + if (tokens.refreshToken) { + setupRefreshTokenFunction(oauthService); + } + + // Clear PKCE parameters + oauthService.clearPKCEParams(); + } catch (error) { + console.error("OAuth login failed:", error); + throw error; + } finally { + setLoading(false); + } + }; + + const logout = async () => { + try { + await clearAuth(); + resetAuthState(); + } catch (error) { + console.error("Failed to logout:", error); + } + }; + + const value: AuthContextType = { + isAuthenticated, + accessToken, + refreshToken, + userInfo, + isWebSession, + loginFromWebSession, + loginWithOAuth, + logout, + loading, + }; + + return {children}; +} + +export function useAuth(): AuthContextType { + const context = useContext(AuthContext); + if (context === undefined) { + throw new Error("useAuth must be used within an AuthProvider"); + } + return context; +} diff --git a/companion/extension/entrypoints/background/index.ts b/companion/extension/entrypoints/background/index.ts new file mode 100644 index 0000000000..557e4e7360 --- /dev/null +++ b/companion/extension/entrypoints/background/index.ts @@ -0,0 +1,223 @@ +/// + +// @ts-ignore - WXT provides this globally +export default defineBackground(() => { + chrome.action.onClicked.addListener((tab) => { + if (tab.id) { + chrome.tabs.sendMessage(tab.id, { action: "icon-clicked" }, (response) => { + if (chrome.runtime.lastError) { + // Expected on pages where content script isn't loaded + } + }); + } + }); + + chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { + if (message.action === "fetch-event-types") { + fetchEventTypes() + .then((eventTypes) => { + sendResponse({ data: eventTypes }); + }) + .catch((error) => { + sendResponse({ error: error.message }); + }); + + return true; + } + + if (message.action === "start-extension-oauth") { + const authUrl = message.authUrl; + const urlObj = new URL(authUrl); + const state = urlObj.searchParams.get("state"); + + if (state) { + chrome.storage.local.set({ oauth_state: state }, () => { + if (chrome.runtime.lastError) { + console.warn("Failed to store OAuth state:", chrome.runtime.lastError.message); + } + }); + } + + handleExtensionOAuth(authUrl) + .then((responseUrl) => { + sendResponse({ success: true, responseUrl }); + }) + .catch((error) => { + sendResponse({ success: false, error: error.message }); + }); + + return true; + } + + if (message.action === "exchange-oauth-tokens") { + handleTokenExchange(message.tokenRequest, message.tokenEndpoint, message.state) + .then((tokens) => { + sendResponse({ success: true, tokens }); + }) + .catch((error) => { + sendResponse({ success: false, error: error.message }); + }); + + return true; + } + + return false; + }); +}); + +async function handleExtensionOAuth(authUrl: string): Promise { + return new Promise((resolve, reject) => { + if (!chrome.identity) { + reject(new Error("Chrome identity API not available")); + return; + } + + chrome.identity.launchWebAuthFlow( + { + url: authUrl, + interactive: true, + }, + (responseUrl) => { + if (chrome.runtime.lastError) { + console.error("OAuth flow failed:", chrome.runtime.lastError.message); + reject(new Error(`OAuth flow failed: ${chrome.runtime.lastError.message}`)); + } else if (responseUrl) { + resolve(responseUrl); + } else { + console.error("OAuth flow returned no response URL"); + reject(new Error("OAuth flow cancelled or failed")); + } + } + ); + }); +} + +async function handleTokenExchange( + tokenRequest: any, + tokenEndpoint: string, + state?: string +): Promise { + // CSRF protection: validate state parameter (defense-in-depth) + // State is already validated in iframe context; this validates in background script + if (state) { + try { + const result = await chrome.storage.local.get(["oauth_state"]); + const storedState = result.oauth_state; + + if (storedState && storedState !== state) { + await chrome.storage.local.remove("oauth_state"); + console.error("State parameter mismatch - possible CSRF attack"); + throw new Error("Invalid state parameter - possible CSRF attack"); + } + + if (storedState && storedState === state) { + await chrome.storage.local.remove("oauth_state"); + } + } catch (error) { + if (error instanceof Error && error.message.includes("Invalid state parameter")) { + throw error; + } + // For storage errors, log but continue (non-blocking) + console.warn("State validation warning (non-blocking):", error); + } + } + + const body = new URLSearchParams(); + Object.keys(tokenRequest).forEach((key) => { + body.append(key, tokenRequest[key]); + }); + + try { + const response = await fetch(tokenEndpoint, { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + Accept: "application/json", + }, + body: body.toString(), + }); + + if (!response.ok) { + const errorData = await response.text(); + console.error("Token exchange error response:", errorData); + throw new Error(`Token exchange failed: ${response.status} ${errorData}`); + } + + const tokenData = await response.json(); + + const tokens = { + accessToken: tokenData.access_token, + refreshToken: tokenData.refresh_token, + tokenType: tokenData.token_type || "Bearer", + expiresAt: tokenData.expires_in ? Date.now() + tokenData.expires_in * 1000 : undefined, + scope: tokenData.scope, + }; + + return tokens; + } catch (error) { + console.error("Token exchange failed:", error); + throw error instanceof Error ? error : new Error(String(error)); + } +} + +async function fetchEventTypes() { + const API_BASE_URL = "https://api.cal.com/v2"; + + const result = await chrome.storage.local.get(["cal_oauth_tokens"]); + const oauthTokens = result.cal_oauth_tokens + ? JSON.parse(result.cal_oauth_tokens as string) + : null; + + if (!oauthTokens?.accessToken) { + throw new Error("No OAuth access token found. Please sign in with OAuth."); + } + + // Get current user to retrieve username + const userResponse = await fetch(`${API_BASE_URL}/me`, { + headers: { + Authorization: `Bearer ${oauthTokens.accessToken}`, + "Content-Type": "application/json", + "cal-api-version": "2024-06-11", + }, + }); + + if (!userResponse.ok) { + throw new Error(`Failed to get user: ${userResponse.statusText}`); + } + + const userData = await userResponse.json(); + const username = userData?.data?.username; + + const params = new URLSearchParams(); + if (username) { + params.append("username", username); + } + + const queryString = params.toString(); + const endpoint = `${API_BASE_URL}/event-types${queryString ? `?${queryString}` : ""}`; + + const response = await fetch(endpoint, { + headers: { + Authorization: `Bearer ${oauthTokens.accessToken}`, + "Content-Type": "application/json", + "cal-api-version": "2024-06-14", + }, + }); + + if (!response.ok) { + throw new Error(`Failed to fetch event types: ${response.statusText}`); + } + + const data = await response.json(); + + // Handle different response formats + if (Array.isArray(data)) { + return data; + } else if (data?.data && Array.isArray(data.data)) { + return data.data; + } else if (data?.eventTypes && Array.isArray(data.eventTypes)) { + return data.eventTypes; + } + + return []; +} diff --git a/companion/extension/entrypoints/content.ts b/companion/extension/entrypoints/content.ts new file mode 100644 index 0000000000..677d860db0 --- /dev/null +++ b/companion/extension/entrypoints/content.ts @@ -0,0 +1,2923 @@ +/// + +export default defineContentScript({ + matches: [""], + main() { + const existingSidebar = document.getElementById("cal-companion-sidebar"); + if (existingSidebar) { + return; + } + + // Initialize Gmail integration if on Gmail + // Wrapped in try-catch to prevent breaking Gmail if anything fails + if (window.location.hostname === "mail.google.com") { + try { + initGmailIntegration(); + console.log("Cal.com: Gmail integration initialized successfully"); + } catch (error) { + // Fail silently - don't break Gmail UI + console.error("Cal.com: Failed to initialize Gmail integration:", error); + } + } + + let isVisible = false; + let isClosed = true; + + // Create sidebar container + const sidebarContainer = document.createElement("div"); + sidebarContainer.id = "cal-companion-sidebar"; + sidebarContainer.style.position = "fixed"; + sidebarContainer.style.top = "0"; + sidebarContainer.style.right = "0"; + sidebarContainer.style.pointerEvents = "none"; + sidebarContainer.style.width = "100vw"; + sidebarContainer.style.height = "100vh"; + sidebarContainer.style.zIndex = "2147483647"; + sidebarContainer.style.backgroundColor = "transparent"; + sidebarContainer.style.transition = "none"; + sidebarContainer.style.transform = "translateX(100%)"; + sidebarContainer.style.display = "none"; + + // Create iframe container with max width + const iframeContainer = document.createElement("div"); + iframeContainer.style.width = "100%"; + iframeContainer.style.height = "100%"; + iframeContainer.style.display = "flex"; + iframeContainer.style.justifyContent = "flex-end"; + iframeContainer.style.pointerEvents = "none"; + + // Create iframe + const iframe = document.createElement("iframe"); + iframe.src = "http://localhost:8081"; + iframe.style.width = "400px"; + iframe.style.height = "100%"; + iframe.style.border = "none"; + iframe.style.borderRadius = "0"; + iframe.style.backgroundColor = "transparent"; + iframe.style.pointerEvents = "auto"; + iframe.style.transition = "none"; + + iframeContainer.appendChild(iframe); + + // Listen for messages from iframe to control width and handle OAuth + window.addEventListener("message", (event) => { + // Security: Only accept messages from our iframe's origin + // This prevents malicious scripts on the host page from manipulating the companion + const iframeOrigin = new URL(iframe.src).origin; + if (event.source !== iframe.contentWindow || event.origin !== iframeOrigin) { + return; + } + + if (event.data.type === "cal-companion-expand") { + // Disable transition for instant expansion + iframe.style.transition = "none"; + iframe.style.width = "100%"; + iframeContainer.style.pointerEvents = "auto"; + iframeContainer.style.justifyContent = "center"; + } else if (event.data.type === "cal-companion-collapse") { + // Disable transition for instant collapse + iframe.style.transition = "none"; + iframe.style.width = "400px"; + iframeContainer.style.pointerEvents = "none"; + iframeContainer.style.justifyContent = "flex-end"; + } else if (event.data.type === "cal-extension-oauth-request") { + // Handle OAuth request from iframe + handleOAuthRequest(event.data.authUrl, iframe.contentWindow); + } else if (event.data.type === "cal-extension-token-exchange-request") { + // Handle token exchange request from iframe + handleTokenExchangeRequest( + event.data.tokenRequest, + event.data.tokenEndpoint, + event.data.state, // Pass state for CSRF validation + iframe.contentWindow + ); + } + }); + + // Handle OAuth requests by forwarding to background script + function handleOAuthRequest(authUrl: string, iframeWindow: Window | null) { + // Send request to background script + chrome.runtime.sendMessage( + { action: "start-extension-oauth", authUrl: authUrl }, + (response) => { + if (chrome.runtime.lastError) { + console.error( + "Failed to communicate with background script:", + chrome.runtime.lastError.message + ); + iframeWindow?.postMessage( + { + type: "cal-extension-oauth-result", + success: false, + error: `Extension communication failed: ${chrome.runtime.lastError.message}`, + }, + "*" + ); + return; + } + + // Forward the response back to iframe + if (response.success) { + iframeWindow?.postMessage( + { + type: "cal-extension-oauth-result", + success: true, + responseUrl: response.responseUrl, + }, + "*" + ); + } else { + iframeWindow?.postMessage( + { + type: "cal-extension-oauth-result", + success: false, + error: response.error || "OAuth flow failed", + }, + "*" + ); + } + } + ); + } + + // Handle token exchange requests by forwarding to background script + function handleTokenExchangeRequest( + tokenRequest: any, + tokenEndpoint: string, + state: string | undefined, + iframeWindow: Window | null + ) { + // Send request to background script + chrome.runtime.sendMessage( + { + action: "exchange-oauth-tokens", + tokenRequest: tokenRequest, + tokenEndpoint: tokenEndpoint, + state: state, // Include state for CSRF validation + }, + (response) => { + if (chrome.runtime.lastError) { + console.error( + "Failed to communicate with background script:", + chrome.runtime.lastError.message + ); + iframeWindow?.postMessage( + { + type: "cal-extension-token-exchange-result", + success: false, + error: `Extension communication failed: ${chrome.runtime.lastError.message}`, + }, + "*" + ); + return; + } + + // Forward the response back to iframe + if (response.success) { + iframeWindow?.postMessage( + { + type: "cal-extension-token-exchange-result", + success: true, + tokens: response.tokens, + }, + "*" + ); + } else { + iframeWindow?.postMessage( + { + type: "cal-extension-token-exchange-result", + success: false, + error: response.error || "Token exchange failed", + }, + "*" + ); + } + } + ); + } + + sidebarContainer.appendChild(iframeContainer); + + // Create floating buttons container + const buttonsContainer = document.createElement("div"); + buttonsContainer.id = "cal-companion-buttons"; + buttonsContainer.style.position = "fixed"; + buttonsContainer.style.top = "20px"; + buttonsContainer.style.right = "420px"; + buttonsContainer.style.display = "flex"; + buttonsContainer.style.flexDirection = "column"; + buttonsContainer.style.gap = "8px"; + buttonsContainer.style.zIndex = "2147483648"; + buttonsContainer.style.transition = "none"; + buttonsContainer.style.display = "none"; + + // Create toggle button + const toggleButton = document.createElement("button"); + toggleButton.innerHTML = "◀"; + toggleButton.style.width = "40px"; + toggleButton.style.height = "40px"; + toggleButton.style.borderRadius = "50%"; + toggleButton.style.border = "1px solid white"; + toggleButton.style.backgroundColor = "rgba(0, 0, 0, 0.5)"; + toggleButton.style.backdropFilter = "blur(10px)"; + toggleButton.style.color = "white"; + toggleButton.style.cursor = "pointer"; + toggleButton.style.fontSize = "16px"; + toggleButton.style.boxShadow = "0 2px 8px rgba(0,0,0,0.2)"; + toggleButton.style.transition = "all 0.2s ease"; + toggleButton.style.display = "flex"; + toggleButton.style.alignItems = "center"; + toggleButton.style.justifyContent = "center"; + toggleButton.title = "Toggle sidebar"; + + // Create close button + const closeButton = document.createElement("button"); + closeButton.innerHTML = ` + + + +`; + closeButton.style.width = "40px"; + closeButton.style.height = "40px"; + closeButton.style.borderRadius = "50%"; + closeButton.style.border = "1px solid rgba(255, 255, 255, 0.5)"; + closeButton.style.backgroundColor = "rgba(0, 0, 0, 0.5)"; + closeButton.style.backdropFilter = "blur(10px)"; + closeButton.style.color = "white"; + closeButton.style.cursor = "pointer"; + closeButton.style.fontSize = "16px"; + closeButton.style.boxShadow = "0 2px 8px rgba(0,0,0,0.2)"; + closeButton.style.transition = "all 0.2s ease"; + closeButton.style.display = "flex"; + closeButton.style.alignItems = "center"; + closeButton.style.justifyContent = "center"; + closeButton.title = "Close sidebar"; + + // Add hover effects + toggleButton.addEventListener("mouseenter", () => { + toggleButton.style.transform = "scale(1.1)"; + }); + toggleButton.addEventListener("mouseleave", () => { + toggleButton.style.transform = "scale(1)"; + }); + + closeButton.addEventListener("mouseenter", () => { + closeButton.style.transform = "scale(1.1)"; + }); + closeButton.addEventListener("mouseleave", () => { + closeButton.style.transform = "scale(1)"; + }); + + // Toggle functionality + toggleButton.addEventListener("click", () => { + if (isClosed) return; + + isVisible = !isVisible; + if (isVisible) { + sidebarContainer.style.transform = "translateX(0)"; + buttonsContainer.style.right = "420px"; + toggleButton.innerHTML = ` + + +`; + } else { + sidebarContainer.style.transform = "translateX(100%)"; + buttonsContainer.style.right = "20px"; + toggleButton.innerHTML = ` + + +`; + } + }); + + // Close functionality + closeButton.addEventListener("click", () => { + isClosed = true; + isVisible = false; + sidebarContainer.style.display = "none"; + buttonsContainer.style.display = "none"; + }); + + // Add buttons to container + buttonsContainer.appendChild(toggleButton); + buttonsContainer.appendChild(closeButton); + + // Add everything to DOM + document.body.appendChild(sidebarContainer); + document.body.appendChild(buttonsContainer); + + // Listen for extension icon click + chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { + if (message.action === "icon-clicked") { + if (isClosed) { + // Reopen closed sidebar + isClosed = false; + isVisible = true; + sidebarContainer.style.display = "block"; + buttonsContainer.style.display = "flex"; + sidebarContainer.style.transform = "translateX(0)"; + buttonsContainer.style.right = "420px"; + toggleButton.innerHTML = ` + + +`; + } else { + // Toggle visible sidebar + isVisible = !isVisible; + if (isVisible) { + sidebarContainer.style.transform = "translateX(0)"; + buttonsContainer.style.right = "420px"; + toggleButton.innerHTML = ` + + +`; + } else { + sidebarContainer.style.transform = "translateX(100%)"; + buttonsContainer.style.right = "20px"; + toggleButton.innerHTML = ` + + +`; + } + } + sendResponse({ success: true }); // Send response to acknowledge + } + }); + + // Gmail integration function + function initGmailIntegration() { + // Cache for event types (refreshed on page reload) + let eventTypesCache: any[] | null = null; + let cacheTimestamp: number | null = null; + const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes + + // Function to inject Cal.com button as a new table cell after Send button + function injectCalButton() { + // Look specifically for Gmail compose Send buttons - they have specific attributes + // Gmail Send button usually has div[role="button"] with specific data attributes inside a td + const sendButtons = document.querySelectorAll( + 'div[role="button"][data-tooltip="Send ‪(Ctrl-Enter)‬"], div[role="button"][data-tooltip*="Send"], div[role="button"][aria-label*="Send"]' + ); + + sendButtons.forEach((sendButton) => { + // Find the parent td cell that contains the send button + const sendButtonCell = sendButton.closest("td"); + if (!sendButtonCell) return; + + // Find the parent table row + const tableRow = sendButtonCell.closest("tr"); + if (!tableRow) return; + + // Check if we already injected our button for this specific send button + const existingCalButton = sendButtonCell.parentElement?.querySelector( + ".cal-companion-gmail-button" + ); + if (existingCalButton) return; + + // Additional check: make sure this is actually in a compose window + // Gmail compose windows have specific containers + const composeWindow = sendButton.closest('[role="dialog"]') || sendButton.closest(".nH"); + if (!composeWindow) return; + + // Create new table cell for Cal.com button + const calButtonCell = document.createElement("td"); + calButtonCell.className = "cal-companion-gmail-button"; + calButtonCell.style.cssText = ` + padding: 0; + margin: 0; + vertical-align: middle; + border: none; + `; + + // Create Cal.com button + const calButton = document.createElement("div"); + calButton.style.cssText = ` + display: inline-flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + margin: 2px 4px; + border-radius: 50%; + background-color: #000000; + border: 2px solid #ffffff; + cursor: pointer; + transition: all 0.2s ease; + box-shadow: 0 1px 3px rgba(0,0,0,0.1); + `; + + // Add Cal.com icon (official logo) + calButton.innerHTML = ` + + + + + + `; + + // Add hover effect + calButton.addEventListener("mouseenter", () => { + calButton.style.backgroundColor = "#333333"; + calButton.style.transform = "scale(1.05)"; + }); + + calButton.addEventListener("mouseleave", () => { + calButton.style.backgroundColor = "#000000"; + calButton.style.transform = "scale(1)"; + }); + + // Add click handler to show Cal.com menu + calButton.addEventListener("click", (e) => { + e.preventDefault(); + e.stopPropagation(); + + // Remove any existing menus + const existingMenu = document.querySelector(".cal-companion-gmail-menu"); + if (existingMenu) { + existingMenu.remove(); + return; + } + + // Create menu + const menu = document.createElement("div"); + menu.className = "cal-companion-gmail-menu"; + menu.style.cssText = ` + position: absolute; + bottom: 100%; + left: 0; + width: 400px; + max-height: 250px; + background: white; + border-radius: 8px; + box-shadow: 0 1px 2px 0 rgba(60,64,67,.3),0 2px 6px 2px rgba(60,64,67,.15); + font-family: "Google Sans",Roboto,RobotoDraft,Helvetica,Arial,sans-serif; + font-size: 14px; + z-index: 9999; + overflow-y: auto; + margin-bottom: 4px; + `; + + // Show loading state + menu.innerHTML = ` +
+ Loading event types... +
+ `; + + // Array to track tooltips for cleanup + const tooltipsToCleanup: HTMLElement[] = []; + + // Fetch event types from Cal.com API + fetchEventTypes(menu, tooltipsToCleanup); + + // Position menu relative to button + calButtonCell.style.position = "relative"; + calButtonCell.appendChild(menu); + + // Close menu when clicking outside + setTimeout(() => { + document.addEventListener("click", function closeMenu(e) { + if (!menu.contains(e.target as Node)) { + // Clean up all tooltips + tooltipsToCleanup.forEach((tooltip) => tooltip.remove()); + menu.remove(); + document.removeEventListener("click", closeMenu); + } + }); + }, 0); + }); + + function createTooltip(text, buttonElement) { + const tooltip = document.createElement("div"); + tooltip.className = "cal-tooltip"; + tooltip.style.cssText = ` + position: fixed; + background-color: #1a1a1a; + color: white; + padding: 4px 8px; + border-radius: 6px; + font-size: 12px; + font-weight: 600; + white-space: nowrap; + pointer-events: none; + opacity: 0; + transition: opacity 0.15s ease; + z-index: 10002; + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); + display: none; + `; + tooltip.textContent = text; + document.body.appendChild(tooltip); + + // Position tooltip on hover + const updatePosition = () => { + const rect = buttonElement.getBoundingClientRect(); + tooltip.style.left = `${rect.left + rect.width / 2}px`; + tooltip.style.top = `${rect.top - 8}px`; + tooltip.style.transform = "translate(-50%, -100%)"; + }; + + buttonElement.addEventListener("mouseenter", () => { + updatePosition(); + tooltip.style.display = "block"; + tooltip.style.opacity = "1"; + }); + + buttonElement.addEventListener("mouseleave", () => { + tooltip.style.opacity = "0"; + setTimeout(() => { + if (tooltip.style.opacity === "0") { + tooltip.style.display = "none"; + } + }, 150); + }); + + return tooltip; + } + + function openCalSidebar() { + // Open Cal.com sidebar or quick schedule flow + if (isClosed) { + // Trigger sidebar open + chrome.runtime.sendMessage({ action: "icon-clicked" }); + } else { + // Toggle sidebar visibility + isVisible = !isVisible; + if (isVisible) { + sidebarContainer.style.transform = "translateX(0)"; + } else { + sidebarContainer.style.transform = "translateX(100%)"; + } + } + } + + async function fetchEventTypes(menu, tooltipsToCleanup) { + try { + // Check cache first + const now = Date.now(); + const isCacheValid = + eventTypesCache && cacheTimestamp && now - cacheTimestamp < CACHE_DURATION; + + let eventTypes: any[] = []; + + if (isCacheValid) { + // Use cached data + eventTypes = eventTypesCache!; + } else { + // Check if extension context is valid + if (!chrome.runtime?.id) { + throw new Error("Extension context invalidated. Please reload the page."); + } + + // Fetch fresh data from background script + const response = await new Promise((resolve, reject) => { + try { + chrome.runtime.sendMessage({ action: "fetch-event-types" }, (response) => { + if (chrome.runtime.lastError) { + reject(new Error(chrome.runtime.lastError.message)); + } else if (response && response.error) { + reject(new Error(response.error)); + } else { + resolve(response); + } + }); + } catch (err) { + reject(err); + } + }); + + if (response && (response as any).data) { + eventTypes = (response as any).data; + } else if (Array.isArray(response)) { + eventTypes = response; + } else { + eventTypes = []; + } + + // Ensure eventTypes is an array + if (!Array.isArray(eventTypes)) { + eventTypes = []; + } + + // Update cache + eventTypesCache = eventTypes; + cacheTimestamp = now; + } + + // Clear loading state + menu.innerHTML = ""; + + if (eventTypes.length === 0) { + menu.innerHTML = ` +
+ No event types found +
+ `; + return; + } + + // Add event types - with additional safety checks + try { + eventTypes.forEach((eventType, index) => { + // Validate eventType object + if (!eventType || typeof eventType !== "object") { + return; + } + + const title = eventType.title || "Untitled Event"; + const length = + eventType.lengthInMinutes || eventType.length || eventType.duration || 30; + const description = eventType.description || ""; + + const menuItem = document.createElement("div"); + menuItem.style.cssText = ` + padding: 14px 16px; + display: flex; + align-items: center; + justify-content: space-between; + transition: background-color 0.1s ease; + border-bottom: ${index < eventTypes.length - 1 ? "1px solid #E5E5EA" : "none"}; + `; + + // Create content wrapper + const contentWrapper = document.createElement("div"); + contentWrapper.style.cssText = ` + flex: 1; + display: flex; + flex-direction: column; + cursor: pointer; + margin-right: 12px; + position: relative; + min-width: 0; + overflow: hidden; + `; + + contentWrapper.innerHTML = ` +
+ ${title} +
+
+ + ${length}min + + ${description ? `${description}` : ""} +
+ `; + + // Create tooltip for content wrapper + const contentTooltip = document.createElement("div"); + contentTooltip.className = "cal-tooltip"; + contentTooltip.style.cssText = ` + position: fixed; + background-color: #1a1a1a; + color: white; + padding: 4px 8px; + border-radius: 6px; + font-size: 12px; + font-weight: 600; + white-space: nowrap; + pointer-events: none; + opacity: 0; + transition: opacity 0.15s ease; + z-index: 10002; + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); + display: none; + `; + contentTooltip.textContent = "Insert link"; + document.body.appendChild(contentTooltip); + tooltipsToCleanup.push(contentTooltip); + + // Show/hide tooltip + contentWrapper.addEventListener("mouseenter", (e) => { + const rect = contentWrapper.getBoundingClientRect(); + // Shift right to better center on the visible content + contentTooltip.style.left = `${rect.left + rect.width / 2 + 80}px`; + contentTooltip.style.top = `${rect.top + 35}px`; + contentTooltip.style.transform = "translate(-50%, -100%)"; + contentTooltip.style.display = "block"; + contentTooltip.style.opacity = "1"; + }); + contentWrapper.addEventListener("mouseleave", () => { + contentTooltip.style.opacity = "0"; + setTimeout(() => { + if (contentTooltip.style.opacity === "0") { + contentTooltip.style.display = "none"; + } + }, 150); + }); + + // Add click handler to content wrapper to insert link + contentWrapper.addEventListener("click", (e) => { + e.stopPropagation(); + // Remove tooltip + contentTooltip.remove(); + menu.remove(); + // Insert link into email text + insertEventTypeLink(eventType); + }); + + // Create buttons container + const buttonsContainer = document.createElement("div"); + buttonsContainer.style.cssText = ` + display: flex; + align-items: center; + gap: 0; + flex-shrink: 0; + `; + + // Preview button + const previewBtn = document.createElement("button"); + previewBtn.innerHTML = ` + + + + + + `; + previewBtn.style.cssText = ` + width: 32px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; + border: 1px solid #e5e5ea; + border-right: none; + border-radius: 6px 0 0 6px; + background: white; + cursor: pointer; + transition: background-color 0.1s ease; + padding: 0; + position: relative; + `; + + // Create tooltip for preview button + const previewTooltip = createTooltip("Preview", previewBtn); + tooltipsToCleanup.push(previewTooltip); + + previewBtn.addEventListener("click", (e) => { + e.stopPropagation(); + const bookingUrl = `https://cal.com/${eventType.users?.[0]?.username || "user"}/${eventType.slug}`; + window.open(bookingUrl, "_blank"); + }); + previewBtn.addEventListener("mouseenter", () => { + previewBtn.style.backgroundColor = "#f8f9fa"; + }); + previewBtn.addEventListener("mouseleave", () => { + previewBtn.style.backgroundColor = "white"; + }); + + // Copy link button + const copyBtn = document.createElement("button"); + copyBtn.innerHTML = ` + + + + + `; + copyBtn.style.cssText = ` + width: 32px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; + border: 1px solid #e5e5ea; + border-right: none; + background: white; + cursor: pointer; + transition: background-color 0.1s ease; + padding: 0; + position: relative; + `; + + // Create tooltip for copy button + const copyTooltip = createTooltip("Copy link", copyBtn); + tooltipsToCleanup.push(copyTooltip); + + copyBtn.addEventListener("click", (e) => { + e.stopPropagation(); + // Copy to clipboard + const bookingUrl = `https://cal.com/${eventType.users?.[0]?.username || "user"}/${eventType.slug}`; + navigator.clipboard + .writeText(bookingUrl) + .then(() => { + showNotification("Link copied!", "success"); + // Change icon to checkmark + copyBtn.innerHTML = ` + + + + `; + setTimeout(() => { + copyBtn.innerHTML = ` + + + + + `; + }, 2000); + }) + .catch(() => { + showNotification("Failed to copy link", "error"); + }); + }); + copyBtn.addEventListener("mouseenter", () => { + copyBtn.style.backgroundColor = "#f8f9fa"; + }); + copyBtn.addEventListener("mouseleave", () => { + copyBtn.style.backgroundColor = "white"; + }); + + // Edit button (replaces three dots menu) + const editBtn = document.createElement("button"); + editBtn.innerHTML = ` + + + + + `; + editBtn.style.cssText = ` + width: 32px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; + border: 1px solid #e5e5ea; + border-radius: 0 6px 6px 0; + background: white; + cursor: pointer; + transition: background-color 0.1s ease; + padding: 0; + position: relative; + `; + + // Create tooltip for edit button + const editTooltip = createTooltip("Edit", editBtn); + tooltipsToCleanup.push(editTooltip); + + editBtn.addEventListener("click", (e) => { + e.stopPropagation(); + const editUrl = `https://app.cal.com/event-types/${eventType.id}`; + window.open(editUrl, "_blank"); + }); + editBtn.addEventListener("mouseenter", () => { + editBtn.style.backgroundColor = "#f8f9fa"; + }); + editBtn.addEventListener("mouseleave", () => { + editBtn.style.backgroundColor = "white"; + }); + + // Assemble buttons + buttonsContainer.appendChild(previewBtn); + buttonsContainer.appendChild(copyBtn); + buttonsContainer.appendChild(editBtn); + + // Hover effect for whole item + menuItem.addEventListener("mouseenter", () => { + menuItem.style.backgroundColor = "#f8f9fa"; + }); + + menuItem.addEventListener("mouseleave", () => { + menuItem.style.backgroundColor = "transparent"; + }); + + // Assemble menu item + menuItem.appendChild(contentWrapper); + menuItem.appendChild(buttonsContainer); + + menu.appendChild(menuItem); + }); + } catch (forEachError) { + menu.innerHTML = ` +
+ Error displaying event types +
+ `; + } + } catch (error) { + menu.innerHTML = ` +
+ Failed to load event types +
+
+ Error: ${(error as Error).message} +
+
+ +
+ `; + } + } + + function insertEventTypeLink(eventType) { + // Construct the Cal.com booking link + const bookingUrl = `https://cal.com/${eventType.users?.[0]?.username || "user"}/${eventType.slug}`; + + // Try to insert at cursor position in the compose field + const inserted = insertTextAtCursor(bookingUrl); + + if (inserted) { + showNotification("Link inserted", "success"); + } else { + // Fallback: copy to clipboard if insertion fails + navigator.clipboard + .writeText(bookingUrl) + .then(() => { + showNotification("Link copied!", "success"); + }) + .catch(() => { + showNotification("Failed to copy link", "error"); + }); + } + } + + function copyEventTypeLink(eventType) { + // Construct the Cal.com booking link + const bookingUrl = `https://cal.com/${eventType.users?.[0]?.username || "user"}/${eventType.slug}`; + + // Try to insert at cursor position in the compose field + const inserted = insertTextAtCursor(bookingUrl); + + if (inserted) { + showNotification("Link inserted", "success"); + } else { + // Fallback: copy to clipboard if insertion fails + navigator.clipboard + .writeText(bookingUrl) + .then(() => { + showNotification("Link copied!", "success"); + }) + .catch(() => { + showNotification("Failed to copy link", "error"); + }); + } + } + + function insertTextAtCursor(text) { + // Find the active compose field + // Gmail uses contenteditable divs for the compose body + const composeBody = + document.querySelector('[role="textbox"][aria-label*="Message Body"]') || + document.querySelector('[role="textbox"][g_editable="true"]') || + document.querySelector('div[contenteditable="true"][role="textbox"]'); + + if (!composeBody) { + return false; + } + + // Focus the compose field + (composeBody as HTMLElement).focus(); + + // Get the current selection + const selection = window.getSelection(); + if (!selection || selection.rangeCount === 0) { + // If no selection, append to the end + const textNode = document.createTextNode(" " + text + " "); + composeBody.appendChild(textNode); + + // Move cursor after inserted text + const range = document.createRange(); + range.setStartAfter(textNode); + range.collapse(true); + selection?.removeAllRanges(); + selection?.addRange(range); + + return true; + } + + // Insert at cursor position + const range = selection.getRangeAt(0); + range.deleteContents(); + + // Create a text node with the link (with spaces around it) + const textNode = document.createTextNode(" " + text + " "); + range.insertNode(textNode); + + // Move cursor after inserted text + range.setStartAfter(textNode); + range.collapse(true); + selection.removeAllRanges(); + selection.addRange(range); + + // Trigger input event so Gmail knows content changed + composeBody.dispatchEvent(new Event("input", { bubbles: true })); + composeBody.dispatchEvent(new Event("change", { bubbles: true })); + + return true; + } + + function showNotification(message, type) { + const notification = document.createElement("div"); + notification.style.cssText = ` + position: fixed; + bottom: 80px; + right: 80px; + padding: 10px 12px; + background: ${type === "success" ? "#111827" : "#752522"}; + color: white; + border: 1px solid #2b2b2b; + border-radius: 8px; + font-family: "Google Sans",Roboto,RobotoDraft,Helvetica,Arial,sans-serif; + font-size: 14px; + font-weight: 600; + z-index: 10000; + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.3), 0 2px 6px 2px rgba(0, 0, 0, 0.15); + display: flex; + align-items: center; + gap: 8px; + opacity: 0; + transform: translateY(10px); + transition: opacity 0.2s ease, transform 0.2s ease; + `; + + // Add check icon for success + if (type === "success") { + const checkIcon = document.createElement("span"); + checkIcon.innerHTML = ` + + + + `; + checkIcon.style.cssText = ` + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + `; + notification.appendChild(checkIcon); + } + + // Add message text + const messageText = document.createElement("span"); + messageText.textContent = message; + notification.appendChild(messageText); + + document.body.appendChild(notification); + + // Trigger fade-in animation + requestAnimationFrame(() => { + notification.style.opacity = "1"; + notification.style.transform = "translateY(0)"; + }); + + // Fade out and remove after 3 seconds + setTimeout(() => { + notification.style.opacity = "0"; + notification.style.transform = "translateY(10px)"; + setTimeout(() => { + notification.remove(); + }, 200); + }, 3000); + } + + // Add tooltip + calButton.title = "Schedule with Cal.com"; + + // Add button to cell + calButtonCell.appendChild(calButton); + + // Insert the new cell after the send button cell + if (sendButtonCell.nextSibling) { + tableRow.insertBefore(calButtonCell, sendButtonCell.nextSibling); + } else { + tableRow.appendChild(calButtonCell); + } + }); + } + + // Initial injection + setTimeout(injectCalButton, 1000); + + // Watch for DOM changes (Gmail is a SPA) + const observer = new MutationObserver(() => { + injectCalButton(); + }); + + observer.observe(document.body, { + childList: true, + subtree: true, + }); + + // Also inject on URL changes (Gmail navigation) + let currentUrl = window.location.href; + setInterval(() => { + if (window.location.href !== currentUrl) { + currentUrl = window.location.href; + setTimeout(injectCalButton, 500); + } + }, 1000); + + // ========== Google Calendar Chip Integration ========== + + /** + * Generate HTML email embed for Cal.com booking + * Based on the email embed feature in main Cal.com codebase + */ + function generateEmailEmbedHTML(params: { + eventType: any; + username: string; + slots: any[]; + duration: number; + timezone: string; + timezoneOffset: string; + }): string { + const { eventType, username, slots, duration, timezone, timezoneOffset } = params; + + // Group slots by date + const slotsByDate: { [date: string]: any[] } = {}; + slots.forEach((slot) => { + if (!slotsByDate[slot.isoDate]) { + slotsByDate[slot.isoDate] = []; + } + slotsByDate[slot.isoDate].push(slot); + }); + + // Generate time slot buttons HTML + const datesHTML = Object.keys(slotsByDate) + .sort() + .map((date) => { + const dateSlots = slotsByDate[date]; + const formattedDate = dateSlots[0].date; // Already formatted like "Thu, 27 November" + + const slotsHTML = dateSlots + .map((slot) => { + // URL-encode the timezone to handle special characters like "/" + const encodedTimezone = encodeURIComponent(timezone); + const bookingURL = `https://cal.com/${username}/${eventType.slug}?duration=${duration}&date=${slot.isoDate}&slot=${slot.isoTimestamp}&cal.tz=${encodedTimezone}`; + + return ` + + + + + + + + +
+ + ${slot.startTime} + +
+ + `; + }) + .join(""); + + return ` + + + + + + + + + +
+ + ${formattedDate} + +
+ + + + ${slotsHTML} + + +
+
+ `; + }) + .join(""); + + // Complete HTML structure + return ` +
+
+ ${eventType.title} +
+
+ Duration: ${duration} mins +
+
+ + Timezone: ${timezone} (${timezoneOffset}) + +
+ ${datesHTML} + +
+ Powered by Cal.com +
+
+


+ `; + } + + /** + * Helper function to insert HTML into Gmail compose field + * @param html - The HTML content to insert + * @param targetComposeElement - Optional: The chip's compose element to ensure we insert in the correct window + */ + function insertGmailHTML(html: string, targetComposeElement?: HTMLElement): boolean { + try { + // Validate input + if (!html || typeof html !== "string") { + console.warn("Cal.com: Invalid HTML to insert"); + return false; + } + + // If a target compose element is provided, find the compose body within its scope + let composeBody: Element | null = null; + + if (targetComposeElement) { + const composeWindow = + targetComposeElement.closest('[role="dialog"]') || + targetComposeElement.closest(".nH") || + targetComposeElement.closest('div[contenteditable="true"]')?.parentElement; + + if (composeWindow) { + composeBody = + composeWindow.querySelector('[role="textbox"][aria-label*="Message Body"]') || + composeWindow.querySelector('[role="textbox"][g_editable="true"]') || + composeWindow.querySelector('div[contenteditable="true"][role="textbox"]'); + } + } + + // Fallback: Try global selectors + if (!composeBody) { + composeBody = + document.querySelector('[role="textbox"][aria-label*="Message Body"]') || + document.querySelector('[role="textbox"][g_editable="true"]') || + document.querySelector('div[contenteditable="true"][role="textbox"]'); + } + + if (!composeBody) { + console.warn("Cal.com: Gmail compose field not found"); + return false; + } + + // Focus the compose body + try { + (composeBody as HTMLElement).focus(); + } catch (focusError) { + console.warn("Cal.com: Failed to focus compose field:", focusError); + } + + const selection = window.getSelection(); + if (!selection || selection.rangeCount === 0) { + // No selection - append at end + try { + const tempDiv = document.createElement("div"); + tempDiv.innerHTML = html; + composeBody.appendChild(tempDiv); + return true; + } catch (appendError) { + console.warn("Cal.com: Failed to append HTML:", appendError); + return false; + } + } + + // Insert at cursor position + try { + const range = selection.getRangeAt(0); + range.deleteContents(); + + const tempDiv = document.createElement("div"); + tempDiv.innerHTML = html; + + // Insert all child nodes + const fragment = document.createDocumentFragment(); + while (tempDiv.firstChild) { + fragment.appendChild(tempDiv.firstChild); + } + + range.insertNode(fragment); + range.collapse(false); + selection.removeAllRanges(); + selection.addRange(range); + + // Trigger input events + (composeBody as HTMLElement).dispatchEvent(new Event("input", { bubbles: true })); + (composeBody as HTMLElement).dispatchEvent(new Event("change", { bubbles: true })); + + return true; + } catch (insertError) { + console.warn("Cal.com: Failed to insert HTML at cursor:", insertError); + return false; + } + } catch (error) { + console.error("Cal.com: Critical error inserting HTML:", error); + return false; + } + } + + /** + * Helper function to insert text into Gmail compose field + * @param text - The text to insert + * @param targetComposeElement - Optional: The chip's compose element to ensure we insert in the correct window + */ + function insertGmailText(text: string, targetComposeElement?: HTMLElement): boolean { + try { + // Validate input + if (!text || typeof text !== "string") { + console.warn("Cal.com: Invalid text to insert"); + return false; + } + + // If a target compose element is provided, find the compose body within its scope + // Otherwise, fall back to the first compose field (legacy behavior) + let composeBody: Element | null = null; + + if (targetComposeElement) { + // Find the compose window that contains the chip + const composeWindow = + targetComposeElement.closest('[role="dialog"]') || + targetComposeElement.closest(".nH") || + targetComposeElement.closest('div[contenteditable="true"]')?.parentElement; + + if (composeWindow) { + // Look for compose body within this specific window + composeBody = + composeWindow.querySelector('[role="textbox"][aria-label*="Message Body"]') || + composeWindow.querySelector('[role="textbox"][g_editable="true"]') || + composeWindow.querySelector('div[contenteditable="true"][role="textbox"]'); + } + } + + // Fallback: Try global selectors if no target or if scoped search failed + if (!composeBody) { + composeBody = + document.querySelector('[role="textbox"][aria-label*="Message Body"]') || + document.querySelector('[role="textbox"][g_editable="true"]') || + document.querySelector('div[contenteditable="true"][role="textbox"]'); + } + + if (!composeBody) { + console.warn("Cal.com: Gmail compose field not found (structure may have changed)"); + return false; + } + + // Try to focus the compose body + try { + (composeBody as HTMLElement).focus(); + } catch (focusError) { + console.warn("Cal.com: Failed to focus compose field:", focusError); + // Continue anyway - might still work + } + + const selection = window.getSelection(); + if (!selection || selection.rangeCount === 0) { + try { + const textNode = document.createTextNode(" " + text + " "); + composeBody.appendChild(textNode); + const range = document.createRange(); + range.setStartAfter(textNode); + range.collapse(true); + selection?.removeAllRanges(); + selection?.addRange(range); + return true; + } catch (appendError) { + console.warn("Cal.com: Failed to append text (fallback method):", appendError); + return false; + } + } + + try { + const range = selection.getRangeAt(0); + range.deleteContents(); + const textNode = document.createTextNode(" " + text + " "); + range.insertNode(textNode); + range.setStartAfter(textNode); + range.collapse(true); + selection.removeAllRanges(); + selection.addRange(range); + composeBody.dispatchEvent(new Event("input", { bubbles: true })); + composeBody.dispatchEvent(new Event("change", { bubbles: true })); + return true; + } catch (insertError) { + console.warn("Cal.com: Failed to insert text at cursor:", insertError); + return false; + } + } catch (error) { + console.error( + "Cal.com: Critical error inserting text (Gmail structure may have changed):", + error + ); + return false; + } + } + + /** + * Helper function to show notification + * Fail silently if DOM manipulation fails (prevents breaking Gmail) + */ + function showGmailNotification(message: string, type: "success" | "error"): void { + try { + const notification = document.createElement("div"); + notification.style.cssText = ` + position: fixed; + bottom: 80px; + right: 80px; + padding: 10px 12px; + background: ${type === "success" ? "#111827" : "#752522"}; + color: white; + border: 1px solid #2b2b2b; + border-radius: 8px; + font-family: "Google Sans",Roboto,Arial,sans-serif; + font-size: 14px; + font-weight: 600; + z-index: 10000; + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.3), 0 2px 6px 2px rgba(0, 0, 0, 0.15); + display: flex; + align-items: center; + gap: 8px; + opacity: 0; + transform: translateY(10px); + transition: opacity 0.2s ease, transform 0.2s ease; + `; + + if (type === "success") { + const icon = document.createElement("span"); + icon.innerHTML = ` + + + + `; + icon.style.cssText = + "display: flex; align-items: center; justify-content: center; flex-shrink: 0;"; + notification.appendChild(icon); + } + + const text = document.createElement("span"); + text.textContent = message; + notification.appendChild(text); + + document.body.appendChild(notification); + + requestAnimationFrame(() => { + try { + notification.style.opacity = "1"; + notification.style.transform = "translateY(0)"; + } catch (e) { + // Ignore animation errors + } + }); + + setTimeout(() => { + try { + notification.style.opacity = "0"; + notification.style.transform = "translateY(10px)"; + setTimeout(() => { + try { + notification.remove(); + } catch (e) { + // Ignore removal errors + } + }, 200); + } catch (e) { + // Ignore animation errors + } + }, 3000); + } catch (error) { + // Silently fail - notifications are non-critical + console.warn("Cal.com: Failed to show notification:", error); + } + } + + /** + * Auto-remove all Cal.com action bars before sending email + */ + function setupAutoRemoveOnSend() { + try { + // Helper function to remove all action bars and marked Google chips + const removeAllActionBars = () => { + // Remove action bars + const allActionBars = document.querySelectorAll(".cal-companion-action-bar"); + if (allActionBars.length > 0) { + console.log(`Cal.com: Removing ${allActionBars.length} action bar(s) before send`); + allActionBars.forEach((bar) => { + try { + // Call cleanup function to remove event listeners before removing DOM node + if ((bar as any).__cleanup) { + (bar as any).__cleanup(); + } + bar.remove(); + } catch (error) { + console.warn("Cal.com: Failed to remove action bar:", error); + } + }); + } + + // Remove Google chips that were marked for removal (user used Cal.com) + const markedChips = document.querySelectorAll( + '.gmail_chip[data-calcom-remove-on-send="true"]' + ); + if (markedChips.length > 0) { + console.log( + `Cal.com: Removing ${markedChips.length} Google chip(s) before send (user used Cal.com)` + ); + markedChips.forEach((chip) => { + try { + chip.remove(); + } catch (error) { + console.warn("Cal.com: Failed to remove Google chip:", error); + } + }); + } + }; + + // Method 1: Watch for clicks on Send button + document.addEventListener( + "click", + (e) => { + const target = e.target as HTMLElement; + + // Check if the clicked element is a Send button + const isSendButton = + target.getAttribute("data-tooltip")?.includes("Send") || + target.getAttribute("aria-label")?.includes("Send") || + target.textContent?.trim() === "Send" || + target.closest('[data-tooltip*="Send"]') || + target.closest('[aria-label*="Send"]') || + target + .closest('[role="button"][data-tooltip]') + ?.getAttribute("data-tooltip") + ?.includes("Send"); + + if (isSendButton) { + console.log("Cal.com: Send button clicked"); + removeAllActionBars(); + } + }, + true + ); // Use capture phase + + // Method 2: Watch for keyboard shortcuts (Ctrl+Enter / Cmd+Enter) + document.addEventListener( + "keydown", + (e) => { + const isCtrlOrCmd = e.ctrlKey || e.metaKey; + const isEnter = e.key === "Enter"; + + if (isCtrlOrCmd && isEnter) { + // Check if we're in a compose window + const activeElement = document.activeElement; + const isInCompose = + activeElement?.getAttribute("role") === "textbox" || + activeElement?.getAttribute("contenteditable") === "true" || + activeElement?.closest('[role="textbox"]'); + + if (isInCompose) { + console.log("Cal.com: Send keyboard shortcut detected (Ctrl/Cmd+Enter)"); + removeAllActionBars(); + } + } + }, + true + ); + + // Note: Action bars are now overlays (like Grammarly), so they won't be included in emails. + // We keep the click and keyboard listeners for clean UI (removing overlays when sending). + // Removed the MutationObserver as it was too aggressive and removing action bars prematurely. + + console.log("Cal.com: Auto-remove on send listeners added (click, keyboard)"); + } catch (error) { + console.warn("Cal.com: Failed to setup auto-remove on send:", error); + } + } + + /** + * Watch for Google Calendar scheduling chips and add Cal.com suggestion button + */ + function watchForGoogleChips() { + try { + const observer = new MutationObserver((mutations) => { + try { + const chips = document.querySelectorAll( + ".gmail_chip.gmail_ad_hoc_v2_content:not([data-calcom-chip-processed])" + ); + + chips.forEach((chip) => { + try { + chip.setAttribute("data-calcom-chip-processed", "true"); + handleGoogleChipDetected(chip as HTMLElement); + } catch (error) { + // Silently fail for individual chips to prevent breaking other chips + console.warn("Cal.com: Failed to process chip:", error); + } + }); + } catch (error) { + // Silently fail to prevent Gmail UI from breaking + console.warn("Cal.com: Failed to detect chips:", error); + } + }); + + observer.observe(document.body, { + childList: true, + subtree: true, + }); + + // Also check for existing chips on page load + setTimeout(() => { + try { + const existingChips = document.querySelectorAll( + ".gmail_chip.gmail_ad_hoc_v2_content:not([data-calcom-chip-processed])" + ); + existingChips.forEach((chip) => { + try { + chip.setAttribute("data-calcom-chip-processed", "true"); + handleGoogleChipDetected(chip as HTMLElement); + } catch (error) { + console.warn("Cal.com: Failed to process existing chip:", error); + } + }); + } catch (error) { + console.warn("Cal.com: Failed to find existing chips:", error); + } + }, 1000); + } catch (error) { + // Critical failure - log but don't break Gmail + console.error("Cal.com: Failed to initialize chip watcher:", error); + } + } + + /** + * Handle when a Google Calendar chip is detected + */ + function handleGoogleChipDetected(chipElement: HTMLElement) { + try { + console.log("Cal.com: handleGoogleChipDetected called"); + + // Validate chip element exists and is in DOM + if (!chipElement || !chipElement.isConnected) { + console.warn("Cal.com: Invalid or disconnected chip element"); + return; + } + + // Only show action bars in ACTIVE compose windows (not in sent/inbox emails) + // Check if chip is in a contenteditable area (compose window) + const composeBody = chipElement.closest('[contenteditable="true"]'); + + if (!composeBody) { + // Silently skip - chip not in compose area (likely in sent/inbox) + return; + } + + const parsedData = parseGoogleChip(chipElement); + + if (!parsedData || parsedData.slots.length === 0) { + // Silently skip - chip not fully loaded or invalid + return; + } + + console.log( + `Cal.com: ✅ Google chip detected - ${parsedData.slots.length} slot${parsedData.slots.length > 1 ? "s" : ""} (${parsedData.detectedDuration}min)` + ); + + // Safely check for parent element + if (!chipElement.parentElement) { + return; + } + + // Check if button already exists + const existingActionBar = chipElement.parentElement.querySelector( + ".cal-companion-action-bar" + ); + const scheduleId = chipElement.getAttribute("data-ad-hoc-schedule-id"); + + if (existingActionBar) { + // Action bar exists - check if schedule ID or duration changed + const existingScheduleId = existingActionBar.getAttribute("data-schedule-id"); + const existingDuration = existingActionBar.getAttribute("data-duration"); + + if ( + existingScheduleId === scheduleId && + existingDuration === String(parsedData.detectedDuration) + ) { + // Nothing changed, no need to recreate + return; + } + // Something changed - remove old action bar and create new one + console.log(`Cal.com: 🔄 Chip updated - ${parsedData.detectedDuration}min`); + try { + existingActionBar.remove(); + } catch (e) { + // Silently ignore removal errors + } + } + + // Watch for mutations on this chip to detect ANY changes + const observer = new MutationObserver(() => { + try { + const newScheduleId = chipElement.getAttribute("data-ad-hoc-schedule-id"); + const actionBar = chipElement.parentElement?.querySelector( + ".cal-companion-action-bar" + ); + const currentScheduleId = actionBar?.getAttribute("data-schedule-id"); + + // Check if schedule ID changed (this changes when time range or duration changes) + if (currentScheduleId && newScheduleId && currentScheduleId !== newScheduleId) { + console.log(`Cal.com: 🔄 Time range/duration changed`); + try { + actionBar?.remove(); + // Recreate action bar with new data + handleGoogleChipDetected(chipElement); + } catch (e) { + // Silently ignore recreation errors + } + } + } catch (error) { + // Silently ignore observer errors - expected during DOM updates + } + }); + + try { + observer.observe(chipElement, { + childList: true, + subtree: true, + attributes: true, + attributeFilter: ["data-ad-hoc-v2-params"], + }); + } catch (error) { + // Silently ignore observer setup errors + } + + // Create our own action bar below the chip + const actionBar = document.createElement("div"); + actionBar.className = "cal-companion-action-bar"; + actionBar.setAttribute("contenteditable", "false"); // Make non-editable + actionBar.setAttribute("data-cal-companion", "true"); // Marker for our elements + actionBar.setAttribute("data-duration", String(parsedData.detectedDuration)); + actionBar.setAttribute("data-slot-count", String(parsedData.slots.length)); + if (scheduleId) { + actionBar.setAttribute("data-schedule-id", scheduleId); + } + actionBar.style.cssText = ` + position: absolute; + z-index: 1000; + padding: 10px 14px; + background: #f8f9fa; + border: 1px solid #e0e0e0; + border-radius: 8px; + display: flex; + align-items: center; + gap: 10px; + font-family: 'Google Sans', Roboto, Arial, sans-serif; + font-size: 14px; + width: 434px; + max-width: 100%; + box-sizing: border-box; + user-select: none; + pointer-events: auto; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + `; + + // Cal.com icon with circular background + const icon = document.createElement("div"); + icon.style.cssText = ` + width: 32px; + height: 32px; + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; + background: #111827; + border-radius: 50%; + `; + icon.innerHTML = ` + + + + + + `; + + // Text with duration indicator + const text = document.createElement("span"); + text.textContent = `Suggest ${parsedData.detectedDuration}min Cal.com links`; + text.setAttribute("contenteditable", "false"); // Make text non-editable + text.style.cssText = ` + color: #1f1f1f; + font-weight: 500; + flex: 1; + user-select: none; + `; + + // Buttons container + const buttonsContainer = document.createElement("div"); + buttonsContainer.style.cssText = ` + display: flex; + gap: 8px; + flex-shrink: 0; + `; + + // "Suggest Links" Button + const suggestButton = document.createElement("button"); + suggestButton.className = "cal-companion-suggest-button"; + suggestButton.textContent = "Suggest Links"; + suggestButton.style.cssText = ` + padding: 8px 16px; + background: #111827; + color: white; + border: none; + border-radius: 6px; + font-size: 14px; + font-weight: 500; + cursor: pointer; + transition: background 0.2s ease; + font-family: 'Google Sans', Roboto, Arial, sans-serif; + `; + + suggestButton.addEventListener("mouseenter", () => { + suggestButton.style.background = "#1f2937"; + }); + + suggestButton.addEventListener("mouseleave", () => { + suggestButton.style.background = "#111827"; + }); + + suggestButton.addEventListener("click", (e) => { + e.preventDefault(); + e.stopPropagation(); + // Re-parse the chip to get fresh data (in case it changed) + const freshParsedData = parseGoogleChip(chipElement); + console.log( + "Cal.com: Suggest Links clicked, fresh parsed data:", + freshParsedData?.detectedDuration, + "min" + ); + if (freshParsedData && freshParsedData.slots.length > 0) { + showCalcomSuggestionMenu(chipElement, freshParsedData); + } else { + console.log("Cal.com: Failed to parse fresh data or no slots found"); + } + }); + + // "Insert Embed" Button + const embedButton = document.createElement("button"); + embedButton.className = "cal-companion-embed-button"; + embedButton.textContent = "Insert Embed"; + embedButton.style.cssText = ` + padding: 8px 16px; + background: white; + color: #111827; + border: 1px solid #111827; + border-radius: 6px; + font-size: 14px; + font-weight: 500; + cursor: pointer; + transition: all 0.2s ease; + font-family: 'Google Sans', Roboto, Arial, sans-serif; + `; + + embedButton.addEventListener("mouseenter", () => { + embedButton.style.background = "#f8f9fa"; + }); + + embedButton.addEventListener("mouseleave", () => { + embedButton.style.background = "white"; + }); + + embedButton.addEventListener("click", async (e) => { + e.preventDefault(); + e.stopPropagation(); + + // Re-parse the chip to get fresh data + const freshParsedData = parseGoogleChip(chipElement); + console.log( + "Cal.com: Insert Embed clicked, fresh parsed data:", + freshParsedData?.detectedDuration, + "min" + ); + + if (!freshParsedData || freshParsedData.slots.length === 0) { + showGmailNotification("No time slots found", "error"); + return; + } + + // Fetch event types to get the matching one + try { + // Disable button and show loading state with opacity + embedButton.disabled = true; + embedButton.style.opacity = "0.6"; + embedButton.style.cursor = "not-allowed"; + + const response: any = await new Promise((resolve, reject) => { + chrome.runtime.sendMessage({ action: "fetch-event-types" }, (result) => { + if (chrome.runtime.lastError) { + reject(new Error(chrome.runtime.lastError.message)); + } else if (result && result.error) { + reject(new Error(result.error)); + } else { + resolve(result); + } + }); + }); + + const eventTypes = response?.data || (Array.isArray(response) ? response : []); + const matchingEventType = eventTypes.find( + (et: any) => et.lengthInMinutes === freshParsedData.detectedDuration + ); + + if (!matchingEventType) { + showGmailNotification( + `No ${freshParsedData.detectedDuration}min event type found`, + "error" + ); + embedButton.disabled = false; + embedButton.style.opacity = "1"; + embedButton.style.cursor = "pointer"; + return; + } + + const username = matchingEventType.users?.[0]?.username || "user"; + + // Generate HTML embed + const embedHTML = generateEmailEmbedHTML({ + eventType: matchingEventType, + username: username, + slots: freshParsedData.slots, + duration: freshParsedData.detectedDuration, + timezone: freshParsedData.timezone, + timezoneOffset: freshParsedData.timezoneOffset, + }); + + // Insert HTML into Gmail + const inserted = insertGmailHTML(embedHTML, chipElement); + + if (inserted) { + showGmailNotification("Cal.com embed inserted!", "success"); + console.log("Cal.com: ✅ Email embed inserted successfully"); + + // Mark chip for removal on send (don't remove yet - keep it visible for user reference) + chipElement.setAttribute("data-calcom-remove-on-send", "true"); + } else { + showGmailNotification("Failed to insert embed", "error"); + } + + embedButton.disabled = false; + embedButton.style.opacity = "1"; + embedButton.style.cursor = "pointer"; + } catch (error) { + console.error("Cal.com: Failed to insert embed:", error); + + // Check if this is the "Extension context invalidated" error + const isContextInvalidated = + error instanceof Error && error.message.includes("Extension context invalidated"); + + if (isContextInvalidated) { + showGmailNotification("Extension reloaded - please reload Gmail", "error"); + } else { + showGmailNotification("Failed to insert embed", "error"); + } + + embedButton.disabled = false; + embedButton.style.opacity = "1"; + embedButton.style.cursor = "pointer"; + } + }); + + buttonsContainer.appendChild(suggestButton); + buttonsContainer.appendChild(embedButton); + + // Close button (×) to remove the action bar completely + const closeBtn = document.createElement("button"); + closeBtn.className = "cal-companion-close-btn"; + closeBtn.setAttribute("aria-label", "Close"); + closeBtn.style.cssText = ` + padding: 6px; + background: transparent; + border: none; + border-radius: 4px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: background 0.15s; + flex-shrink: 0; + color: #5f6368; + `; + closeBtn.innerHTML = ` + + + + `; + + closeBtn.addEventListener("mouseenter", () => { + closeBtn.style.background = "#e8eaed"; + }); + + closeBtn.addEventListener("mouseleave", () => { + closeBtn.style.background = "transparent"; + }); + + closeBtn.addEventListener("click", (e) => { + e.preventDefault(); + e.stopPropagation(); + + // Remove the action bar completely + try { + // Call cleanup function if it exists + if ((actionBar as any).__cleanup) { + (actionBar as any).__cleanup(); + } + actionBar.remove(); + } catch (error) { + // Silently ignore removal errors + } + }); + + // Assemble action bar + try { + actionBar.appendChild(icon); + actionBar.appendChild(text); + actionBar.appendChild(buttonsContainer); + actionBar.appendChild(closeBtn); + + // Position the action bar as an overlay (like Grammarly) + // This ensures it never becomes part of the email body + const positionActionBar = () => { + try { + if (!document.body.contains(chipElement)) { + // Chip removed, clean up + actionBar.remove(); + return; + } + + const chipRect = chipElement.getBoundingClientRect(); + + // Position below the chip with 8px gap + const top = chipRect.bottom + window.scrollY + 8; + const left = chipRect.left + window.scrollX; + + actionBar.style.top = `${top}px`; + actionBar.style.left = `${left}px`; + } catch (error) { + // Silently ignore positioning errors + } + }; + + // Before appending, clean up orphaned or duplicate action bars for THIS chip only + // (happens when Google changes duration - creates new chip, but old action bar still floating) + const existingOverlayBars = document.querySelectorAll(".cal-companion-action-bar"); + existingOverlayBars.forEach((bar) => { + const barScheduleId = bar.getAttribute("data-schedule-id"); + + // Only remove action bars that: + // 1. Belong to THIS chip (same schedule ID) - we're about to create a new one + // 2. OR their chip no longer exists (orphaned) + if (barScheduleId === scheduleId) { + // This bar belongs to the current chip - remove it (we'll create a fresh one) + try { + if ((bar as any).__cleanup) { + (bar as any).__cleanup(); + } + bar.remove(); + } catch (e) { + // Ignore removal errors + } + } else { + // This bar belongs to a different chip - check if that chip still exists + try { + const chipForBar = document.querySelector( + `.gmail_chip.gmail_ad_hoc_v2_content[data-ad-hoc-schedule-id="${barScheduleId}"]` + ); + if (!chipForBar) { + // Chip is gone, this is an orphaned action bar - remove it + if ((bar as any).__cleanup) { + (bar as any).__cleanup(); + } + bar.remove(); + } + // Else: chip exists, keep this action bar (belongs to another chip) + } catch (e) { + // Ignore errors when checking + } + } + }); + + // Append to document.body (outside email content, like Grammarly) + document.body.appendChild(actionBar); + + // Initial positioning with slight delay to ensure chip is rendered + setTimeout(() => { + positionActionBar(); + }, 100); + + // Update position on scroll and resize + const updatePosition = () => { + if (document.body.contains(chipElement) && document.body.contains(actionBar)) { + positionActionBar(); + } else { + // Chip removed, clean up + try { + actionBar.remove(); + } catch (e) { + // Ignore + } + } + }; + + window.addEventListener("scroll", updatePosition, true); + window.addEventListener("resize", updatePosition); + + // Store cleanup function + (actionBar as any).__cleanup = () => { + window.removeEventListener("scroll", updatePosition, true); + window.removeEventListener("resize", updatePosition); + }; + } catch (error) { + console.error("Cal.com: Failed to create or insert action bar:", error); + // Clean up if something failed + try { + actionBar.remove(); + } catch (e) { + // Ignore cleanup errors + } + } + } catch (error) { + // Catch-all to prevent breaking Gmail UI + console.error("Cal.com: Error handling Google chip:", error); + return; + } + } + + /** + * Show Cal.com suggestion menu for Google Calendar chip - CENTERED FULL-SCREEN MODAL + */ + async function showCalcomSuggestionMenu(chipElement: HTMLElement, parsedData: any) { + console.log( + "Cal.com: Opening menu for", + parsedData.detectedDuration, + "min with", + parsedData.slots.length, + "slots" + ); + + // Remove existing menu if any (to support reopening with new data) + const existingBackdrop = document.querySelector(".cal-companion-google-chip-backdrop"); + if (existingBackdrop) { + console.log("Cal.com: Removing existing backdrop"); + existingBackdrop.remove(); + // Wait a tick to ensure DOM is updated before creating new menu + await new Promise((resolve) => setTimeout(resolve, 0)); + } + + // Create full-screen backdrop (like FullScreenModal in companion) + const backdrop = document.createElement("div"); + backdrop.className = "cal-companion-google-chip-backdrop"; + backdrop.style.cssText = ` + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 2147483647; + display: flex; + align-items: center; + justify-content: center; + background-color: rgba(0, 0, 0, 0.5); + backdrop-filter: blur(4px); + `; + + // Create menu (centered modal content) + const menu = document.createElement("div"); + menu.className = "cal-companion-google-chip-menu"; + menu.style.cssText = ` + width: 480px; + max-width: 90vw; + max-height: 80vh; + background: white; + border-radius: 16px; + box-shadow: 0 20px 60px rgba(0,0,0,0.3); + overflow: hidden; + display: flex; + flex-direction: column; + font-family: "Google Sans", Roboto, Arial, sans-serif; + pointer-events: auto; + `; + + // Header + const header = document.createElement("div"); + header.style.cssText = ` + padding: 20px 24px; + border-bottom: 1px solid #e5e5ea; + display: flex; + justify-content: space-between; + align-items: center; + background: white; + flex-shrink: 0; + `; + header.innerHTML = ` +
+
📅 Suggest Cal.com Links
+
${parsedData.slots.length} time slot${parsedData.slots.length > 1 ? "s" : ""} • ${parsedData.detectedDuration}min each
+
+ + `; + + menu.appendChild(header); + backdrop.appendChild(menu); + document.body.appendChild(backdrop); + + // Close on backdrop click + backdrop.addEventListener("click", (e) => { + if (e.target === backdrop) { + backdrop.remove(); + } + }); + + // Close button click and hover + const closeBtn = header.querySelector(".close-menu") as HTMLButtonElement; + closeBtn.addEventListener("click", () => { + backdrop.remove(); + }); + closeBtn.addEventListener("mouseenter", () => { + closeBtn.style.background = "#f0f0f0"; + }); + closeBtn.addEventListener("mouseleave", () => { + closeBtn.style.background = "none"; + }); + + // Create scrollable content container + const contentContainer = document.createElement("div"); + contentContainer.className = "cal-companion-menu-content"; + contentContainer.style.cssText = ` + overflow-y: auto; + flex: 1; + `; + menu.appendChild(contentContainer); + + // Show loading + const loadingDiv = document.createElement("div"); + loadingDiv.style.cssText = + "padding: 32px 20px; text-align: center; color: #666; font-size: 14px;"; + loadingDiv.textContent = "Loading event types..."; + contentContainer.appendChild(loadingDiv); + + try { + // Fetch event types (will use cache if available) + let eventTypes: any[] = []; + + // Check cache first + const now = Date.now(); + const isCacheValid = + eventTypesCache && cacheTimestamp && now - cacheTimestamp < CACHE_DURATION; + + if (isCacheValid) { + eventTypes = eventTypesCache!; + } else { + // Fetch from background script + const response: any = await new Promise((resolve, reject) => { + chrome.runtime.sendMessage({ action: "fetch-event-types" }, (response) => { + if (chrome.runtime.lastError) { + reject(new Error(chrome.runtime.lastError.message)); + } else if (response && response.error) { + reject(new Error(response.error)); + } else { + resolve(response); + } + }); + }); + + if (response && response.data) { + eventTypes = response.data; + } else if (Array.isArray(response)) { + eventTypes = response; + } + + // Update cache + eventTypesCache = eventTypes; + cacheTimestamp = now; + } + + if (!eventTypes || eventTypes.length === 0) { + loadingDiv.textContent = "No event types found"; + return; + } + + loadingDiv.remove(); + + // Filter event types by matching duration + const matchingEventTypes = eventTypes.filter( + (et: any) => et.lengthInMinutes === parsedData.detectedDuration + ); + + // If no matching event types, show create prompt + if (matchingEventTypes.length === 0) { + const noMatchDiv = document.createElement("div"); + noMatchDiv.style.cssText = "padding: 20px 16px; text-align: center;"; + noMatchDiv.innerHTML = ` +
+ + + + + +
+
+ No ${parsedData.detectedDuration}min Event Type Found +
+
+ Google is suggesting ${parsedData.detectedDuration}-minute time slots, but you don't have any ${parsedData.detectedDuration}min event types configured. +
+ + `; + contentContainer.appendChild(noMatchDiv); + + // Add click handler for create button + const createBtn = noMatchDiv.querySelector(".create-event-type-btn"); + createBtn?.addEventListener("click", () => { + // Get username from any existing event type, or use a default + const username = + eventTypes.length > 0 ? eventTypes[0].users?.[0]?.username || "user" : "user"; + + const createUrl = `https://app.cal.com/event-types?dialog=new&eventPage=${username}`; + window.open(createUrl, "_blank"); + showGmailNotification( + `Opening Cal.com to create ${parsedData.detectedDuration}min event type`, + "success" + ); + + console.log("Cal.com: Opening create URL:", createUrl); + }); + + createBtn?.addEventListener("mouseenter", (e) => { + (e.target as HTMLElement).style.backgroundColor = "#333"; + }); + + createBtn?.addEventListener("mouseleave", (e) => { + (e.target as HTMLElement).style.backgroundColor = "#000"; + }); + + return; // Don't show slots if no matching event types + } + + // Event type selector (only show matching event types) + const selectorDiv = document.createElement("div"); + selectorDiv.style.cssText = + "padding: 12px 16px; border-bottom: 1px solid #e5e5ea; background: #f8f9fa; position: relative; z-index: 5; pointer-events: auto;"; + + // Create label + const label = document.createElement("label"); + label.style.cssText = + "font-size: 12px; color: #666; font-weight: 500; display: block; margin-bottom: 6px;"; + label.textContent = + matchingEventTypes.length === 1 + ? `Event Type (${parsedData.detectedDuration}min)` + : `Select ${parsedData.detectedDuration}min Event Type`; + selectorDiv.appendChild(label); + + // Track selected event type + let selectedEventTypeIndex = 0; + + if (matchingEventTypes.length === 1) { + // Single event type - just show it + const displayDiv = document.createElement("div"); + displayDiv.style.cssText = ` + padding: 10px 12px; + border: 1px solid #e5e5ea; + border-radius: 8px; + font-size: 14px; + background: white; + font-family: inherit; + color: #000; + font-weight: 500; + box-sizing: border-box; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + `; + displayDiv.textContent = matchingEventTypes[0].title; + selectorDiv.appendChild(displayDiv); + } else { + // Multiple event types - custom dropdown + const customDropdown = document.createElement("div"); + customDropdown.className = "custom-event-type-dropdown"; + customDropdown.style.cssText = "position: relative;"; + + // Selected display button + const selectedDisplay = document.createElement("div"); + selectedDisplay.style.cssText = ` + padding: 10px 12px; + border: 1px solid #e5e5ea; + border-radius: 8px; + font-size: 14px; + background: white; + font-family: inherit; + color: #000; + cursor: pointer; + box-sizing: border-box; + display: flex; + justify-content: space-between; + align-items: center; + transition: border-color 0.15s; + pointer-events: auto; + `; + selectedDisplay.innerHTML = ` + ${matchingEventTypes[0].title} (${matchingEventTypes[0].lengthInMinutes}min) + + + + `; + + // Dropdown options container + const optionsContainer = document.createElement("div"); + optionsContainer.style.cssText = ` + position: absolute; + top: 100%; + left: 0; + right: 0; + margin-top: 4px; + background: white; + border: 1px solid #e5e5ea; + border-radius: 8px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + max-height: 200px; + overflow-y: auto; + display: none; + z-index: 1000; + pointer-events: auto; + `; + + // Create options + matchingEventTypes.forEach((et: any, index: number) => { + const option = document.createElement("div"); + option.className = "dropdown-option"; + option.style.cssText = ` + padding: 10px 12px; + font-size: 14px; + color: #000; + cursor: pointer; + transition: background-color 0.1s; + border-bottom: ${index < matchingEventTypes.length - 1 ? "1px solid #f0f0f0" : "none"}; + pointer-events: auto; + `; + option.textContent = `${et.title} (${et.lengthInMinutes}min)`; + option.setAttribute("data-index", index.toString()); + + // Hover effect + option.addEventListener("mouseenter", () => { + option.style.backgroundColor = "#f8f9fa"; + }); + option.addEventListener("mouseleave", () => { + option.style.backgroundColor = "white"; + }); + + // Click to select + option.addEventListener("click", (e) => { + e.stopPropagation(); + selectedEventTypeIndex = index; + const selectedText = selectedDisplay.querySelector(".selected-text"); + if (selectedText) { + selectedText.textContent = `${et.title} (${et.lengthInMinutes}min)`; + } + optionsContainer.style.display = "none"; + console.log("Cal.com: Event type changed to:", et.slug); + }); + + optionsContainer.appendChild(option); + }); + + // Toggle dropdown on click + selectedDisplay.addEventListener("click", (e) => { + e.stopPropagation(); + const isOpen = optionsContainer.style.display === "block"; + optionsContainer.style.display = isOpen ? "none" : "block"; + selectedDisplay.style.borderColor = isOpen ? "#e5e5ea" : "#000"; + }); + + // Hover effect on selected display + selectedDisplay.addEventListener("mouseenter", () => { + selectedDisplay.style.borderColor = "#000"; + }); + selectedDisplay.addEventListener("mouseleave", () => { + if (optionsContainer.style.display !== "block") { + selectedDisplay.style.borderColor = "#e5e5ea"; + } + }); + + customDropdown.appendChild(selectedDisplay); + customDropdown.appendChild(optionsContainer); + selectorDiv.appendChild(customDropdown); + + // Close dropdown when clicking outside (with cleanup) + const closeDropdown = (e: MouseEvent) => { + if (!customDropdown.contains(e.target as Node)) { + optionsContainer.style.display = "none"; + selectedDisplay.style.borderColor = "#e5e5ea"; + } + }; + document.addEventListener("click", closeDropdown); + + // Clean up listener when modal closes + backdrop.addEventListener( + "remove", + () => { + document.removeEventListener("click", closeDropdown); + }, + { once: true } + ); + } + + contentContainer.appendChild(selectorDiv); + + // Time slots list + const slotsContainer = document.createElement("div"); + slotsContainer.style.cssText = "padding: 12px;"; + + parsedData.slots.forEach((slot: any, index: number) => { + const slotItem = document.createElement("div"); + slotItem.style.cssText = ` + padding: 14px; + margin: 6px 0; + border: 1px solid #e5e5ea; + border-radius: 10px; + display: flex; + justify-content: space-between; + align-items: center; + transition: all 0.15s; + cursor: default; + background: white; + `; + + slotItem.innerHTML = ` +
+
${slot.date}
+
${slot.startTime} – ${slot.endTime}
+
+ + `; + + // Add pointer events to slot item + slotItem.style.pointerEvents = "auto"; + + // Hover effect on entire slot item + slotItem.addEventListener("mouseenter", () => { + console.log("Cal.com: Mouse entered slot", index); + slotItem.style.borderColor = "#000"; + slotItem.style.backgroundColor = "#f8f9fa"; + const btn = slotItem.querySelector(".insert-slot-btn") as HTMLElement; + if (btn) btn.style.backgroundColor = "#333"; + }); + + slotItem.addEventListener("mouseleave", () => { + slotItem.style.borderColor = "#e5e5ea"; + slotItem.style.backgroundColor = "white"; + const btn = slotItem.querySelector(".insert-slot-btn") as HTMLElement; + if (btn) btn.style.backgroundColor = "#000"; + }); + + slotsContainer.appendChild(slotItem); + }); + + contentContainer.appendChild(slotsContainer); + + // Add event listeners for insert buttons + const insertButtons = menu.querySelectorAll(".insert-slot-btn"); + + console.log("Cal.com: Found", insertButtons.length, "insert buttons"); + + insertButtons.forEach((btn) => { + btn.addEventListener("click", (e) => { + console.log("Cal.com: Insert button clicked!"); + e.preventDefault(); + e.stopPropagation(); + const slotIndex = parseInt(btn.getAttribute("data-slot-index")!); + const slot = parsedData.slots[slotIndex]; + console.log("Cal.com: Inserting link for slot", slotIndex, slot); + + // Get selected event type from the tracked index + const selectedEventType = matchingEventTypes[selectedEventTypeIndex]; + const selectedSlug = selectedEventType.slug; + const selectedUsername = selectedEventType.users?.[0]?.username || "user"; + + // Generate Cal.com URL with slot parameters + const baseUrl = `https://cal.com/${selectedUsername}/${selectedSlug}`; + const params = new URLSearchParams({ + overlayCalendar: "true", + date: slot.isoDate, + slot: slot.isoTimestamp, + }); + const calcomUrl = `${baseUrl}?${params.toString()}`; + + console.log("Cal.com: Generated URL:", calcomUrl); + + // Insert link into Gmail compose (pass chipElement to target the correct compose window) + const inserted = insertGmailText(calcomUrl, chipElement); + + if (inserted) { + showGmailNotification("Cal.com link inserted!", "success"); + backdrop.remove(); + + // Mark chip for removal on send (don't remove yet - keep it visible for user reference) + chipElement.setAttribute("data-calcom-remove-on-send", "true"); + } else { + showGmailNotification("Failed to insert link", "error"); + } + }); + }); + } catch (error) { + console.error("Error showing Cal.com suggestion menu:", error); + loadingDiv.remove(); + + // Show user-friendly error message + const errorDiv = document.createElement("div"); + errorDiv.style.cssText = "padding: 20px 16px; text-align: center;"; + + const isContextInvalidated = + error instanceof Error && error.message.includes("Extension context invalidated"); + + errorDiv.innerHTML = ` +
+ + + + + +
+
+ ${isContextInvalidated ? "Extension Reloaded" : "Failed to Load"} +
+
+ ${ + isContextInvalidated + ? "The extension was updated. Please reload this page to continue." + : "Failed to load event types. Please try again." + } +
+ ${ + isContextInvalidated + ? `` + : "" + } + `; + + contentContainer.appendChild(errorDiv); + + // Add reload handler if context invalidated + if (isContextInvalidated) { + const reloadBtn = errorDiv.querySelector(".reload-page-btn"); + reloadBtn?.addEventListener("click", () => { + window.location.reload(); + }); + reloadBtn?.addEventListener("mouseenter", (e) => { + (e.target as HTMLElement).style.backgroundColor = "#ff5252"; + }); + reloadBtn?.addEventListener("mouseleave", (e) => { + (e.target as HTMLElement).style.backgroundColor = "#ff6b6b"; + }); + } + } + } + + /** + * Parse Google Calendar scheduling chip + * Returns null if structure has changed or parsing fails (fail gracefully) + */ + function parseGoogleChip(chipElement: HTMLElement): any { + try { + // Validate input + if (!chipElement || typeof chipElement.getAttribute !== "function") { + console.warn("Cal.com: Invalid chip element passed to parser"); + return null; + } + + // Get schedule ID - if this is missing, this might not be a valid chip + const scheduleId = chipElement.getAttribute("data-ad-hoc-schedule-id"); + + if (!scheduleId) { + // Silently return - this is likely not a fully-loaded chip + // (happens frequently during Gmail DOM updates) + return null; + } + + console.log( + "Cal.com: Valid chip detected - Schedule ID:", + scheduleId?.slice(0, 20) + "..." + ); + + // Parse timezone (non-critical - fallback to UTC if structure changed) + let timezone = "UTC"; + let timezoneOffset = "GMT+00:00"; + + try { + // First, try to get the IANA timezone from data-ad-hoc-v2-params + const paramsAttr = chipElement.getAttribute("data-ad-hoc-v2-params"); + console.log("Cal.com: data-ad-hoc-v2-params:", paramsAttr); + + if (paramsAttr) { + // The timezone is at the end of the params string, like: "Asia/Kolkata" + // Try multiple patterns as Gmail might format it differently + let tzMatch = paramsAttr.match(/"([^&]+)"\]/); + + // Also try without HTML entities + if (!tzMatch) { + tzMatch = paramsAttr.match(/"([^"]+)"\]/); + } + + // Also try with escaped quotes + if (!tzMatch) { + tzMatch = paramsAttr.match(/\\"([^\\]+)\\"\]/); + } + + if (tzMatch && tzMatch[1]) { + timezone = tzMatch[1]; // e.g., "Asia/Kolkata" + console.log("Cal.com: ✅ Parsed IANA timezone from data attribute:", timezone); + } else { + console.warn("Cal.com: ⚠️ Failed to extract timezone from params attribute"); + } + } else { + console.warn("Cal.com: ⚠️ No data-ad-hoc-v2-params attribute found"); + } + + // Also get the display timezone and offset from the UI text + const timezoneText = chipElement.querySelector("td")?.textContent?.trim() || ""; + const timezoneMatch = timezoneText.match(/^(.+?)\s*-\s*(.+?)\s*\((.+?)\)$/); + + if (timezoneMatch) { + timezoneOffset = timezoneMatch[3]?.trim() || "GMT+00:00"; + console.log("Cal.com: Parsed timezone offset from UI:", timezoneOffset); + } + } catch (tzError) { + // Non-critical error - continue with default timezone + console.warn("Cal.com: Failed to parse timezone, using UTC:", tzError); + } + + // Find all time slot links - critical for functionality + let slotLinks: Element[] = []; + try { + slotLinks = Array.from(chipElement.querySelectorAll("a[href*='slotStartTime']")); + } catch (error) { + console.warn("Cal.com: Failed to find slot links:", error); + return null; + } + + if (slotLinks.length === 0) { + // Gmail structure might have changed or chip not fully loaded + return null; + } + + const slots: any[] = []; + let detectedDuration = 60; + + slotLinks.forEach((link) => { + try { + const href = (link as HTMLAnchorElement).href; + if (!href) return; + + const url = new URL(href); + + const slotStartTime = url.searchParams.get("slotStartTime"); + const slotDurationMinutes = url.searchParams.get("slotDurationMinutes"); + + if (!slotStartTime || !slotDurationMinutes) return; + + const durationMinutes = parseInt(slotDurationMinutes, 10); + if (isNaN(durationMinutes) || durationMinutes <= 0) return; + + detectedDuration = durationMinutes; + + const startTimestamp = parseInt(slotStartTime, 10); + if (isNaN(startTimestamp)) return; + + const startDate = new Date(startTimestamp); + const endDate = new Date(startDate.getTime() + durationMinutes * 60 * 1000); + + // Validate dates + if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) return; + + const dateOptions: Intl.DateTimeFormatOptions = { + weekday: "short", + day: "numeric", + month: "long", + }; + const timeOptions: Intl.DateTimeFormatOptions = { + hour: "numeric", + minute: "2-digit", + hour12: true, + }; + + const date = startDate.toLocaleDateString("en-US", dateOptions); + const startTime = startDate.toLocaleTimeString("en-US", timeOptions).toLowerCase(); + const endTime = endDate.toLocaleTimeString("en-US", timeOptions).toLowerCase(); + + const isoDate = startDate.toISOString().split("T")[0]; + const isoTimestamp = startDate.toISOString(); + + slots.push({ + date, + startTime, + endTime, + durationMinutes, + isoDate, + isoTimestamp, + googleUrl: href, + }); + } catch (slotError) { + // Skip individual slot if it fails - don't break entire parsing + console.warn("Cal.com: Failed to parse individual slot:", slotError); + } + }); + + if (slots.length === 0) { + // No valid slots found - Gmail structure might have changed + return null; + } + + console.log( + `Cal.com: ✅ Parsed chip - ${slots.length} slots, ${detectedDuration}min, timezone: ${timezone}` + ); + + return { + scheduleId, + timezone, + timezoneOffset, + slots, + detectedDuration, + }; + } catch (error) { + // Critical error in parsing - fail gracefully without breaking Gmail + console.warn( + "Cal.com: Failed to parse Google chip (Gmail structure may have changed):", + error + ); + return null; + } + } + + // Start watching for Google Calendar chips + watchForGoogleChips(); + + // Setup auto-remove action bars before sending email + setupAutoRemoveOnSend(); + } + }, +}); + +function defineContentScript(config: { matches: string[]; main: () => void }) { + return config; +} diff --git a/companion/extension/public/icon-128.png b/companion/extension/public/icon-128.png new file mode 100644 index 0000000000..6ad39d9bfb Binary files /dev/null and b/companion/extension/public/icon-128.png differ diff --git a/companion/extension/public/icon-16.png b/companion/extension/public/icon-16.png new file mode 100644 index 0000000000..811fcdf3c4 Binary files /dev/null and b/companion/extension/public/icon-16.png differ diff --git a/companion/extension/public/icon-48.png b/companion/extension/public/icon-48.png new file mode 100644 index 0000000000..f570ab73d5 Binary files /dev/null and b/companion/extension/public/icon-48.png differ diff --git a/companion/extension/public/icon.png b/companion/extension/public/icon.png new file mode 100644 index 0000000000..2d352eca86 Binary files /dev/null and b/companion/extension/public/icon.png differ diff --git a/companion/extension/public/manifest.json b/companion/extension/public/manifest.json new file mode 100644 index 0000000000..9c3a63ef08 --- /dev/null +++ b/companion/extension/public/manifest.json @@ -0,0 +1,22 @@ +{ + "manifest_version": 3, + "name": "Cal.com Companion", + "version": "1.7.0", + "description": "Your calendar companion for quick booking and scheduling", + "permissions": ["activeTab", "http://localhost:8081/*", "identity"], + "content_security_policy": { + "extension_pages": "script-src 'self'; object-src 'self'; frame-src 'self' http://localhost:8081;" + }, + "action": { + "default_title": "Cal.com Companion" + }, + "background": { + "service_worker": "background.js" + }, + "content_scripts": [ + { + "matches": [""], + "js": ["content-scripts/content.js"] + } + ] +} diff --git a/companion/extension/public/oauth/callback.html b/companion/extension/public/oauth/callback.html new file mode 100644 index 0000000000..89188bb930 --- /dev/null +++ b/companion/extension/public/oauth/callback.html @@ -0,0 +1,10 @@ + + + + + Cal.com OAuth Callback + + +

Processing authentication...

+ + diff --git a/companion/global.css b/companion/global.css new file mode 100644 index 0000000000..28214b8222 --- /dev/null +++ b/companion/global.css @@ -0,0 +1,24 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + /* Force light color scheme for all web components */ + :root { + color-scheme: light only; + } + + html { + color-scheme: light; + } + + * { + color-scheme: light; + } + + input:focus { + --tw-ring-color: rgb(0 0 0); + --tw-border-opacity: 1; + border-color: rgb(0 0 0); + } +} diff --git a/companion/index.js b/companion/index.js new file mode 100644 index 0000000000..80d3d998f1 --- /dev/null +++ b/companion/index.js @@ -0,0 +1 @@ +import "expo-router/entry"; diff --git a/companion/metro.config.js b/companion/metro.config.js new file mode 100644 index 0000000000..b0963fe7f7 --- /dev/null +++ b/companion/metro.config.js @@ -0,0 +1,6 @@ +const { getDefaultConfig } = require("expo/metro-config"); +const { withNativeWind } = require("nativewind/metro"); + +const config = getDefaultConfig(__dirname); + +module.exports = withNativeWind(config, { input: "./global.css" }); diff --git a/companion/nativewind-env.d.ts b/companion/nativewind-env.d.ts new file mode 100644 index 0000000000..a13e3136bb --- /dev/null +++ b/companion/nativewind-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/companion/package-lock.json b/companion/package-lock.json new file mode 100644 index 0000000000..b22faeeba1 --- /dev/null +++ b/companion/package-lock.json @@ -0,0 +1,16506 @@ +{ + "name": "cal-companion", + "version": "1.7.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "cal-companion", + "version": "1.7.0", + "dependencies": { + "@expo/ui": "^0.2.0-beta.7", + "@expo/vector-icons": "^15.0.3", + "@react-native-segmented-control/segmented-control": "^2.5.7", + "@types/react": "~19.1.10", + "@types/react-dom": "~19.1.7", + "base64-js": "^1.5.1", + "expo": "~54.0.0", + "expo-auth-session": "^7.0.9", + "expo-constants": "~18.0.10", + "expo-crypto": "^15.0.7", + "expo-device": "^8.0.9", + "expo-glass-effect": "~0.1.6", + "expo-linking": "~8.0.8", + "expo-router": "~6.0.14", + "expo-secure-store": "^15.0.7", + "expo-status-bar": "~3.0.8", + "expo-web-browser": "^15.0.9", + "nativewind": "^4.2.1", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "react-native": "0.81.5", + "react-native-context-menu-view": "^1.20.0", + "react-native-gesture-handler": "^2.29.1", + "react-native-reanimated": "~3.17.4", + "react-native-safe-area-context": "5.4.0", + "react-native-svg": "^15.15.0", + "react-native-web": "^0.21.2", + "react-native-worklets": "^0.6.1" + }, + "devDependencies": { + "@types/chrome": "^0.1.31", + "babel-preset-expo": "^54.0.7", + "husky": "^9.0.11", + "lint-staged": "^15.2.0", + "prettier": "^3.2.5", + "prettier-plugin-tailwindcss": "^0.5.11", + "tailwindcss": "^3.4.17", + "wxt": "^0.20.11" + } + }, + "node_modules/@0no-co/graphql.web": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@0no-co/graphql.web/-/graphql.web-1.2.0.tgz", + "integrity": "sha512-/1iHy9TTr63gE1YcR5idjx8UREz1s0kFhydf3bBLCXyqjhkIc6igAzTOx3zPifCwFR87tsh/4Pa9cNts6d2otw==", + "license": "MIT", + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" + }, + "peerDependenciesMeta": { + "graphql": { + "optional": true + } + } + }, + "node_modules/@1natsu/wait-element": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@1natsu/wait-element/-/wait-element-4.1.2.tgz", + "integrity": "sha512-qWxSJD+Q5b8bKOvESFifvfZ92DuMsY+03SBNjTO34ipJLP6mZ9yK4bQz/vlh48aEQXoJfaZBqUwKL5BdI5iiWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "defu": "^6.1.4", + "many-keys-map": "^2.0.1" + } + }, + "node_modules/@aklinker1/rollup-plugin-visualizer": { + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/@aklinker1/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.12.0.tgz", + "integrity": "sha512-X24LvEGw6UFmy0lpGJDmXsMyBD58XmX1bbwsaMLhNoM+UMQfQ3b2RtC+nz4b/NoRK5r6QJSKJHBNVeUdwqybaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "open": "^8.4.0", + "picomatch": "^2.3.1", + "source-map": "^0.7.4", + "yargs": "^17.5.1" + }, + "bin": { + "rollup-plugin-visualizer": "dist/bin/cli.js" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "rollup": "2.x || 3.x || 4.x" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@aklinker1/rollup-plugin-visualizer/node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@aklinker1/rollup-plugin-visualizer/node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/@babel/compat-data": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", + "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.5.tgz", + "integrity": "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ==", + "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.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.28.5", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "regexpu-core": "^6.3.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.5.tgz", + "integrity": "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==", + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "debug": "^4.4.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.22.10" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "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==", + "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==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "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==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-wrap-function": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", + "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "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==", + "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==", + "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==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.3.tgz", + "integrity": "sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g==", + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.3", + "@babel/types": "^7.28.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "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/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.28.0.tgz", + "integrity": "sha512-zOiZqvANjWDUaUS9xMxbMcK/Zccztbe/6ikvUXaG9nsPH3w6qh5UaPGAnirI/WhIbZ8m3OHU0ReyPrknG+ZKeg==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-syntax-decorators": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.27.1.tgz", + "integrity": "sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-export-default-from": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.27.1.tgz", + "integrity": "sha512-eBC/3KSekshx19+N40MzjWqJd7KTEdOoLesAfa4IDFI8eRz5a47i5Oszus6zG/cwIXN63YhgLOMSSNJx49sENg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-flow": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.27.1.tgz", + "integrity": "sha512-p9OkPbZ5G7UT1MofwYFigGebnrzGJacoBSQM0/6bi/PUMVE+qlWDD/OalvQKbwgQzU6dl0xAv6r4X7Jme0RYxA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", + "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.28.0.tgz", + "integrity": "sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-remap-async-to-generator": "^7.27.1", + "@babel/traverse": "^7.28.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz", + "integrity": "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-remap-async-to-generator": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.5.tgz", + "integrity": "sha512-45DmULpySVvmq9Pj3X9B+62Xe+DJGov27QravQJU1LLcapR6/10i+gYVAucGGJpHBp5mYxIMK4nDAT/QDLr47g==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz", + "integrity": "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.3.tgz", + "integrity": "sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.28.3", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.4.tgz", + "integrity": "sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-globals": "^7.28.0", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/traverse": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz", + "integrity": "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/template": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-syntax-flow": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.5.tgz", + "integrity": "sha512-axUuqnUTBuXyHGcJEVVh9pORaN6wC5bYfE7FGzPiaWa3syib9m7g+/IT/4VgCOe2Upef43PHzeAvcrVek6QuuA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", + "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz", + "integrity": "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz", + "integrity": "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.27.1.tgz", + "integrity": "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.4.tgz", + "integrity": "sha512-373KA2HQzKhQCYiRVIRr+3MjpCObqzDlyrM6u4I201wL8Mp2wHf7uB8GhDwis03k2ti8Zr65Zyyqs1xOxUF/Ew==", + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.28.0", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/traverse": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.27.1.tgz", + "integrity": "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.5.tgz", + "integrity": "sha512-N6fut9IZlPnjPwgiQkXNhb+cT8wQKFlJNqcZkWlcTqkcqx6/kU4ynGmLFoa4LViBSirn05YAwk+sQBbPfxtYzQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.27.1.tgz", + "integrity": "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.27.1.tgz", + "integrity": "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.27.1.tgz", + "integrity": "sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "@babel/plugin-transform-react-jsx": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.4.tgz", + "integrity": "sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.28.5.tgz", + "integrity": "sha512-20NUVgOrinudkIBzQ2bNxP08YpKprUkRTiRSd2/Z5GOdPImJGkoN4Z7IQe1T5AdyKI1i5L6RBmluqdSzvaq9/w==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "babel-plugin-polyfill-corejs2": "^0.4.14", + "babel-plugin-polyfill-corejs3": "^0.13.0", + "babel-plugin-polyfill-regenerator": "^0.6.5", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", + "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz", + "integrity": "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", + "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.5.tgz", + "integrity": "sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", + "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "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" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "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" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template/node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.5", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse--for-generate-function-map": { + "name": "@babel/traverse", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.5", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse--for-generate-function-map/node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@devicefarmer/adbkit": { + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@devicefarmer/adbkit/-/adbkit-3.3.8.tgz", + "integrity": "sha512-7rBLLzWQnBwutH2WZ0EWUkQdihqrnLYCUMaB44hSol9e0/cdIhuNFcqZO0xNheAU6qqHVA8sMiLofkYTgb+lmw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@devicefarmer/adbkit-logcat": "^2.1.2", + "@devicefarmer/adbkit-monkey": "~1.2.1", + "bluebird": "~3.7", + "commander": "^9.1.0", + "debug": "~4.3.1", + "node-forge": "^1.3.1", + "split": "~1.0.1" + }, + "bin": { + "adbkit": "bin/adbkit" + }, + "engines": { + "node": ">= 0.10.4" + } + }, + "node_modules/@devicefarmer/adbkit-logcat": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@devicefarmer/adbkit-logcat/-/adbkit-logcat-2.1.3.tgz", + "integrity": "sha512-yeaGFjNBc/6+svbDeul1tNHtNChw6h8pSHAt5D+JsedUrMTN7tla7B15WLDyekxsuS2XlZHRxpuC6m92wiwCNw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@devicefarmer/adbkit-monkey": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@devicefarmer/adbkit-monkey/-/adbkit-monkey-1.2.1.tgz", + "integrity": "sha512-ZzZY/b66W2Jd6NHbAhLyDWOEIBWC11VizGFk7Wx7M61JZRz7HR9Cq5P+65RKWUU7u6wgsE8Lmh9nE4Mz+U2eTg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.10.4" + } + }, + "node_modules/@devicefarmer/adbkit/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/@devicefarmer/adbkit/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@egjs/hammerjs": { + "version": "2.0.17", + "resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz", + "integrity": "sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==", + "license": "MIT", + "dependencies": { + "@types/hammerjs": "^2.0.36" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@expo/code-signing-certificates": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/@expo/code-signing-certificates/-/code-signing-certificates-0.0.5.tgz", + "integrity": "sha512-BNhXkY1bblxKZpltzAx98G2Egj9g1Q+JRcvR7E99DOj862FTCX+ZPsAUtPTr7aHxwtrL7+fL3r0JSmM9kBm+Bw==", + "license": "MIT", + "dependencies": { + "node-forge": "^1.2.1", + "nullthrows": "^1.1.1" + } + }, + "node_modules/@expo/config": { + "version": "12.0.10", + "resolved": "https://registry.npmjs.org/@expo/config/-/config-12.0.10.tgz", + "integrity": "sha512-lJMof5Nqakq1DxGYlghYB/ogSBjmv4Fxn1ovyDmcjlRsQdFCXgu06gEUogkhPtc9wBt9WlTTfqENln5HHyLW6w==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "~7.10.4", + "@expo/config-plugins": "~54.0.2", + "@expo/config-types": "^54.0.8", + "@expo/json-file": "^10.0.7", + "deepmerge": "^4.3.1", + "getenv": "^2.0.0", + "glob": "^10.4.2", + "require-from-string": "^2.0.2", + "resolve-from": "^5.0.0", + "resolve-workspace-root": "^2.0.0", + "semver": "^7.6.0", + "slugify": "^1.3.4", + "sucrase": "3.35.0" + } + }, + "node_modules/@expo/config-plugins": { + "version": "54.0.2", + "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-54.0.2.tgz", + "integrity": "sha512-jD4qxFcURQUVsUFGMcbo63a/AnviK8WUGard+yrdQE3ZrB/aurn68SlApjirQQLEizhjI5Ar2ufqflOBlNpyPg==", + "license": "MIT", + "dependencies": { + "@expo/config-types": "^54.0.8", + "@expo/json-file": "~10.0.7", + "@expo/plist": "^0.4.7", + "@expo/sdk-runtime-versions": "^1.0.0", + "chalk": "^4.1.2", + "debug": "^4.3.5", + "getenv": "^2.0.0", + "glob": "^10.4.2", + "resolve-from": "^5.0.0", + "semver": "^7.5.4", + "slash": "^3.0.0", + "slugify": "^1.6.6", + "xcode": "^3.0.1", + "xml2js": "0.6.0" + } + }, + "node_modules/@expo/config-plugins/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@expo/config-plugins/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@expo/config-plugins/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@expo/config-plugins/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/@expo/config-plugins/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@expo/config-plugins/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@expo/config-types": { + "version": "54.0.8", + "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-54.0.8.tgz", + "integrity": "sha512-lyIn/x/Yz0SgHL7IGWtgTLg6TJWC9vL7489++0hzCHZ4iGjVcfZmPTUfiragZ3HycFFj899qN0jlhl49IHa94A==", + "license": "MIT" + }, + "node_modules/@expo/devcert": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@expo/devcert/-/devcert-1.2.0.tgz", + "integrity": "sha512-Uilcv3xGELD5t/b0eM4cxBFEKQRIivB3v7i+VhWLV/gL98aw810unLKKJbGAxAIhY6Ipyz8ChWibFsKFXYwstA==", + "license": "MIT", + "dependencies": { + "@expo/sudo-prompt": "^9.3.1", + "debug": "^3.1.0", + "glob": "^10.4.2" + } + }, + "node_modules/@expo/devcert/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/@expo/devtools": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/@expo/devtools/-/devtools-0.1.7.tgz", + "integrity": "sha512-dfIa9qMyXN+0RfU6SN4rKeXZyzKWsnz6xBSDccjL4IRiE+fQ0t84zg0yxgN4t/WK2JU5v6v4fby7W7Crv9gJvA==", + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/@expo/devtools/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@expo/devtools/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@expo/devtools/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@expo/devtools/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/@expo/devtools/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@expo/devtools/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@expo/env": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@expo/env/-/env-2.0.7.tgz", + "integrity": "sha512-BNETbLEohk3HQ2LxwwezpG8pq+h7Fs7/vAMP3eAtFT1BCpprLYoBBFZH7gW4aqGfqOcVP4Lc91j014verrYNGg==", + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "debug": "^4.3.4", + "dotenv": "~16.4.5", + "dotenv-expand": "~11.0.6", + "getenv": "^2.0.0" + } + }, + "node_modules/@expo/env/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@expo/env/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@expo/env/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@expo/env/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/@expo/env/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@expo/env/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@expo/fingerprint": { + "version": "0.15.3", + "resolved": "https://registry.npmjs.org/@expo/fingerprint/-/fingerprint-0.15.3.tgz", + "integrity": "sha512-8YPJpEYlmV171fi+t+cSLMX1nC5ngY9j2FiN70dHldLpd6Ct6ouGhk96svJ4BQZwsqwII2pokwzrDAwqo4Z0FQ==", + "license": "MIT", + "dependencies": { + "@expo/spawn-async": "^1.7.2", + "arg": "^5.0.2", + "chalk": "^4.1.2", + "debug": "^4.3.4", + "getenv": "^2.0.0", + "glob": "^10.4.2", + "ignore": "^5.3.1", + "minimatch": "^9.0.0", + "p-limit": "^3.1.0", + "resolve-from": "^5.0.0", + "semver": "^7.6.0" + }, + "bin": { + "fingerprint": "bin/cli.js" + } + }, + "node_modules/@expo/fingerprint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@expo/fingerprint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@expo/fingerprint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@expo/fingerprint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/@expo/fingerprint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@expo/fingerprint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@expo/image-utils": { + "version": "0.8.7", + "resolved": "https://registry.npmjs.org/@expo/image-utils/-/image-utils-0.8.7.tgz", + "integrity": "sha512-SXOww4Wq3RVXLyOaXiCCuQFguCDh8mmaHBv54h/R29wGl4jRY8GEyQEx8SypV/iHt1FbzsU/X3Qbcd9afm2W2w==", + "license": "MIT", + "dependencies": { + "@expo/spawn-async": "^1.7.2", + "chalk": "^4.0.0", + "getenv": "^2.0.0", + "jimp-compact": "0.16.1", + "parse-png": "^2.1.0", + "resolve-from": "^5.0.0", + "resolve-global": "^1.0.0", + "semver": "^7.6.0", + "temp-dir": "~2.0.0", + "unique-string": "~2.0.0" + } + }, + "node_modules/@expo/image-utils/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@expo/image-utils/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@expo/image-utils/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@expo/image-utils/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/@expo/image-utils/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@expo/image-utils/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@expo/json-file": { + "version": "10.0.7", + "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-10.0.7.tgz", + "integrity": "sha512-z2OTC0XNO6riZu98EjdNHC05l51ySeTto6GP7oSQrCvQgG9ARBwD1YvMQaVZ9wU7p/4LzSf1O7tckL3B45fPpw==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "~7.10.4", + "json5": "^2.2.3" + } + }, + "node_modules/@expo/mcp-tunnel": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@expo/mcp-tunnel/-/mcp-tunnel-0.1.0.tgz", + "integrity": "sha512-rJ6hl0GnIZj9+ssaJvFsC7fwyrmndcGz+RGFzu+0gnlm78X01957yjtHgjcmnQAgL5hWEOR6pkT0ijY5nU5AWw==", + "license": "MIT", + "dependencies": { + "ws": "^8.18.3", + "zod": "^3.25.76", + "zod-to-json-schema": "^3.24.6" + }, + "peerDependencies": { + "@modelcontextprotocol/sdk": "^1.13.2" + }, + "peerDependenciesMeta": { + "@modelcontextprotocol/sdk": { + "optional": true + } + } + }, + "node_modules/@expo/mcp-tunnel/node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@expo/metro": { + "version": "54.1.0", + "resolved": "https://registry.npmjs.org/@expo/metro/-/metro-54.1.0.tgz", + "integrity": "sha512-MgdeRNT/LH0v1wcO0TZp9Qn8zEF0X2ACI0wliPtv5kXVbXWI+yK9GyrstwLAiTXlULKVIg3HVSCCvmLu0M3tnw==", + "license": "MIT", + "dependencies": { + "metro": "0.83.2", + "metro-babel-transformer": "0.83.2", + "metro-cache": "0.83.2", + "metro-cache-key": "0.83.2", + "metro-config": "0.83.2", + "metro-core": "0.83.2", + "metro-file-map": "0.83.2", + "metro-resolver": "0.83.2", + "metro-runtime": "0.83.2", + "metro-source-map": "0.83.2", + "metro-transform-plugins": "0.83.2", + "metro-transform-worker": "0.83.2" + } + }, + "node_modules/@expo/metro-runtime": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@expo/metro-runtime/-/metro-runtime-6.1.2.tgz", + "integrity": "sha512-nvM+Qv45QH7pmYvP8JB1G8JpScrWND3KrMA6ZKe62cwwNiX/BjHU28Ear0v/4bQWXlOY0mv6B8CDIm8JxXde9g==", + "license": "MIT", + "dependencies": { + "anser": "^1.4.9", + "pretty-format": "^29.7.0", + "stacktrace-parser": "^0.1.10", + "whatwg-fetch": "^3.0.0" + }, + "peerDependencies": { + "expo": "*", + "react": "*", + "react-dom": "*", + "react-native": "*" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/@expo/osascript": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/@expo/osascript/-/osascript-2.3.7.tgz", + "integrity": "sha512-IClSOXxR0YUFxIriUJVqyYki7lLMIHrrzOaP01yxAL1G8pj2DWV5eW1y5jSzIcIfSCNhtGsshGd1tU/AYup5iQ==", + "license": "MIT", + "dependencies": { + "@expo/spawn-async": "^1.7.2", + "exec-async": "^2.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@expo/package-manager": { + "version": "1.9.8", + "resolved": "https://registry.npmjs.org/@expo/package-manager/-/package-manager-1.9.8.tgz", + "integrity": "sha512-4/I6OWquKXYnzo38pkISHCOCOXxfeEmu4uDoERq1Ei/9Ur/s9y3kLbAamEkitUkDC7gHk1INxRWEfFNzGbmOrA==", + "license": "MIT", + "dependencies": { + "@expo/json-file": "^10.0.7", + "@expo/spawn-async": "^1.7.2", + "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/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@expo/package-manager/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@expo/package-manager/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@expo/package-manager/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/@expo/package-manager/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@expo/package-manager/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@expo/plist": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/@expo/plist/-/plist-0.4.7.tgz", + "integrity": "sha512-dGxqHPvCZKeRKDU1sJZMmuyVtcASuSYh1LPFVaM1DuffqPL36n6FMEL0iUqq2Tx3xhWk8wCnWl34IKplUjJDdA==", + "license": "MIT", + "dependencies": { + "@xmldom/xmldom": "^0.8.8", + "base64-js": "^1.2.3", + "xmlbuilder": "^15.1.1" + } + }, + "node_modules/@expo/schema-utils": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/@expo/schema-utils/-/schema-utils-0.1.7.tgz", + "integrity": "sha512-jWHoSuwRb5ZczjahrychMJ3GWZu54jK9ulNdh1d4OzAEq672K9E5yOlnlBsfIHWHGzUAT+0CL7Yt1INiXTz68g==", + "license": "MIT" + }, + "node_modules/@expo/sdk-runtime-versions": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@expo/sdk-runtime-versions/-/sdk-runtime-versions-1.0.0.tgz", + "integrity": "sha512-Doz2bfiPndXYFPMRwPyGa1k5QaKDVpY806UJj570epIiMzWaYyCtobasyfC++qfIXVb5Ocy7r3tP9d62hAQ7IQ==", + "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==", + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@expo/sudo-prompt": { + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/@expo/sudo-prompt/-/sudo-prompt-9.3.2.tgz", + "integrity": "sha512-HHQigo3rQWKMDzYDLkubN5WQOYXJJE2eNqIQC2axC2iO3mHdwnIR7FgZVvHWtBwAdzBgAP0ECp8KqS8TiMKvgw==", + "license": "MIT" + }, + "node_modules/@expo/ui": { + "version": "0.2.0-beta.7", + "resolved": "https://registry.npmjs.org/@expo/ui/-/ui-0.2.0-beta.7.tgz", + "integrity": "sha512-oz2HEpwll+yMFUKbryZ84IgxjLx7RPxxMDVKpCEsK0OhETrLF5NxHlpCkKjdLuQL3QiVSvj5kn6hBFknco3aCw==", + "license": "MIT", + "dependencies": { + "sf-symbols-typescript": "^2.1.0" + }, + "peerDependencies": { + "expo": "*", + "react": "*", + "react-native": "*" + } + }, + "node_modules/@expo/vector-icons": { + "version": "15.0.3", + "resolved": "https://registry.npmjs.org/@expo/vector-icons/-/vector-icons-15.0.3.tgz", + "integrity": "sha512-SBUyYKphmlfUBqxSfDdJ3jAdEVSALS2VUPOUyqn48oZmb2TL/O7t7/PQm5v4NQujYEPLPMTLn9KVw6H7twwbTA==", + "license": "MIT", + "peerDependencies": { + "expo-font": ">=14.0.4", + "react": "*", + "react-native": "*" + } + }, + "node_modules/@expo/ws-tunnel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@expo/ws-tunnel/-/ws-tunnel-1.0.6.tgz", + "integrity": "sha512-nDRbLmSrJar7abvUjp3smDwH8HcbZcoOEa5jVPUv9/9CajgmWw20JNRwTuBRzWIWIkEJDkz20GoNA+tSwUqk0Q==", + "license": "MIT" + }, + "node_modules/@expo/xcpretty": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@expo/xcpretty/-/xcpretty-4.3.2.tgz", + "integrity": "sha512-ReZxZ8pdnoI3tP/dNnJdnmAk7uLT4FjsKDGW7YeDdvdOMz2XCQSmSCM9IWlrXuWtMF9zeSB6WJtEhCQ41gQOfw==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/code-frame": "7.10.4", + "chalk": "^4.1.0", + "find-up": "^5.0.0", + "js-yaml": "^4.1.0" + }, + "bin": { + "excpretty": "build/cli.js" + } + }, + "node_modules/@expo/xcpretty/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@expo/xcpretty/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/@expo/xcpretty/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@expo/xcpretty/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@expo/xcpretty/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/@expo/xcpretty/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@expo/xcpretty/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@expo/xcpretty/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@expo/xcpretty/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@expo/xcpretty/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@expo/xcpretty/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@isaacs/ttlcache": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@isaacs/ttlcache/-/ttlcache-1.4.1.tgz", + "integrity": "sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/create-cache-key-function": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-29.7.0.tgz", + "integrity": "sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==", + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/transform/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/transform/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/transform/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/@jest/transform/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/transform/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/types/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/types/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/types/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/@jest/types/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/types/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pnpm/config.env-replace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", + "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@pnpm/network.ca-file": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", + "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "4.2.10" + }, + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true, + "license": "ISC" + }, + "node_modules/@pnpm/npm-conf": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.3.1.tgz", + "integrity": "sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pnpm/config.env-replace": "^1.1.0", + "@pnpm/network.ca-file": "^1.0.1", + "config-chain": "^1.1.11" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", + "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-collection": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", + "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz", + "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-direction": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", + "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", + "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-escape-keydown": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", + "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", + "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", + "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", + "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", + "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz", + "integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz", + "integrity": "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", + "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", + "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@react-native-segmented-control/segmented-control": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/@react-native-segmented-control/segmented-control/-/segmented-control-2.5.7.tgz", + "integrity": "sha512-l84YeVX8xAU3lvOJSvV4nK/NbGhIm2gBfveYolwaoCbRp+/SLXtc6mYrQmM9ScXNwU14mnzjQTpTHWl5YPnkzQ==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.0", + "react-native": ">=0.62" + } + }, + "node_modules/@react-native/assets-registry": { + "version": "0.81.5", + "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.81.5.tgz", + "integrity": "sha512-705B6x/5Kxm1RKRvSv0ADYWm5JOnoiQ1ufW7h8uu2E6G9Of/eE6hP/Ivw3U5jI16ERqZxiKQwk34VJbB0niX9w==", + "license": "MIT", + "engines": { + "node": ">= 20.19.4" + } + }, + "node_modules/@react-native/babel-plugin-codegen": { + "version": "0.81.5", + "resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.81.5.tgz", + "integrity": "sha512-oF71cIH6je3fSLi6VPjjC3Sgyyn57JLHXs+mHWc9MoCiJJcM4nqsS5J38zv1XQ8d3zOW2JtHro+LF0tagj2bfQ==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.3", + "@react-native/codegen": "0.81.5" + }, + "engines": { + "node": ">= 20.19.4" + } + }, + "node_modules/@react-native/babel-preset": { + "version": "0.81.5", + "resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.81.5.tgz", + "integrity": "sha512-UoI/x/5tCmi+pZ3c1+Ypr1DaRMDLI3y+Q70pVLLVgrnC3DHsHRIbHcCHIeG/IJvoeFqFM2sTdhSOLJrf8lOPrA==", + "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.81.5", + "babel-plugin-syntax-hermes-parser": "0.29.1", + "babel-plugin-transform-flow-enums": "^0.0.2", + "react-refresh": "^0.14.0" + }, + "engines": { + "node": ">= 20.19.4" + }, + "peerDependencies": { + "@babel/core": "*" + } + }, + "node_modules/@react-native/codegen": { + "version": "0.81.5", + "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.81.5.tgz", + "integrity": "sha512-a2TDA03Up8lpSa9sh5VRGCQDXgCTOyDOFH+aqyinxp1HChG8uk89/G+nkJ9FPd0rqgi25eCTR16TWdS3b+fA6g==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.25.2", + "@babel/parser": "^7.25.3", + "glob": "^7.1.1", + "hermes-parser": "0.29.1", + "invariant": "^2.2.4", + "nullthrows": "^1.1.1", + "yargs": "^17.6.2" + }, + "engines": { + "node": ">= 20.19.4" + }, + "peerDependencies": { + "@babel/core": "*" + } + }, + "node_modules/@react-native/codegen/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "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": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "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": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@react-native/codegen/node_modules/hermes-estree": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.29.1.tgz", + "integrity": "sha512-jl+x31n4/w+wEqm0I2r4CMimukLbLQEYpisys5oCre611CI5fc9TxhqkBBCJ1edDG4Kza0f7CgNz8xVMLZQOmQ==", + "license": "MIT" + }, + "node_modules/@react-native/codegen/node_modules/hermes-parser": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.29.1.tgz", + "integrity": "sha512-xBHWmUtRC5e/UL0tI7Ivt2riA/YBq9+SiYFU7C1oBa/j2jYGlIF9043oak1F47ihuDIxQ5nbsKueYJDRY02UgA==", + "license": "MIT", + "dependencies": { + "hermes-estree": "0.29.1" + } + }, + "node_modules/@react-native/codegen/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@react-native/community-cli-plugin": { + "version": "0.81.5", + "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.81.5.tgz", + "integrity": "sha512-yWRlmEOtcyvSZ4+OvqPabt+NS36vg0K/WADTQLhrYrm9qdZSuXmq8PmdJWz/68wAqKQ+4KTILiq2kjRQwnyhQw==", + "license": "MIT", + "dependencies": { + "@react-native/dev-middleware": "0.81.5", + "debug": "^4.4.0", + "invariant": "^2.2.4", + "metro": "^0.83.1", + "metro-config": "^0.83.1", + "metro-core": "^0.83.1", + "semver": "^7.1.3" + }, + "engines": { + "node": ">= 20.19.4" + }, + "peerDependencies": { + "@react-native-community/cli": "*", + "@react-native/metro-config": "*" + }, + "peerDependenciesMeta": { + "@react-native-community/cli": { + "optional": true + }, + "@react-native/metro-config": { + "optional": true + } + } + }, + "node_modules/@react-native/debugger-frontend": { + "version": "0.81.5", + "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.81.5.tgz", + "integrity": "sha512-bnd9FSdWKx2ncklOetCgrlwqSGhMHP2zOxObJbOWXoj7GHEmih4MKarBo5/a8gX8EfA1EwRATdfNBQ81DY+h+w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 20.19.4" + } + }, + "node_modules/@react-native/dev-middleware": { + "version": "0.81.5", + "resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.81.5.tgz", + "integrity": "sha512-WfPfZzboYgo/TUtysuD5xyANzzfka8Ebni6RIb2wDxhb56ERi7qDrE4xGhtPsjCL4pQBXSVxyIlCy0d8I6EgGA==", + "license": "MIT", + "dependencies": { + "@isaacs/ttlcache": "^1.4.1", + "@react-native/debugger-frontend": "0.81.5", + "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": "^6.2.3" + }, + "engines": { + "node": ">= 20.19.4" + } + }, + "node_modules/@react-native/dev-middleware/node_modules/ws": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz", + "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", + "license": "MIT", + "dependencies": { + "async-limiter": "~1.0.0" + } + }, + "node_modules/@react-native/gradle-plugin": { + "version": "0.81.5", + "resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.81.5.tgz", + "integrity": "sha512-hORRlNBj+ReNMLo9jme3yQ6JQf4GZpVEBLxmTXGGlIL78MAezDZr5/uq9dwElSbcGmLEgeiax6e174Fie6qPLg==", + "license": "MIT", + "engines": { + "node": ">= 20.19.4" + } + }, + "node_modules/@react-native/js-polyfills": { + "version": "0.81.5", + "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.81.5.tgz", + "integrity": "sha512-fB7M1CMOCIUudTRuj7kzxIBTVw2KXnsgbQ6+4cbqSxo8NmRRhA0Ul4ZUzZj3rFd3VznTL4Brmocv1oiN0bWZ8w==", + "license": "MIT", + "engines": { + "node": ">= 20.19.4" + } + }, + "node_modules/@react-native/normalize-colors": { + "version": "0.81.5", + "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.81.5.tgz", + "integrity": "sha512-0HuJ8YtqlTVRXGZuGeBejLE04wSQsibpTI+RGOyVqxZvgtlLLC/Ssw0UmbHhT4lYMp2fhdtvKZSs5emWB1zR/g==", + "license": "MIT" + }, + "node_modules/@react-native/virtualized-lists": { + "version": "0.81.5", + "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.81.5.tgz", + "integrity": "sha512-UVXgV/db25OPIvwZySeToXD/9sKKhOdkcWmmf4Jh8iBZuyfML+/5CasaZ1E7Lqg6g3uqVQq75NqIwkYmORJMPw==", + "license": "MIT", + "dependencies": { + "invariant": "^2.2.4", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 20.19.4" + }, + "peerDependencies": { + "@types/react": "^19.1.0", + "react": "*", + "react-native": "*" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@react-navigation/bottom-tabs": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@react-navigation/bottom-tabs/-/bottom-tabs-7.8.4.tgz", + "integrity": "sha512-Ie+7EgUxfZmVXm4RCiJ96oaiwJVFgVE8NJoeUKLLcYEB/99wKbhuKPJNtbkpR99PHfhq64SE7476BpcP4xOFhw==", + "license": "MIT", + "dependencies": { + "@react-navigation/elements": "^2.8.1", + "color": "^4.2.3", + "sf-symbols-typescript": "^2.1.0" + }, + "peerDependencies": { + "@react-navigation/native": "^7.1.19", + "react": ">= 18.2.0", + "react-native": "*", + "react-native-safe-area-context": ">= 4.0.0", + "react-native-screens": ">= 4.0.0" + } + }, + "node_modules/@react-navigation/core": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-7.13.0.tgz", + "integrity": "sha512-Fc/SO23HnlGnkou/z8JQUzwEMvhxuUhr4rdPTIZp/c8q1atq3k632Nfh8fEiGtk+MP1wtIvXdN2a5hBIWpLq3g==", + "license": "MIT", + "dependencies": { + "@react-navigation/routers": "^7.5.1", + "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-navigation/core/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@react-navigation/core/node_modules/react-is": { + "version": "19.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.0.tgz", + "integrity": "sha512-x3Ax3kNSMIIkyVYhWPyO09bu0uttcAIoecO/um/rKGQ4EltYWVYtyiGkS/3xMynrbVQdS69Jhlv8FXUEZehlzA==", + "license": "MIT" + }, + "node_modules/@react-navigation/elements": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-2.8.1.tgz", + "integrity": "sha512-MLmuS5kPAeAFFOylw89WGjgEFBqGj/KBK6ZrFrAOqLnTqEzk52/SO1olb5GB00k6ZUCDZKJOp1BrLXslxE6TgQ==", + "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.1.19", + "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-navigation/native": { + "version": "7.1.19", + "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-7.1.19.tgz", + "integrity": "sha512-fM7q8di4Q8sp2WUhiUWOe7bEDRyRhbzsKQOd5N2k+lHeCx3UncsRYuw4Q/KN0EovM3wWKqMMmhy/YWuEO04kgw==", + "license": "MIT", + "dependencies": { + "@react-navigation/core": "^7.13.0", + "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-navigation/native-stack": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-7.6.2.tgz", + "integrity": "sha512-CB6chGNLwJYiyOeyCNUKx33yT7XJSwRZIeKHf4S1vs+Oqu3u9zMnvGUIsesNgbgX0xy16gBqYsrWgr0ZczBTtA==", + "license": "MIT", + "dependencies": { + "@react-navigation/elements": "^2.8.1", + "color": "^4.2.3", + "sf-symbols-typescript": "^2.1.0", + "warn-once": "^0.1.1" + }, + "peerDependencies": { + "@react-navigation/native": "^7.1.19", + "react": ">= 18.2.0", + "react-native": "*", + "react-native-safe-area-context": ">= 4.0.0", + "react-native-screens": ">= 4.0.0" + } + }, + "node_modules/@react-navigation/native/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@react-navigation/routers": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-7.5.1.tgz", + "integrity": "sha512-pxipMW/iEBSUrjxz2cDD7fNwkqR4xoi0E/PcfTQGCcdJwLoaxzab5kSadBLj1MTJyT0YRrOXL9umHpXtp+Dv4w==", + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.53.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.1.tgz", + "integrity": "sha512-bxZtughE4VNVJlL1RdoSE545kc4JxL7op57KKoi59/gwuU5rV6jLWFXXc8jwgFoT6vtj+ZjO+Z2C5nrY0Cl6wA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.53.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.1.tgz", + "integrity": "sha512-44a1hreb02cAAfAKmZfXVercPFaDjqXCK+iKeVOlJ9ltvnO6QqsBHgKVPTu+MJHSLLeMEUbeG2qiDYgbFPU48g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.53.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.1.tgz", + "integrity": "sha512-usmzIgD0rf1syoOZ2WZvy8YpXK5G1V3btm3QZddoGSa6mOgfXWkkv+642bfUUldomgrbiLQGrPryb7DXLovPWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.53.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.1.tgz", + "integrity": "sha512-is3r/k4vig2Gt8mKtTlzzyaSQ+hd87kDxiN3uDSDwggJLUV56Umli6OoL+/YZa/KvtdrdyNfMKHzL/P4siOOmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.53.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.1.tgz", + "integrity": "sha512-QJ1ksgp/bDJkZB4daldVmHaEQkG4r8PUXitCOC2WRmRaSaHx5RwPoI3DHVfXKwDkB+Sk6auFI/+JHacTekPRSw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.53.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.1.tgz", + "integrity": "sha512-J6ma5xgAzvqsnU6a0+jgGX/gvoGokqpkx6zY4cWizRrm0ffhHDpJKQgC8dtDb3+MqfZDIqs64REbfHDMzxLMqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.53.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.1.tgz", + "integrity": "sha512-JzWRR41o2U3/KMNKRuZNsDUAcAVUYhsPuMlx5RUldw0E4lvSIXFUwejtYz1HJXohUmqs/M6BBJAUBzKXZVddbg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.53.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.1.tgz", + "integrity": "sha512-L8kRIrnfMrEoHLHtHn+4uYA52fiLDEDyezgxZtGUTiII/yb04Krq+vk3P2Try+Vya9LeCE9ZHU8CXD6J9EhzHQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.53.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.1.tgz", + "integrity": "sha512-ysAc0MFRV+WtQ8li8hi3EoFi7us6d1UzaS/+Dp7FYZfg3NdDljGMoVyiIp6Ucz7uhlYDBZ/zt6XI0YEZbUO11Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.53.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.1.tgz", + "integrity": "sha512-UV6l9MJpDbDZZ/fJvqNcvO1PcivGEf1AvKuTcHoLjVZVFeAMygnamCTDikCVMRnA+qJe+B3pSbgX2+lBMqgBhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.53.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.1.tgz", + "integrity": "sha512-UDUtelEprkA85g95Q+nj3Xf0M4hHa4DiJ+3P3h4BuGliY4NReYYqwlc0Y8ICLjN4+uIgCEvaygYlpf0hUj90Yg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.53.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.1.tgz", + "integrity": "sha512-vrRn+BYhEtNOte/zbc2wAUQReJXxEx2URfTol6OEfY2zFEUK92pkFBSXRylDM7aHi+YqEPJt9/ABYzmcrS4SgQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.53.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.1.tgz", + "integrity": "sha512-gto/1CxHyi4A7YqZZNznQYrVlPSaodOBPKM+6xcDSCMVZN/Fzb4K+AIkNz/1yAYz9h3Ng+e2fY9H6bgawVq17w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.53.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.1.tgz", + "integrity": "sha512-KZ6Vx7jAw3aLNjFR8eYVcQVdFa/cvBzDNRFM3z7XhNNunWjA03eUrEwJYPk0G8V7Gs08IThFKcAPS4WY/ybIrQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.53.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.1.tgz", + "integrity": "sha512-HvEixy2s/rWNgpwyKpXJcHmE7om1M89hxBTBi9Fs6zVuLU4gOrEMQNbNsN/tBVIMbLyysz/iwNiGtMOpLAOlvA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.53.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.1.tgz", + "integrity": "sha512-E/n8x2MSjAQgjj9IixO4UeEUeqXLtiA7pyoXCFYLuXpBA/t2hnbIdxHfA7kK9BFsYAoNU4st1rHYdldl8dTqGA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.53.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.1.tgz", + "integrity": "sha512-IhJ087PbLOQXCN6Ui/3FUkI9pWNZe/Z7rEIVOzMsOs1/HSAECCvSZ7PkIbkNqL/AZn6WbZvnoVZw/qwqYMo4/w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.53.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.1.tgz", + "integrity": "sha512-0++oPNgLJHBblreu0SFM7b3mAsBJBTY0Ksrmu9N6ZVrPiTkRgda52mWR7TKhHAsUb9noCjFvAw9l6ZO1yzaVbA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.53.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.1.tgz", + "integrity": "sha512-VJXivz61c5uVdbmitLkDlbcTk9Or43YC2QVLRkqp86QoeFSqI81bNgjhttqhKNMKnQMWnecOCm7lZz4s+WLGpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.53.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.1.tgz", + "integrity": "sha512-NmZPVTUOitCXUH6erJDzTQ/jotYw4CnkMDjCYRxNHVD9bNyfrGoIse684F9okwzKCV4AIHRbUkeTBc9F2OOH5Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.53.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.1.tgz", + "integrity": "sha512-2SNj7COIdAf6yliSpLdLG8BEsp5lgzRehgfkP0Av8zKfQFKku6JcvbobvHASPJu4f3BFxej5g+HuQPvqPhHvpQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.53.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.1.tgz", + "integrity": "sha512-rLarc1Ofcs3DHtgSzFO31pZsCh8g05R2azN1q3fF+H423Co87My0R+tazOEvYVKXSLh8C4LerMK41/K7wlklcg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "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/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/chrome": { + "version": "0.1.31", + "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.1.31.tgz", + "integrity": "sha512-8fC9t02LRdqlSQ9bktaiLjP0NKujPNPaCKPfXxfIjZ35A9u831XY0pt0UEf2oup2SjcUEAaIYOB8mzJ51jhjmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/filesystem": "*", + "@types/har-format": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/filesystem": { + "version": "0.0.36", + "resolved": "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.36.tgz", + "integrity": "sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/filewriter": "*" + } + }, + "node_modules/@types/filewriter": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.33.tgz", + "integrity": "sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/hammerjs": { + "version": "2.0.46", + "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.46.tgz", + "integrity": "sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw==", + "license": "MIT" + }, + "node_modules/@types/har-format": { + "version": "1.2.16", + "resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.16.tgz", + "integrity": "sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/minimatch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.0.tgz", + "integrity": "sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/react": { + "version": "19.1.17", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.17.tgz", + "integrity": "sha512-Qec1E3mhALmaspIrhWt9jkQMNdw6bReVu64mjvhbhq2NFPftLPVr+l1SZgmw/66WwBNpDh7ao5AT6gF5v41PFA==", + "license": "MIT", + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.1.11", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.11.tgz", + "integrity": "sha512-3BKc/yGdNTYQVVw4idqHtSOcFsgGuBbMveKCOgF8wQ5QtrYOc3jDIlzg3jef04zcXFIHLelyGlj0T+BJ8+KN+w==", + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.0.0" + } + }, + "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/yargs": { + "version": "17.0.34", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.34.tgz", + "integrity": "sha512-KExbHVa92aJpw9WDQvzBaGVE2/Pz+pLZQloT2hjL8IqsZnV62rlPOYvNnLmf/L2dyllfVUOVBj64M0z/46eR2A==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "license": "ISC" + }, + "node_modules/@urql/core": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@urql/core/-/core-5.2.0.tgz", + "integrity": "sha512-/n0ieD0mvvDnVAXEQgX/7qJiVcvYvNkOHeBvkwtylfjydar123caCXcl58PXFY11oU1oquJocVXHxLAbtv4x1A==", + "license": "MIT", + "dependencies": { + "@0no-co/graphql.web": "^1.0.13", + "wonka": "^6.3.2" + } + }, + "node_modules/@urql/exchange-retry": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@urql/exchange-retry/-/exchange-retry-1.3.2.tgz", + "integrity": "sha512-TQMCz2pFJMfpNxmSfX1VSfTjwUIFx/mL+p1bnfM1xjjdla7Z+KnGMW/EhFbpckp3LyWAH4PgOsMwOMnIN+MBFg==", + "license": "MIT", + "dependencies": { + "@urql/core": "^5.1.2", + "wonka": "^6.3.2" + }, + "peerDependencies": { + "@urql/core": "^5.0.0" + } + }, + "node_modules/@webext-core/fake-browser": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@webext-core/fake-browser/-/fake-browser-1.3.2.tgz", + "integrity": "sha512-jFyPWWz+VkHAC9DRIiIPOyu6X/KlC8dYqSKweHz6tsDb86QawtVgZSpYcM+GOQBlZc5DHFo92jJ7cIq4uBnU0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.merge": "^4.6.2" + } + }, + "node_modules/@webext-core/isolated-element": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@webext-core/isolated-element/-/isolated-element-1.1.2.tgz", + "integrity": "sha512-CNHYhsIR8TPkPb+4yqTIuzaGnVn/Fshev5fyoPW+/8Cyc93tJbCjP9PC1XSK6fDWu+xASdPHLZaoa2nWAYoxeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-potential-custom-element-name": "^1.0.1" + } + }, + "node_modules/@webext-core/match-patterns": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@webext-core/match-patterns/-/match-patterns-1.0.3.tgz", + "integrity": "sha512-NY39ACqCxdKBmHgw361M9pfJma8e4AZo20w9AY+5ZjIj1W2dvXC8J31G5fjfOGbulW9w4WKpT8fPooi0mLkn9A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@wxt-dev/browser": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@wxt-dev/browser/-/browser-0.1.4.tgz", + "integrity": "sha512-9x03I15i79XU8qYwjv4le0K2HdMl/Yga2wUBSoUbcrCnamv8P3nvuYxREQ9C5QY/qPAfeEVdAtaTrS3KWak71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/filesystem": "*", + "@types/har-format": "*" + } + }, + "node_modules/@wxt-dev/storage": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@wxt-dev/storage/-/storage-1.2.6.tgz", + "integrity": "sha512-f6AknnpJvhNHW4s0WqwSGCuZAj0fjP3EVNPBO5kB30pY+3Zt/nqZGqJN6FgBLCSkYjPJ8VL1hNX5LMVmvxQoDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@wxt-dev/browser": "^0.1.4", + "async-mutex": "^0.5.0", + "dequal": "^2.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/wxt-dev" + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.11", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz", + "integrity": "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/adm-zip": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz", + "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/anser": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/anser/-/anser-1.4.10.tgz", + "integrity": "sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww==", + "license": "MIT" + }, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.1.0" + } + }, + "node_modules/ansi-align/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/ansi-align/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-align/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "license": "MIT" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/aria-hidden": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/array-differ": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-4.0.0.tgz", + "integrity": "sha512-Q6VPTLMsmXZ47ENG3V+wQyZS1ZxXMxFyYzA+Z/GMrJ6yIutAIEf9wTyroTzmGjNfox9/h3GdGBCVh43GVFx4Uw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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-union": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-3.0.1.tgz", + "integrity": "sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "license": "MIT" + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "license": "MIT" + }, + "node_modules/async-mutex": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", + "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/atomically": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/atomically/-/atomically-2.1.0.tgz", + "integrity": "sha512-+gDffFXRW6sl/HCwbta7zK4uNqbPjv4YJEAdz7Vu+FLQHe77eZ4bvbJGi4hE0QPeJlMYMA3piXEr1UL3dAwx7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "stubborn-fs": "^2.0.0", + "when-exit": "^2.1.4" + } + }, + "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-jest/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/babel-jest/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/babel-jest/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/babel-jest/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/babel-jest/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-jest/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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.14", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz", + "integrity": "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.7", + "@babel/helper-define-polyfill-provider": "^0.6.5", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "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.5", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.5.tgz", + "integrity": "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==", + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.5" + }, + "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.29.1", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.29.1.tgz", + "integrity": "sha512-2WFYnoWGdmih1I1J5eIqxATOeycOqRwYxAQBu3cUu/rhwInwHUg7k60AFNbuGjSDL8tje5GDrAnxzRLcu2pYcA==", + "license": "MIT", + "dependencies": { + "hermes-parser": "0.29.1" + } + }, + "node_modules/babel-plugin-syntax-hermes-parser/node_modules/hermes-estree": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.29.1.tgz", + "integrity": "sha512-jl+x31n4/w+wEqm0I2r4CMimukLbLQEYpisys5oCre611CI5fc9TxhqkBBCJ1edDG4Kza0f7CgNz8xVMLZQOmQ==", + "license": "MIT" + }, + "node_modules/babel-plugin-syntax-hermes-parser/node_modules/hermes-parser": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.29.1.tgz", + "integrity": "sha512-xBHWmUtRC5e/UL0tI7Ivt2riA/YBq9+SiYFU7C1oBa/j2jYGlIF9043oak1F47ihuDIxQ5nbsKueYJDRY02UgA==", + "license": "MIT", + "dependencies": { + "hermes-estree": "0.29.1" + } + }, + "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": "54.0.7", + "resolved": "https://registry.npmjs.org/babel-preset-expo/-/babel-preset-expo-54.0.7.tgz", + "integrity": "sha512-JENWk0bvxW4I1ftveO8GRtX2t2TH6N4Z0TPvIHxroZ/4SswUfyNsUNbbP7Fm4erj3ar/JHGri5kTZ+s3xdjHZw==", + "license": "MIT", + "dependencies": { + "@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.81.5", + "babel-plugin-react-compiler": "^1.0.0", + "babel-plugin-react-native-web": "~0.21.0", + "babel-plugin-syntax-hermes-parser": "^0.29.1", + "babel-plugin-transform-flow-enums": "^0.0.2", + "debug": "^4.3.4", + "resolve-from": "^5.0.0" + }, + "peerDependencies": { + "@babel/runtime": "^7.20.0", + "expo": "*", + "react-refresh": ">=0.14.0 <1.0.0" + }, + "peerDependenciesMeta": { + "@babel/runtime": { + "optional": true + }, + "expo": { + "optional": true + } + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.25", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.25.tgz", + "integrity": "sha512-2NovHVesVF5TXefsGX1yzx1xgr7+m9JQenvz6FQY3qd+YXkKkYiv+vTCc7OriP9mcDZpTC5mAOYN4ocd29+erA==", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/better-opn": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/better-opn/-/better-opn-3.0.2.tgz", + "integrity": "sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==", + "license": "MIT", + "dependencies": { + "open": "^8.0.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/better-opn/node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "license": "Unlicense", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC" + }, + "node_modules/boxen": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-8.0.1.tgz", + "integrity": "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-align": "^3.0.1", + "camelcase": "^8.0.0", + "chalk": "^5.3.0", + "cli-boxes": "^3.0.0", + "string-width": "^7.2.0", + "type-fest": "^4.21.0", + "widest-line": "^5.0.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/boxen/node_modules/camelcase": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", + "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/boxen/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/boxen/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/bplist-creator": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.1.0.tgz", + "integrity": "sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg==", + "license": "MIT", + "dependencies": { + "stream-buffers": "2.2.x" + } + }, + "node_modules/bplist-parser": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.1.tgz", + "integrity": "sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA==", + "license": "MIT", + "dependencies": { + "big-integer": "1.6.x" + }, + "engines": { + "node": ">= 5.10.0" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.27.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.27.0.tgz", + "integrity": "sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.8.19", + "caniuse-lite": "^1.0.30001751", + "electron-to-chromium": "^1.5.238", + "node-releases": "^2.0.26", + "update-browserslist-db": "^1.1.4" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/c12": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/c12/-/c12-3.3.1.tgz", + "integrity": "sha512-LcWQ01LT9tkoUINHgpIOv3mMs+Abv7oVCrtpMRi1PaapVEpWoMga5WuT7/DqFTu7URP9ftbOmimNw1KNIGh9DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.3", + "confbox": "^0.2.2", + "defu": "^6.1.4", + "dotenv": "^17.2.3", + "exsolve": "^1.0.7", + "giget": "^2.0.0", + "jiti": "^2.6.1", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "perfect-debounce": "^2.0.0", + "pkg-types": "^2.3.0", + "rc9": "^2.1.2" + }, + "peerDependencies": { + "magicast": "^0.3.5" + }, + "peerDependenciesMeta": { + "magicast": { + "optional": true + } + } + }, + "node_modules/c12/node_modules/dotenv": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001754", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001754.tgz", + "integrity": "sha512-x6OeBXueoAceOmotzx3PO4Zpt4rzpeIFsSr6AAePTZxSkXiYDUmpypEl7e2+8NCd9bD7bXjqyef8CJYPC1jfxg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "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/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/chrome-launcher": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.15.2.tgz", + "integrity": "sha512-zdLEwNo3aUVzIhKhTtXfxhdvZhUghrnmkvcAq2NoDd+LeOHKf03H5jwZ8T/STsAlzyALkBVK552iaG1fGf1xVQ==", + "license": "Apache-2.0", + "dependencies": { + "@types/node": "*", + "escape-string-regexp": "^4.0.0", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^1.0.0" + }, + "bin": { + "print-chrome-path": "bin/print-chrome-path.js" + }, + "engines": { + "node": ">=12.13.0" + } + }, + "node_modules/chrome-launcher/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chromium-edge-launcher": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/chromium-edge-launcher/-/chromium-edge-launcher-0.2.0.tgz", + "integrity": "sha512-JfJjUnq25y9yg4FABRRVPmBGWPZZi+AQXT4mxupb67766/0UlhG8PAZCz6xzEMXTbW3CsSoE8PcCWA49n35mKg==", + "license": "Apache-2.0", + "dependencies": { + "@types/node": "*", + "escape-string-regexp": "^4.0.0", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^1.0.0", + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + } + }, + "node_modules/chromium-edge-launcher/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "license": "MIT" + }, + "node_modules/citty": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "consola": "^3.2.3" + } + }, + "node_modules/cli-boxes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", + "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", + "license": "MIT", + "dependencies": { + "restore-cursor": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cli-truncate/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/cliui/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, + "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/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/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/comment-json": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.4.1.tgz", + "integrity": "sha512-r1To31BQD5060QdkC+Iheai7gHwoSZobzunqkf2/kQ6xIAfJyrKNAFUwdKvkK7Qgu7pVTKQEa7ok7Ed3ycAJgg==", + "license": "MIT", + "dependencies": { + "array-timsort": "^1.0.3", + "core-util-is": "^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", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "license": "MIT", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "compressible": "~2.0.18", + "debug": "2.6.9", + "negotiator": "~0.6.4", + "on-headers": "~1.1.0", + "safe-buffer": "5.2.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/compression/node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "engines": [ + "node >= 0.8" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/confbox": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", + "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/configstore": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-7.1.0.tgz", + "integrity": "sha512-N4oog6YJWbR9kGyXvS7jEykLDXIE2C0ILYqNBZBp9iwiJpoCBWYsuAdW6PPFn6w06jjnC+3JstVvWHO4cZqvRg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "atomically": "^2.0.3", + "dot-prop": "^9.0.0", + "graceful-fs": "^4.2.11", + "xdg-basedir": "^5.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/connect/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/connect/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "license": "MIT" + }, + "node_modules/core-js-compat": { + "version": "3.46.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.46.0.tgz", + "integrity": "sha512-p9hObIIEENxSV8xIu+V68JjSeARg6UVMG5mR+JEUguG3sI6MsiS1njz2jHmyJDvA+8jX/sytkBHup6kxhM9law==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.26.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/cross-fetch": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz", + "integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.7.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/css-in-js-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-3.1.0.tgz", + "integrity": "sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==", + "license": "MIT", + "dependencies": { + "hyphenate-style-name": "^1.0.3" + } + }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/css-tree/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/debounce": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", + "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-browser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", + "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/defu": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "dev": true, + "license": "MIT" + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "dev": true, + "license": "MIT" + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT" + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "license": "Apache-2.0" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "license": "MIT" + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-prop": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-9.0.0.tgz", + "integrity": "sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^4.18.2" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dot-prop/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotenv-expand": { + "version": "11.0.7", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.7.tgz", + "integrity": "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==", + "license": "BSD-2-Clause", + "dependencies": { + "dotenv": "^16.4.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.249", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.249.tgz", + "integrity": "sha512-5vcfL3BBe++qZ5kuFhD/p8WOM1N9m3nwvJPULJx+4xf2usSlZFJ0qoNYO2fOX4hi3ocuDcmDobtA+5SFr4OmBg==", + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-editor": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/env-editor/-/env-editor-0.4.2.tgz", + "integrity": "sha512-ObFo8v4rQJAE59M69QzwloxPZtd33TpYEIjtKD1rrFDcM1Gd7IkDxEBU+HriziN6HSHQnBJi8Dmy+JWkav5HKA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "license": "MIT", + "dependencies": { + "stackframe": "^1.3.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-goat": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-4.0.0.tgz", + "integrity": "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "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/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true, + "license": "MIT" + }, + "node_modules/exec-async": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/exec-async/-/exec-async-2.2.0.tgz", + "integrity": "sha512-87OpwcEiMia/DeiKFzaQNBNFeN3XkkpYIh9FyOqq5mS2oKv3CBE67PXoEKcr6nodWdXNogTiQ0jE2NGuoffXPw==", + "license": "MIT" + }, + "node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/execa/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/expo": { + "version": "54.0.23", + "resolved": "https://registry.npmjs.org/expo/-/expo-54.0.23.tgz", + "integrity": "sha512-b4uQoiRwQ6nwqsT2709RS15CWYNGF3eJtyr1KyLw9WuMAK7u4jjofkhRiO0+3o1C2NbV+WooyYTOZGubQQMBaQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.20.0", + "@expo/cli": "54.0.16", + "@expo/config": "~12.0.10", + "@expo/config-plugins": "~54.0.2", + "@expo/devtools": "0.1.7", + "@expo/fingerprint": "0.15.3", + "@expo/metro": "~54.1.0", + "@expo/metro-config": "54.0.9", + "@expo/vector-icons": "^15.0.3", + "@ungap/structured-clone": "^1.3.0", + "babel-preset-expo": "~54.0.7", + "expo-asset": "~12.0.9", + "expo-constants": "~18.0.10", + "expo-file-system": "~19.0.17", + "expo-font": "~14.0.9", + "expo-keep-awake": "~15.0.7", + "expo-modules-autolinking": "3.0.21", + "expo-modules-core": "3.0.25", + "pretty-format": "^29.7.0", + "react-refresh": "^0.14.2", + "whatwg-url-without-unicode": "8.0.0-3" + }, + "bin": { + "expo": "bin/cli", + "expo-modules-autolinking": "bin/autolinking", + "fingerprint": "bin/fingerprint" + }, + "peerDependencies": { + "@expo/dom-webview": "*", + "@expo/metro-runtime": "*", + "react": "*", + "react-native": "*", + "react-native-webview": "*" + }, + "peerDependenciesMeta": { + "@expo/dom-webview": { + "optional": true + }, + "@expo/metro-runtime": { + "optional": true + }, + "react-native-webview": { + "optional": true + } + } + }, + "node_modules/expo-application": { + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/expo-application/-/expo-application-7.0.7.tgz", + "integrity": "sha512-Jt1/qqnoDUbZ+bK91+dHaZ1vrPDtRBOltRa681EeedkisqguuEeUx4UHqwVyDK2oHWsK6lO3ojetoA4h8OmNcg==", + "peerDependencies": { + "expo": "*" + } + }, + "node_modules/expo-auth-session": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/expo-auth-session/-/expo-auth-session-7.0.9.tgz", + "integrity": "sha512-mPSwaRWOJYas160lXi5P/7BkLy0xbh+er+IMmAYHqf2+iz2WWs9W/4lMAklQVJG2mCyOZi24XrkffvB2izCa1g==", + "dependencies": { + "expo-application": "~7.0.7", + "expo-constants": "~18.0.10", + "expo-crypto": "~15.0.7", + "expo-linking": "~8.0.9", + "expo-web-browser": "~15.0.9", + "invariant": "^2.2.4" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, + "node_modules/expo-constants": { + "version": "18.0.10", + "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-18.0.10.tgz", + "integrity": "sha512-Rhtv+X974k0Cahmvx6p7ER5+pNhBC0XbP1lRviL2J1Xl4sT2FBaIuIxF/0I0CbhOsySf0ksqc5caFweAy9Ewiw==", + "license": "MIT", + "dependencies": { + "@expo/config": "~12.0.10", + "@expo/env": "~2.0.7" + }, + "peerDependencies": { + "expo": "*", + "react-native": "*" + } + }, + "node_modules/expo-crypto": { + "version": "15.0.7", + "resolved": "https://registry.npmjs.org/expo-crypto/-/expo-crypto-15.0.7.tgz", + "integrity": "sha512-FUo41TwwGT2e5rA45PsjezI868Ch3M6wbCZsmqTWdF/hr+HyPcrp1L//dsh/hsrsyrQdpY/U96Lu71/wXePJeg==", + "dependencies": { + "base64-js": "^1.3.0" + }, + "peerDependencies": { + "expo": "*" + } + }, + "node_modules/expo-device": { + "version": "8.0.9", + "resolved": "https://registry.npmjs.org/expo-device/-/expo-device-8.0.9.tgz", + "integrity": "sha512-XqRpaljDNAYZGZzMpC+b9KZfzfydtkwx3pJAp6ODDH+O/5wjAw+mLc5wQMGJCx8/aqVmMsAokec7iebxDPFZDA==", + "license": "MIT", + "dependencies": { + "ua-parser-js": "^0.7.33" + }, + "peerDependencies": { + "expo": "*" + } + }, + "node_modules/expo-device/node_modules/ua-parser-js": { + "version": "0.7.41", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.41.tgz", + "integrity": "sha512-O3oYyCMPYgNNHuO7Jjk3uacJWZF8loBgwrfd/5LE/HyZ3lUIOdniQ7DNXJcIgZbwioZxk0fLfI4EVnetdiX5jg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + }, + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + } + ], + "license": "MIT", + "bin": { + "ua-parser-js": "script/cli.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/expo-font": { + "version": "14.0.9", + "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-14.0.9.tgz", + "integrity": "sha512-xCoQbR/36qqB6tew/LQ6GWICpaBmHLhg/Loix5Rku/0ZtNaXMJv08M9o1AcrdiGTn/Xf/BnLu6DgS45cWQEHZg==", + "license": "MIT", + "dependencies": { + "fontfaceobserver": "^2.1.0" + }, + "peerDependencies": { + "expo": "*", + "react": "*", + "react-native": "*" + } + }, + "node_modules/expo-glass-effect": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/expo-glass-effect/-/expo-glass-effect-0.1.6.tgz", + "integrity": "sha512-cwYJJJcvzwvl5XULEAVCrR5EPe/x/M/LOHq7wcsRR4ATuce2z/ZGGv/7cGcbOcHlWn30a3g3BIiuEynGCC47wg==", + "license": "MIT", + "peerDependencies": { + "expo": "*", + "react": "*", + "react-native": "*" + } + }, + "node_modules/expo-linking": { + "version": "8.0.9", + "resolved": "https://registry.npmjs.org/expo-linking/-/expo-linking-8.0.9.tgz", + "integrity": "sha512-a0UHhlVyfwIbn8b1PSFPoFiIDJeps2iEq109hVH3CHd0CMKuRxFfNio9Axe2BjXhiJCYWR4OV1iIyzY/GjiVkQ==", + "dependencies": { + "expo-constants": "~18.0.10", + "invariant": "^2.2.4" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, + "node_modules/expo-modules-autolinking": { + "version": "3.0.21", + "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-3.0.21.tgz", + "integrity": "sha512-pOtPDLln3Ju8DW1zRW4OwZ702YqZ8g+kM/tEY1sWfv22kWUtxkvK+ytRDRpRdnKEnC28okbhWqeMnmVkSFzP6Q==", + "license": "MIT", + "dependencies": { + "@expo/spawn-async": "^1.7.2", + "chalk": "^4.1.0", + "commander": "^7.2.0", + "require-from-string": "^2.0.2", + "resolve-from": "^5.0.0" + }, + "bin": { + "expo-modules-autolinking": "bin/expo-modules-autolinking.js" + } + }, + "node_modules/expo-modules-autolinking/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/expo-modules-autolinking/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/expo-modules-autolinking/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/expo-modules-autolinking/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/expo-modules-autolinking/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/expo-modules-autolinking/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/expo-modules-core": { + "version": "3.0.25", + "resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-3.0.25.tgz", + "integrity": "sha512-0P8PT8UV6c5/+p8zeVM/FXvBgn/ErtGcMaasqUgbzzBUg94ktbkIrij9t9reGCrir03BYt/Bcpv+EQtYC8JOug==", + "license": "MIT", + "dependencies": { + "invariant": "^2.2.4" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, + "node_modules/expo-router": { + "version": "6.0.14", + "resolved": "https://registry.npmjs.org/expo-router/-/expo-router-6.0.14.tgz", + "integrity": "sha512-vizLO4SgnMEL+PPs2dXr+etEOuksjue7yUQBCtfCEdqoDkQlB0r35zI7rS34Wt53sxKWSlM2p+038qQEpxtiFw==", + "license": "MIT", + "dependencies": { + "@expo/metro-runtime": "^6.1.2", + "@expo/schema-utils": "^0.1.7", + "@radix-ui/react-slot": "1.2.0", + "@radix-ui/react-tabs": "^1.1.12", + "@react-navigation/bottom-tabs": "^7.4.0", + "@react-navigation/native": "^7.1.8", + "@react-navigation/native-stack": "^7.3.16", + "client-only": "^0.0.1", + "debug": "^4.3.4", + "escape-string-regexp": "^4.0.0", + "expo-server": "^1.0.3", + "fast-deep-equal": "^3.1.3", + "invariant": "^2.2.4", + "nanoid": "^3.3.8", + "query-string": "^7.1.3", + "react-fast-compare": "^3.2.2", + "react-native-is-edge-to-edge": "^1.1.6", + "semver": "~7.6.3", + "server-only": "^0.0.1", + "sf-symbols-typescript": "^2.1.0", + "shallowequal": "^1.1.0", + "use-latest-callback": "^0.2.1", + "vaul": "^1.1.2" + }, + "peerDependencies": { + "@expo/metro-runtime": "^6.1.2", + "@react-navigation/drawer": "^7.5.0", + "@testing-library/react-native": ">= 12.0.0", + "expo": "*", + "expo-constants": "^18.0.10", + "expo-linking": "^8.0.8", + "react": "*", + "react-dom": "*", + "react-native": "*", + "react-native-gesture-handler": "*", + "react-native-reanimated": "*", + "react-native-safe-area-context": ">= 5.4.0", + "react-native-screens": "*", + "react-native-web": "*", + "react-server-dom-webpack": ">= 19.0.0" + }, + "peerDependenciesMeta": { + "@react-navigation/drawer": { + "optional": true + }, + "@testing-library/react-native": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "react-native-gesture-handler": { + "optional": true + }, + "react-native-reanimated": { + "optional": true + }, + "react-native-web": { + "optional": true + }, + "react-server-dom-webpack": { + "optional": true + } + } + }, + "node_modules/expo-router/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/expo-router/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/expo-secure-store": { + "version": "15.0.7", + "resolved": "https://registry.npmjs.org/expo-secure-store/-/expo-secure-store-15.0.7.tgz", + "integrity": "sha512-9q7+G1Zxr5P6J5NRIlm86KulvmYwc6UnQlYPjQLDu1drDnerz6AT6l884dPu29HgtDTn4rR0heYeeGFhMKM7/Q==", + "peerDependencies": { + "expo": "*" + } + }, + "node_modules/expo-server": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/expo-server/-/expo-server-1.0.4.tgz", + "integrity": "sha512-IN06r3oPxFh3plSXdvBL7dx0x6k+0/g0bgxJlNISs6qL5Z+gyPuWS750dpTzOeu37KyBG0RcyO9cXUKzjYgd4A==", + "license": "MIT", + "engines": { + "node": ">=20.16.0" + } + }, + "node_modules/expo-status-bar": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/expo-status-bar/-/expo-status-bar-3.0.8.tgz", + "integrity": "sha512-L248XKPhum7tvREoS1VfE0H6dPCaGtoUWzRsUv7hGKdiB4cus33Rc0sxkWkoQ77wE8stlnUlL5lvmT0oqZ3ZBw==", + "license": "MIT", + "dependencies": { + "react-native-is-edge-to-edge": "^1.2.1" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, + "node_modules/expo-web-browser": { + "version": "15.0.9", + "resolved": "https://registry.npmjs.org/expo-web-browser/-/expo-web-browser-15.0.9.tgz", + "integrity": "sha512-Dj8kNFO+oXsxqCDNlUT/GhOrJnm10kAElH++3RplLydogFm5jTzXYWDEeNIDmV+F+BzGYs+sIhxiBf7RyaxXZg==", + "peerDependencies": { + "expo": "*", + "react-native": "*" + } + }, + "node_modules/expo/node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/expo/node_modules/@expo/cli": { + "version": "54.0.16", + "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-54.0.16.tgz", + "integrity": "sha512-hY/OdRaJMs5WsVPuVSZ+RLH3VObJmL/pv5CGCHEZHN2PxZjSZSdctyKV8UcFBXTF0yIKNAJ9XLs1dlNYXHh4Cw==", + "license": "MIT", + "dependencies": { + "@0no-co/graphql.web": "^1.0.8", + "@expo/code-signing-certificates": "^0.0.5", + "@expo/config": "~12.0.10", + "@expo/config-plugins": "~54.0.2", + "@expo/devcert": "^1.1.2", + "@expo/env": "~2.0.7", + "@expo/image-utils": "^0.8.7", + "@expo/json-file": "^10.0.7", + "@expo/mcp-tunnel": "~0.1.0", + "@expo/metro": "~54.1.0", + "@expo/metro-config": "~54.0.9", + "@expo/osascript": "^2.3.7", + "@expo/package-manager": "^1.9.8", + "@expo/plist": "^0.4.7", + "@expo/prebuild-config": "^54.0.6", + "@expo/schema-utils": "^0.1.7", + "@expo/spawn-async": "^1.7.2", + "@expo/ws-tunnel": "^1.0.1", + "@expo/xcpretty": "^4.3.0", + "@react-native/dev-middleware": "0.81.5", + "@urql/core": "^5.0.6", + "@urql/exchange-retry": "^1.3.0", + "accepts": "^1.3.8", + "arg": "^5.0.2", + "better-opn": "~3.0.2", + "bplist-creator": "0.1.0", + "bplist-parser": "^0.3.1", + "chalk": "^4.0.0", + "ci-info": "^3.3.0", + "compression": "^1.7.4", + "connect": "^3.7.0", + "debug": "^4.3.4", + "env-editor": "^0.4.1", + "expo-server": "^1.0.4", + "freeport-async": "^2.0.0", + "getenv": "^2.0.0", + "glob": "^10.4.2", + "lan-network": "^0.1.6", + "minimatch": "^9.0.0", + "node-forge": "^1.3.1", + "npm-package-arg": "^11.0.0", + "ora": "^3.4.0", + "picomatch": "^3.0.1", + "pretty-bytes": "^5.6.0", + "pretty-format": "^29.7.0", + "progress": "^2.0.3", + "prompts": "^2.3.2", + "qrcode-terminal": "0.11.0", + "require-from-string": "^2.0.2", + "requireg": "^0.2.2", + "resolve": "^1.22.2", + "resolve-from": "^5.0.0", + "resolve.exports": "^2.0.3", + "semver": "^7.6.0", + "send": "^0.19.0", + "slugify": "^1.3.4", + "source-map-support": "~0.5.21", + "stacktrace-parser": "^0.1.10", + "structured-headers": "^0.4.1", + "tar": "^7.4.3", + "terminal-link": "^2.1.1", + "undici": "^6.18.2", + "wrap-ansi": "^7.0.0", + "ws": "^8.12.1" + }, + "bin": { + "expo-internal": "build/bin/cli" + }, + "peerDependencies": { + "expo": "*", + "expo-router": "*", + "react-native": "*" + }, + "peerDependenciesMeta": { + "expo-router": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/expo/node_modules/@expo/cli/node_modules/@expo/prebuild-config": { + "version": "54.0.6", + "resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-54.0.6.tgz", + "integrity": "sha512-xowuMmyPNy+WTNq+YX0m0EFO/Knc68swjThk4dKivgZa8zI1UjvFXOBIOp8RX4ljCXLzwxQJM5oBBTvyn+59ZA==", + "license": "MIT", + "dependencies": { + "@expo/config": "~12.0.10", + "@expo/config-plugins": "~54.0.2", + "@expo/config-types": "^54.0.8", + "@expo/image-utils": "^0.8.7", + "@expo/json-file": "^10.0.7", + "@react-native/normalize-colors": "0.81.5", + "debug": "^4.3.1", + "resolve-from": "^5.0.0", + "semver": "^7.6.0", + "xml2js": "0.6.0" + }, + "peerDependencies": { + "expo": "*" + } + }, + "node_modules/expo/node_modules/@expo/metro-config": { + "version": "54.0.9", + "resolved": "https://registry.npmjs.org/@expo/metro-config/-/metro-config-54.0.9.tgz", + "integrity": "sha512-CRI4WgFXrQ2Owyr8q0liEBJveUIF9DcYAKadMRsJV7NxGNBdrIIKzKvqreDfsGiRqivbLsw6UoNb3UE7/SvPfg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.20.0", + "@babel/core": "^7.20.0", + "@babel/generator": "^7.20.5", + "@expo/config": "~12.0.10", + "@expo/env": "~2.0.7", + "@expo/json-file": "~10.0.7", + "@expo/metro": "~54.1.0", + "@expo/spawn-async": "^1.7.2", + "browserslist": "^4.25.0", + "chalk": "^4.1.0", + "debug": "^4.3.2", + "dotenv": "~16.4.5", + "dotenv-expand": "~11.0.6", + "getenv": "^2.0.0", + "glob": "^10.4.2", + "hermes-parser": "^0.29.1", + "jsc-safe-url": "^0.2.4", + "lightningcss": "^1.30.1", + "minimatch": "^9.0.0", + "postcss": "~8.4.32", + "resolve-from": "^5.0.0" + }, + "peerDependencies": { + "expo": "*" + }, + "peerDependenciesMeta": { + "expo": { + "optional": true + } + } + }, + "node_modules/expo/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/expo/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/expo/node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/expo/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/expo/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/expo/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/expo/node_modules/expo-asset": { + "version": "12.0.9", + "resolved": "https://registry.npmjs.org/expo-asset/-/expo-asset-12.0.9.tgz", + "integrity": "sha512-vrdRoyhGhBmd0nJcssTSk1Ypx3Mbn/eXaaBCQVkL0MJ8IOZpAObAjfD5CTy8+8RofcHEQdh3wwZVCs7crvfOeg==", + "license": "MIT", + "dependencies": { + "@expo/image-utils": "^0.8.7", + "expo-constants": "~18.0.9" + }, + "peerDependencies": { + "expo": "*", + "react": "*", + "react-native": "*" + } + }, + "node_modules/expo/node_modules/expo-file-system": { + "version": "19.0.17", + "resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-19.0.17.tgz", + "integrity": "sha512-WwaS01SUFrxBnExn87pg0sCTJjZpf2KAOzfImG0o8yhkU7fbYpihpl/oocXBEsNbj58a8hVt1Y4CVV5c1tzu/g==", + "license": "MIT", + "peerDependencies": { + "expo": "*", + "react-native": "*" + } + }, + "node_modules/expo/node_modules/expo-keep-awake": { + "version": "15.0.7", + "resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-15.0.7.tgz", + "integrity": "sha512-CgBNcWVPnrIVII5G54QDqoE125l+zmqR4HR8q+MQaCfHet+dYpS5vX5zii/RMayzGN4jPgA4XYIQ28ePKFjHoA==", + "license": "MIT", + "peerDependencies": { + "expo": "*", + "react": "*" + } + }, + "node_modules/expo/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/expo/node_modules/hermes-estree": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.29.1.tgz", + "integrity": "sha512-jl+x31n4/w+wEqm0I2r4CMimukLbLQEYpisys5oCre611CI5fc9TxhqkBBCJ1edDG4Kza0f7CgNz8xVMLZQOmQ==", + "license": "MIT" + }, + "node_modules/expo/node_modules/hermes-parser": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.29.1.tgz", + "integrity": "sha512-xBHWmUtRC5e/UL0tI7Ivt2riA/YBq9+SiYFU7C1oBa/j2jYGlIF9043oak1F47ihuDIxQ5nbsKueYJDRY02UgA==", + "license": "MIT", + "dependencies": { + "hermes-estree": "0.29.1" + } + }, + "node_modules/expo/node_modules/picomatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz", + "integrity": "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/expo/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/expo/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/expo/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/expo/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/expo/node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/exponential-backoff": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", + "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==", + "license": "Apache-2.0" + }, + "node_modules/exsolve": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", + "integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "license": "MIT" + }, + "node_modules/fast-redact": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", + "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fbjs": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.5.tgz", + "integrity": "sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==", + "license": "MIT", + "dependencies": { + "cross-fetch": "^3.1.5", + "fbjs-css-vars": "^1.0.0", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^1.0.35" + } + }, + "node_modules/fbjs-css-vars": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz", + "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==", + "license": "MIT" + }, + "node_modules/fbjs/node_modules/promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "license": "MIT", + "dependencies": { + "asap": "~2.0.3" + } + }, + "node_modules/filesize": { + "version": "11.0.13", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-11.0.13.tgz", + "integrity": "sha512-mYJ/qXKvREuO0uH8LTQJ6v7GsUvVOguqxg2VTwQUkyTPXXRRWPdjuUPVqdBrJQhvci48OHlNGRnux+Slr2Rnvw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 10.8.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/filter-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", + "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/firefox-profile": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/firefox-profile/-/firefox-profile-4.7.0.tgz", + "integrity": "sha512-aGApEu5bfCNbA4PGUZiRJAIU6jKmghV2UVdklXAofnNtiDjqYw0czLS46W7IfFqVKgKhFB8Ao2YoNGHY4BoIMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "adm-zip": "~0.5.x", + "fs-extra": "^11.2.0", + "ini": "^4.1.3", + "minimist": "^1.2.8", + "xml2js": "^0.6.2" + }, + "bin": { + "firefox-profile": "lib/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/firefox-profile/node_modules/ini": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", + "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/firefox-profile/node_modules/xml2js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", + "dev": true, + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/firefox-profile/node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/flow-enums-runtime": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/flow-enums-runtime/-/flow-enums-runtime-0.0.6.tgz", + "integrity": "sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw==", + "license": "MIT" + }, + "node_modules/fontfaceobserver": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/fontfaceobserver/-/fontfaceobserver-2.3.0.tgz", + "integrity": "sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg==", + "license": "BSD-2-Clause" + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data-encoder": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-4.1.0.tgz", + "integrity": "sha512-G6NsmEW15s0Uw9XnCg+33H3ViYRyiM0hMrMhhqQOR8NFc5GhYrI+6I3u7OTw7b91J2g8rtvMBZJDbcGb2YUniw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/formdata-node": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-6.0.3.tgz", + "integrity": "sha512-8e1++BCiTzUno9v5IZ2J6bv4RU+3UKDmqWUQD0MIMVCd9AdhWkO1gw57oo1mNEX1dMq2EGI+FbWz4B92pscSQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/freeport-async": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/freeport-async/-/freeport-async-2.0.0.tgz", + "integrity": "sha512-K7od3Uw45AJg00XUmy15+Hae2hOcgKcmN3/EF6Y7i01O0gaqiRx8sUSpsb9+BRNL8RPBrhzPsVfy8q9ADlJuWQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "11.3.2", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.2.tgz", + "integrity": "sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/fx-runner": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/fx-runner/-/fx-runner-1.4.0.tgz", + "integrity": "sha512-rci1g6U0rdTg6bAaBboP7XdRu01dzTAaKXxFf+PUqGuCv6Xu7o8NZdY1D5MvKGIjb6EdS1g3VlXOgksir1uGkg==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "commander": "2.9.0", + "shell-quote": "1.7.3", + "spawn-sync": "1.0.15", + "when": "3.7.7", + "which": "1.2.4", + "winreg": "0.0.12" + }, + "bin": { + "fx-runner": "bin/fx-runner" + } + }, + "node_modules/fx-runner/node_modules/commander": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "integrity": "sha512-bmkUukX8wAOjHdN26xj5c4ctEV22TQ7dQYhSmuckKhToXrkUn0iIaolHdIxYYqD55nhpSPA9zPQ1yP57GdXP2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-readlink": ">= 1.0.0" + }, + "engines": { + "node": ">= 0.6.x" + } + }, + "node_modules/fx-runner/node_modules/isexe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-1.1.2.tgz", + "integrity": "sha512-d2eJzK691yZwPHcv1LbeAOa91yMJ9QmfTgSO1oXB65ezVhXQsxBac2vEB4bMVms9cGzaA99n6V2viHMq82VLDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fx-runner/node_modules/shell-quote": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", + "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fx-runner/node_modules/which": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/which/-/which-1.2.4.tgz", + "integrity": "sha512-zDRAqDSBudazdfM9zpiI30Fu9ve47htYXcGi3ln0wfKu2a7SmrT6F3VDoYONu//48V8Vz4TdCRNPjtvyRO3yBA==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-absolute": "^0.1.7", + "isexe": "^1.1.1" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", + "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-port-please": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/get-port-please/-/get-port-please-3.2.0.tgz", + "integrity": "sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A==", + "dev": true, + "license": "MIT" + }, + "node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/getenv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/getenv/-/getenv-2.0.0.tgz", + "integrity": "sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/giget": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", + "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.0", + "defu": "^6.1.4", + "node-fetch-native": "^1.6.6", + "nypm": "^0.6.0", + "pathe": "^2.0.3" + }, + "bin": { + "giget": "dist/cli.mjs" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/global-directory": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", + "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "4.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/global-directory/node_modules/ini": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", + "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/global-dirs": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", + "integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==", + "license": "MIT", + "dependencies": { + "ini": "^1.3.4" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha512-8tLu60LgxF6XpdbK8OW3FA+IfTNBn1ZHGHKF4KQbEeSkajYw5PlYJcKluntgegDPTg8UkHjpet1T82vk6TQ68w==", + "dev": true, + "license": "MIT" + }, + "node_modules/growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==", + "dev": true, + "license": "MIT" + }, + "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/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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/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/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/html-escaper": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", + "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/htmlparser2": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.0.0.tgz", + "integrity": "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.1", + "entities": "^6.0.0" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/husky": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", + "dev": true, + "license": "MIT", + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/hyphenate-style-name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.1.0.tgz", + "integrity": "sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==", + "license": "BSD-3-Clause" + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/image-size": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.2.1.tgz", + "integrity": "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==", + "license": "MIT", + "dependencies": { + "queue": "6.0.2" + }, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=16.x" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/inline-style-prefixer": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-7.0.1.tgz", + "integrity": "sha512-lhYo5qNTQp3EvSSp3sRvXMbVQTLrvGV6DycRMJ5dm2BLMiJ30wpXKdDdgX+GmJZ5uQMucwRKHamXSst3Sj/Giw==", + "license": "MIT", + "dependencies": { + "css-in-js-utils": "^3.1.0" + } + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/is-absolute": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.1.7.tgz", + "integrity": "sha512-Xi9/ZSn4NFapG8RP98iNPMOeaV3mXPisxKxzKtHVqr3g56j/fBn+yZmnxSVAA8lmZbl2J9b/a4kJvfU3hqQYgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-relative": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-in-ci": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-in-ci/-/is-in-ci-1.0.0.tgz", + "integrity": "sha512-eUuAjybVTHMYWm/U+vBO1sY/JOCgoPCXRxzdju0K+K0BiGW0SChEL1MLC0PoCIR1OlPo5YAp8HuQoUlsWEICwg==", + "dev": true, + "license": "MIT", + "bin": { + "is-in-ci": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container/node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-installed-globally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-1.0.0.tgz", + "integrity": "sha512-K55T22lfpQ63N4KEN57jZUAaAYqYHEe8veb/TycJRk9DdSCLLcovXz/mL6mOnhQaZsQGwPhuFopdQIlqGSEjiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "global-directory": "^4.0.1", + "is-path-inside": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-npm": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-6.1.0.tgz", + "integrity": "sha512-O2z4/kNgyjhQwVR1Wpkbfc19JIhggF97NZNCpWTnjH7kVcZMUrnut9XSN7txI7VdyIYk5ZatOq3zvSuWpU8hoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-4.0.0.tgz", + "integrity": "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-primitive": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-3.0.1.tgz", + "integrity": "sha512-GljRxhWvlCNRfZyORiH77FwdFwGcMO620o37EOYC0ORWdq+WYNVqW0w2Juzew4M+L81l6/QS3t5gkkihyRqv9w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-relative": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.1.3.tgz", + "integrity": "sha512-wBOr+rNM4gkAZqoLRJI4myw5WzzIdQosFAAbnvfXP5z1LyzgAI3ivOKehC5KfqlQJZoihVhirgtCBj378Eg8GA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util/node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/jest-message-util/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-message-util/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/jest-message-util/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-message-util/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-util/node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-util/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-util/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/jest-util/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-util/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-validate/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-validate/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-validate/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/jest-validate/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-validate/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jimp-compact": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/jimp-compact/-/jimp-compact-0.16.1.tgz", + "integrity": "sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww==", + "license": "MIT" + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "devOptional": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsc-safe-url": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/jsc-safe-url/-/jsc-safe-url-0.2.4.tgz", + "integrity": "sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q==", + "license": "0BSD" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz", + "integrity": "sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dev": true, + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ky": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/ky/-/ky-1.14.0.tgz", + "integrity": "sha512-Rczb6FMM6JT0lvrOlP5WUOCB7s9XKxzwgErzhKlKde1bEV90FXplV1o87fpt4PU/asJFiqjYJxAJyzJhcrxOsQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/ky?sponsor=1" + } + }, + "node_modules/lan-network": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/lan-network/-/lan-network-0.1.7.tgz", + "integrity": "sha512-mnIlAEMu4OyEvUNdzco9xpuB9YVcPkQec+QsgycBCtPZvEqWPCDPfbAE4OJMdBBWpZWtpCn1xw9jJYlwjWI5zQ==", + "license": "MIT", + "bin": { + "lan-network": "dist/lan-network-cli.js" + } + }, + "node_modules/latest-version": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-9.0.0.tgz", + "integrity": "sha512-7W0vV3rqv5tokqkBAFV1LbR7HPOWzXQDpDgEuib/aJ1jsZZx6x3c2mBI+TJhJzOhkGeaLbCKEHXEXLfirtG2JA==", + "dev": true, + "license": "MIT", + "dependencies": { + "package-json": "^10.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/lighthouse-logger": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-1.4.2.tgz", + "integrity": "sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g==", + "license": "Apache-2.0", + "dependencies": { + "debug": "^2.6.9", + "marky": "^1.2.2" + } + }, + "node_modules/lighthouse-logger/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/lighthouse-logger/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/lightningcss": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", + "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.30.2", + "lightningcss-darwin-arm64": "1.30.2", + "lightningcss-darwin-x64": "1.30.2", + "lightningcss-freebsd-x64": "1.30.2", + "lightningcss-linux-arm-gnueabihf": "1.30.2", + "lightningcss-linux-arm64-gnu": "1.30.2", + "lightningcss-linux-arm64-musl": "1.30.2", + "lightningcss-linux-x64-gnu": "1.30.2", + "lightningcss-linux-x64-musl": "1.30.2", + "lightningcss-win32-arm64-msvc": "1.30.2", + "lightningcss-win32-x64-msvc": "1.30.2" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz", + "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz", + "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==", + "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/lightningcss-darwin-x64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz", + "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==", + "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/lightningcss-freebsd-x64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz", + "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==", + "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/lightningcss-linux-arm-gnueabihf": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz", + "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==", + "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/lightningcss-linux-arm64-gnu": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz", + "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==", + "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/lightningcss-linux-arm64-musl": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz", + "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==", + "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/lightningcss-linux-x64-gnu": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz", + "integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==", + "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/lightningcss-linux-x64-musl": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz", + "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==", + "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/lightningcss-win32-arm64-msvc": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz", + "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==", + "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/lightningcss-win32-x64-msvc": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz", + "integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==", + "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/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/linkedom": { + "version": "0.18.12", + "resolved": "https://registry.npmjs.org/linkedom/-/linkedom-0.18.12.tgz", + "integrity": "sha512-jalJsOwIKuQJSeTvsgzPe9iJzyfVaEJiEXl+25EkKevsULHvMJzpNqwvj1jOESWdmgKDiXObyjOYwlUqG7wo1Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "css-select": "^5.1.0", + "cssom": "^0.5.0", + "html-escaper": "^3.0.3", + "htmlparser2": "^10.0.0", + "uhyphen": "^0.2.0" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "canvas": ">= 2" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/lint-staged": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.5.2.tgz", + "integrity": "sha512-YUSOLq9VeRNAo/CTaVmhGDKG+LBtA8KF1X4K5+ykMSwWST1vDxJRB2kv2COgLb1fvpCo+A/y9A0G0znNVmdx4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.4.1", + "commander": "^13.1.0", + "debug": "^4.4.0", + "execa": "^8.0.1", + "lilconfig": "^3.1.3", + "listr2": "^8.2.5", + "micromatch": "^4.0.8", + "pidtree": "^0.6.0", + "string-argv": "^0.3.2", + "yaml": "^2.7.0" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": ">=18.12.0" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/lint-staged/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/lint-staged/node_modules/commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/listr2": { + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.3.3.tgz", + "integrity": "sha512-LWzX2KsqcB1wqQ4AHgYb4RsDXauQiqhjLk+6hjbaeHG4zpjjVAB6wC/gz6X0l+Du1cN3pUB5ZlrvTbhGSNnUQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-truncate": "^4.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/listr2/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/listr2/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/listr2/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/local-pkg": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.2.tgz", + "integrity": "sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mlly": "^1.7.4", + "pkg-types": "^2.3.0", + "quansync": "^0.2.11" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "license": "MIT", + "dependencies": { + "chalk": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-escapes": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.2.0.tgz", + "integrity": "sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-update/node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", + "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", + "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/magicast": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", + "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.4", + "@babel/types": "^7.25.4", + "source-map-js": "^1.2.0" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/many-keys-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/many-keys-map/-/many-keys-map-2.0.1.tgz", + "integrity": "sha512-DHnZAD4phTbZ+qnJdjoNEVU1NecYoSdbOOoVmTDH46AuxDkEVh3MxTVpXq10GtcTC6mndN9dkv1rNfpjRcLnOw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/fregante" + } + }, + "node_modules/marky": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/marky/-/marky-1.3.0.tgz", + "integrity": "sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ==", + "license": "Apache-2.0" + }, + "node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "license": "CC0-1.0" + }, + "node_modules/memoize-one": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", + "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==", + "license": "MIT" + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/metro": { + "version": "0.83.2", + "resolved": "https://registry.npmjs.org/metro/-/metro-0.83.2.tgz", + "integrity": "sha512-HQgs9H1FyVbRptNSMy/ImchTTE5vS2MSqLoOo7hbDoBq6hPPZokwJvBMwrYSxdjQZmLXz2JFZtdvS+ZfgTc9yw==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.24.7", + "@babel/core": "^7.25.2", + "@babel/generator": "^7.25.0", + "@babel/parser": "^7.25.3", + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.3", + "@babel/types": "^7.25.2", + "accepts": "^1.3.7", + "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.32.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.83.2", + "metro-cache": "0.83.2", + "metro-cache-key": "0.83.2", + "metro-config": "0.83.2", + "metro-core": "0.83.2", + "metro-file-map": "0.83.2", + "metro-resolver": "0.83.2", + "metro-runtime": "0.83.2", + "metro-source-map": "0.83.2", + "metro-symbolicate": "0.83.2", + "metro-transform-plugins": "0.83.2", + "metro-transform-worker": "0.83.2", + "mime-types": "^2.1.27", + "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" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-babel-transformer": { + "version": "0.83.2", + "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.83.2.tgz", + "integrity": "sha512-rirY1QMFlA1uxH3ZiNauBninwTioOgwChnRdDcbB4tgRZ+bGX9DiXoh9QdpppiaVKXdJsII932OwWXGGV4+Nlw==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.25.2", + "flow-enums-runtime": "^0.0.6", + "hermes-parser": "0.32.0", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-cache": { + "version": "0.83.2", + "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.83.2.tgz", + "integrity": "sha512-Z43IodutUZeIS7OTH+yQFjc59QlFJ6s5OvM8p2AP9alr0+F8UKr8ADzFzoGKoHefZSKGa4bJx7MZJLF6GwPDHQ==", + "license": "MIT", + "dependencies": { + "exponential-backoff": "^3.1.1", + "flow-enums-runtime": "^0.0.6", + "https-proxy-agent": "^7.0.5", + "metro-core": "0.83.2" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-cache-key": { + "version": "0.83.2", + "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.83.2.tgz", + "integrity": "sha512-3EMG/GkGKYoTaf5RqguGLSWRqGTwO7NQ0qXKmNBjr0y6qD9s3VBXYlwB+MszGtmOKsqE9q3FPrE5Nd9Ipv7rZw==", + "license": "MIT", + "dependencies": { + "flow-enums-runtime": "^0.0.6" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-config": { + "version": "0.83.2", + "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.83.2.tgz", + "integrity": "sha512-1FjCcdBe3e3D08gSSiU9u3Vtxd7alGH3x/DNFqWDFf5NouX4kLgbVloDDClr1UrLz62c0fHh2Vfr9ecmrOZp+g==", + "license": "MIT", + "dependencies": { + "connect": "^3.6.5", + "flow-enums-runtime": "^0.0.6", + "jest-validate": "^29.7.0", + "metro": "0.83.2", + "metro-cache": "0.83.2", + "metro-core": "0.83.2", + "metro-runtime": "0.83.2", + "yaml": "^2.6.1" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-core": { + "version": "0.83.2", + "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.83.2.tgz", + "integrity": "sha512-8DRb0O82Br0IW77cNgKMLYWUkx48lWxUkvNUxVISyMkcNwE/9ywf1MYQUE88HaKwSrqne6kFgCSA/UWZoUT0Iw==", + "license": "MIT", + "dependencies": { + "flow-enums-runtime": "^0.0.6", + "lodash.throttle": "^4.1.1", + "metro-resolver": "0.83.2" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-file-map": { + "version": "0.83.2", + "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.83.2.tgz", + "integrity": "sha512-cMSWnEqZrp/dzZIEd7DEDdk72PXz6w5NOKriJoDN9p1TDQ5nAYrY2lHi8d6mwbcGLoSlWmpPyny9HZYFfPWcGQ==", + "license": "MIT", + "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" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-minify-terser": { + "version": "0.83.2", + "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.83.2.tgz", + "integrity": "sha512-zvIxnh7U0JQ7vT4quasKsijId3dOAWgq+ip2jF/8TMrPUqQabGrs04L2dd0haQJ+PA+d4VvK/bPOY8X/vL2PWw==", + "license": "MIT", + "dependencies": { + "flow-enums-runtime": "^0.0.6", + "terser": "^5.15.0" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-resolver": { + "version": "0.83.2", + "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.83.2.tgz", + "integrity": "sha512-Yf5mjyuiRE/Y+KvqfsZxrbHDA15NZxyfg8pIk0qg47LfAJhpMVEX+36e6ZRBq7KVBqy6VDX5Sq55iHGM4xSm7Q==", + "license": "MIT", + "dependencies": { + "flow-enums-runtime": "^0.0.6" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-runtime": { + "version": "0.83.2", + "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.83.2.tgz", + "integrity": "sha512-nnsPtgRvFbNKwemqs0FuyFDzXLl+ezuFsUXDbX8o0SXOfsOPijqiQrf3kuafO1Zx1aUWf4NOrKJMAQP5EEHg9A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.25.0", + "flow-enums-runtime": "^0.0.6" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-source-map": { + "version": "0.83.2", + "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.83.2.tgz", + "integrity": "sha512-5FL/6BSQvshIKjXOennt9upFngq2lFvDakZn5LfauIVq8+L4sxXewIlSTcxAtzbtjAIaXeOSVMtCJ5DdfCt9AA==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.3", + "@babel/traverse--for-generate-function-map": "npm:@babel/traverse@^7.25.3", + "@babel/types": "^7.25.2", + "flow-enums-runtime": "^0.0.6", + "invariant": "^2.2.4", + "metro-symbolicate": "0.83.2", + "nullthrows": "^1.1.1", + "ob1": "0.83.2", + "source-map": "^0.5.6", + "vlq": "^1.0.0" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-symbolicate": { + "version": "0.83.2", + "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.83.2.tgz", + "integrity": "sha512-KoU9BLwxxED6n33KYuQQuc5bXkIxF3fSwlc3ouxrrdLWwhu64muYZNQrukkWzhVKRNFIXW7X2iM8JXpi2heIPw==", + "license": "MIT", + "dependencies": { + "flow-enums-runtime": "^0.0.6", + "invariant": "^2.2.4", + "metro-source-map": "0.83.2", + "nullthrows": "^1.1.1", + "source-map": "^0.5.6", + "vlq": "^1.0.0" + }, + "bin": { + "metro-symbolicate": "src/index.js" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-transform-plugins": { + "version": "0.83.2", + "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.83.2.tgz", + "integrity": "sha512-5WlW25WKPkiJk2yA9d8bMuZrgW7vfA4f4MBb9ZeHbTB3eIAoNN8vS8NENgG/X/90vpTB06X66OBvxhT3nHwP6A==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.25.2", + "@babel/generator": "^7.25.0", + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.3", + "flow-enums-runtime": "^0.0.6", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-transform-worker": { + "version": "0.83.2", + "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.83.2.tgz", + "integrity": "sha512-G5DsIg+cMZ2KNfrdLnWMvtppb3+Rp1GMyj7Bvd9GgYc/8gRmvq1XVEF9XuO87Shhb03kFhGqMTgZerz3hZ1v4Q==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.25.2", + "@babel/generator": "^7.25.0", + "@babel/parser": "^7.25.3", + "@babel/types": "^7.25.2", + "flow-enums-runtime": "^0.0.6", + "metro": "0.83.2", + "metro-babel-transformer": "0.83.2", + "metro-cache": "0.83.2", + "metro-cache-key": "0.83.2", + "metro-minify-terser": "0.83.2", + "metro-source-map": "0.83.2", + "metro-transform-plugins": "0.83.2", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro/node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/metro/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/metro/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/metro/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/metro/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/metro/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/metro/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mlly": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz", + "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.15.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.1" + } + }, + "node_modules/mlly/node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/mlly/node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/multimatch": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-6.0.0.tgz", + "integrity": "sha512-I7tSVxHGPlmPN/enE3mS1aOSo6bWBfls+3HmuEeCUBCE7gWnm3cBXCBkpurzFjVRwC6Kld8lLaZ1Iv5vOcjvcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/minimatch": "^3.0.5", + "array-differ": "^4.0.0", + "array-union": "^3.0.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/multimatch/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/multimatch/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nano-spawn": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/nano-spawn/-/nano-spawn-1.0.3.tgz", + "integrity": "sha512-jtpsQDetTnvS2Ts1fiRdci5rx0VYws5jGyC+4IYOTnIQ/wwdf6JdomlHBwqC3bJYOvaKu0C2GSZ1A60anrYpaA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/nano-spawn?sponsor=1" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/nativewind": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/nativewind/-/nativewind-4.2.1.tgz", + "integrity": "sha512-10uUB2Dlli3MH3NDL5nMHqJHz1A3e/E6mzjTj6cl7hHECClJ7HpE6v+xZL+GXdbwQSnWE+UWMIMsNz7yOQkAJQ==", + "license": "MIT", + "dependencies": { + "comment-json": "^4.2.5", + "debug": "^4.3.7", + "react-native-css-interop": "0.2.1" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "tailwindcss": ">3.3.0" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/nested-error-stacks": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.0.1.tgz", + "integrity": "sha512-SrQrok4CATudVzBS7coSz26QRSmlK9TzzoFbeKfcPBUFPjcQM9Rqvr/DlJkOrwI/0KcgvMub1n1g5Jt9EgRn4A==", + "license": "MIT" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "license": "(BSD-3-Clause OR GPL-2.0)", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "license": "MIT" + }, + "node_modules/node-notifier": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-10.0.1.tgz", + "integrity": "sha512-YX7TSyDukOZ0g+gmzjB6abKu+hTGvO8+8+gIFDsRCU2t8fLV/P2unmt+LGFaIa4y64aX98Qksa97rgz4vMNeLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "growly": "^1.3.0", + "is-wsl": "^2.2.0", + "semver": "^7.3.5", + "shellwords": "^0.1.1", + "uuid": "^8.3.2", + "which": "^2.0.2" + } + }, + "node_modules/node-notifier/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-package-arg": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.3.tgz", + "integrity": "sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw==", + "license": "ISC", + "dependencies": { + "hosted-git-info": "^7.0.0", + "proc-log": "^4.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/nullthrows": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", + "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==", + "license": "MIT" + }, + "node_modules/nypm": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.2.tgz", + "integrity": "sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g==", + "dev": true, + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.2", + "pathe": "^2.0.3", + "pkg-types": "^2.3.0", + "tinyexec": "^1.0.1" + }, + "bin": { + "nypm": "dist/cli.mjs" + }, + "engines": { + "node": "^14.16.0 || >=16.10.0" + } + }, + "node_modules/ob1": { + "version": "0.83.2", + "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.83.2.tgz", + "integrity": "sha512-XlK3w4M+dwd1g1gvHzVbxiXEbUllRONEgcF2uEO0zm4nxa0eKlh41c6N65q1xbiDOeKKda1tvNOAD33fNjyvCg==", + "license": "MIT", + "dependencies": { + "flow-enums-runtime": "^0.0.6" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/ofetch": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.5.1.tgz", + "integrity": "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "destr": "^2.0.5", + "node-fetch-native": "^1.6.7", + "ufo": "^1.6.1" + } + }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-3.4.0.tgz", + "integrity": "sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg==", + "license": "MIT", + "dependencies": { + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-spinners": "^2.0.0", + "log-symbols": "^2.2.0", + "strip-ansi": "^5.2.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/ora/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ora/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/os-shim": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/os-shim/-/os-shim-0.1.3.tgz", + "integrity": "sha512-jd0cvB8qQ5uVt0lvCIexBaROw1KyKm5sbulg2fWOHjETisuCzWyt+eTZKEMs8v6HwzoGs8xik26jg7eCM6pS+A==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-10.0.1.tgz", + "integrity": "sha512-ua1L4OgXSBdsu1FPb7F3tYH0F48a6kxvod4pLUlGY9COeJAJQNX/sNH2IiEmsxw7lqYiAwrdHMjz1FctOsyDQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ky": "^1.2.0", + "registry-auth-token": "^5.0.2", + "registry-url": "^6.0.1", + "semver": "^7.6.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true, + "license": "(MIT AND Zlib)" + }, + "node_modules/parse-json": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-7.1.1.tgz", + "integrity": "sha512-SgOTCX/EZXtZxBE5eJ97P4yGM5n37BwRU+YMsH4vNzFqJV/oWFXXCmwFlgWUM4PrakybVOueJJ6pwHqSVhTFDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.21.4", + "error-ex": "^1.3.2", + "json-parse-even-better-errors": "^3.0.0", + "lines-and-columns": "^2.0.3", + "type-fest": "^3.8.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-json/node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/parse-json/node_modules/lines-and-columns": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.4.tgz", + "integrity": "sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/parse-json/node_modules/type-fest": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", + "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-png": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/parse-png/-/parse-png-2.1.0.tgz", + "integrity": "sha512-Nt/a5SfCLiTnQAjx3fHlqp8hRgTL3z7kTQZzvIMS9uCAepnCyjpdEc6M/sz69WqMBdaDBw9sF1F1UaHROYzGkQ==", + "license": "MIT", + "dependencies": { + "pngjs": "^3.3.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/perfect-debounce": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.0.0.tgz", + "integrity": "sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, + "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pino": { + "version": "9.7.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-9.7.0.tgz", + "integrity": "sha512-vnMCM6xZTb1WDmLvtG2lE/2p+t9hDEIvTWJsu6FejkE62vB7gDhvzrpFR4Cw2to+9JNQxVnkAKVPA1KPB98vWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0", + "fast-redact": "^3.1.1", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^5.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^3.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", + "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "split2": "^4.0.0" + } + }, + "node_modules/pino-std-serializers": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz", + "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-types": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", + "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.2.2", + "exsolve": "^1.0.7", + "pathe": "^2.0.3" + } + }, + "node_modules/plist": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", + "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", + "license": "MIT", + "dependencies": { + "@xmldom/xmldom": "^0.8.8", + "base64-js": "^1.5.1", + "xmlbuilder": "^15.1.1" + }, + "engines": { + "node": ">=10.4.0" + } + }, + "node_modules/pngjs": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", + "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss": { + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "license": "MIT" + }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-plugin-tailwindcss": { + "version": "0.5.14", + "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.5.14.tgz", + "integrity": "sha512-Puaz+wPUAhFp8Lo9HuciYKM2Y2XExESjeT+9NQoVFXZsPPnc9VYss2SpxdQ6vbatmt8/4+SN0oe0I1cPDABg9Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.21.3" + }, + "peerDependencies": { + "@ianvs/prettier-plugin-sort-imports": "*", + "@prettier/plugin-pug": "*", + "@shopify/prettier-plugin-liquid": "*", + "@trivago/prettier-plugin-sort-imports": "*", + "@zackad/prettier-plugin-twig-melody": "*", + "prettier": "^3.0", + "prettier-plugin-astro": "*", + "prettier-plugin-css-order": "*", + "prettier-plugin-import-sort": "*", + "prettier-plugin-jsdoc": "*", + "prettier-plugin-marko": "*", + "prettier-plugin-organize-attributes": "*", + "prettier-plugin-organize-imports": "*", + "prettier-plugin-sort-imports": "*", + "prettier-plugin-style-order": "*", + "prettier-plugin-svelte": "*" + }, + "peerDependenciesMeta": { + "@ianvs/prettier-plugin-sort-imports": { + "optional": true + }, + "@prettier/plugin-pug": { + "optional": true + }, + "@shopify/prettier-plugin-liquid": { + "optional": true + }, + "@trivago/prettier-plugin-sort-imports": { + "optional": true + }, + "@zackad/prettier-plugin-twig-melody": { + "optional": true + }, + "prettier-plugin-astro": { + "optional": true + }, + "prettier-plugin-css-order": { + "optional": true + }, + "prettier-plugin-import-sort": { + "optional": true + }, + "prettier-plugin-jsdoc": { + "optional": true + }, + "prettier-plugin-marko": { + "optional": true + }, + "prettier-plugin-organize-attributes": { + "optional": true + }, + "prettier-plugin-organize-imports": { + "optional": true + }, + "prettier-plugin-sort-imports": { + "optional": true + }, + "prettier-plugin-style-order": { + "optional": true + }, + "prettier-plugin-svelte": { + "optional": true + } + } + }, + "node_modules/pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/proc-log": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", + "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/process-warning": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz", + "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/promise": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", + "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", + "license": "MIT", + "dependencies": { + "asap": "~2.0.6" + } + }, + "node_modules/promise-toolbox": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/promise-toolbox/-/promise-toolbox-0.21.0.tgz", + "integrity": "sha512-NV8aTmpwrZv+Iys54sSFOBx3tuVaOBvvrft5PNppnxy9xpU/akHbaWIril22AB22zaPgrgwKdD0KsrM0ptUtpg==", + "dev": true, + "license": "ISC", + "dependencies": { + "make-error": "^1.3.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "dev": true, + "license": "ISC" + }, + "node_modules/publish-browser-extension": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/publish-browser-extension/-/publish-browser-extension-3.0.3.tgz", + "integrity": "sha512-cBINZCkLo7YQaGoUvEHthZ0sDzgJQht28IS+SFMT2omSNhGsPiVNRkWir3qLiTrhGhW9Ci2KVHpA1QAMoBdL2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "consola": "^3.4.2", + "dotenv": "^17.2.3", + "form-data-encoder": "^4.1.0", + "formdata-node": "^6.0.3", + "listr2": "^8.3.3", + "ofetch": "^1.4.1", + "zod": "^3.25.76 || ^4.0.0" + }, + "bin": { + "publish-extension": "bin/publish-extension.cjs" + } + }, + "node_modules/publish-browser-extension/node_modules/dotenv": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pupa": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-3.3.0.tgz", + "integrity": "sha512-LjgDO2zPtoXP2wJpDjZrGdojii1uqO0cnwKoIoUzkfS98HDmbeiGmYiXo3lXeFlq2xvne1QFQhwYXSUCLKtEuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-goat": "^4.0.0" + }, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/qrcode-terminal": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.11.0.tgz", + "integrity": "sha512-Uu7ii+FQy4Qf82G4xu7ShHhjhGahEpCWc3x8UavY3CTcWV+ufmmCtwkr7ZKsX42jdL0kr1B5FKUeqJvAn51jzQ==", + "bin": { + "qrcode-terminal": "bin/qrcode-terminal.js" + } + }, + "node_modules/quansync": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz", + "integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, + "node_modules/query-string": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", + "integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==", + "license": "MIT", + "dependencies": { + "decode-uri-component": "^0.2.2", + "filter-obj": "^1.1.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/queue": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", + "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", + "license": "MIT", + "dependencies": { + "inherits": "~2.0.3" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", + "dev": true, + "license": "MIT" + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc9": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", + "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "defu": "^6.1.4", + "destr": "^2.0.3" + } + }, + "node_modules/react": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", + "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-devtools-core": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-6.1.5.tgz", + "integrity": "sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==", + "license": "MIT", + "dependencies": { + "shell-quote": "^1.6.1", + "ws": "^7" + } + }, + "node_modules/react-dom": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", + "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.26.0" + }, + "peerDependencies": { + "react": "^19.1.0" + } + }, + "node_modules/react-fast-compare": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", + "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==", + "license": "MIT" + }, + "node_modules/react-freeze": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/react-freeze/-/react-freeze-1.0.4.tgz", + "integrity": "sha512-r4F0Sec0BLxWicc7HEyo2x3/2icUTrRmDjaaRyzzn+7aDyFZliszMDOgLVwSnQnYENOlL1o569Ze2HZefk8clA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">=17.0.0" + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" + }, + "node_modules/react-native": { + "version": "0.81.5", + "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.81.5.tgz", + "integrity": "sha512-1w+/oSjEXZjMqsIvmkCRsOc8UBYv163bTWKTI8+1mxztvQPhCRYGTvZ/PL1w16xXHneIj/SLGfxWg2GWN2uexw==", + "license": "MIT", + "dependencies": { + "@jest/create-cache-key-function": "^29.7.0", + "@react-native/assets-registry": "0.81.5", + "@react-native/codegen": "0.81.5", + "@react-native/community-cli-plugin": "0.81.5", + "@react-native/gradle-plugin": "0.81.5", + "@react-native/js-polyfills": "0.81.5", + "@react-native/normalize-colors": "0.81.5", + "@react-native/virtualized-lists": "0.81.5", + "abort-controller": "^3.0.0", + "anser": "^1.4.9", + "ansi-regex": "^5.0.0", + "babel-jest": "^29.7.0", + "babel-plugin-syntax-hermes-parser": "0.29.1", + "base64-js": "^1.5.1", + "commander": "^12.0.0", + "flow-enums-runtime": "^0.0.6", + "glob": "^7.1.1", + "invariant": "^2.2.4", + "jest-environment-node": "^29.7.0", + "memoize-one": "^5.0.0", + "metro-runtime": "^0.83.1", + "metro-source-map": "^0.83.1", + "nullthrows": "^1.1.1", + "pretty-format": "^29.7.0", + "promise": "^8.3.0", + "react-devtools-core": "^6.1.5", + "react-refresh": "^0.14.0", + "regenerator-runtime": "^0.13.2", + "scheduler": "0.26.0", + "semver": "^7.1.3", + "stacktrace-parser": "^0.1.10", + "whatwg-fetch": "^3.0.0", + "ws": "^6.2.3", + "yargs": "^17.6.2" + }, + "bin": { + "react-native": "cli.js" + }, + "engines": { + "node": ">= 20.19.4" + }, + "peerDependencies": { + "@types/react": "^19.1.0", + "react": "^19.1.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-native-context-menu-view": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/react-native-context-menu-view/-/react-native-context-menu-view-1.20.0.tgz", + "integrity": "sha512-g1EYZiPaJWjeq7GWeL9TaYSnKmL9/hiDE7Arvj8r8fLip/l+BvS+BRtblX9qk9VpQdDWyd6L6A4yL2sz6+ohQw==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.1 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-native": ">=0.60.0-rc.0 <1.0.x" + } + }, + "node_modules/react-native-css-interop": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/react-native-css-interop/-/react-native-css-interop-0.2.1.tgz", + "integrity": "sha512-B88f5rIymJXmy1sNC/MhTkb3xxBej1KkuAt7TiT9iM7oXz3RM8Bn+7GUrfR02TvSgKm4cg2XiSuLEKYfKwNsjA==", + "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-gesture-handler": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.29.1.tgz", + "integrity": "sha512-du3qmv0e3Sm7qsd9SfmHps+AggLiylcBBQ8ztz7WUtd8ZjKs5V3kekAbi9R2W9bRLSg47Ntp4GGMYZOhikQdZA==", + "license": "MIT", + "dependencies": { + "@egjs/hammerjs": "^2.0.17", + "hoist-non-react-statics": "^3.3.0", + "invariant": "^2.2.4" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, + "node_modules/react-native-is-edge-to-edge": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.2.1.tgz", + "integrity": "sha512-FLbPWl/MyYQWz+KwqOZsSyj2JmLKglHatd3xLZWskXOpRaio4LfEDEz8E/A6uD8QoTHW6Aobw1jbEwK7KMgR7Q==", + "license": "MIT", + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, + "node_modules/react-native-reanimated": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.17.5.tgz", + "integrity": "sha512-SxBK7wQfJ4UoWoJqQnmIC7ZjuNgVb9rcY5Xc67upXAFKftWg0rnkknTw6vgwnjRcvYThrjzUVti66XoZdDJGtw==", + "license": "MIT", + "dependencies": { + "@babel/plugin-transform-arrow-functions": "^7.0.0-0", + "@babel/plugin-transform-class-properties": "^7.0.0-0", + "@babel/plugin-transform-classes": "^7.0.0-0", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.0.0-0", + "@babel/plugin-transform-optional-chaining": "^7.0.0-0", + "@babel/plugin-transform-shorthand-properties": "^7.0.0-0", + "@babel/plugin-transform-template-literals": "^7.0.0-0", + "@babel/plugin-transform-unicode-regex": "^7.0.0-0", + "@babel/preset-typescript": "^7.16.7", + "convert-source-map": "^2.0.0", + "invariant": "^2.2.4", + "react-native-is-edge-to-edge": "1.1.7" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0", + "react": "*", + "react-native": "*" + } + }, + "node_modules/react-native-reanimated/node_modules/react-native-is-edge-to-edge": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.1.7.tgz", + "integrity": "sha512-EH6i7E8epJGIcu7KpfXYXiV2JFIYITtq+rVS8uEb+92naMRBdxhTuS8Wn2Q7j9sqyO0B+Xbaaf9VdipIAmGW4w==", + "license": "MIT", + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, + "node_modules/react-native-safe-area-context": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-5.4.0.tgz", + "integrity": "sha512-JaEThVyJcLhA+vU0NU8bZ0a1ih6GiF4faZ+ArZLqpYbL6j7R3caRqj+mE3lEtKCuHgwjLg3bCxLL1GPUJZVqUA==", + "license": "MIT", + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, + "node_modules/react-native-screens": { + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.16.0.tgz", + "integrity": "sha512-yIAyh7F/9uWkOzCi1/2FqvNvK6Wb9Y1+Kzn16SuGfN9YFJDTbwlzGRvePCNTOX0recpLQF3kc2FmvMUhyTCH1Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "react-freeze": "^1.0.0", + "react-native-is-edge-to-edge": "^1.2.1", + "warn-once": "^0.1.0" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, + "node_modules/react-native-svg": { + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.15.0.tgz", + "integrity": "sha512-/Wx6F/IZ88B/GcF88bK8K7ZseJDYt+7WGaiggyzLvTowChQ8BM5idmcd4pK+6QJP6a6DmzL2sfOMukFUn/NArg==", + "license": "MIT", + "dependencies": { + "css-select": "^5.1.0", + "css-tree": "^1.1.3", + "warn-once": "0.1.1" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, + "node_modules/react-native-web": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.21.2.tgz", + "integrity": "sha512-SO2t9/17zM4iEnFvlu2DA9jqNbzNhoUP+AItkoCOyFmDMOhUnBBznBDCYN92fGdfAkfQlWzPoez6+zLxFNsZEg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.6", + "@react-native/normalize-colors": "^0.74.1", + "fbjs": "^3.0.4", + "inline-style-prefixer": "^7.0.1", + "memoize-one": "^6.0.0", + "nullthrows": "^1.1.1", + "postcss-value-parser": "^4.2.0", + "styleq": "^0.1.3" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + } + }, + "node_modules/react-native-web/node_modules/@react-native/normalize-colors": { + "version": "0.74.89", + "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.74.89.tgz", + "integrity": "sha512-qoMMXddVKVhZ8PA1AbUCk83trpd6N+1nF2A6k1i6LsQObyS92fELuk8kU/lQs6M7BsMHwqyLCpQJ1uFgNvIQXg==", + "license": "MIT" + }, + "node_modules/react-native-web/node_modules/memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", + "license": "MIT" + }, + "node_modules/react-native-worklets": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/react-native-worklets/-/react-native-worklets-0.6.1.tgz", + "integrity": "sha512-URca8l7c7Uog7gv4mcg9KILdJlnbvwdS5yfXQYf5TDkD2W1VY1sduEKrD+sA3lUPXH/TG1vmXAvNxCNwPMYgGg==", + "license": "MIT", + "dependencies": { + "@babel/plugin-transform-arrow-functions": "^7.0.0-0", + "@babel/plugin-transform-class-properties": "^7.0.0-0", + "@babel/plugin-transform-classes": "^7.0.0-0", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.0.0-0", + "@babel/plugin-transform-optional-chaining": "^7.0.0-0", + "@babel/plugin-transform-shorthand-properties": "^7.0.0-0", + "@babel/plugin-transform-template-literals": "^7.0.0-0", + "@babel/plugin-transform-unicode-regex": "^7.0.0-0", + "@babel/preset-typescript": "^7.16.7", + "convert-source-map": "^2.0.0", + "semver": "7.7.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0", + "react": "*", + "react-native": "*" + } + }, + "node_modules/react-native-worklets/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/react-native/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/react-native/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/react-native/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "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": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/react-native/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/react-native/node_modules/ws": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz", + "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", + "license": "MIT", + "dependencies": { + "async-limiter": "~1.0.0" + } + }, + "node_modules/react-refresh": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-remove-scroll": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz", + "integrity": "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==", + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "license": "MIT", + "dependencies": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "license": "MIT" + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz", + "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==", + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "license": "MIT" + }, + "node_modules/regexpu-core": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz", + "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==", + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.2.2", + "regjsgen": "^0.8.0", + "regjsparser": "^0.13.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.2.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/registry-auth-token": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.1.0.tgz", + "integrity": "sha512-GdekYuwLXLxMuFTwAPg5UKGLW/UXzQrZvH/Zj791BQif5T05T0RsaLfHc9q3ZOKi7n+BoprPD9mJ0O0k4xzUlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pnpm/npm-conf": "^2.1.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/registry-url": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-6.0.1.tgz", + "integrity": "sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "rc": "1.2.8" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "license": "MIT" + }, + "node_modules/regjsparser": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.0.tgz", + "integrity": "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==", + "license": "BSD-2-Clause", + "dependencies": { + "jsesc": "~3.1.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requireg": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/requireg/-/requireg-0.2.2.tgz", + "integrity": "sha512-nYzyjnFcPNGR3lx9lwPPPnuQxv6JWEZd2Ci0u9opN7N5zUEPIhY/GbL3vMGOr2UXwEg9WwSyV9X9Y/kLFgPsOg==", + "dependencies": { + "nested-error-stacks": "~2.0.1", + "rc": "~1.2.7", + "resolve": "~1.7.1" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/requireg/node_modules/resolve": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz", + "integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==", + "license": "MIT", + "dependencies": { + "path-parse": "^1.0.5" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-global": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-global/-/resolve-global-1.0.0.tgz", + "integrity": "sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==", + "license": "MIT", + "dependencies": { + "global-dirs": "^0.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-workspace-root": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-workspace-root/-/resolve-workspace-root-2.0.0.tgz", + "integrity": "sha512-IsaBUZETJD5WsI11Wt8PKHwaIe45or6pwNc8yflvLJ4DWtImK9kuLoH5kUva/2Mmx/RdIyr4aONNSa2v9LTJsw==", + "license": "MIT" + }, + "node_modules/resolve.exports": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", + "license": "MIT", + "dependencies": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "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": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/rollup": { + "version": "4.53.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.1.tgz", + "integrity": "sha512-n2I0V0lN3E9cxxMqBCT3opWOiQBzRN7UG60z/WDKqdX2zHUS/39lezBcsckZFsV6fUTSnfqI7kHf60jDAPGKug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.53.1", + "@rollup/rollup-android-arm64": "4.53.1", + "@rollup/rollup-darwin-arm64": "4.53.1", + "@rollup/rollup-darwin-x64": "4.53.1", + "@rollup/rollup-freebsd-arm64": "4.53.1", + "@rollup/rollup-freebsd-x64": "4.53.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.1", + "@rollup/rollup-linux-arm-musleabihf": "4.53.1", + "@rollup/rollup-linux-arm64-gnu": "4.53.1", + "@rollup/rollup-linux-arm64-musl": "4.53.1", + "@rollup/rollup-linux-loong64-gnu": "4.53.1", + "@rollup/rollup-linux-ppc64-gnu": "4.53.1", + "@rollup/rollup-linux-riscv64-gnu": "4.53.1", + "@rollup/rollup-linux-riscv64-musl": "4.53.1", + "@rollup/rollup-linux-s390x-gnu": "4.53.1", + "@rollup/rollup-linux-x64-gnu": "4.53.1", + "@rollup/rollup-linux-x64-musl": "4.53.1", + "@rollup/rollup-openharmony-arm64": "4.53.1", + "@rollup/rollup-win32-arm64-msvc": "4.53.1", + "@rollup/rollup-win32-ia32-msvc": "4.53.1", + "@rollup/rollup-win32-x64-gnu": "4.53.1", + "@rollup/rollup-win32-x64-msvc": "4.53.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/sax": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.3.tgz", + "integrity": "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==", + "license": "BlueOak-1.0.0" + }, + "node_modules/scheduler": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "license": "MIT" + }, + "node_modules/scule": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/scule/-/scule-1.3.0.tgz", + "integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==", + "dev": true, + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/send/node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/serialize-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz", + "integrity": "sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-static/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/server-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/server-only/-/server-only-0.0.1.tgz", + "integrity": "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==", + "license": "MIT" + }, + "node_modules/set-value": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-4.1.0.tgz", + "integrity": "sha512-zTEg4HL0RwVrqcWs3ztF+x1vkxfm0lP+MQQFPiMJTKVceBwEV0A569Ou8l9IYQG8jOZdMVI1hGsc0tmeD2o/Lw==", + "dev": true, + "funding": [ + "https://github.com/sponsors/jonschlinkert", + "https://paypal.me/jonathanschlinkert", + "https://jonschlinkert.dev/sponsor" + ], + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4", + "is-primitive": "^3.0.1" + }, + "engines": { + "node": ">=11.0" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/sf-symbols-typescript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/sf-symbols-typescript/-/sf-symbols-typescript-2.1.0.tgz", + "integrity": "sha512-ezT7gu/SHTPIOEEoG6TF+O0m5eewl0ZDAO4AtdBi5HjsrUI6JdCG17+Q8+aKp0heM06wZKApRCn5olNbs0Wb/A==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "dev": true, + "license": "MIT" + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-plist": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/simple-plist/-/simple-plist-1.3.1.tgz", + "integrity": "sha512-iMSw5i0XseMnrhtIzRb7XpQEXepa9xhWxGUojHBL43SIpQuDQkh3Wpy67ZbDzZVr6EKxvwVChnVpdl8hEVLDiw==", + "license": "MIT", + "dependencies": { + "bplist-creator": "0.1.0", + "bplist-parser": "0.3.1", + "plist": "^3.0.5" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", + "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", + "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", + "license": "MIT" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "license": "MIT" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/slugify": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz", + "integrity": "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/sonic-boom": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", + "integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==", + "dev": true, + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spawn-sync": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/spawn-sync/-/spawn-sync-1.0.15.tgz", + "integrity": "sha512-9DWBgrgYZzNghseho0JOuh+5fg9u6QWhAWa51QC7+U5rCheZ/j1DrEZnyE0RBBRqZ9uEXGPgSSM0nky6burpVw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "concat-stream": "^1.4.7", + "os-shim": "^0.1.2" + } + }, + "node_modules/split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "license": "BSD-3-Clause" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", + "license": "MIT" + }, + "node_modules/stacktrace-parser": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.11.tgz", + "integrity": "sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==", + "license": "MIT", + "dependencies": { + "type-fest": "^0.7.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/stdin-discarder": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stream-buffers": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-2.2.0.tgz", + "integrity": "sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==", + "license": "Unlicense", + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/strip-bom": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-5.0.0.tgz", + "integrity": "sha512-p+byADHF7SzEcVnLvc/r3uognM1hUhObuHXxJcgLCfD194XAkaLbjq3Wzb0N5G2tgIjH0dgT708Z51QxMeu60A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-literal": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", + "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/strip-literal/node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/structured-headers": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/structured-headers/-/structured-headers-0.4.1.tgz", + "integrity": "sha512-0MP/Cxx5SzeeZ10p/bZI0S6MpgD+yxAhi1BOQ34jgnMXsCq3j1t6tQnZu+KdlL7dvJTLT3g9xN8tl10TqgFMcg==", + "license": "MIT" + }, + "node_modules/stubborn-fs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/stubborn-fs/-/stubborn-fs-2.0.0.tgz", + "integrity": "sha512-Y0AvSwDw8y+nlSNFXMm2g6L51rBGdAQT20J3YSOqxC53Lo3bjWRtr2BKcfYoAf352WYpsZSTURrA0tqhfgudPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "stubborn-utils": "^1.0.1" + } + }, + "node_modules/stubborn-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stubborn-utils/-/stubborn-utils-1.0.2.tgz", + "integrity": "sha512-zOh9jPYI+xrNOyisSelgym4tolKTJCQd5GBhK0+0xJvcYDcwlOoxF/rnFKQ2KRZknXSG9jWAp66fwP6AxN9STg==", + "dev": true, + "license": "MIT" + }, + "node_modules/styleq": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/styleq/-/styleq-0.1.3.tgz", + "integrity": "sha512-3ZUifmCDCQanjeej1f6kyl/BeP/Vae5EYkQ9iJfUm/QwZvlgnZzyflqAsAWYURdtea8Vkvswu2GrC57h3qffcA==", + "license": "MIT" + }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "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/supports-hyperlinks": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", + "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.18", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.18.tgz", + "integrity": "sha512-6A2rnmW5xZMdw11LYjhcI5846rt9pbLSabY5XPxo+XWdxwZaFEn47Go4NzFiHu9sNNmr/kXivP1vStfvMaK1GQ==", + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.7", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/tailwindcss/node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/tailwindcss/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/tailwindcss/node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/tailwindcss/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/tar": { + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.2.tgz", + "integrity": "sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/temp-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "license": "MIT", + "dependencies": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terser": { + "version": "5.44.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.44.1.tgz", + "integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==", + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT" + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "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": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/thread-stream": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", + "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==", + "dev": true, + "license": "MIT", + "dependencies": { + "real-require": "^0.2.0" + } + }, + "node_modules/throat": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", + "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", + "license": "MIT" + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tmp": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "license": "BSD-3-Clause" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "license": "Apache-2.0" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", + "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ua-parser-js": { + "version": "1.0.41", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.41.tgz", + "integrity": "sha512-LbBDqdIC5s8iROCUjMbW1f5dJQTEFB1+KO9ogbvlb3nm9n4YHa5p4KTvFPWvh2Hs8gZMBuiB1/8+pdfe/tDPug==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + }, + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + } + ], + "license": "MIT", + "bin": { + "ua-parser-js": "script/cli.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ufo": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", + "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/uhyphen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/uhyphen/-/uhyphen-0.2.0.tgz", + "integrity": "sha512-qz3o9CHXmJJPGBdqzab7qAYuW8kQGKNEuoHFYrBwV6hWIMcpAmxDLXojcHfFr9US1Pe6zUswEIJIbLI610fuqA==", + "dev": true, + "license": "ISC" + }, + "node_modules/undici": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.22.0.tgz", + "integrity": "sha512-hU/10obOIu62MGYjdskASR3CUAiYaFTtC9Pa6vHyf//mAipSvSQg6od2CnJswq7fvzNS3zJhxoRkgNVaHurWKw==", + "license": "MIT", + "engines": { + "node": ">=18.17" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT" + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "license": "MIT", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz", + "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz", + "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unimport": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/unimport/-/unimport-5.5.0.tgz", + "integrity": "sha512-/JpWMG9s1nBSlXJAQ8EREFTFy3oy6USFd8T6AoBaw1q2GGcF4R9yp3ofg32UODZlYEO5VD0EWE1RpI9XDWyPYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.15.0", + "escape-string-regexp": "^5.0.0", + "estree-walker": "^3.0.3", + "local-pkg": "^1.1.2", + "magic-string": "^0.30.19", + "mlly": "^1.8.0", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "pkg-types": "^2.3.0", + "scule": "^1.3.0", + "strip-literal": "^3.1.0", + "tinyglobby": "^0.2.15", + "unplugin": "^2.3.10", + "unplugin-utils": "^0.3.0" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/unimport/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unimport/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "license": "MIT", + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unplugin": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.10.tgz", + "integrity": "sha512-6NCPkv1ClwH+/BGE9QeoTIl09nuiAt0gS28nn1PvYXsGKRwM2TCbFA2QiilmehPDTXIe684k4rZI1yl3A1PCUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "acorn": "^8.15.0", + "picomatch": "^4.0.3", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/unplugin-utils": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/unplugin-utils/-/unplugin-utils-0.3.1.tgz", + "integrity": "sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog==", + "dev": true, + "license": "MIT", + "dependencies": { + "pathe": "^2.0.3", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/unplugin-utils/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/unplugin/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz", + "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/update-notifier": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-7.3.1.tgz", + "integrity": "sha512-+dwUY4L35XFYEzE+OAL3sarJdUioVovq+8f7lcIJ7wnmnYQV5UD1Y/lcwaMSyaQ6Bj3JMj1XSTjZbNLHn/19yA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boxen": "^8.0.1", + "chalk": "^5.3.0", + "configstore": "^7.0.0", + "is-in-ci": "^1.0.0", + "is-installed-globally": "^1.0.0", + "is-npm": "^6.0.0", + "latest-version": "^9.0.0", + "pupa": "^3.1.0", + "semver": "^7.6.3", + "xdg-basedir": "^5.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/yeoman/update-notifier?sponsor=1" + } + }, + "node_modules/update-notifier/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-latest-callback": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.2.6.tgz", + "integrity": "sha512-FvRG9i1HSo0wagmX63Vrm8SnlUU3LMM3WyZkQ76RnslpBrX694AdG4A0zQBx2B3ZifFA0yv/BaEHGBnEax5rZg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", + "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/validate-npm-package-name": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", + "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vaul": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vaul/-/vaul-1.1.2.tgz", + "integrity": "sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-dialog": "^1.1.1" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" + } + }, + "node_modules/vite": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", + "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.1", + "es-module-lexer": "^1.7.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vite/node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/vlq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/vlq/-/vlq-1.0.1.tgz", + "integrity": "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==", + "license": "MIT" + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/warn-once": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/warn-once/-/warn-once-0.1.1.tgz", + "integrity": "sha512-VkQZJbO8zVImzYFteBXvBOZEl1qL175WH8VmZcxF2fZAoudNhNDvHi+doCaAEdU2l2vtcIwa2zn0QK5+I1HQ3Q==", + "license": "MIT" + }, + "node_modules/watchpack": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", + "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/web-ext-run": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/web-ext-run/-/web-ext-run-0.2.4.tgz", + "integrity": "sha512-rQicL7OwuqWdQWI33JkSXKcp7cuv1mJG8u3jRQwx/8aDsmhbTHs9ZRmNYOL+LX0wX8edIEQX8jj4bB60GoXtKA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@babel/runtime": "7.28.2", + "@devicefarmer/adbkit": "3.3.8", + "chrome-launcher": "1.2.0", + "debounce": "1.2.1", + "es6-error": "4.1.1", + "firefox-profile": "4.7.0", + "fx-runner": "1.4.0", + "multimatch": "6.0.0", + "node-notifier": "10.0.1", + "parse-json": "7.1.1", + "pino": "9.7.0", + "promise-toolbox": "0.21.0", + "set-value": "4.1.0", + "source-map-support": "0.5.21", + "strip-bom": "5.0.0", + "strip-json-comments": "5.0.2", + "tmp": "0.2.5", + "update-notifier": "7.3.1", + "watchpack": "2.4.4", + "zip-dir": "2.0.0" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + } + }, + "node_modules/web-ext-run/node_modules/@babel/runtime": { + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.2.tgz", + "integrity": "sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/web-ext-run/node_modules/chrome-launcher": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-1.2.0.tgz", + "integrity": "sha512-JbuGuBNss258bvGil7FT4HKdC3SC2K7UAEUqiPy3ACS3Yxo3hAW6bvFpCu2HsIJLgTqxgEX6BkujvzZfLpUD0Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/node": "*", + "escape-string-regexp": "^4.0.0", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^2.0.1" + }, + "bin": { + "print-chrome-path": "bin/print-chrome-path.cjs" + }, + "engines": { + "node": ">=12.13.0" + } + }, + "node_modules/web-ext-run/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/web-ext-run/node_modules/lighthouse-logger": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-2.0.2.tgz", + "integrity": "sha512-vWl2+u5jgOQuZR55Z1WM0XDdrJT6mzMP8zHUct7xTlWhuQs+eV0g+QL0RQdFjT54zVmbhLCP8vIVpy1wGn/gCg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.4.1", + "marky": "^1.2.2" + } + }, + "node_modules/web-ext-run/node_modules/strip-json-comments": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.2.tgz", + "integrity": "sha512-4X2FR3UwhNUE9G49aIsJW5hRRR3GXGTBTZRMfv568O60ojM8HcWjV/VxAxCDW3SUND33O6ZY66ZuRcdkj73q2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/webpack-virtual-modules": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", + "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/whatwg-fetch": { + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", + "license": "MIT" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/whatwg-url-without-unicode": { + "version": "8.0.0-3", + "resolved": "https://registry.npmjs.org/whatwg-url-without-unicode/-/whatwg-url-without-unicode-8.0.0-3.tgz", + "integrity": "sha512-HoKuzZrUlgpz35YO27XgD28uh/WJH4B0+3ttFqRo//lmq+9T/mIOJ6kqmINI9HpUpz1imRC/nR/lxKpJiv0uig==", + "license": "MIT", + "dependencies": { + "buffer": "^5.4.3", + "punycode": "^2.1.1", + "webidl-conversions": "^5.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/whatwg-url/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/when": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/when/-/when-3.7.7.tgz", + "integrity": "sha512-9lFZp/KHoqH6bPKjbWqa+3Dg/K/r2v0X/3/G2x4DBGchVS2QX2VXL3cZV994WQVnTM1/PD71Az25nAzryEUugw==", + "dev": true, + "license": "MIT" + }, + "node_modules/when-exit": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/when-exit/-/when-exit-2.1.5.tgz", + "integrity": "sha512-VGkKJ564kzt6Ms1dbgPP/yuIoQCrsFAnRbptpC5wOEsDaNsbCB2bnfnaA8i/vRs5tjUSEOtIuvl9/MyVsvQZCg==", + "dev": true, + "license": "MIT" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/widest-line": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz", + "integrity": "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/widest-line/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/widest-line/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/winreg": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/winreg/-/winreg-0.0.12.tgz", + "integrity": "sha512-typ/+JRmi7RqP1NanzFULK36vczznSNN8kWVA9vIqXyv8GhghUlwhGp1Xj3Nms1FsPcNnsQrJOR10N58/nQ9hQ==", + "dev": true, + "license": "BSD" + }, + "node_modules/wonka": { + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/wonka/-/wonka-6.3.5.tgz", + "integrity": "sha512-SSil+ecw6B4/Dm7Pf2sAshKQ5hWFvfyGlfPbEd6A14dOH6VDjrmbY86u6nZvy9omGwwIPFR8V41+of1EezgoUw==", + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wsl-utils/node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wxt": { + "version": "0.20.11", + "resolved": "https://registry.npmjs.org/wxt/-/wxt-0.20.11.tgz", + "integrity": "sha512-DqqHc/5COs8GR21ii99bANXf/mu6zuDpiXFV1YKNsqO5/PvkrCx5arY0aVPL5IATsuacAnNzdj4eMc1qbzS53Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@1natsu/wait-element": "^4.1.2", + "@aklinker1/rollup-plugin-visualizer": "5.12.0", + "@webext-core/fake-browser": "^1.3.2", + "@webext-core/isolated-element": "^1.1.2", + "@webext-core/match-patterns": "^1.0.3", + "@wxt-dev/browser": "^0.1.4", + "@wxt-dev/storage": "^1.0.0", + "async-mutex": "^0.5.0", + "c12": "^3.2.0", + "cac": "^6.7.14", + "chokidar": "^4.0.3", + "ci-info": "^4.3.0", + "consola": "^3.4.2", + "defu": "^6.1.4", + "dotenv": "^17.2.2", + "dotenv-expand": "^12.0.3", + "esbuild": "^0.25.0", + "fast-glob": "^3.3.3", + "filesize": "^11.0.2", + "fs-extra": "^11.3.1", + "get-port-please": "^3.2.0", + "giget": "^1.2.3 || ^2.0.0", + "hookable": "^5.5.3", + "import-meta-resolve": "^4.2.0", + "is-wsl": "^3.1.0", + "json5": "^2.2.3", + "jszip": "^3.10.1", + "linkedom": "^0.18.12", + "magicast": "^0.3.5", + "minimatch": "^10.0.3", + "nano-spawn": "^1.0.2", + "normalize-path": "^3.0.0", + "nypm": "^0.6.1", + "ohash": "^2.0.11", + "open": "^10.2.0", + "ora": "^8.2.0", + "perfect-debounce": "^2.0.0", + "picocolors": "^1.1.1", + "prompts": "^2.4.2", + "publish-browser-extension": "^2.3.0 || ^3.0.2", + "scule": "^1.3.0", + "unimport": "^3.13.1 || ^4.0.0 || ^5.0.0", + "vite": "^5.4.19 || ^6.3.4 || ^7.0.0", + "vite-node": "^2.1.4 || ^3.1.2", + "web-ext-run": "^0.2.4" + }, + "bin": { + "wxt": "bin/wxt.mjs", + "wxt-publish-extension": "bin/wxt-publish-extension.cjs" + }, + "funding": { + "url": "https://github.com/sponsors/wxt-dev" + } + }, + "node_modules/wxt/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/wxt/node_modules/ci-info": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", + "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wxt/node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wxt/node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wxt/node_modules/dotenv": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/wxt/node_modules/dotenv-expand": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-12.0.3.tgz", + "integrity": "sha512-uc47g4b+4k/M/SeaW1y4OApx+mtLWl92l5LMPP0GNXctZqELk+YGgOPIIC5elYmUH4OuoK3JLhuRUYegeySiFA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dotenv": "^16.4.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/wxt/node_modules/dotenv-expand/node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/wxt/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wxt/node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wxt/node_modules/log-symbols": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wxt/node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wxt/node_modules/minimatch": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/wxt/node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wxt/node_modules/open": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wxt/node_modules/ora": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wxt/node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wxt/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xcode": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/xcode/-/xcode-3.0.1.tgz", + "integrity": "sha512-kCz5k7J7XbJtjABOvkc5lJmkiDh8VhjVCGNiqdKCscmVpdVUpEAyXv1xmCLkQJ5dsHqx3IPO4XW+NTDhU/fatA==", + "license": "Apache-2.0", + "dependencies": { + "simple-plist": "^1.1.0", + "uuid": "^7.0.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/xdg-basedir": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", + "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xml2js": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.0.tgz", + "integrity": "sha512-eLTh0kA8uHceqesPqSE+VvO1CDDJWMwlQfB6LuN6T8w6MaDJ8Txm8P7s5cHD0miF0V+GGTZrDQfxPZQVsur33w==", + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xml2js/node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xmlbuilder": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", + "license": "MIT", + "engines": { + "node": ">=8.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zip-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/zip-dir/-/zip-dir-2.0.0.tgz", + "integrity": "sha512-uhlsJZWz26FLYXOD6WVuq+fIcZ3aBPGo/cFdiLlv3KNwpa52IF3ISV8fLhQLiqVu5No3VhlqlgthN6gehil1Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "async": "^3.2.0", + "jszip": "^3.2.2" + } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.24.6", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz", + "integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.24.1" + } + } + } +} diff --git a/companion/package.json b/companion/package.json new file mode 100644 index 0000000000..bdc0411304 --- /dev/null +++ b/companion/package.json @@ -0,0 +1,64 @@ +{ + "name": "cal-companion", + "displayName": "Cal.com Companion", + "version": "1.7.0", + "main": "index.js", + "scripts": { + "start": "expo start", + "android": "expo start --android", + "ios": "expo start --ios", + "web": "expo start --web", + "ext:build": "wxt build", + "ext": "wxt", + "ext:zip": "wxt zip", + "format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,css,md}\"", + "format:check": "prettier --check \"**/*.{js,jsx,ts,tsx,json,css,md}\"", + "prepare": "cd .. && husky companion/.husky" + }, + "dependencies": { + "@expo/ui": "^0.2.0-beta.7", + "@expo/vector-icons": "^15.0.3", + "@react-native-segmented-control/segmented-control": "^2.5.7", + "@types/react": "~19.1.10", + "@types/react-dom": "~19.1.7", + "base64-js": "^1.5.1", + "expo": "~54.0.0", + "expo-auth-session": "^7.0.9", + "expo-constants": "~18.0.10", + "expo-crypto": "^15.0.7", + "expo-device": "^8.0.9", + "expo-glass-effect": "~0.1.6", + "expo-linking": "~8.0.8", + "expo-router": "~6.0.14", + "expo-secure-store": "^15.0.7", + "expo-status-bar": "~3.0.8", + "expo-web-browser": "^15.0.9", + "nativewind": "^4.2.1", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "react-native": "0.81.5", + "react-native-context-menu-view": "^1.20.0", + "react-native-gesture-handler": "^2.29.1", + "react-native-reanimated": "~3.17.4", + "react-native-safe-area-context": "5.4.0", + "react-native-svg": "^15.15.0", + "react-native-web": "^0.21.2", + "react-native-worklets": "^0.6.1" + }, + "private": true, + "devDependencies": { + "@types/chrome": "^0.1.31", + "babel-preset-expo": "^54.0.7", + "husky": "^9.0.11", + "lint-staged": "^15.2.0", + "prettier": "^3.2.5", + "prettier-plugin-tailwindcss": "^0.5.11", + "tailwindcss": "^3.4.17", + "wxt": "^0.20.11" + }, + "lint-staged": { + "**/*.{js,jsx,ts,tsx,json,css,md}": [ + "prettier --write" + ] + } +} diff --git a/companion/services/calcom.ts b/companion/services/calcom.ts new file mode 100644 index 0000000000..02692a1917 --- /dev/null +++ b/companion/services/calcom.ts @@ -0,0 +1,1146 @@ +import type { + EventType, + CreateEventTypeInput, + GetEventTypesResponse, + Booking, + GetBookingsResponse, + BookingParticipationResult, + Schedule, + GetSchedulesResponse, + GetScheduleResponse, + UserProfile, + ConferencingOption, + Webhook, + CreateWebhookInput, + UpdateWebhookInput, + PrivateLink, + CreatePrivateLinkInput, + UpdatePrivateLinkInput, +} from "./types"; + +const API_BASE_URL = "https://api.cal.com/v2"; + +// Authentication configuration +interface AuthConfig { + accessToken?: string; + refreshToken?: string; +} + +// Global auth state +const authConfig: AuthConfig = {}; + +// Token refresh callback - will be set by AuthContext +let tokenRefreshCallback: ((accessToken: string, refreshToken?: string) => Promise) | null = + null; + +// Refresh token function - will be set by AuthContext +let refreshTokenFunction: + | ((refreshToken: string) => Promise<{ accessToken: string; refreshToken?: string }>) + | null = null; + +// Re-export types for backward compatibility +export type { + EventType, + CreateEventTypeInput, + Booking, + BookingParticipationResult, + Schedule, + UserProfile, + ConferencingOption, + Webhook, + CreateWebhookInput, + UpdateWebhookInput, + PrivateLink, + CreatePrivateLinkInput, + UpdatePrivateLinkInput, +}; + +export const getBookingParticipation = ( + booking: Booking, + userId?: number, + userEmail?: string +): BookingParticipationResult => { + const normalizedUserEmail = userEmail?.toLowerCase(); + const emailEq = (a?: string, b?: string) => a?.toLowerCase() === b?.toLowerCase(); + const idEq = (a?: string | number, b?: string | number) => + a !== undefined && b !== undefined && String(a) === String(b); + + const isOrganizer = + !!booking.user && + (idEq(booking.user.id, userId) || emailEq(booking.user.email, normalizedUserEmail)); + + const isHost = + Array.isArray(booking.hosts) && + booking.hosts.some((host) => idEq(host.id, userId) || emailEq(host.email, normalizedUserEmail)); + + const isAttendee = + Array.isArray(booking.attendees) && + booking.attendees.some( + (attendee) => idEq(attendee.id, userId) || emailEq(attendee.email, normalizedUserEmail) + ); + + const isParticipating = isOrganizer || isHost || isAttendee; + + return { isOrganizer, isHost, isAttendee, isParticipating }; +}; + +export class CalComAPIService { + private static userProfile: UserProfile | null = null; + + /** + * Set OAuth access token for authentication + */ + static setAccessToken(accessToken: string, refreshToken?: string): void { + authConfig.accessToken = accessToken; + if (refreshToken) { + authConfig.refreshToken = refreshToken; + } + } + + /** + * Set refresh token function for automatic token refresh + */ + static setRefreshTokenFunction( + refreshFn: (refreshToken: string) => Promise<{ accessToken: string; refreshToken?: string }> + ): void { + refreshTokenFunction = refreshFn; + } + + /** + * Clear all authentication + */ + static clearAuth(): void { + authConfig.accessToken = undefined; + authConfig.refreshToken = undefined; + tokenRefreshCallback = null; + refreshTokenFunction = null; + } + + /** + * Set token refresh callback for OAuth token refresh + */ + static setTokenRefreshCallback( + callback: (accessToken: string, refreshToken?: string) => Promise + ): void { + tokenRefreshCallback = callback; + } + + /** + * Get current authentication header + */ + private static getAuthHeader(): string { + if (authConfig.accessToken) { + return `Bearer ${authConfig.accessToken}`; + } else { + throw new Error("No authentication configured. Please sign in with OAuth."); + } + } + + // Get current user profile + static async getCurrentUser(): Promise { + try { + const response = await this.makeRequest<{ status: string; data: UserProfile }>( + "/me", + { + headers: { + "cal-api-version": "2024-06-11", + }, + }, + "2024-06-11" + ); + return response.data; + } catch (error) { + throw error; + } + } + + // Update current user profile + static async updateUserProfile(updates: { + email?: string; + name?: string; + timeFormat?: number; + defaultScheduleId?: number; + weekStart?: string; + timeZone?: string; + locale?: string; + avatarUrl?: string; + bio?: string; + metadata?: Record; + }): Promise { + try { + const response = await this.makeRequest<{ status: string; data: UserProfile }>( + "/me", + { + method: "PATCH", + headers: { + "Content-Type": "application/json", + "cal-api-version": "2024-06-11", + }, + body: JSON.stringify(updates), + }, + "2024-06-11" + ); + + if (response && response.data) { + // Update cached profile + this.userProfile = response.data; + return response.data; + } + + throw new Error("Invalid response from update user profile API"); + } catch (error) { + console.error("updateUserProfile error:", error); + throw error; + } + } + + // Get and cache user profile + static async getUserProfile(): Promise { + if (this.userProfile) { + return this.userProfile; + } + + this.userProfile = await this.getCurrentUser(); + return this.userProfile; + } + + // Get cached username or fetch if not available + static async getUsername(): Promise { + const profile = await this.getUserProfile(); + return profile.username; + } + + // Build shareable link for event type + static async buildEventTypeLink(eventTypeSlug: string): Promise { + const username = await this.getUsername(); + return `https://cal.com/${username}/${eventTypeSlug}`; + } + + // Clear cached profile (useful for logout) + static clearUserProfile(): void { + this.userProfile = null; + } + + // Test function for bookings API specifically + static async testRawBookingsAPI(): Promise { + try { + const url = `${API_BASE_URL}/bookings?status=upcoming&status=unconfirmed&limit=50`; + + const response = await fetch(url, { + headers: { + Authorization: this.getAuthHeader(), + "Content-Type": "application/json", + "cal-api-version": "2024-08-13", + }, + }); + + const responseText = await response.text(); + + try { + const responseJson = JSON.parse(responseText); + } catch (parseError) {} + } catch (error) {} + } + + private static async makeRequest( + endpoint: string, + options: RequestInit = {}, + apiVersion: string = "2024-08-13", + isRetry: boolean = false + ): Promise { + const url = `${API_BASE_URL}${endpoint}`; + + const response = await fetch(url, { + ...options, + headers: { + Authorization: this.getAuthHeader(), + "Content-Type": "application/json", + "cal-api-version": apiVersion, + ...options.headers, + }, + }); + + if (!response.ok) { + const errorBody = await response.text(); + + // Parse error for better user messages + let errorMessage = response.statusText; + + try { + const errorJson = JSON.parse(errorBody); + errorMessage = errorJson?.error?.message || errorJson?.message || response.statusText; + } catch (parseError) { + // If JSON parsing fails, use the raw error body + errorMessage = errorBody || response.statusText; + } + + // Handle specific error cases + if (response.status === 401) { + if (!isRetry && authConfig.refreshToken && refreshTokenFunction && tokenRefreshCallback) { + try { + const newTokens = await refreshTokenFunction(authConfig.refreshToken); + + authConfig.accessToken = newTokens.accessToken; + if (newTokens.refreshToken) { + authConfig.refreshToken = newTokens.refreshToken; + } + + // Notify AuthContext to update stored tokens + await tokenRefreshCallback(newTokens.accessToken, newTokens.refreshToken); + + // Retry the original request with the new token + return this.makeRequest(endpoint, options, apiVersion, true); + } catch (refreshError) { + console.error("Token refresh failed:", refreshError); + this.clearAuth(); + throw new Error("Authentication failed. Please sign in again."); + } + } + + if (errorMessage.includes("expired")) { + throw new Error("Your authentication has expired. Please sign in again."); + } + throw new Error("Authentication failed. Please check your credentials."); + } + + throw new Error(`API Error: ${errorMessage}`); + } + + return response.json(); + } + + // Delete an event type + static async deleteEventType(eventTypeId: number): Promise { + try { + console.log(`Deleting event type with ID: ${eventTypeId}`); + await this.makeRequest( + `/event-types/${eventTypeId}`, + { + method: "DELETE", + }, + "2024-06-14" + ); + console.log("Delete completed"); + } catch (error) { + console.error("Delete API error:", error); + throw error; + } + } + + // Create an event type + static async createEventType(input: CreateEventTypeInput): Promise { + try { + console.log("Creating event type with input:", JSON.stringify(input, null, 2)); + + const response = await this.makeRequest<{ status: string; data: EventType }>( + "/event-types", + { + method: "POST", + headers: { + "Content-Type": "application/json", + "cal-api-version": "2024-06-14", + }, + body: JSON.stringify(input), + }, + "2024-06-14" + ); + + if (response && response.data) { + console.log("Event type created successfully:", response.data); + return response.data; + } + + throw new Error("Invalid response from create event type API"); + } catch (error) { + console.error("createEventType error:", error); + throw error; + } + } + + // Cancel a booking + static async cancelBooking(bookingUid: string, reason?: string): Promise { + try { + const body: { reason?: string } = {}; + if (reason) { + body.reason = reason; + } + + await this.makeRequest(`/bookings/${bookingUid}/cancel`, { + method: "POST", + body: JSON.stringify(body), + }); + } catch (error) { + throw error; + } + } + + // Reschedule a booking + static async rescheduleBooking( + bookingUid: string, + input: { + start: string; // ISO 8601 datetime string + reschedulingReason?: string; + } + ): Promise { + try { + console.log(`Rescheduling booking ${bookingUid} to:`, input.start); + + const response = await this.makeRequest<{ status: string; data: Booking }>( + `/bookings/${bookingUid}/reschedule`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + "cal-api-version": "2024-08-13", + }, + body: JSON.stringify(input), + }, + "2024-08-13" + ); + + if (response && response.data) { + console.log("Booking rescheduled successfully:", response.data); + return response.data; + } + + throw new Error("Invalid response from reschedule booking API"); + } catch (error) { + console.error("rescheduleBooking error:", error); + throw error; + } + } + + static async getEventTypes(): Promise { + try { + // Get current user to extract username + let username: string | undefined; + try { + const currentUser = await this.getCurrentUser(); + // Extract username from response + if (currentUser?.username) { + username = currentUser.username; + } + } catch (error) {} + + // Build query string with username if available + const params = new URLSearchParams(); + if (username) { + params.append("username", username); + } + + const queryString = params.toString(); + const endpoint = `/event-types${queryString ? `?${queryString}` : ""}`; + + const response = await this.makeRequest(endpoint, {}, "2024-06-14"); + + // Handle different possible response structures + let eventTypesArray: EventType[] = []; + + if (Array.isArray(response)) { + eventTypesArray = response; + } else if (response && typeof response === "object") { + if (response.data && Array.isArray(response.data)) { + eventTypesArray = response.data; + } else if (response.eventTypes && Array.isArray(response.eventTypes)) { + eventTypesArray = response.eventTypes; + } else if (response.items && Array.isArray(response.items)) { + eventTypesArray = response.items; + } else if (response.data && typeof response.data === "object") { + if (response.data.eventTypes && Array.isArray(response.data.eventTypes)) { + eventTypesArray = response.data.eventTypes; + } else if (response.data.items && Array.isArray(response.data.items)) { + eventTypesArray = response.data.items; + } else { + const keys = Object.keys(response.data); + if (keys.length > 0) { + eventTypesArray = Object.values(response.data).filter( + (item): item is EventType => item && typeof item === "object" && "id" in item + ) as EventType[]; + } + } + } else { + const possibleArrays = Object.values(response).filter((val) => Array.isArray(val)); + if (possibleArrays.length > 0) { + eventTypesArray = possibleArrays[0] as EventType[]; + } + } + } + + return eventTypesArray; + } catch (error) { + throw error; + } + } + + static async getBookingByUid(bookingUid: string): Promise { + try { + const response = await this.makeRequest<{ status: string; data: Booking }>( + `/bookings/${bookingUid}`, + { + headers: { + "cal-api-version": "2024-08-13", + }, + }, + "2024-08-13" + ); + console.log("getBookingByUid raw response:", JSON.stringify(response, null, 2)); + if (response && response.data) { + console.log("getBookingByUid booking data:", JSON.stringify(response.data, null, 2)); + console.log("getBookingByUid user field:", response.data.user); + console.log("getBookingByUid hosts field:", response.data.hosts); + console.log("getBookingByUid attendees field:", response.data.attendees); + return response.data; + } + throw new Error("Invalid response from get booking API"); + } catch (error) { + console.error("getBookingByUid error:", error); + throw error; + } + } + + static async getBookings(filters?: { + status?: string[]; + fromDate?: string; + toDate?: string; + eventTypeId?: number; + limit?: number; + offset?: number; + }): Promise { + try { + // Build query parameters + const params = new URLSearchParams(); + + if (filters?.status?.length) { + filters.status.forEach((status) => params.append("status", status)); + } + if (filters?.fromDate) { + params.append("fromDate", filters.fromDate); + } + if (filters?.toDate) { + params.append("toDate", filters.toDate); + } + if (filters?.eventTypeId) { + params.append("eventTypeId", filters.eventTypeId.toString()); + } + if (filters?.limit) { + params.append("limit", filters.limit.toString()); + } + if (filters?.offset) { + params.append("offset", filters.offset.toString()); + } + + const queryString = params.toString(); + const endpoint = `/bookings${queryString ? `?${queryString}` : ""}`; + + const response = await this.makeRequest(endpoint); + + // Handle different possible response structures (same logic as event types) + let bookingsArray: Booking[] = []; + + if (Array.isArray(response)) { + bookingsArray = response; + } else if (response && typeof response === "object") { + if (response.data && Array.isArray(response.data)) { + bookingsArray = response.data; + } else if (response.bookings && Array.isArray(response.bookings)) { + bookingsArray = response.bookings; + } else if (response.items && Array.isArray(response.items)) { + bookingsArray = response.items; + } else if (response.data && typeof response.data === "object") { + if (response.data.bookings && Array.isArray(response.data.bookings)) { + bookingsArray = response.data.bookings; + } else if (response.data.items && Array.isArray(response.data.items)) { + bookingsArray = response.data.items; + } else { + // Convert object values to array as last resort + const keys = Object.keys(response.data); + if (keys.length > 0) { + bookingsArray = Object.values(response.data).filter( + (item): item is Booking => + item && typeof item === "object" && ("id" in item || "uid" in item) + ) as Booking[]; + } + } + } else { + // Try to extract any arrays from the response + const possibleArrays = Object.values(response).filter((val) => Array.isArray(val)); + if (possibleArrays.length > 0) { + bookingsArray = possibleArrays[0] as Booking[]; + } + } + } + + // Get current user to filter bookings + let currentUser; + try { + currentUser = await this.getCurrentUser(); + } catch (error) { + return bookingsArray; + } + + // Extract user info from response + let userId: number | undefined; + let userEmail: string | undefined; + + if (currentUser) { + userId = currentUser.id; + userEmail = currentUser.email; + } + + // Filter bookings to only show ones where the current user is participating + const userBookings = bookingsArray.filter((booking) => { + const { isOrganizer, isHost, isAttendee, isParticipating } = getBookingParticipation( + booking, + userId, + userEmail + ); + + return isParticipating; + }); + + return userBookings; + } catch (error) { + throw error; + } + } + + // Get all schedules + static async getSchedules(): Promise { + try { + const response = await this.makeRequest<{ status: string; data: Schedule[] }>( + "/schedules", + { + headers: { + "cal-api-version": "2024-06-11", // Override version for schedules + }, + }, + "2024-06-11" + ); + + let schedulesArray: Schedule[] = []; + + // Handle response structure: { status: "success", data: [...] } + if ( + response && + response.status === "success" && + response.data && + Array.isArray(response.data) + ) { + schedulesArray = response.data; + } else if (Array.isArray(response)) { + // Fallback: response might be array directly + schedulesArray = response; + } else if (response && response.data && Array.isArray(response.data)) { + // Fallback: response.data might be array + schedulesArray = response.data; + } + + return schedulesArray; + } catch (error) { + throw error; + } + } + + // Create a new schedule + static async createSchedule(input: { + name: string; + timeZone: string; + isDefault?: boolean; + availability?: Array<{ + days: string[]; // e.g., ["Monday", "Tuesday"] + startTime: string; // e.g., "09:00" + endTime: string; // e.g., "17:00" + }>; + overrides?: Array<{ + date: string; // e.g., "2024-05-20" + startTime: string; // e.g., "12:00" + endTime: string; // e.g., "14:00" + }>; + }): Promise { + try { + console.log("Creating schedule with input:", JSON.stringify(input, null, 2)); + + const response = await this.makeRequest<{ status: string; data: Schedule }>( + "/schedules", + { + method: "POST", + headers: { + "Content-Type": "application/json", + "cal-api-version": "2024-06-11", + }, + body: JSON.stringify(input), + }, + "2024-06-11" + ); + + if (response && response.data) { + console.log("Schedule created successfully:", response.data); + return response.data; + } + + throw new Error("Invalid response from create schedule API"); + } catch (error) { + console.error("createSchedule error:", error); + throw error; + } + } + + // Get specific schedule by ID + static async getScheduleById(scheduleId: number): Promise { + try { + const response = await this.makeRequest( + `/schedules/${scheduleId}`, + { + headers: { + "cal-api-version": "2024-06-11", // Override version for schedules + }, + }, + "2024-06-11" + ); + + console.log("getScheduleById raw response:", JSON.stringify(response, null, 2)); + + if (response && response.data) { + console.log("Returning schedule data:", response.data); + return response.data; + } + + // Sometimes the response might be the schedule directly + if (response && response.id) { + console.log("Returning schedule directly:", response); + return response; + } + + console.log("No schedule data found in response"); + return null; + } catch (error) { + console.error("getScheduleById error:", error); + throw error; + } + } + + // Get conferencing options + static async getConferencingOptions(): Promise { + try { + const response = await this.makeRequest<{ status: string; data: ConferencingOption[] }>( + "/conferencing" + ); + + if (response && response.data && Array.isArray(response.data)) { + return response.data; + } + + return []; + } catch (error) { + console.error("getConferencingOptions error:", error); + throw error; + } + } + + // Get a single event type by ID + static async getEventTypeById(eventTypeId: number): Promise { + try { + const response = await this.makeRequest<{ status: string; data: EventType }>( + `/event-types/${eventTypeId}`, + {}, + "2024-06-14" + ); + + if (response && response.data) { + return response.data; + } + + return null; + } catch (error) { + // Handle 404 errors gracefully - resource doesn't exist or user doesn't have access + if (error instanceof Error) { + // Check if error message contains 404 status code + const statusMatch = error.message.match(/API Error: (\d+)/); + if (statusMatch && statusMatch[1] === "404") { + console.warn(`Event type ${eventTypeId} not found`); + return null; + } + } + console.error("getEventTypeById error:", error); + throw error; + } + } + + // Update an event type + static async updateEventType( + eventTypeId: number, + updates: Partial + ): Promise { + try { + console.log(`Updating event type ${eventTypeId} with:`, JSON.stringify(updates, null, 2)); + + const response = await this.makeRequest<{ status: string; data: EventType }>( + `/event-types/${eventTypeId}`, + { + method: "PATCH", + headers: { + "Content-Type": "application/json", + "cal-api-version": "2024-06-14", + }, + body: JSON.stringify(updates), + }, + "2024-06-14" + ); + + if (response && response.data) { + console.log("Event type updated successfully:", response.data); + return response.data; + } + + throw new Error("Invalid response from update event type API"); + } catch (error) { + console.error("updateEventType error:", error); + throw error; + } + } + + // Update a schedule + static async updateSchedule( + scheduleId: number, + updates: { + isDefault?: boolean; + name?: string; + timeZone?: string; + availability?: Array<{ + days: string[]; // Day names like "Monday", "Tuesday" + startTime: string; // Format: "09:00" + endTime: string; // Format: "10:00" + }>; + overrides?: Array<{ + date: string; // Format: "2024-05-20" + startTime: string; // Format: "12:00" + endTime: string; // Format: "14:00" + }>; + } + ): Promise { + try { + const response = await this.makeRequest<{ status: string; data: Schedule }>( + `/schedules/${scheduleId}`, + { + method: "PATCH", + headers: { + "Content-Type": "application/json", + "cal-api-version": "2024-06-11", + }, + body: JSON.stringify(updates), + }, + "2024-06-11" + ); + + if (response && response.data) { + return response.data; + } + + throw new Error("Invalid response from update schedule API"); + } catch (error) { + console.error("updateSchedule error:", error); + throw error; + } + } + + // Duplicate a schedule + static async duplicateSchedule(scheduleId: number): Promise { + try { + const response = await this.makeRequest<{ status: string; data: Schedule }>( + `/atoms/schedules/${scheduleId}/duplicate`, + { + method: "POST", + headers: { + "cal-api-version": "2024-06-11", + }, + }, + "2024-06-11" + ); + + if (response && response.data) { + return response.data; + } + + throw new Error("Invalid response from duplicate schedule API"); + } catch (error) { + console.error("duplicateSchedule error:", error); + throw error; + } + } + + // Delete a schedule + static async deleteSchedule(scheduleId: number): Promise { + try { + await this.makeRequest<{ status: string }>( + `/schedules/${scheduleId}`, + { + method: "DELETE", + headers: { + "cal-api-version": "2024-06-11", + }, + }, + "2024-06-11" + ); + } catch (error) { + console.error("deleteSchedule error:", error); + throw error; + } + } + + // ============================================ + // WEBHOOKS + // ============================================ + + // Get all global webhooks + static async getWebhooks(): Promise { + try { + const response = await this.makeRequest<{ status: string; data: Webhook[] }>("/webhooks"); + + if (response && response.data && Array.isArray(response.data)) { + return response.data; + } + + return []; + } catch (error) { + console.error("getWebhooks error:", error); + throw error; + } + } + + // Create a global webhook + static async createWebhook(input: CreateWebhookInput): Promise { + try { + const response = await this.makeRequest<{ status: string; data: Webhook }>("/webhooks", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(input), + }); + + if (response && response.data) { + return response.data; + } + + throw new Error("Invalid response from create webhook API"); + } catch (error) { + console.error("createWebhook error:", error); + throw error; + } + } + + // Update a global webhook + static async updateWebhook(webhookId: string, updates: UpdateWebhookInput): Promise { + try { + const response = await this.makeRequest<{ status: string; data: Webhook }>( + `/webhooks/${webhookId}`, + { + method: "PATCH", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(updates), + } + ); + + if (response && response.data) { + return response.data; + } + + throw new Error("Invalid response from update webhook API"); + } catch (error) { + console.error("updateWebhook error:", error); + throw error; + } + } + + // Delete a global webhook + static async deleteWebhook(webhookId: string): Promise { + try { + await this.makeRequest(`/webhooks/${webhookId}`, { + method: "DELETE", + }); + } catch (error) { + console.error("deleteWebhook error:", error); + throw error; + } + } + + // Get webhooks for a specific event type + static async getEventTypeWebhooks(eventTypeId: number): Promise { + try { + const response = await this.makeRequest<{ status: string; data: Webhook[] }>( + `/event-types/${eventTypeId}/webhooks` + ); + + if (response && response.data && Array.isArray(response.data)) { + return response.data; + } + + return []; + } catch (error) { + console.error("getEventTypeWebhooks error:", error); + throw error; + } + } + + // Create a webhook for a specific event type + static async createEventTypeWebhook( + eventTypeId: number, + input: CreateWebhookInput + ): Promise { + try { + const response = await this.makeRequest<{ status: string; data: Webhook }>( + `/event-types/${eventTypeId}/webhooks`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(input), + } + ); + + if (response && response.data) { + return response.data; + } + + throw new Error("Invalid response from create event type webhook API"); + } catch (error) { + console.error("createEventTypeWebhook error:", error); + throw error; + } + } + + // Update an event type webhook + static async updateEventTypeWebhook( + eventTypeId: number, + webhookId: string, + updates: UpdateWebhookInput + ): Promise { + try { + const response = await this.makeRequest<{ status: string; data: Webhook }>( + `/event-types/${eventTypeId}/webhooks/${webhookId}`, + { + method: "PATCH", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(updates), + } + ); + + if (response && response.data) { + return response.data; + } + + throw new Error("Invalid response from update event type webhook API"); + } catch (error) { + console.error("updateEventTypeWebhook error:", error); + throw error; + } + } + + // Delete an event type webhook + static async deleteEventTypeWebhook(eventTypeId: number, webhookId: string): Promise { + try { + await this.makeRequest(`/event-types/${eventTypeId}/webhooks/${webhookId}`, { + method: "DELETE", + }); + } catch (error) { + console.error("deleteEventTypeWebhook error:", error); + throw error; + } + } + + // ============================================ + // PRIVATE LINKS + // ============================================ + + // Get all private links for an event type + static async getEventTypePrivateLinks(eventTypeId: number): Promise { + try { + const response = await this.makeRequest<{ status: string; data: PrivateLink[] }>( + `/event-types/${eventTypeId}/private-links` + ); + + if (response && response.data && Array.isArray(response.data)) { + return response.data; + } + + return []; + } catch (error) { + console.error("getEventTypePrivateLinks error:", error); + throw error; + } + } + + // Create a private link for an event type + static async createEventTypePrivateLink( + eventTypeId: number, + input: CreatePrivateLinkInput = {} + ): Promise { + try { + const response = await this.makeRequest<{ status: string; data: PrivateLink }>( + `/event-types/${eventTypeId}/private-links`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(input), + } + ); + + if (response && response.data) { + return response.data; + } + + throw new Error("Invalid response from create private link API"); + } catch (error) { + console.error("createEventTypePrivateLink error:", error); + throw error; + } + } + + // Update a private link + static async updateEventTypePrivateLink( + eventTypeId: number, + linkId: number, + updates: UpdatePrivateLinkInput + ): Promise { + try { + const response = await this.makeRequest<{ status: string; data: PrivateLink }>( + `/event-types/${eventTypeId}/private-links/${linkId}`, + { + method: "PATCH", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(updates), + } + ); + + if (response && response.data) { + return response.data; + } + + throw new Error("Invalid response from update private link API"); + } catch (error) { + console.error("updateEventTypePrivateLink error:", error); + throw error; + } + } + + // Delete a private link + static async deleteEventTypePrivateLink(eventTypeId: number, linkId: number): Promise { + try { + await this.makeRequest(`/event-types/${eventTypeId}/private-links/${linkId}`, { + method: "DELETE", + }); + } catch (error) { + console.error("deleteEventTypePrivateLink error:", error); + throw error; + } + } +} diff --git a/companion/services/oauthService.ts b/companion/services/oauthService.ts new file mode 100644 index 0000000000..40d68757e5 --- /dev/null +++ b/companion/services/oauthService.ts @@ -0,0 +1,496 @@ +/// + +import * as AuthSession from "expo-auth-session"; +import * as WebBrowser from "expo-web-browser"; +import * as Crypto from "expo-crypto"; +import { Platform } from "react-native"; +import { fromByteArray } from "base64-js"; + +// Complete warm up for WebBrowser on mobile +WebBrowser.maybeCompleteAuthSession(); + +export interface OAuthTokens { + accessToken: string; + refreshToken?: string; + tokenType: string; + expiresAt?: number; + scope?: string; +} + +export interface OAuthConfig { + clientId: string; + redirectUri: string; + calcomBaseUrl: string; +} + +export class CalComOAuthService { + private config: OAuthConfig; + private codeVerifier: string | null = null; + private state: string | null = null; + + constructor(config: OAuthConfig) { + this.config = config; + } + + private async generatePKCEParams(): Promise<{ + codeVerifier: string; + codeChallenge: string; + state: string; + }> { + const codeVerifier = this.generateRandomBase64Url(); + const codeChallenge = await this.generateCodeChallenge(codeVerifier); + const state = this.generateRandomBase64Url(); + + this.codeVerifier = codeVerifier; + this.state = state; + + return { codeVerifier, codeChallenge, state }; + } + + private async generateCodeChallenge(codeVerifier: string): Promise { + try { + const base64Hash = await Crypto.digestStringAsync( + Crypto.CryptoDigestAlgorithm.SHA256, + codeVerifier, + { encoding: Crypto.CryptoEncoding.BASE64 } + ); + + return base64Hash.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, ""); + } catch (error) { + console.error("Failed to generate code challenge:", error); + throw new Error("Failed to generate OAuth code challenge"); + } + } + + private generateRandomBase64Url(): string { + const bytes = new Uint8Array(Crypto.getRandomBytes(32)); + + const base64 = + typeof btoa !== "undefined" ? btoa(String.fromCharCode(...bytes)) : fromByteArray(bytes); + + return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, ""); + } + + private buildAuthorizationUrl(codeChallenge: string, state: string): string { + const params = new URLSearchParams({ + client_id: this.config.clientId, + response_type: "code", + redirect_uri: this.config.redirectUri, + state: state, + code_challenge: codeChallenge, + code_challenge_method: "S256", + }); + + return `${this.config.calcomBaseUrl}/auth/oauth2/authorize?${params.toString()}`; + } + + async startAuthorizationFlow(): Promise { + try { + const { codeChallenge, state } = await this.generatePKCEParams(); + + // Web only: check for stored callback with state-specific keys to prevent race conditions + if (Platform.OS === "web" && typeof window !== "undefined") { + const storedCode = window.localStorage.getItem(`oauth_callback_code_${state}`); + const storedState = window.localStorage.getItem(`oauth_callback_state_${state}`); + + if (storedCode && storedState) { + // CSRF protection: verify state matches + if (storedState !== state) { + window.localStorage.removeItem(`oauth_callback_code_${state}`); + window.localStorage.removeItem(`oauth_callback_state_${state}`); + throw new Error("Invalid state parameter - possible CSRF attack"); + } + + window.localStorage.removeItem(`oauth_callback_code_${state}`); + window.localStorage.removeItem(`oauth_callback_state_${state}`); + + return await this.exchangeCodeForTokens(storedCode, state); + } + } + + // Note: State stored in background script (iframe may not have chrome.storage access) + + const authResult = await this.getAuthorizationResult(codeChallenge, state); + if (authResult.type === "success") { + const code = authResult.params?.code || authResult.params?.authorizationCode; + const returnedState = authResult.params?.state; + + if (returnedState !== state) { + throw new Error("Invalid state parameter - possible CSRF attack"); + } + + if (!code) { + throw new Error("No authorization code received"); + } + + return await this.exchangeCodeForTokens(code, state); + } + + if (authResult.type === "error") { + const errorDescription = + authResult.params?.error_description || + ("error" in authResult ? authResult.error?.message : undefined) || + "Unknown error"; + throw new Error(`OAuth error: ${errorDescription}`); + } + + if (authResult.type === "cancel") { + throw new Error("OAuth flow was cancelled by user"); + } + + throw new Error("OAuth flow failed or was dismissed"); + } catch (error) { + console.error("OAuth authorization error:", error); + throw error; + } + } + + // Detect browser extension environment using reliable indicators + private isBrowserExtension(): boolean { + if (Platform.OS !== "web" || typeof window === "undefined") { + return false; + } + + if (typeof chrome !== "undefined" && chrome.runtime?.id) { + return true; + } + + if (window.location.protocol === "chrome-extension:") { + return true; + } + + // Extension content script injects companion app as iframe + if (window.parent !== window) { + try { + const parentOrigin = window.parent.location.origin; + const currentOrigin = window.location.origin; + + if (parentOrigin !== currentOrigin && window.location.hostname === "localhost") { + return true; + } + } catch { + // Cross-origin access blocked - expected for extension iframes + if (window.location.hostname === "localhost") { + return true; + } + } + } + + return false; + } + + private async launchExtensionAuthFlow(authUrl: string): Promise { + return new Promise((resolve, reject) => { + if (typeof chrome !== "undefined" && chrome.identity) { + chrome.identity.launchWebAuthFlow( + { + url: authUrl, + interactive: true, + }, + (responseUrl) => { + if (chrome.runtime.lastError) { + reject(new Error(`OAuth flow failed: ${chrome.runtime.lastError.message}`)); + } else if (responseUrl) { + resolve(responseUrl); + } else { + reject(new Error("OAuth flow cancelled or failed")); + } + } + ); + return; + } + + // Iframe: communicate with parent window + if (window.parent !== window) { + let timeoutId: NodeJS.Timeout | null = null; + + const messageHandler = (event: MessageEvent) => { + // Security: only accept messages from parent + if (event.source !== window.parent) { + return; + } + + if (event.data.type === "cal-extension-oauth-result") { + window.removeEventListener("message", messageHandler); + if (timeoutId) { + clearTimeout(timeoutId); + } + + if (event.data.success) { + resolve(event.data.responseUrl); + } else { + console.error("OAuth flow failed:", event.data.error); + reject(new Error(event.data.error || "OAuth flow failed")); + } + } + }; + + window.addEventListener("message", messageHandler); + + window.parent.postMessage( + { + type: "cal-extension-oauth-request", + authUrl: authUrl, + }, + "*" + ); + + timeoutId = setTimeout(() => { + console.error("OAuth flow timeout - no response from extension"); + window.removeEventListener("message", messageHandler); + reject(new Error("OAuth flow timeout - no response from extension")); + }, 30000); + } else { + reject(new Error("Chrome extension context not detected")); + } + }); + } + + private async getAuthorizationResult( + codeChallenge: string, + state: string + ): Promise }> { + const authUrl = this.buildAuthorizationUrl(codeChallenge, state); + + const isExtension = this.isBrowserExtension(); + + if (isExtension) { + try { + const responseUrl = await this.launchExtensionAuthFlow(authUrl); + const params = this.parseCallbackUrl(responseUrl); + return { type: "success" as const, params }; + } catch (error) { + console.error("Extension OAuth flow failed:", error); + return { type: "error", params: { error: error.message } } as { + type: string; + params: Record; + }; + } + } else if (Platform.OS === "web") { + const discovery = await this.getDiscoveryEndpoints(); + const request = new AuthSession.AuthRequest({ + clientId: this.config.clientId, + redirectUri: this.config.redirectUri, + responseType: AuthSession.ResponseType.Code, + state: state, + codeChallenge: codeChallenge, + codeChallengeMethod: AuthSession.CodeChallengeMethod.S256, + }); + + return await request.promptAsync(discovery); + } else { + const result = await WebBrowser.openAuthSessionAsync(authUrl, this.config.redirectUri); + + if (result.type === "success") { + const params = this.parseCallbackUrl(result.url); + return { type: "success" as const, params }; + } + + return { type: result.type, params: {} } as { type: string; params: Record }; + } + } + + private async getDiscoveryEndpoints(): Promise { + try { + const discovery = await AuthSession.fetchDiscoveryAsync( + `${this.config.calcomBaseUrl}/.well-known/openid_configuration` + ); + return discovery; + } catch { + return { + authorizationEndpoint: `${this.config.calcomBaseUrl}/auth/oauth2/authorize`, + tokenEndpoint: `${this.config.calcomBaseUrl}/api/auth/oauth/token`, + } as AuthSession.DiscoveryDocument; + } + } + + private parseCallbackUrl(url: string): Record { + const urlObj = new URL(url); + const params: Record = {}; + + urlObj.searchParams.forEach((value, key) => { + params[key] = value; + }); + + if (Object.keys(params).length === 0 && urlObj.hash) { + const hashParams = new URLSearchParams(urlObj.hash.substring(1)); + hashParams.forEach((value, key) => { + params[key] = value; + }); + } + + return params; + } + + private async exchangeCodeForTokens(code: string, state?: string): Promise { + if (!this.codeVerifier) { + throw new Error("No code verifier available"); + } + + // Extension: use APIs to avoid CORS + if (this.isBrowserExtension() && window.parent !== window) { + return await this.exchangeTokensViaExtension(code, state); + } + + const tokenEndpoint = `${this.config.calcomBaseUrl}/api/auth/oauth/token`; + + const body = new URLSearchParams(); + body.append("grant_type", "authorization_code"); + body.append("client_id", this.config.clientId); + body.append("code", code); + body.append("redirect_uri", this.config.redirectUri); + body.append("code_verifier", this.codeVerifier); + + const response = await fetch(tokenEndpoint, { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + Accept: "application/json", + }, + body: body.toString(), + }); + + if (!response.ok) { + const errorData = await response.text(); + console.error("Token exchange error response:", errorData); + + try { + const errorJson = JSON.parse(errorData); + console.error("Parsed error:", errorJson); + } catch { + console.error("Could not parse error response as JSON"); + } + + throw new Error(`Token exchange failed: ${response.status} ${errorData}`); + } + + const tokenData = await response.json(); + + const tokens: OAuthTokens = { + accessToken: tokenData.access_token, + refreshToken: tokenData.refresh_token, + tokenType: tokenData.token_type || "Bearer", + expiresAt: tokenData.expires_in ? Date.now() + tokenData.expires_in * 1000 : undefined, + scope: tokenData.scope, + }; + + return tokens; + } + + private async exchangeTokensViaExtension(code: string, state?: string): Promise { + return new Promise((resolve, reject) => { + let timeoutId: NodeJS.Timeout | null = null; + + const messageHandler = (event: MessageEvent) => { + // Security: only accept messages from parent + if (event.source !== window.parent) { + return; + } + + if (event.data.type === "cal-extension-token-exchange-result") { + window.removeEventListener("message", messageHandler); + if (timeoutId) { + clearTimeout(timeoutId); + } + + if (event.data.success) { + resolve(event.data.tokens); + } else { + console.error("Token exchange failed via extension:", event.data.error); + reject(new Error(event.data.error || "Token exchange failed")); + } + } + }; + + window.addEventListener("message", messageHandler); + + window.parent.postMessage( + { + type: "cal-extension-token-exchange-request", + tokenRequest: { + grant_type: "authorization_code", + client_id: this.config.clientId, + code: code, + redirect_uri: this.config.redirectUri, + code_verifier: this.codeVerifier, + }, + state: state, // CSRF validation in background script + tokenEndpoint: `${this.config.calcomBaseUrl}/api/auth/oauth/token`, + }, + "*" + ); + + timeoutId = setTimeout(() => { + window.removeEventListener("message", messageHandler); + reject(new Error("Token exchange timeout")); + }, 30000); + }); + } + + async refreshAccessToken(refreshToken: string): Promise { + const tokenEndpoint = `${this.config.calcomBaseUrl}/api/auth/oauth/refreshToken`; + + const body = new URLSearchParams({ + grant_type: "refresh_token", + client_id: this.config.clientId, + refresh_token: refreshToken, + }); + + const response = await fetch(tokenEndpoint, { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + Accept: "application/json", + }, + body: body.toString(), + }); + + if (!response.ok) { + const errorData = await response.text(); + throw new Error(`Token refresh failed: ${response.status} ${errorData}`); + } + + const tokenData = await response.json(); + + const tokens: OAuthTokens = { + accessToken: tokenData.access_token, + refreshToken: tokenData.refresh_token || refreshToken, + tokenType: tokenData.token_type || "Bearer", + expiresAt: tokenData.expires_in ? Date.now() + tokenData.expires_in * 1000 : undefined, + scope: tokenData.scope, + }; + + return tokens; + } + + isTokenExpired(tokens: OAuthTokens): boolean { + if (!tokens.expiresAt) { + return false; + } + // 5-minute buffer before expiry + return Date.now() >= tokens.expiresAt - 5 * 60 * 1000; + } + clearPKCEParams(): void { + this.codeVerifier = null; + this.state = null; + } +} + +export function createCalComOAuthService(overrides: Partial = {}): CalComOAuthService { + let defaultRedirectUri: string; + + const defaultConfig: OAuthConfig = { + clientId: process.env.EXPO_PUBLIC_CALCOM_OAUTH_CLIENT_ID || "", + redirectUri: process.env.EXPO_PUBLIC_CALCOM_OAUTH_REDIRECT_URI, + calcomBaseUrl: "https://app.cal.com", + ...overrides, + }; + + if (!defaultConfig.clientId) { + throw new Error( + "OAuth client ID is required. Set EXPO_PUBLIC_CALCOM_OAUTH_CLIENT_ID environment variable." + ); + } + + return new CalComOAuthService(defaultConfig); +} diff --git a/companion/services/types/bookings.types.ts b/companion/services/types/bookings.types.ts new file mode 100644 index 0000000000..69eddfd0ab --- /dev/null +++ b/companion/services/types/bookings.types.ts @@ -0,0 +1,64 @@ +export interface Booking { + id: number; + uid: string; + title: string; + description?: string; + startTime: string; + endTime: string; + start?: string; + end?: string; + eventTypeId: number; + eventType?: { + id: number; + title: string; + slug: string; + }; + hosts?: Array<{ + id?: number | string; + name?: string; + email?: string; + username?: string; + timeZone?: string; + }>; + user?: { + id: number; + email: string; + name: string; + timeZone: string; + }; + attendees?: Array<{ + id?: number | string; + email: string; + name: string; + timeZone: string; + noShow?: boolean; + }>; + status: "ACCEPTED" | "PENDING" | "CANCELLED" | "REJECTED"; + paid?: boolean; + payment?: Array<{ + id: number; + success: boolean; + paymentOption: string; + }>; + rescheduled?: boolean; + fromReschedule?: string; + recurringEventId?: string; + recurringBookingUid?: string; + smsReminderNumber?: string; + location?: string; + cancellationReason?: string; + rejectionReason?: string; + responses?: Record; +} + +export interface GetBookingsResponse { + status: "success"; + data: Booking[]; +} + +export interface BookingParticipationResult { + isOrganizer: boolean; + isHost: boolean; + isAttendee: boolean; + isParticipating: boolean; +} diff --git a/companion/services/types/event-types.types.ts b/companion/services/types/event-types.types.ts new file mode 100644 index 0000000000..a83610c03c --- /dev/null +++ b/companion/services/types/event-types.types.ts @@ -0,0 +1,288 @@ +// Event Type Creation/Update Types +export interface BookingLimitsCount { + day?: number; + week?: number; + month?: number; + year?: number; +} + +export interface BookingLimitsDuration { + day?: number; + week?: number; + month?: number; + year?: number; +} + +export interface BookerActiveBookingsLimit { + count: number; +} + +export interface BusinessDaysWindow { + type: "businessDays"; + value: number; + rolling?: boolean; + rollingWindow?: number; +} + +export interface CalendarDaysWindow { + type: "calendarDays"; + value: number; + rolling?: boolean; + rollingWindow?: number; +} + +export interface RangeWindow { + type: "range"; + value: [string, string]; // ISO date strings +} + +export type BookingWindow = + | BusinessDaysWindow + | CalendarDaysWindow + | RangeWindow + | { disabled: true }; + +export interface BookerLayouts { + enabledLayouts: Array<"month_view" | "week_view" | "column_view">; + defaultLayout: "month_view" | "week_view" | "column_view"; +} + +export interface ConfirmationPolicy { + noticeThreshold?: { + count: number; + unit: "hours" | "minutes"; + }; +} + +export interface Recurrence { + interval: number; + occurrences: number; + frequency: "daily" | "weekly" | "monthly" | "yearly"; +} + +export interface Seats { + seatsPerTimeSlot?: number; + showAttendeeInfo?: boolean; + showAvailabilityCount?: boolean; + seatsShowAvailabilityCount?: boolean; +} + +export interface EventTypeColor { + lightEventTypeColor?: string; + darkEventTypeColor?: string; +} + +export interface EmailSettings { + additionalGuestsEmails?: string[]; +} + +// Booking Field Types +export interface BaseBookingField { + name: string; + type: string; + label?: string; + placeholder?: string; + required?: boolean; + hidden?: boolean; +} + +export interface NameDefaultField extends BaseBookingField { + type: "name"; + variant?: "fullName" | "firstAndLastName"; +} + +export interface EmailDefaultField extends BaseBookingField { + type: "email"; +} + +export interface PhoneField extends BaseBookingField { + type: "phone"; +} + +export interface AddressField extends BaseBookingField { + type: "address"; +} + +export interface TextField extends BaseBookingField { + type: "text"; +} + +export interface NumberField extends BaseBookingField { + type: "number"; +} + +export interface TextAreaField extends BaseBookingField { + type: "textarea"; +} + +export interface SelectField extends BaseBookingField { + type: "select" | "multiselect"; + options?: Array<{ label: string; value: string }>; +} + +export interface CheckboxField extends BaseBookingField { + type: "checkbox"; +} + +export interface RadioField extends BaseBookingField { + type: "radio" | "radioInput"; + options?: Array<{ label: string; value: string }>; +} + +export interface BooleanField extends BaseBookingField { + type: "boolean"; +} + +export type BookingField = + | NameDefaultField + | EmailDefaultField + | PhoneField + | AddressField + | TextField + | NumberField + | TextAreaField + | SelectField + | CheckboxField + | RadioField + | BooleanField; + +export interface EventType { + id: number; + title: string; + slug: string; + description?: string; + length: number; // Deprecated: use lengthInMinutes instead + lengthInMinutes?: number; // API returns this field + lengthInMinutesOptions?: number[]; + + // Locations + locations?: Array<{ + type: string; + address?: string; + link?: string; + phone?: string; + integration?: string; + credentialId?: number; + public?: boolean; + }>; + + // Pricing + price?: number; + currency?: string; + + // Basic settings + disableGuests?: boolean; + lockTimeZoneToggleOnBookingPage?: boolean; + requiresConfirmation?: boolean; + requiresBookerEmailVerification?: boolean; + hideCalendarNotes?: boolean; + successRedirectUrl?: string; + forwardParamsSuccessRedirect?: boolean; + + // Schedule and buffers + scheduleId?: number; + slotInterval?: number; + minimumBookingNotice?: number; + beforeEventBuffer?: number; + afterEventBuffer?: number; + + // Limits + bookingLimitsCount?: BookingLimitsCount | { disabled: true }; + bookingLimitsDuration?: BookingLimitsDuration | { disabled: true }; + bookerActiveBookingsLimit?: BookerActiveBookingsLimit | { disabled: true }; + onlyShowFirstAvailableSlot?: boolean; + bookingWindow?: BookingWindow; + offsetStart?: number; + + // Advanced + bookerLayouts?: BookerLayouts; + confirmationPolicy?: ConfirmationPolicy | { disabled: true }; + + // Recurring + recurrence?: Recurrence | { disabled: true }; + + // Seats + seats?: Seats | { disabled: true }; + + // Customization + bookingFields?: BookingField[]; + hidden?: boolean; + eventTypeColor?: EventTypeColor; + + // Ownership + userId?: number; + teamId?: number; + hosts?: Array<{ + userId: number; + isFixed: boolean; + }>; + + // Metadata + metadata?: Record; +} + +export interface CreateEventTypeInput { + // Basic fields + title: string; + slug: string; + description?: string; + lengthInMinutes: number; + lengthInMinutesOptions?: number[]; + + // Locations + locations?: Array<{ + type: string; + address?: string; + link?: string; + phone?: string; + integration?: string; + credentialId?: number; + public?: boolean; + }>; + + // Schedule + scheduleId?: number; + + // Booking settings + disableGuests?: boolean; + slotInterval?: number; + minimumBookingNotice?: number; + beforeEventBuffer?: number; + afterEventBuffer?: number; + + // Limits + bookingLimitsCount?: BookingLimitsCount | { disabled: true }; + bookingLimitsDuration?: BookingLimitsDuration | { disabled: true }; + bookerActiveBookingsLimit?: BookerActiveBookingsLimit | { disabled: true }; + onlyShowFirstAvailableSlot?: boolean; + + // Booking window + bookingWindow?: BookingWindow; + + // Advanced settings + offsetStart?: number; + bookerLayouts?: BookerLayouts; + confirmationPolicy?: ConfirmationPolicy | { disabled: true }; + recurrence?: Recurrence | { disabled: true }; + seats?: Seats | { disabled: true }; + + // Customization + bookingFields?: BookingField[]; + hidden?: boolean; + eventTypeColor?: EventTypeColor; + emailSettings?: EmailSettings; + + // Additional fields + requiresConfirmation?: boolean; + requiresBookerEmailVerification?: boolean; + hideCalendarNotes?: boolean; + lockTimeZoneToggleOnBookingPage?: boolean; + successRedirectUrl?: string; + forwardParamsSuccessRedirect?: boolean; + metadata?: Record; +} + +export interface GetEventTypesResponse { + status: "success"; + data: EventType[]; +} diff --git a/companion/services/types/index.ts b/companion/services/types/index.ts new file mode 100644 index 0000000000..29169f1296 --- /dev/null +++ b/companion/services/types/index.ts @@ -0,0 +1,7 @@ +// Re-export all types for easy importing +export * from "./event-types.types"; +export * from "./bookings.types"; +export * from "./schedules.types"; +export * from "./users.types"; +export * from "./webhooks.types"; +export * from "./private-links.types"; diff --git a/companion/services/types/private-links.types.ts b/companion/services/types/private-links.types.ts new file mode 100644 index 0000000000..1b224f8701 --- /dev/null +++ b/companion/services/types/private-links.types.ts @@ -0,0 +1,25 @@ +// Private Link Types +export interface PrivateLink { + id: number; + link: string; + enabled: boolean; + eventTypeId: number; +} + +export interface CreatePrivateLinkInput { + enabled?: boolean; +} + +export interface UpdatePrivateLinkInput { + enabled?: boolean; +} + +export interface GetPrivateLinksResponse { + status: string; + data: PrivateLink[]; +} + +export interface GetPrivateLinkResponse { + status: string; + data: PrivateLink; +} diff --git a/companion/services/types/schedules.types.ts b/companion/services/types/schedules.types.ts new file mode 100644 index 0000000000..be64af6f53 --- /dev/null +++ b/companion/services/types/schedules.types.ts @@ -0,0 +1,31 @@ +export interface ScheduleAvailability { + days: string[]; + startTime: string; + endTime: string; +} + +export interface ScheduleOverride { + date: string; + startTime: string; + endTime: string; +} + +export interface Schedule { + id: number; + ownerId: number; + name: string; + timeZone: string; + availability: ScheduleAvailability[]; + isDefault: boolean; + overrides: ScheduleOverride[]; +} + +export interface GetSchedulesResponse { + status: "success"; + data: Schedule[]; +} + +export interface GetScheduleResponse { + status: "success"; + data: Schedule | null; +} diff --git a/companion/services/types/users.types.ts b/companion/services/types/users.types.ts new file mode 100644 index 0000000000..aa1b92896f --- /dev/null +++ b/companion/services/types/users.types.ts @@ -0,0 +1,35 @@ +export interface UserProfile { + id: number; + username: string; + name: string; + email: string; + bio?: string; + avatarUrl?: string; + timeZone?: string; + weekStart?: string; + timeFormat?: number; + defaultScheduleId?: number; + locale?: string; + organizationId?: number; + organization?: { + id: number; + isPlatform: boolean; + }; + metadata?: Record; + brandColor?: string; + darkBrandColor?: string; + theme?: string; +} + +export interface ConferencingOption { + id: number; + type: string; + userId: number | null; + teamId: number | null; + appId: string | null; + subscriptionId: number | null; + paymentStatus: string | null; + billingCycleStart: string | null; + invalid: boolean; + delegationCredentialId: string | null; +} diff --git a/companion/services/types/webhooks.types.ts b/companion/services/types/webhooks.types.ts new file mode 100644 index 0000000000..30b47e673b --- /dev/null +++ b/companion/services/types/webhooks.types.ts @@ -0,0 +1,36 @@ +// Webhook Types +export interface Webhook { + id: string; + subscriberUrl: string; + eventTriggers: string[]; + active: boolean; + payloadTemplate?: string; + secret?: string; + eventTypeId?: number; +} + +export interface CreateWebhookInput { + subscriberUrl: string; + eventTriggers: string[]; + active?: boolean; + payloadTemplate?: string; + secret?: string; +} + +export interface UpdateWebhookInput { + subscriberUrl?: string; + eventTriggers?: string[]; + active?: boolean; + payloadTemplate?: string; + secret?: string; +} + +export interface GetWebhooksResponse { + status: string; + data: Webhook[]; +} + +export interface GetWebhookResponse { + status: string; + data: Webhook; +} diff --git a/companion/services/webAuth.ts b/companion/services/webAuth.ts new file mode 100644 index 0000000000..0f05dd13b4 --- /dev/null +++ b/companion/services/webAuth.ts @@ -0,0 +1,192 @@ +import { Platform } from "react-native"; + +export interface WebSessionInfo { + isLoggedIn: boolean; + accessToken?: string; + refreshToken?: string; + userInfo?: any; +} + +export class WebAuthService { + // Check if running on web + static isWeb(): boolean { + return Platform.OS === "web"; + } + + // Get cookies from the browser + static getCookies(): { [key: string]: string } { + if (!this.isWeb()) return {}; + + const cookies: { [key: string]: string } = {}; + if (typeof document !== "undefined") { + document.cookie.split(";").forEach((cookie) => { + const [name, value] = cookie.trim().split("="); + if (name && value) { + cookies[name] = decodeURIComponent(value); + } + }); + } + return cookies; + } + + // Check for Cal.com session cookies + static async checkCalComSession(): Promise { + if (!this.isWeb()) { + return { isLoggedIn: false }; + } + + try { + // Check for common Cal.com session cookies + const cookies = this.getCookies(); + + // Look for Cal.com authentication cookies + // Common patterns: next-auth.session-token, __Secure-next-auth.session-token, etc. + const sessionCookieNames = [ + "next-auth.session-token", + "__Secure-next-auth.session-token", + "__Host-next-auth.session-token", + "next-auth.csrf-token", + "cal-session", + "session-token", + ]; + + const foundSessionCookie = sessionCookieNames.find((name) => cookies[name]); + + if (foundSessionCookie) { + // Try to validate the session with Cal.com API + const sessionInfo = await this.validateWebSession(); + return sessionInfo; + } + + return { isLoggedIn: false }; + } catch (error) { + return { isLoggedIn: false }; + } + } + + // Validate web session by calling Cal.com API with cookies + static async validateWebSession(): Promise { + if (!this.isWeb()) { + return { isLoggedIn: false }; + } + + try { + // First try to call the NextAuth session endpoint + const sessionResponse = await fetch("https://app.cal.com/api/auth/session", { + method: "GET", + credentials: "include", // Include cookies + headers: { + "Content-Type": "application/json", + }, + }); + + if (sessionResponse.ok) { + const sessionData = await sessionResponse.json(); + + if (sessionData && sessionData.user) { + // User is logged in via web session + return { + isLoggedIn: true, + userInfo: sessionData.user, + }; + } + } + + // Try the internal Cal.com me endpoint (this might work with cookies) + const meResponse = await fetch("https://app.cal.com/api/me", { + method: "GET", + credentials: "include", + headers: { + "Content-Type": "application/json", + }, + }); + + if (meResponse.ok) { + const userData = await meResponse.json(); + + if (userData && (userData.id || userData.user || userData.data)) { + return { + isLoggedIn: true, + userInfo: userData.user || userData.data || userData, + }; + } + } + + // Try to check if user is logged in by attempting to access a protected page + const dashboardResponse = await fetch("https://app.cal.com/api/trpc/viewer.me", { + method: "GET", + credentials: "include", + headers: { + "Content-Type": "application/json", + }, + }); + + if (dashboardResponse.ok) { + const dashboardData = await dashboardResponse.json(); + + if (dashboardData && dashboardData.result && dashboardData.result.data) { + return { + isLoggedIn: true, + userInfo: dashboardData.result.data, + }; + } + } + + return { isLoggedIn: false }; + } catch (error) { + return { isLoggedIn: false }; + } + } + + // Try to get API tokens from web session + static async getTokensFromWebSession(): Promise<{ accessToken?: string; refreshToken?: string }> { + if (!this.isWeb()) { + return {}; + } + + try { + // Some Cal.com implementations might expose tokens via specific endpoints + // This is a fallback approach - in practice, web sessions might not expose tokens directly + + // Check localStorage/sessionStorage for any stored tokens + if (typeof window !== "undefined") { + const localStorage = window.localStorage; + const sessionStorage = window.sessionStorage; + + const possibleTokenKeys = [ + "cal-access-token", + "cal-auth-token", + "next-auth.token", + "auth-token", + "access-token", + ]; + + for (const key of possibleTokenKeys) { + const localToken = localStorage.getItem(key); + const sessionToken = sessionStorage.getItem(key); + + if (localToken || sessionToken) { + return { + accessToken: localToken || sessionToken || undefined, + }; + } + } + } + + return {}; + } catch (error) { + return {}; + } + } + + // Redirect to Cal.com web login instead of using WebView + static redirectToWebLogin(): void { + if (!this.isWeb()) return; + + // For web, redirect directly to Cal.com login + const currentUrl = window.location.href; + const loginUrl = `https://app.cal.com/auth/signin?callbackUrl=${encodeURIComponent(currentUrl)}`; + + window.location.href = loginUrl; + } +} diff --git a/companion/tailwind.config.js b/companion/tailwind.config.js new file mode 100644 index 0000000000..6288219191 --- /dev/null +++ b/companion/tailwind.config.js @@ -0,0 +1,9 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: ["./app/**/*.{js,jsx,ts,tsx}", "./components/**/*.{js,jsx,ts,tsx}"], + presets: [require("nativewind/preset")], + theme: { + extend: {}, + }, + plugins: [], +}; diff --git a/companion/tsconfig.json b/companion/tsconfig.json new file mode 100644 index 0000000000..b365f86f06 --- /dev/null +++ b/companion/tsconfig.json @@ -0,0 +1,6 @@ +{ + "compilerOptions": { + "types": ["nativewind/types"] + }, + "extends": "expo/tsconfig.base" +} diff --git a/companion/utils/defaultLocations.ts b/companion/utils/defaultLocations.ts new file mode 100644 index 0000000000..94e99129cf --- /dev/null +++ b/companion/utils/defaultLocations.ts @@ -0,0 +1,97 @@ +/** + * Default location types that are available alongside conferencing apps + * These match the pattern from packages/app-store/locations.ts + */ + +export enum DefaultLocationType { + AttendeeInPerson = "attendeeInPerson", + InPerson = "inPerson", + Phone = "phone", + UserPhone = "userPhone", + Link = "link", + Conferencing = "conferencing", + SomewhereElse = "somewhereElse", +} + +export type DefaultLocation = { + type: DefaultLocationType; + label: string; + iconUrl: string; + category: "in person" | "conferencing" | "other" | "phone"; + organizerInputType?: "text" | "phone" | null; + organizerInputPlaceholder?: string; + organizerInputLabel?: string; + messageForOrganizer?: string; +}; + +/** + * Default locations available in Cal.com + * Icons are served from https://app.cal.com/{iconPath} + */ +export const defaultLocations: DefaultLocation[] = [ + { + type: DefaultLocationType.AttendeeInPerson, + label: "In Person (Attendee Address)", + iconUrl: "https://app.cal.com/map-pin-dark.svg", + category: "in person", + }, + { + type: DefaultLocationType.SomewhereElse, + label: "Custom Attendee Location", + iconUrl: "https://app.cal.com/message-pin.svg", + category: "other", + }, + { + type: DefaultLocationType.InPerson, + label: "In Person (Organizer Address)", + iconUrl: "https://app.cal.com/map-pin-dark.svg", + category: "in person", + organizerInputType: "text", + organizerInputPlaceholder: "Enter address or place", + organizerInputLabel: "Address", + messageForOrganizer: "Provide an Address or Place", + }, + { + type: DefaultLocationType.Link, + label: "Link Meeting", + iconUrl: "https://app.cal.com/link.svg", + category: "other", + organizerInputType: "text", + organizerInputPlaceholder: "https://meet.example.com/join/123456", + organizerInputLabel: "Meeting Link", + messageForOrganizer: "Provide a Meeting Link", + }, + { + type: DefaultLocationType.Phone, + label: "Attendee Phone Number", + iconUrl: "https://app.cal.com/phone.svg", + category: "phone", + organizerInputType: null, + messageForOrganizer: "Cal will ask your invitee to enter a phone number before scheduling.", + }, + { + type: DefaultLocationType.UserPhone, + label: "Organizer Phone Number", + iconUrl: "https://app.cal.com/phone.svg", + category: "phone", + organizerInputType: "phone", + organizerInputPlaceholder: "Enter phone number", + organizerInputLabel: "Phone Number", + messageForOrganizer: "Provide your phone number with country code", + }, +]; + +/** + * Get icon URL for a default location type + */ +export function getDefaultLocationIconUrl(locationType: string): string | null { + const location = defaultLocations.find((loc) => loc.type === locationType); + return location ? location.iconUrl : null; +} + +/** + * Check if a location type is a default location (not a conferencing app) + */ +export function isDefaultLocation(locationType: string): boolean { + return defaultLocations.some((loc) => loc.type === locationType); +} diff --git a/companion/utils/getAppIconUrl.ts b/companion/utils/getAppIconUrl.ts new file mode 100644 index 0000000000..21d316c794 --- /dev/null +++ b/companion/utils/getAppIconUrl.ts @@ -0,0 +1,123 @@ +/** + * Maps conferencing app types or appIds to their icon URLs. + * Icons are publicly accessible at: https://app.cal.com/app-store/{dirName}/icon.svg + */ + +const APP_TYPE_TO_DIR_NAME: Record = { + // Video conferencing apps + zoom_video: "zoomvideo", + google_video: "googlevideo", + office365_video: "office365video", + teams_video: "office365video", + daily_video: "dailyvideo", + jitsi_video: "jitsivideo", + huddle01_video: "huddle01video", + tandem_video: "tandemvideo", + whereby_video: "whereby", + webex_video: "webex", + sylapsvideo_video: "sylapsvideo", + shimmervideo_video: "shimmervideo", + riverside_video: "riverside", + roam_video: "roam", + ping_video: "ping", + mirotalk_video: "mirotalk", + nextcloudtalk_conferencing: "nextcloudtalk", + jelly_conferencing: "jelly", + "horizon-workrooms_video": "horizon-workrooms", + "element-call_conferencing": "element-call", + eightxeight_video: "eightxeight", + discord_video: "discord", + dialpad_video: "dialpad", + demodesk_video: "demodesk", + campfire_video: "campfire", + facetime_video: "facetime", + whatsapp_video: "whatsapp", + telegram_video: "telegram", + skype_video: "skype", + signal_video: "signal", + sirius_video: "sirius_video", + sirius_video_video: "sirius_video", // Handle double _video suffix + salesroom_video: "salesroom", +}; + +const APP_ID_TO_DIR_NAME: Record = { + zoom: "zoomvideo", + "google-meet": "googlevideo", + msteams: "office365video", + office365: "office365video", + teams: "office365video", + "cal-video": "dailyvideo", + "daily-video": "dailyvideo", + daily: "dailyvideo", + jitsi: "jitsivideo", + huddle01: "huddle01video", + tandem: "tandemvideo", + whereby: "whereby", + webex: "webex", + sylapsvideo: "sylapsvideo", + shimmervideo: "shimmervideo", + riverside: "riverside", + roam: "roam", + ping: "ping", + mirotalk: "mirotalk", + nextcloudtalk: "nextcloudtalk", + jelly: "jelly", + "horizon-workrooms": "horizon-workrooms", + "element-call": "element-call", + eightxeight: "eightxeight", + discord: "discord", + dialpad: "dialpad", + demodesk: "demodesk", + campfire: "campfire", + facetime: "facetime", + whatsapp: "whatsapp", + telegram: "telegram", + skype: "skype", + signal: "signal", + sirius_video: "sirius_video", + salesroom: "salesroom", +}; + +/** + * Generates the icon URL for a conferencing app based on its type or appId. + * Uses publicly available icons from https://app.cal.com/app-store/{dirName}/icon.svg + * Special cases are handled (e.g., Google Meet uses logo.webp) + * + * @param type - The credential type (e.g., "zoom_video", "google_video") + * @param appId - The app identifier (e.g., "zoom", "google-meet") + * @returns The full icon URL or null if the app is not found + * + * @example + * getAppIconUrl("zoom_video", "zoom") // "https://app.cal.com/app-store/zoomvideo/icon.svg" + * getAppIconUrl("google_video", "google-meet") // "https://app.cal.com/app-store/googlevideo/logo.webp" + */ +export function getAppIconUrl(type: string, appId: string | null): string | null { + let dirName: string | null = null; + let iconFileName = "icon.svg"; + + // Try to find by appId first (more specific) + if (appId && APP_ID_TO_DIR_NAME[appId]) { + dirName = APP_ID_TO_DIR_NAME[appId]; + } else if (APP_TYPE_TO_DIR_NAME[type]) { + // Fallback to type + dirName = APP_TYPE_TO_DIR_NAME[type]; + } + + if (!dirName) { + return null; + } + + // Special cases for icon file names + if (appId === "google-meet" || type === "google_video") { + // Google Meet uses logo.webp instead of icon.svg + iconFileName = "logo.webp"; + } else if (appId === "riverside" || type === "riverside_video") { + // Riverside uses icon-dark.svg + iconFileName = "icon-dark.svg"; + } else if (appId === "sirius_video" || type === "sirius_video" || type === "sirius_video_video") { + // Sirius Video uses icon-dark.svg + iconFileName = "icon-dark.svg"; + } + + return `https://app.cal.com/app-store/${dirName}/${iconFileName}`; +} diff --git a/companion/utils/gmail-google-chip-parser.ts b/companion/utils/gmail-google-chip-parser.ts new file mode 100644 index 0000000000..1989103b80 --- /dev/null +++ b/companion/utils/gmail-google-chip-parser.ts @@ -0,0 +1,155 @@ +/** + * Parser for Google Calendar scheduling chips in Gmail + * Extracts time slots from the Gmail ad-hoc scheduling chip + */ + +export interface GoogleTimeSlot { + date: string; // "Wed, 26 November" + startTime: string; // "3:30 pm" + endTime: string; // "4:30 pm" + durationMinutes: number; // 60 + isoDate: string; // "2025-11-27" + isoTimestamp: string; // "2025-11-27T05:30:00.000Z" + googleUrl: string; // Original Google Calendar URL +} + +export interface ParsedGoogleChip { + scheduleId: string; + timezone: string; // "Asia/Kolkata" + timezoneOffset: string; // "GMT+05:30" + slots: GoogleTimeSlot[]; + detectedDuration: number; // 15, 30, 45, or 60 +} + +/** + * Parse Google Calendar scheduling chip from Gmail + */ +export function parseGoogleChip(chipElement: HTMLElement): ParsedGoogleChip | null { + try { + // 1. Get schedule ID from data attribute + const scheduleId = chipElement.getAttribute("data-ad-hoc-schedule-id"); + if (!scheduleId) { + console.warn("No schedule ID found in Google chip"); + return null; + } + + // 2. Parse timezone - first try to get IANA timezone from data attribute + let timezone = "UTC"; + let timezoneOffset = "GMT+00:00"; + + // Try to get IANA timezone from data-ad-hoc-v2-params + const paramsAttr = chipElement.getAttribute("data-ad-hoc-v2-params"); + if (paramsAttr) { + // The timezone is at the end of the params string, like: "Asia/Kolkata" + // Try multiple patterns as Gmail might format it differently + let tzMatch = paramsAttr.match(/"([^&]+)"\]/); + + // Also try without HTML entities + if (!tzMatch) { + tzMatch = paramsAttr.match(/"([^"]+)"\]/); + } + + // Also try with escaped quotes + if (!tzMatch) { + tzMatch = paramsAttr.match(/\\"([^\\]+)\\"\]/); + } + + if (tzMatch && tzMatch[1]) { + timezone = tzMatch[1]; // e.g., "Asia/Kolkata" + } + } + + // Also get the display timezone offset from the UI text + const timezoneText = chipElement.querySelector("td")?.textContent?.trim() || ""; + const timezoneMatch = timezoneText.match(/^(.+?)\s*-\s*(.+?)\s*\((.+?)\)$/); + + if (timezoneMatch) { + timezoneOffset = timezoneMatch[3].trim(); // e.g., "GMT+05:30" + } + + // 3. Find all time slot links + const slotLinks = Array.from(chipElement.querySelectorAll("a[href*='slotStartTime']")); + + if (slotLinks.length === 0) { + console.warn("No time slot links found in Google chip"); + return null; + } + + const slots: GoogleTimeSlot[] = []; + let detectedDuration = 60; // Default + + slotLinks.forEach((link) => { + const href = (link as HTMLAnchorElement).href; + const url = new URL(href); + + // Extract parameters + const slotStartTime = url.searchParams.get("slotStartTime"); + const slotDurationMinutes = url.searchParams.get("slotDurationMinutes"); + + if (!slotStartTime || !slotDurationMinutes) { + return; + } + + const durationMinutes = parseInt(slotDurationMinutes, 10); + detectedDuration = durationMinutes; // Use the duration from slots + + // Convert Unix timestamp (milliseconds) to Date + const startDate = new Date(parseInt(slotStartTime, 10)); + const endDate = new Date(startDate.getTime() + durationMinutes * 60 * 1000); + + // Format date and time using the chip's timezone for consistency + const dateOptions: Intl.DateTimeFormatOptions = { + weekday: "short", + day: "numeric", + month: "long", + timeZone: timezone, // Use chip's timezone + }; + const timeOptions: Intl.DateTimeFormatOptions = { + hour: "numeric", + minute: "2-digit", + hour12: true, + timeZone: timezone, // Use chip's timezone + }; + + const date = startDate.toLocaleDateString("en-US", dateOptions); + const startTime = startDate + .toLocaleTimeString("en-US", timeOptions) + .toLowerCase() + .replace(/\s/g, " "); + const endTime = endDate + .toLocaleTimeString("en-US", timeOptions) + .toLowerCase() + .replace(/\s/g, " "); + + // ISO format for Cal.com + const isoDate = startDate.toISOString().split("T")[0]; // "2025-11-27" + const isoTimestamp = startDate.toISOString(); // "2025-11-27T05:30:00.000Z" + + slots.push({ + date, + startTime, + endTime, + durationMinutes, + isoDate, + isoTimestamp, + googleUrl: href, + }); + }); + + if (slots.length === 0) { + console.warn("No valid slots parsed from Google chip"); + return null; + } + + return { + scheduleId, + timezone, + timezoneOffset, + slots, + detectedDuration, + }; + } catch (error) { + console.error("Error parsing Google chip:", error); + return null; + } +} diff --git a/companion/utils/parsers/event-type-parsers.ts b/companion/utils/parsers/event-type-parsers.ts new file mode 100644 index 0000000000..61d30a6e05 --- /dev/null +++ b/companion/utils/parsers/event-type-parsers.ts @@ -0,0 +1,29 @@ +/** + * Helper functions to parse form values into API-compatible formats + */ + +export const parseBufferTime = (buffer: string): number => { + const match = buffer.match(/(\d+)/); + return match ? parseInt(match[1]) : 0; +}; + +export const parseMinimumNotice = (value: string, unit: string): number => { + const val = parseInt(value) || 0; + if (unit === "Hours") return val * 60; + if (unit === "Days") return val * 24 * 60; + return val; // Minutes +}; + +export const parseFrequencyUnit = (unit: string): string | null => { + const normalized = unit.toLowerCase(); + if (normalized.includes("day")) return "day"; + if (normalized.includes("week")) return "week"; + if (normalized.includes("month")) return "month"; + if (normalized.includes("year")) return "year"; + return null; +}; + +export const parseSlotInterval = (interval: string): number => { + const match = interval.match(/(\d+)/); + return match ? parseInt(match[1]) : 0; +}; diff --git a/companion/utils/slugify.ts b/companion/utils/slugify.ts new file mode 100644 index 0000000000..26704f4077 --- /dev/null +++ b/companion/utils/slugify.ts @@ -0,0 +1,33 @@ +// forDisplayingInput is used to allow user to type "-" at the end and not replace with empty space. +// For eg:- "test-slug" is the slug user wants to set but while typing "test-" would get replace to "test" because of replace(/-+$/, "") + +export const slugify = (str: string, forDisplayingInput?: boolean) => { + if (!str) { + return ""; + } + + const s = str + .toLowerCase() // Convert to lowercase + .trim() // Remove whitespace from both sides + .normalize("NFD") // Normalize to decomposed form for handling accents + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + .replace(/\p{Diacritic}/gu, "") // Remove any diacritics (accents) from characters + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + .replace(/[^.\p{L}\p{N}\p{Zs}\p{Emoji}]+/gu, "-") // Replace any non-alphanumeric characters (including Unicode and except "." period) with a dash + .replace(/[\s_#]+/g, "-") // Replace whitespace, # and underscores with a single dash + .replace(/^-+/, "") // Remove dashes from start + .replace(/\.{2,}/g, ".") // Replace consecutive periods with a single period + .replace(/^\.+/, "") // Remove periods from the start + .replace( + /([\u2700-\u27BF]|[\uE000-\uF8FF]|\uD83C[\uDC00-\uDFFF]|\uD83D[\uDC00-\uDFFF]|[\u2011-\u26FF]|\uD83E[\uDD10-\uDDFF])/g, + "" + ) // Removes emojis + .replace(/\s+/g, " ") + .replace(/-+/g, "-"); // Replace consecutive dashes with a single dash + + return forDisplayingInput ? s : s.replace(/-+$/, "").replace(/\.*$/, ""); // Remove dashes and period from end +}; + +export default slugify; diff --git a/companion/wxt.config.ts b/companion/wxt.config.ts new file mode 100644 index 0000000000..f93e8dea94 --- /dev/null +++ b/companion/wxt.config.ts @@ -0,0 +1,63 @@ +import { defineConfig } from "wxt"; + +export default defineConfig({ + srcDir: "extension", + entrypointsDir: "entrypoints", + publicDir: "extension/public", + outDir: ".output", + manifest: { + name: "Cal.com Companion", + version: "1.7.0", + description: "Your calendar companion for quick booking and scheduling", + permissions: ["activeTab", "storage", "identity"], + host_permissions: [ + "http://localhost:8081/*", + "https://api.cal.com/*", + "https://app.cal.com/*", + "https://mail.google.com/*", + ], + content_security_policy: { + extension_pages: + "script-src 'self'; object-src 'self'; frame-src 'self' http://localhost:8081;", + }, + action: { + default_title: "Cal.com Companion", + default_icon: { + "16": "icon-16.png", + "48": "icon-48.png", + "128": "icon-128.png", + }, + }, + icons: { + "16": "icon-16.png", + "48": "icon-48.png", + "128": "icon-128.png", + }, + }, + vite: () => ({ + resolve: { + alias: { + "react-native": "react-native-web", + }, + }, + define: { + global: "globalThis", + __DEV__: JSON.stringify(false), + // Expose environment variables to the extension + "import.meta.env.EXPO_PUBLIC_CALCOM_OAUTH_CLIENT_ID": JSON.stringify( + process.env.EXPO_PUBLIC_CALCOM_OAUTH_CLIENT_ID + ), + "import.meta.env.EXPO_PUBLIC_CALCOM_OAUTH_REDIRECT_URI": JSON.stringify( + process.env.EXPO_PUBLIC_CALCOM_OAUTH_REDIRECT_URI + ), + }, + optimizeDeps: { + include: ["react-native-web"], + esbuildOptions: { + loader: { + ".js": "jsx", + }, + }, + }, + }), +});