diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000000..61ef9aa16c
Binary files /dev/null and b/.DS_Store differ
diff --git a/.env b/.env
index 1d44286e25..b3a51cce41 100644
--- a/.env
+++ b/.env
@@ -5,22 +5,27 @@ DOMAIN=localhost
# To test the local Traefik config
# DOMAIN=localhost.tiangolo.com
+
# Used by the backend to generate links in emails to the frontend
FRONTEND_HOST=http://localhost:5173
# In staging and production, set this env var to the frontend host, e.g.
# FRONTEND_HOST=https://dashboard.example.com
+
# Environment: local, staging, production
ENVIRONMENT=local
+
PROJECT_NAME="Full Stack FastAPI Project"
STACK_NAME=full-stack-fastapi-project
+
# Backend
-BACKEND_CORS_ORIGINS="http://localhost,http://localhost:5173,https://localhost,https://localhost:5173,http://localhost.tiangolo.com"
-SECRET_KEY=changethis
+BACKEND_CORS_ORIGINS="http://localhost,http://localhost:5173,http://localhost:8081,https://localhost,https://localhost:5173,https://localhost:8081,http://localhost.tiangolo.com"
+SECRET_KEY=VfIGsNDeZkTOiSHIgoG9DjhpTyaLGBb-lZvYa8-wbTM
FIRST_SUPERUSER=admin@example.com
-FIRST_SUPERUSER_PASSWORD=changethis
+FIRST_SUPERUSER_PASSWORD=ierjXObp0qpZq82J
+
# Emails
SMTP_HOST=
@@ -31,15 +36,21 @@ SMTP_TLS=True
SMTP_SSL=False
SMTP_PORT=587
+
# Postgres
POSTGRES_SERVER=localhost
POSTGRES_PORT=5432
POSTGRES_DB=app
POSTGRES_USER=postgres
-POSTGRES_PASSWORD=changethis
+POSTGRES_PASSWORD=SP52qKZTPKFTpi0w7adJJS80G4BpGDC4X2uxH5H7FYY
+
SENTRY_DSN=
+
# Configure these with your own Docker registry images
DOCKER_IMAGE_BACKEND=backend
DOCKER_IMAGE_FRONTEND=frontend
+
+
+
diff --git a/.expo/README.md b/.expo/README.md
new file mode 100644
index 0000000000..fd146b4d3a
--- /dev/null
+++ b/.expo/README.md
@@ -0,0 +1,15 @@
+> Why do I have a folder named ".expo" in my project?
+
+The ".expo" folder is created when an Expo project is started using "expo start" command.
+
+> What do the files contain?
+
+- "devices.json": contains information about devices that have recently opened this project. This is used to populate the "Development sessions" list in your development builds.
+- "packager-info.json": contains port numbers and process PIDs that are used to serve the application to the mobile device/simulator.
+- "settings.json": contains the server configuration that is used to serve the application manifest.
+
+> Should I commit the ".expo" folder?
+
+No, you should not share the ".expo" folder. It does not contain any information that is relevant for other developers working on the project, it is specific to your machine.
+
+Upon project creation, the ".expo" folder is already added to your ".gitignore" file.
diff --git a/.expo/settings.json b/.expo/settings.json
new file mode 100644
index 0000000000..92bc513bfd
--- /dev/null
+++ b/.expo/settings.json
@@ -0,0 +1,8 @@
+{
+ "hostType": "lan",
+ "lanType": "ip",
+ "dev": true,
+ "minify": false,
+ "urlRandomness": null,
+ "https": false
+}
diff --git a/KonditionExpo/.gitignore b/KonditionExpo/.gitignore
new file mode 100644
index 0000000000..f610ec0d64
--- /dev/null
+++ b/KonditionExpo/.gitignore
@@ -0,0 +1,39 @@
+# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files
+
+# dependencies
+node_modules/
+
+# 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.*
+
+# macOS
+.DS_Store
+*.pem
+
+# local env files
+.env*.local
+
+# typescript
+*.tsbuildinfo
+
+app-example
diff --git a/KonditionExpo/README.md b/KonditionExpo/README.md
new file mode 100644
index 0000000000..48dd63ff3e
--- /dev/null
+++ b/KonditionExpo/README.md
@@ -0,0 +1,50 @@
+# Welcome to your Expo app π
+
+This is an [Expo](https://expo.dev) project created with [`create-expo-app`](https://www.npmjs.com/package/create-expo-app).
+
+## Get started
+
+1. Install dependencies
+
+ ```bash
+ npm install
+ ```
+
+2. Start the app
+
+ ```bash
+ npx expo start
+ ```
+
+In the output, you'll find options to open the app in a
+
+- [development build](https://docs.expo.dev/develop/development-builds/introduction/)
+- [Android emulator](https://docs.expo.dev/workflow/android-studio-emulator/)
+- [iOS simulator](https://docs.expo.dev/workflow/ios-simulator/)
+- [Expo Go](https://expo.dev/go), a limited sandbox for trying out app development with Expo
+
+You can start developing by editing the files inside the **app** directory. This project uses [file-based routing](https://docs.expo.dev/router/introduction).
+
+## Get a fresh project
+
+When you're ready, run:
+
+```bash
+npm run reset-project
+```
+
+This command will move the starter code to the **app-example** directory and create a blank **app** directory where you can start developing.
+
+## Learn more
+
+To learn more about developing your project with Expo, look at the following resources:
+
+- [Expo documentation](https://docs.expo.dev/): Learn fundamentals, or go into advanced topics with our [guides](https://docs.expo.dev/guides).
+- [Learn Expo tutorial](https://docs.expo.dev/tutorial/introduction/): Follow a step-by-step tutorial where you'll create a project that runs on Android, iOS, and the web.
+
+## Join the community
+
+Join our community of developers creating universal apps.
+
+- [Expo on GitHub](https://github.com/expo/expo): View our open source platform and contribute.
+- [Discord community](https://chat.expo.dev): Chat with Expo users and ask questions.
diff --git a/KonditionExpo/TESTING_GUIDE.md b/KonditionExpo/TESTING_GUIDE.md
new file mode 100644
index 0000000000..7d406c883d
--- /dev/null
+++ b/KonditionExpo/TESTING_GUIDE.md
@@ -0,0 +1,177 @@
+# Authentication Testing Guide
+
+## Quick Start
+
+### 1. Start the Backend
+
+**Option A: Using Docker Compose (Recommended)**
+```bash
+cd KonditionFastAPI
+docker compose watch
+```
+
+**Option B: Direct Python**
+```bash
+cd KonditionFastAPI
+uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
+```
+
+### 2. Test on Different Platforms
+
+#### Web (Should work immediately)
+```bash
+cd KonditionExpo
+npm run web
+```
+
+#### iOS Simulator (Should work immediately)
+```bash
+npm run ios
+```
+
+#### Android Emulator (Should work immediately)
+```bash
+npm run android
+```
+
+#### Physical Device / Expo Go
+1. Find your computer's IP address:
+ - **macOS**: `ifconfig | grep "inet " | grep -v 127.0.0.1`
+ - **Windows**: `ipconfig`
+ - **Linux**: `ip addr show`
+
+2. Start Expo:
+ ```bash
+ npm start
+ ```
+
+3. Open the app and go to **Profile > Developer Tools**
+
+4. Set custom URL: `http://[YOUR_IP]:8000/api/v1`
+
+5. Test the connection using the "Test API Connection" button
+
+## Testing Checklist
+
+### Signout Functionality
+- [ ] Click signout button in profile screen
+- [ ] Confirmation dialog appears
+- [ ] Loading state shows "Signing Out..."
+- [ ] Successfully redirects to login screen
+- [ ] No errors in console
+
+### Network Connectivity
+- [ ] Web platform connects to localhost:8000
+- [ ] iOS Simulator connects to localhost:8000
+- [ ] Android Emulator connects to 10.0.2.2:8000
+- [ ] Physical device connects with custom IP
+- [ ] Error messages are user-friendly
+
+### DevTools
+- [ ] Developer Tools accessible from profile
+- [ ] Shows current API URL
+- [ ] Connection test works
+- [ ] Custom URL can be set
+- [ ] Quick URL buttons work
+
+## Common Issues & Solutions
+
+### "Network request failed"
+1. Check if backend is running: `curl http://localhost:8000/docs`
+2. Verify backend is accessible externally: `curl http://[YOUR_IP]:8000/docs`
+3. Check firewall settings
+4. Ensure devices are on same network
+
+### Signout not working
+1. Check console for error messages
+2. Clear app data/cache
+3. Restart the app
+4. Check AsyncStorage permissions
+
+### DevTools not showing
+1. Make sure you're on the Profile tab
+2. Scroll down to "Other" section
+3. Look for blue "Developer Tools" text
+
+## Debug Information
+
+The app now logs detailed information to help with debugging:
+
+```
+Platform detected: ios (ios_simulator)
+Using API URL: http://localhost:8000/api/v1
+Making request to: http://localhost:8000/api/v1/login/access-token
+Starting logout process...
+Logout successful
+```
+
+Check the console/logs for these messages to understand what's happening.
+
+## Network Setup for Mobile Testing
+
+### Step 1: Find Your IP
+```bash
+# macOS/Linux
+ifconfig | grep "inet " | grep -v 127.0.0.1
+
+# Windows
+ipconfig
+```
+
+### Step 2: Start Backend with External Access
+```bash
+uvicorn app.main:app --host 0.0.0.0 --port 8000
+```
+
+### Step 3: Configure Mobile App
+1. Open app on mobile device
+2. Go to Profile > Developer Tools
+3. Enter: `http://[YOUR_IP]:8000/api/v1`
+4. Test connection
+
+### Step 4: Verify Setup
+- Backend accessible: `http://[YOUR_IP]:8000/docs`
+- API working: Test connection in DevTools
+- Authentication: Try login/logout
+
+## Platform-Specific Notes
+
+### Web
+- Uses localhost:8000 automatically
+- No additional setup required
+- Best for initial development
+
+### iOS Simulator
+- Uses localhost:8000 automatically
+- Behaves like web platform
+- Good for iOS-specific testing
+
+### Android Emulator
+- Uses 10.0.2.2:8000 automatically
+- Special IP for Android emulator networking
+- No manual configuration needed
+
+### Physical Device
+- Requires manual IP configuration
+- Use DevTools for easy setup
+- Must be on same network as computer
+
+## Success Indicators
+
+β
**Authentication Working**:
+- Login redirects to main app
+- Logout shows confirmation and redirects to login
+- User data loads correctly
+- No network errors
+
+β
**Platform Compatibility**:
+- Web works with localhost
+- Mobile works with appropriate URLs
+- Error messages are helpful
+- DevTools provide easy testing
+
+β
**User Experience**:
+- Loading states show during auth actions
+- Clear error messages for failures
+- Smooth navigation between screens
+- No crashes or freezes
\ No newline at end of file
diff --git a/KonditionExpo/app.json b/KonditionExpo/app.json
new file mode 100644
index 0000000000..d44ba2be29
--- /dev/null
+++ b/KonditionExpo/app.json
@@ -0,0 +1,53 @@
+{
+ "expo": {
+ "name": "KonditionExpo",
+ "slug": "KonditionExpo",
+ "version": "1.0.0",
+ "orientation": "portrait",
+ "icon": "./assets/images/icon.png",
+ "scheme": "konditionexpo",
+ "userInterfaceStyle": "automatic",
+ "newArchEnabled": true,
+ "notification": {
+ "vapidPublicKey": "BHIYUSgOOx7MC1EXPT5HSZ6snrLWCe4uV2tloytm3uB8rZtTiNl5234SLOWPzdBa8TcIr1CubVkh_8jXipesK8E",
+ "serviceWorkerPath": "service-worker.js"
+ },
+ "ios": {
+ "supportsTablet": true
+ },
+ "android": {
+ "adaptiveIcon": {
+ "foregroundImage": "./assets/images/adaptive-icon.png",
+ "backgroundColor": "#ffffff"
+ },
+ "edgeToEdgeEnabled": true
+ },
+ "web": {
+ "bundler": "metro",
+ "output": "static",
+ "favicon": "./assets/images/favicon.png"
+ },
+ "plugins": [
+ "expo-router",
+ [
+ "expo-splash-screen",
+ {
+ "image": "./assets/images/splash-icon.png",
+ "imageWidth": 200,
+ "resizeMode": "contain",
+ "backgroundColor": "#ffffff"
+ }
+ ],
+ [
+ "expo-notifications",
+ {
+ "icon": "./assets/images/bell.png",
+ "color": "#ffffff"
+ }
+ ]
+ ],
+ "experiments": {
+ "typedRoutes": true
+ }
+ }
+}
diff --git a/KonditionExpo/app/(tabs)/_layout.tsx b/KonditionExpo/app/(tabs)/_layout.tsx
new file mode 100644
index 0000000000..bb910c7a4e
--- /dev/null
+++ b/KonditionExpo/app/(tabs)/_layout.tsx
@@ -0,0 +1,73 @@
+import { Tabs } from 'expo-router';
+import React from 'react';
+import { Platform } from 'react-native';
+
+import { HapticTab } from '@/components/HapticTab';
+import { IconSymbol } from '@/components/ui/IconSymbol';
+import TabBarBackground from '@/components/ui/TabBarBackground';
+import { Colors } from '@/constants/Colors';
+import { useColorScheme } from '@/hooks/useColorScheme';
+
+export default function TabLayout() {
+ const colorScheme = useColorScheme();
+
+ return (
+
+ ,
+ }}
+ />
+ ,
+ }}
+ />
+ ,
+ }}
+ />
+ ,
+ }}
+ />
+ ,
+ }}
+ />
+ ,
+ }}
+ />
+
+ );
+}
diff --git a/KonditionExpo/app/(tabs)/explore.tsx b/KonditionExpo/app/(tabs)/explore.tsx
new file mode 100644
index 0000000000..1e3f34ba8d
--- /dev/null
+++ b/KonditionExpo/app/(tabs)/explore.tsx
@@ -0,0 +1,147 @@
+import { StyleSheet, ScrollView, View } from 'react-native';
+import { Ionicons } from '@expo/vector-icons';
+
+import { ThemedText } from '@/components/ThemedText';
+import { ThemedView } from '@/components/ThemedView';
+import { useThemeColor } from '@/hooks/useThemeColor';
+
+interface FeatureItemProps {
+ number: number;
+ title: string;
+ icon: keyof typeof Ionicons.glyphMap;
+}
+
+const FeatureItem = ({ number, title, icon }: FeatureItemProps) => {
+ const tintColor = useThemeColor({}, 'tint');
+
+ return (
+
+
+ {number}
+
+
+ {title}
+
+
+
+ );
+};
+
+export default function ExploreScreen() {
+ const backgroundColor = useThemeColor({}, 'background');
+
+ return (
+
+
+
+ FEATURES
+
+
+ Explore all the features Kondition has to offer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ },
+ headerContainer: {
+ padding: 20,
+ paddingTop: 60,
+ alignItems: 'center',
+ marginBottom: 20,
+ },
+ headerTitle: {
+ fontSize: 32,
+ fontWeight: 'bold',
+ marginBottom: 10,
+ textAlign: 'center',
+ },
+ headerSubtitle: {
+ fontSize: 16,
+ textAlign: 'center',
+ opacity: 0.8,
+ marginBottom: 10,
+ },
+ featureItem: {
+ marginHorizontal: 20,
+ marginBottom: 16,
+ borderRadius: 12,
+ overflow: 'hidden',
+ },
+ featureNumberContainer: {
+ paddingVertical: 8,
+ paddingHorizontal: 16,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ featureNumber: {
+ color: 'white',
+ fontWeight: 'bold',
+ fontSize: 18,
+ },
+ featureContent: {
+ padding: 16,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ },
+ featureTitle: {
+ fontSize: 18,
+ fontWeight: 'bold',
+ },
+ featureIcon: {
+ marginLeft: 8,
+ },
+});
diff --git a/KonditionExpo/app/(tabs)/feed.tsx b/KonditionExpo/app/(tabs)/feed.tsx
new file mode 100644
index 0000000000..fbe72440c6
--- /dev/null
+++ b/KonditionExpo/app/(tabs)/feed.tsx
@@ -0,0 +1,123 @@
+import React, { useEffect, useState } from 'react';
+import { View, StyleSheet, Alert } from 'react-native';
+import { router } from 'expo-router';
+import { useFeed, FeedType } from '../../contexts/FeedContext';
+import { FeedToggle, PostList, CreatePostButton } from '../../components/feed';
+import { WorkoutPostResponse } from '../../services/api';
+
+export default function FeedScreen() {
+ const {
+ getCurrentFeed,
+ currentFeedType,
+ setCurrentFeedType,
+ loadFeed,
+ loadMorePosts,
+ refreshFeed,
+ isLoading,
+ error,
+ hasMore,
+ personalFeed,
+ publicFeed,
+ combinedFeed,
+ clearError,
+ } = useFeed();
+
+ const [isRefreshing, setIsRefreshing] = useState(false);
+
+ useEffect(() => {
+ // Load initial feed when component mounts
+ loadFeed(currentFeedType, true);
+ }, []);
+
+ useEffect(() => {
+ // Show error alert if there's an error
+ if (error) {
+ Alert.alert('Error', error, [
+ { text: 'OK', onPress: clearError }
+ ]);
+ }
+ }, [error, clearError]);
+
+ const handleFeedTypeChange = async (feedType: FeedType) => {
+ setCurrentFeedType(feedType);
+
+ // Load feed if it's empty or switch to a different type
+ const targetFeed = getFeedForType(feedType);
+ if (targetFeed.length === 0) {
+ await loadFeed(feedType, true);
+ }
+ };
+
+ const getFeedForType = (feedType: FeedType): WorkoutPostResponse[] => {
+ switch (feedType) {
+ case 'personal':
+ return personalFeed;
+ case 'public':
+ return publicFeed;
+ case 'combined':
+ return combinedFeed;
+ default:
+ return personalFeed;
+ }
+ };
+
+ const handleRefresh = async () => {
+ setIsRefreshing(true);
+ try {
+ await refreshFeed();
+ } finally {
+ setIsRefreshing(false);
+ }
+ };
+
+ const handleLoadMore = async () => {
+ await loadMorePosts();
+ };
+
+ const handleCreatePost = () => {
+ router.push('/create-post' as any);
+ };
+
+ const handlePostPress = (post: WorkoutPostResponse) => {
+ // Navigate to post detail or user profile
+ console.log('Post pressed:', post.id);
+ // TODO: Implement post detail navigation
+ };
+
+ const postCounts = {
+ personal: personalFeed.length,
+ public: publicFeed.length,
+ combined: combinedFeed.length,
+ };
+
+ const currentFeed = getCurrentFeed();
+
+ return (
+
+
+
+
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: '#f5f5f5',
+ },
+});
\ No newline at end of file
diff --git a/KonditionExpo/app/(tabs)/history.tsx b/KonditionExpo/app/(tabs)/history.tsx
new file mode 100644
index 0000000000..a995d73391
--- /dev/null
+++ b/KonditionExpo/app/(tabs)/history.tsx
@@ -0,0 +1,362 @@
+import React, { useState, useEffect } from 'react';
+import {
+ SafeAreaView,
+ View,
+ Text,
+ StyleSheet,
+ ScrollView,
+ TouchableOpacity,
+ RefreshControl,
+ ActivityIndicator,
+ Alert,
+} from 'react-native';
+import { router } from 'expo-router';
+import { apiService, WorkoutResponse } from '@/services/api';
+import { useAuth } from '@/contexts/AuthContext';
+
+const WorkoutHistoryScreen = () => {
+ const { user } = useAuth();
+ const [workouts, setWorkouts] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [refreshing, setRefreshing] = useState(false);
+ const [error, setError] = useState(null);
+
+ const fetchWorkouts = async (isRefresh = false) => {
+ try {
+ if (isRefresh) {
+ setRefreshing(true);
+ } else {
+ setLoading(true);
+ }
+ setError(null);
+
+ const workoutData = await apiService.getWorkouts();
+ // Ensure workoutData is an array
+ if (Array.isArray(workoutData)) {
+ setWorkouts(workoutData);
+ } else {
+ console.warn('API returned non-array data:', workoutData);
+ setWorkouts([]);
+ setError('Invalid data format received from server');
+ }
+ } catch (err) {
+ console.error('Error fetching workouts:', err);
+ setError(err instanceof Error ? err.message : 'Failed to load workouts');
+ setWorkouts([]); // Ensure workouts is always an array
+ } finally {
+ setLoading(false);
+ setRefreshing(false);
+ }
+ };
+
+ useEffect(() => {
+ fetchWorkouts();
+ }, []);
+
+ const onRefresh = () => {
+ fetchWorkouts(true);
+ };
+
+ const formatDate = (dateString: string) => {
+ const date = new Date(dateString);
+ return date.toLocaleDateString('en-US', {
+ year: 'numeric',
+ month: 'short',
+ day: 'numeric',
+ });
+ };
+
+ const formatDuration = (minutes?: number) => {
+ if (!minutes) return 'N/A';
+ const hours = Math.floor(minutes / 60);
+ const mins = minutes % 60;
+ if (hours > 0) {
+ return `${hours}h ${mins}m`;
+ }
+ return `${mins}m`;
+ };
+
+ const getCompletionStatus = (workout: WorkoutResponse) => {
+ return workout.is_completed ? 'Completed' : 'Incomplete';
+ };
+
+ const getStatusColor = (workout: WorkoutResponse) => {
+ return workout.is_completed ? '#4CAF50' : '#FF9800';
+ };
+
+ const handleWorkoutPress = (workout: WorkoutResponse) => {
+ // Navigate to the detailed workout view
+ router.push({
+ pathname: '/workout-detail',
+ params: { workoutId: workout.id }
+ });
+ };
+
+ const renderWorkoutItem = (workout: WorkoutResponse) => (
+ handleWorkoutPress(workout)}
+ >
+
+ {workout.name}
+
+ {getCompletionStatus(workout)}
+
+
+
+
+ {formatDate(workout.completed_date || workout.created_at)}
+
+
+
+
+ Duration
+ {formatDuration(workout.duration_minutes)}
+
+
+
+ Exercises
+ {workout.exercise_count || 0}
+
+
+
+ {workout.description && (
+
+ {workout.description}
+
+ )}
+
+
+ Tap to view exercises β
+
+
+ );
+
+ const renderEmptyState = () => (
+
+ No Workouts Yet
+
+ Start your fitness journey by completing your first workout!
+
+
+ );
+
+ const renderErrorState = () => (
+
+ Unable to Load Workouts
+ {error}
+ fetchWorkouts()}>
+ Try Again
+
+
+ );
+
+ if (loading && !refreshing) {
+ return (
+
+
+
+ Loading workout history...
+
+
+ );
+ }
+
+ return (
+
+
+ Workout History
+
+ {workouts.length} {workouts.length === 1 ? 'workout' : 'workouts'} completed
+
+
+
+
+ }
+ >
+ {error ? (
+ renderErrorState()
+ ) : !Array.isArray(workouts) || workouts.length === 0 ? (
+ renderEmptyState()
+ ) : (
+ workouts.map(renderWorkoutItem)
+ )}
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: '#FFFFFF',
+ },
+ header: {
+ padding: 20,
+ backgroundColor: '#F8F9FA',
+ borderBottomWidth: 1,
+ borderBottomColor: '#E9ECEF',
+ },
+ headerTitle: {
+ fontSize: 28,
+ fontWeight: 'bold',
+ color: '#333',
+ marginBottom: 4,
+ },
+ headerSubtitle: {
+ fontSize: 16,
+ color: '#666',
+ },
+ scrollView: {
+ flex: 1,
+ },
+ scrollContent: {
+ padding: 16,
+ paddingBottom: 80,
+ },
+ loadingContainer: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ loadingText: {
+ marginTop: 16,
+ fontSize: 16,
+ color: '#666',
+ },
+ workoutCard: {
+ backgroundColor: '#FFFFFF',
+ borderRadius: 16,
+ padding: 16,
+ marginBottom: 16,
+ shadowColor: '#000',
+ shadowOffset: {
+ width: 0,
+ height: 2,
+ },
+ shadowOpacity: 0.1,
+ shadowRadius: 4,
+ elevation: 3,
+ borderWidth: 1,
+ borderColor: '#F0F0F0',
+ },
+ workoutHeader: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ marginBottom: 8,
+ },
+ workoutName: {
+ fontSize: 18,
+ fontWeight: 'bold',
+ color: '#333',
+ flex: 1,
+ marginRight: 12,
+ },
+ statusBadge: {
+ paddingHorizontal: 8,
+ paddingVertical: 4,
+ borderRadius: 12,
+ },
+ statusText: {
+ color: '#FFFFFF',
+ fontSize: 12,
+ fontWeight: '600',
+ },
+ workoutDate: {
+ fontSize: 14,
+ color: '#666',
+ marginBottom: 12,
+ },
+ workoutDetails: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ marginBottom: 8,
+ },
+ detailItem: {
+ flex: 1,
+ },
+ detailLabel: {
+ fontSize: 12,
+ color: '#999',
+ marginBottom: 2,
+ },
+ detailValue: {
+ fontSize: 16,
+ fontWeight: '600',
+ color: '#333',
+ },
+ workoutDescription: {
+ fontSize: 14,
+ color: '#666',
+ fontStyle: 'italic',
+ marginTop: 8,
+ },
+ emptyState: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ paddingVertical: 60,
+ },
+ emptyTitle: {
+ fontSize: 24,
+ fontWeight: 'bold',
+ color: '#333',
+ marginBottom: 8,
+ },
+ emptyMessage: {
+ fontSize: 16,
+ color: '#666',
+ textAlign: 'center',
+ lineHeight: 24,
+ },
+ errorState: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ paddingVertical: 60,
+ },
+ errorTitle: {
+ fontSize: 20,
+ fontWeight: 'bold',
+ color: '#FF6B6B',
+ marginBottom: 8,
+ },
+ errorMessage: {
+ fontSize: 16,
+ color: '#666',
+ textAlign: 'center',
+ marginBottom: 20,
+ lineHeight: 24,
+ },
+ retryButton: {
+ backgroundColor: '#70A1FF',
+ paddingHorizontal: 24,
+ paddingVertical: 12,
+ borderRadius: 12,
+ },
+ retryText: {
+ color: '#FFFFFF',
+ fontSize: 16,
+ fontWeight: '600',
+ },
+ viewDetailsContainer: {
+ marginTop: 8,
+ paddingTop: 8,
+ borderTopWidth: 1,
+ borderTopColor: '#F0F0F0',
+ alignItems: 'center',
+ },
+ viewDetailsText: {
+ fontSize: 14,
+ color: '#70A1FF',
+ fontWeight: '600',
+ },
+});
+
+export default WorkoutHistoryScreen;
\ No newline at end of file
diff --git a/KonditionExpo/app/(tabs)/index.tsx b/KonditionExpo/app/(tabs)/index.tsx
new file mode 100644
index 0000000000..d5bc3f4d0a
--- /dev/null
+++ b/KonditionExpo/app/(tabs)/index.tsx
@@ -0,0 +1,2 @@
+import HomeScreen from '@/app/home';
+export default HomeScreen;
\ No newline at end of file
diff --git a/KonditionExpo/app/(tabs)/profile.tsx b/KonditionExpo/app/(tabs)/profile.tsx
new file mode 100644
index 0000000000..e1b286fc79
--- /dev/null
+++ b/KonditionExpo/app/(tabs)/profile.tsx
@@ -0,0 +1,250 @@
+import React, { useState } from 'react';
+import { SafeAreaView, View, Text, StyleSheet, ScrollView, TouchableOpacity, Image, Switch } from 'react-native';
+import { useAuth } from '@/contexts/AuthContext';
+import { router } from 'expo-router';
+import { DevTools } from '@/components/DevTools';
+import { Dialog } from '@/components/ui/Dialog';
+import { Button } from '@/components/ui/Button';
+
+const ProfileScreen = () => {
+ const { user, logout, isLoading } = useAuth();
+ const [notificationsEnabled, setNotificationsEnabled] = useState(false);
+ const [showDevTools, setShowDevTools] = useState(false);
+ const [showLogoutDialog, setShowLogoutDialog] = useState(false);
+
+ const toggleNotifications = () => setNotificationsEnabled(prev => !prev);
+
+ // Unit conversion functions
+ const cmToFeetInches = (cm: number): { feet: number; inches: number } => {
+ const totalInches = cm / 2.54;
+ const feet = Math.floor(totalInches / 12);
+ const inches = Math.round(totalInches % 12);
+ return { feet, inches };
+ };
+
+ const kgToLbs = (kg: number): number => {
+ return Math.round(kg * 2.20462);
+ };
+
+ // Calculate age from date of birth
+ const calculateAge = (dateOfBirth: string | undefined): number => {
+ if (!dateOfBirth) return 0;
+ const today = new Date();
+ const birthDate = new Date(dateOfBirth);
+ let age = today.getFullYear() - birthDate.getFullYear();
+ const monthDiff = today.getMonth() - birthDate.getMonth();
+ if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
+ age--;
+ }
+ return age;
+ };
+
+ const age = calculateAge(user?.date_of_birth);
+
+ // Display formatted height in feet and inches
+ const getDisplayHeight = (): string => {
+ if (!user?.height) return '-';
+ const { feet, inches } = cmToFeetInches(user.height);
+ return `${feet}' ${inches}"`;
+ };
+
+ // Display formatted weight in pounds
+ const getDisplayWeight = (): string => {
+ if (!user?.weight) return '-';
+ return `${kgToLbs(user.weight)} lbs`;
+ };
+
+ // Navigate to edit profile screen
+ const handleEditProfile = () => {
+ router.push('/edit-profile');
+ };
+
+ const handleLogout = () => {
+ setShowLogoutDialog(true);
+ };
+
+ const confirmLogout = async () => {
+ try {
+ console.log('Starting logout process...');
+ setShowLogoutDialog(false);
+ await logout();
+ console.log('Logout successful');
+ // Navigation will be handled automatically by ProtectedRoute
+ } catch (error) {
+ console.error('Logout error:', error);
+ // For web compatibility, we'll use a simple alert or could create another dialog
+ if (typeof window !== 'undefined') {
+ // Web environment
+ alert(`Failed to sign out: ${error instanceof Error ? error.message : 'Unknown error'}. Please try again.`);
+ } else {
+ // Mobile environment - this shouldn't happen but as fallback
+ console.error('Logout failed:', error);
+ }
+ }
+ };
+
+ const cancelLogout = () => {
+ setShowLogoutDialog(false);
+ };
+
+ return (
+
+
+ {/* Profile Box */}
+
+ {user?.full_name || user?.email || 'User'}
+ {user?.email || 'No email'}
+
+ Edit
+
+
+
+
+ Height
+ {getDisplayHeight()}
+
+
+ Weight
+ {getDisplayWeight()}
+
+
+ Age
+ {age > 0 ? age.toString() : '-'}
+
+
+
+
+ {/* Account Box */}
+
+ Account
+ Personal Data
+ Achievement
+ Activity History
+ Workout Progress
+
+
+ {/* Notification Box */}
+
+ Notification
+
+ Pop-up Notifications
+
+
+
+
+ {/* Other Box */}
+
+ Other
+ Privacy Policy
+ Contact Us
+ Settings
+ setShowDevTools(true)}>
+ Developer Tools
+
+
+
+ {/* Logout Button */}
+
+
+ {isLoading ? 'Signing Out...' : 'Sign Out'}
+
+
+
+
+ {/* DevTools Modal */}
+ setShowDevTools(false)}
+ />
+
+ {/* Logout Confirmation Dialog */}
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: { flex: 1, backgroundColor: '#FFFFFF' },
+ content: { padding: 16, paddingBottom: 80 },
+ profileBox: { backgroundColor: '#E5F1FF', borderRadius: 20, padding: 16, marginBottom: 24 },
+ profileName: { fontSize: 24, fontWeight: 'bold', color: '#333' },
+ profileEmail: { fontSize: 14, color: '#666', marginTop: 4 },
+ editBtn: { backgroundColor: '#70A1FF', borderRadius: 12, paddingVertical: 6, paddingHorizontal: 12, alignSelf: 'flex-start', marginTop: 8 },
+ editText: { color: '#FFF' },
+ statsRow: { flexDirection: 'row', justifyContent: 'space-between', marginTop: 16 },
+ statBox: { backgroundColor: '#FFF', borderRadius: 12, padding: 12, width: '30%', alignItems: 'center' },
+ statLabel: { fontSize: 12, color: '#555' },
+ statValue: { fontSize: 16, fontWeight: 'bold', color: '#333' },
+ sectionBox: { backgroundColor: '#F5F8FF', borderRadius: 16, padding: 16, marginBottom: 24 },
+ sectionTitle: { fontSize: 16, fontWeight: 'bold', color: '#333', marginBottom: 8 },
+ optionItem: { fontSize: 14, color: '#555', paddingVertical: 6 },
+ toggleRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' },
+ logoutButton: {
+ backgroundColor: '#FF6B6B',
+ borderRadius: 16,
+ padding: 16,
+ alignItems: 'center',
+ marginTop: 16,
+ marginBottom: 24
+ },
+ logoutText: {
+ color: '#FFF',
+ fontSize: 16,
+ fontWeight: 'bold'
+ },
+ dialogFooter: {
+ flexDirection: 'row',
+ gap: 12,
+ },
+ cancelButton: {
+ flex: 1,
+ },
+ confirmButton: {
+ flex: 1,
+ backgroundColor: '#FF6B6B',
+ },
+ dialogText: {
+ fontSize: 16,
+ color: '#333',
+ textAlign: 'center',
+ lineHeight: 24,
+ },
+});
+
+export default ProfileScreen;
\ No newline at end of file
diff --git a/KonditionExpo/app/(tabs)/progress.tsx b/KonditionExpo/app/(tabs)/progress.tsx
new file mode 100644
index 0000000000..33e662d6ca
--- /dev/null
+++ b/KonditionExpo/app/(tabs)/progress.tsx
@@ -0,0 +1,446 @@
+import React, { useState } from 'react';
+import {
+ SafeAreaView,
+ View,
+ Text,
+ StyleSheet,
+ ScrollView,
+ TouchableOpacity,
+ Modal,
+ Alert,
+ Dimensions
+} from 'react-native';
+import { LineChart } from 'react-native-chart-kit';
+import { useWorkout, Workout } from '@/contexts/WorkoutContext';
+import { Button } from '@/components/ui/Button';
+import { Input } from '@/components/ui/Input';
+import { router } from 'expo-router';
+
+const { width } = Dimensions.get('window');
+
+interface WorkoutItemProps {
+ workout: Workout;
+ onPress: () => void;
+}
+
+const WorkoutItem = ({ workout, onPress }: WorkoutItemProps) => {
+ const formatDate = (date: Date) => {
+ return date.toLocaleDateString('en-US', {
+ month: 'short',
+ day: 'numeric',
+ year: 'numeric'
+ });
+ };
+
+ const getTotalSets = () => {
+ return workout.exercises.reduce((total, exercise) => total + exercise.sets.length, 0);
+ };
+
+ return (
+
+
+ {workout.name}
+ {formatDate(workout.date)}
+
+
+ {workout.exercises.length} exercises
+ {getTotalSets()} sets
+ {workout.duration}min
+
+
+ );
+};
+
+const ProgressScreen = () => {
+ const { workouts, currentWorkout, startWorkout } = useWorkout();
+ const [showNewWorkoutModal, setShowNewWorkoutModal] = useState(false);
+ const [newWorkoutName, setNewWorkoutName] = useState('');
+
+ const backgroundColor = '#FFFFFF';
+ const textColor = '#333';
+ const tintColor = '#70A1FF';
+
+ const handleStartWorkout = () => {
+ if (!newWorkoutName.trim()) {
+ Alert.alert('Error', 'Please enter a workout name');
+ return;
+ }
+ startWorkout(newWorkoutName.trim());
+ setNewWorkoutName('');
+ setShowNewWorkoutModal(false);
+ router.push('../workout');
+ };
+
+ const getWeeklyWorkouts = () => {
+ const last7Days = Array.from({ length: 7 }, (_, i) => {
+ const date = new Date();
+ date.setDate(date.getDate() - (6 - i));
+ return date.toDateString();
+ });
+
+ return last7Days.map(dateString => {
+ const workoutsOnDay = workouts.filter(w =>
+ w.date.toDateString() === dateString
+ );
+ return workoutsOnDay.length;
+ });
+ };
+
+ const getWeeklyLabels = () => {
+ return Array.from({ length: 7 }, (_, i) => {
+ const date = new Date();
+ date.setDate(date.getDate() - (6 - i));
+ return date.toLocaleDateString('en-US', { weekday: 'short' });
+ });
+ };
+
+ const getTotalWorkouts = () => workouts.length;
+ const getThisWeekWorkouts = () => {
+ const startOfWeek = new Date();
+ startOfWeek.setDate(startOfWeek.getDate() - startOfWeek.getDay());
+ return workouts.filter(w => w.date >= startOfWeek).length;
+ };
+
+ const getAverageWorkoutDuration = () => {
+ if (workouts.length === 0) return 0;
+ const total = workouts.reduce((sum, w) => sum + w.duration, 0);
+ return Math.round(total / workouts.length);
+ };
+
+ const weeklyData = getWeeklyWorkouts();
+ const weeklyLabels = getWeeklyLabels();
+
+ return (
+
+
+ {/* Header */}
+
+ Progress
+
+ Track your fitness journey
+
+
+
+ {/* Current Workout Banner */}
+ {currentWorkout && (
+
+
+ Workout in Progress: {currentWorkout.name}
+
+ router.push('../workout')}
+ >
+ Continue
+
+
+ )}
+
+ {/* Stats Cards */}
+
+
+ {getTotalWorkouts()}
+ Total Workouts
+
+
+ {getThisWeekWorkouts()}
+ This Week
+
+
+ {getAverageWorkoutDuration()}
+ Avg Duration
+
+
+
+ {/* Weekly Chart */}
+ {workouts.length > 0 && (
+
+
+ Weekly Activity
+
+ tintColor,
+ strokeWidth: 2,
+ },
+ ],
+ }}
+ width={width - 64}
+ height={200}
+ chartConfig={{
+ backgroundGradientFrom: backgroundColor,
+ backgroundGradientTo: backgroundColor,
+ color: () => tintColor,
+ strokeWidth: 2,
+ decimalPlaces: 0,
+ }}
+ bezier
+ style={styles.chart}
+ />
+
+ )}
+
+ {/* Start New Workout Button */}
+
+
+
+ {/* Recent Workouts */}
+
+
+ Recent Workouts
+
+ {workouts.length === 0 ? (
+
+
+ No workouts yet. Start your first workout to track your progress!
+
+
+ ) : (
+ workouts
+ .sort((a, b) => b.date.getTime() - a.date.getTime())
+ .slice(0, 10)
+ .map((workout) => (
+ {/* Navigate to workout details */}}
+ />
+ ))
+ )}
+
+
+
+ {/* New Workout Modal */}
+ setShowNewWorkoutModal(false)}
+ >
+
+
+
+ Start New Workout
+
+
+
+
+
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: '#FFFFFF',
+ },
+ content: {
+ padding: 20,
+ paddingBottom: 100,
+ },
+ header: {
+ marginBottom: 20,
+ paddingTop: 20,
+ },
+ headerTitle: {
+ fontSize: 28,
+ fontWeight: 'bold',
+ marginBottom: 5,
+ color: '#333',
+ },
+ headerSubtitle: {
+ fontSize: 16,
+ opacity: 0.7,
+ color: '#666',
+ },
+ currentWorkoutBanner: {
+ padding: 16,
+ borderRadius: 12,
+ marginBottom: 20,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ backgroundColor: '#B07FFD',
+ },
+ currentWorkoutText: {
+ color: 'white',
+ fontSize: 16,
+ fontWeight: '600',
+ flex: 1,
+ },
+ continueBtn: {
+ backgroundColor: 'rgba(255,255,255,0.2)',
+ paddingHorizontal: 16,
+ paddingVertical: 8,
+ borderRadius: 8,
+ },
+ continueBtnText: {
+ color: 'white',
+ fontWeight: '600',
+ },
+ statsContainer: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ marginBottom: 20,
+ },
+ statCard: {
+ backgroundColor: '#E5F1FF',
+ padding: 16,
+ borderRadius: 12,
+ alignItems: 'center',
+ flex: 1,
+ marginHorizontal: 4,
+ },
+ statNumber: {
+ fontSize: 24,
+ fontWeight: 'bold',
+ color: '#333',
+ marginBottom: 4,
+ },
+ statLabel: {
+ fontSize: 12,
+ color: '#555',
+ textAlign: 'center',
+ },
+ chartContainer: {
+ marginBottom: 20,
+ backgroundColor: '#F0F6FF',
+ borderRadius: 16,
+ padding: 16,
+ },
+ sectionTitle: {
+ fontSize: 20,
+ fontWeight: 'bold',
+ marginBottom: 16,
+ color: '#333',
+ },
+ chart: {
+ borderRadius: 12,
+ },
+ actionContainer: {
+ marginBottom: 20,
+ },
+ startWorkoutBtn: {
+ marginBottom: 10,
+ backgroundColor: '#70A1FF',
+ },
+ workoutHistoryContainer: {
+ marginBottom: 20,
+ },
+ workoutItem: {
+ backgroundColor: '#F9FAFF',
+ padding: 16,
+ borderRadius: 12,
+ marginBottom: 12,
+ borderWidth: 1,
+ borderColor: '#E5F1FF',
+ },
+ workoutHeader: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ marginBottom: 8,
+ },
+ workoutName: {
+ fontSize: 16,
+ fontWeight: '600',
+ color: '#333',
+ flex: 1,
+ },
+ workoutDate: {
+ fontSize: 12,
+ color: '#666',
+ },
+ workoutStats: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ },
+ workoutStat: {
+ fontSize: 12,
+ color: '#666',
+ },
+ emptyState: {
+ padding: 40,
+ alignItems: 'center',
+ backgroundColor: '#F5F8FF',
+ borderRadius: 16,
+ marginVertical: 20,
+ },
+ emptyStateText: {
+ fontSize: 16,
+ textAlign: 'center',
+ opacity: 0.6,
+ color: '#666',
+ },
+ modalOverlay: {
+ flex: 1,
+ backgroundColor: 'rgba(0,0,0,0.5)',
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ modalContent: {
+ width: '90%',
+ padding: 20,
+ borderRadius: 12,
+ elevation: 5,
+ shadowColor: '#000',
+ shadowOffset: { width: 0, height: 2 },
+ shadowOpacity: 0.25,
+ shadowRadius: 4,
+ backgroundColor: '#FFFFFF',
+ },
+ modalTitle: {
+ fontSize: 20,
+ fontWeight: 'bold',
+ marginBottom: 20,
+ textAlign: 'center',
+ color: '#333',
+ },
+ modalInput: {
+ marginBottom: 20,
+ },
+ modalButtons: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ },
+ modalButton: {
+ flex: 1,
+ marginHorizontal: 5,
+ },
+});
+
+export default ProgressScreen;
\ No newline at end of file
diff --git a/KonditionExpo/app/(tabs)/social.tsx b/KonditionExpo/app/(tabs)/social.tsx
new file mode 100644
index 0000000000..730a2571dd
--- /dev/null
+++ b/KonditionExpo/app/(tabs)/social.tsx
@@ -0,0 +1,351 @@
+import React, { useState, useEffect, useCallback } from 'react';
+import {
+ SafeAreaView,
+ View,
+ Text,
+ StyleSheet,
+ ScrollView,
+ TouchableOpacity,
+ RefreshControl,
+ Alert,
+} from 'react-native';
+import { Ionicons } from '@expo/vector-icons';
+import { useSocial } from '@/contexts/SocialContext';
+import { SearchBar } from '@/components/social/SearchBar';
+import { UserList } from '@/components/social/UserList';
+import { UserSearchResult, UserProfile } from '@/services/api';
+
+type ConnectionsTab = 'following' | 'followers';
+
+const SocialScreen = () => {
+ const {
+ searchResults,
+ searchLoading,
+ searchError,
+ followers,
+ following,
+ followersLoading,
+ followingLoading,
+ followersError,
+ followingError,
+ searchUsers,
+ clearSearchResults,
+ getFollowers,
+ getFollowing,
+ refreshFollowers,
+ refreshFollowing,
+ } = useSocial();
+
+ const [activeTab, setActiveTab] = useState('following');
+ const [refreshing, setRefreshing] = useState(false);
+ const [showSearch, setShowSearch] = useState(true);
+
+ useEffect(() => {
+ // Load initial data
+ loadConnections();
+ }, []);
+
+ const loadConnections = async () => {
+ try {
+ await Promise.all([
+ getFollowing(0, 100),
+ getFollowers(0, 100),
+ ]);
+ } catch (error) {
+ console.error('Failed to load connections:', error);
+ }
+ };
+
+ const handleRefresh = async () => {
+ setRefreshing(true);
+ try {
+ if (showSearch && searchResults.length > 0) {
+ // If we're showing search results, don't refresh connections
+ setRefreshing(false);
+ return;
+ }
+
+ if (activeTab === 'following') {
+ await refreshFollowing();
+ } else {
+ await refreshFollowers();
+ }
+ } catch (error) {
+ Alert.alert('Error', 'Failed to refresh data');
+ } finally {
+ setRefreshing(false);
+ }
+ };
+
+ const handleSearch = useCallback(async (query: string) => {
+ setShowSearch(true);
+ await searchUsers(query);
+ }, [searchUsers]);
+
+ const handleClearSearch = useCallback(() => {
+ clearSearchResults();
+ setShowSearch(false);
+ }, [clearSearchResults]);
+
+ const handleUserPress = (user: UserSearchResult | UserProfile) => {
+ // TODO: Navigate to user profile screen
+ Alert.alert('User Profile', `View profile for ${user.full_name || user.email}`);
+ };
+
+ const renderTabButton = (tab: ConnectionsTab, label: string, count: number) => (
+ setActiveTab(tab)}
+ >
+
+ {label}
+
+
+ {count}
+
+
+ );
+
+ const renderSearchSection = () => (
+
+
+
+ {searchError && (
+
+
+ {searchError}
+
+ )}
+
+ {showSearch && (
+
+
+ Search Results ({searchResults.length})
+
+
+
+ )}
+
+ );
+
+ const renderConnectionsSection = () => {
+ if (showSearch && searchResults.length > 0) {
+ return null;
+ }
+
+ const currentUsers = activeTab === 'following' ? following : followers;
+ const currentLoading = activeTab === 'following' ? followingLoading : followersLoading;
+ const currentError = activeTab === 'following' ? followingError : followersError;
+
+ // Debug logging
+ console.log(`[Social] Active tab: ${activeTab}, Users count: ${currentUsers.length}`);
+ if (activeTab === 'following' && currentUsers.length > 0) {
+ console.log(`[Social] Following users:`, currentUsers.map(u => ({
+ name: u.full_name || u.email,
+ is_following: 'is_following' in u ? u.is_following : 'N/A'
+ })));
+ }
+
+ return (
+
+ My Connections
+
+
+ {renderTabButton('following', 'Following', following.length)}
+ {renderTabButton('followers', 'Followers', followers.length)}
+
+
+ {currentError && (
+
+
+ {currentError}
+
+ )}
+
+
+
+ );
+ };
+
+ return (
+
+
+ Social
+
+ Connect with other fitness enthusiasts
+
+
+
+
+ }
+ >
+ {renderSearchSection()}
+ {renderConnectionsSection()}
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: '#FFFFFF',
+ },
+ header: {
+ padding: 20,
+ backgroundColor: '#F8F9FA',
+ borderBottomWidth: 1,
+ borderBottomColor: '#E9ECEF',
+ },
+ headerTitle: {
+ fontSize: 28,
+ fontWeight: 'bold',
+ color: '#333',
+ marginBottom: 4,
+ },
+ headerSubtitle: {
+ fontSize: 16,
+ color: '#666',
+ },
+ scrollView: {
+ flex: 1,
+ },
+ scrollContent: {
+ paddingBottom: 80,
+ },
+ searchSection: {
+ backgroundColor: '#FFFFFF',
+ },
+ searchResults: {
+ marginTop: 8,
+ },
+ connectionsSection: {
+ marginTop: 16,
+ },
+ sectionTitle: {
+ fontSize: 20,
+ fontWeight: '600',
+ color: '#333',
+ marginHorizontal: 16,
+ marginBottom: 12,
+ },
+ tabContainer: {
+ flexDirection: 'row',
+ marginHorizontal: 16,
+ marginBottom: 16,
+ backgroundColor: '#F8F9FA',
+ borderRadius: 12,
+ padding: 4,
+ },
+ tabButton: {
+ flex: 1,
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'center',
+ paddingVertical: 12,
+ paddingHorizontal: 16,
+ borderRadius: 8,
+ },
+ activeTabButton: {
+ backgroundColor: '#FFFFFF',
+ shadowColor: '#000',
+ shadowOffset: {
+ width: 0,
+ height: 1,
+ },
+ shadowOpacity: 0.1,
+ shadowRadius: 2,
+ elevation: 2,
+ },
+ tabButtonText: {
+ fontSize: 16,
+ fontWeight: '500',
+ color: '#666',
+ marginRight: 6,
+ },
+ activeTabButtonText: {
+ color: '#70A1FF',
+ fontWeight: '600',
+ },
+ tabCount: {
+ fontSize: 14,
+ fontWeight: '600',
+ color: '#999',
+ backgroundColor: '#F0F0F0',
+ paddingHorizontal: 6,
+ paddingVertical: 2,
+ borderRadius: 10,
+ minWidth: 20,
+ textAlign: 'center',
+ },
+ activeTabCount: {
+ color: '#70A1FF',
+ backgroundColor: '#E8F0FF',
+ },
+ errorContainer: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ marginHorizontal: 16,
+ marginVertical: 8,
+ padding: 12,
+ backgroundColor: '#FFF5F5',
+ borderRadius: 8,
+ borderWidth: 1,
+ borderColor: '#FED7D7',
+ },
+ errorText: {
+ marginLeft: 8,
+ fontSize: 14,
+ color: '#E53E3E',
+ flex: 1,
+ },
+});
+
+export default SocialScreen;
\ No newline at end of file
diff --git a/KonditionExpo/app/+not-found.tsx b/KonditionExpo/app/+not-found.tsx
new file mode 100644
index 0000000000..215b0ed18c
--- /dev/null
+++ b/KonditionExpo/app/+not-found.tsx
@@ -0,0 +1,32 @@
+import { Link, Stack } from 'expo-router';
+import { StyleSheet } from 'react-native';
+
+import { ThemedText } from '@/components/ThemedText';
+import { ThemedView } from '@/components/ThemedView';
+
+export default function NotFoundScreen() {
+ return (
+ <>
+
+
+ This screen does not exist.
+
+ Go to home screen!
+
+
+ >
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ alignItems: 'center',
+ justifyContent: 'center',
+ padding: 20,
+ },
+ link: {
+ marginTop: 15,
+ paddingVertical: 15,
+ },
+});
diff --git a/KonditionExpo/app/_layout.tsx b/KonditionExpo/app/_layout.tsx
new file mode 100644
index 0000000000..0c3a3eb102
--- /dev/null
+++ b/KonditionExpo/app/_layout.tsx
@@ -0,0 +1,160 @@
+// app/_layout.tsx
+
+import React, { useEffect, useRef, useState } from "react";
+import { Platform, Alert } from "react-native";
+
+import { DarkTheme, DefaultTheme, ThemeProvider } from "@react-navigation/native";
+import { useFonts } from "expo-font";
+import { Stack } from "expo-router";
+import { StatusBar } from "expo-status-bar";
+import { AuthProvider } from "@/contexts/AuthContext";
+import { ProtectedRoute } from "@/components/ProtectedRoute";
+import "react-native-reanimated";
+
+import { useColorScheme } from "@/hooks/useColorScheme";
+import { UserProvider } from "@/contexts/UserContext";
+import { WorkoutProvider } from "@/contexts/WorkoutContext";
+import { SocialProvider } from "@/contexts/SocialContext";
+import { FeedProvider } from "@/contexts/FeedContext";
+
+// βββ IMPORTS FOR PUSH NOTIFICATIONS βββββββββββββββββββββββββββββββββββββββββββββ
+import * as Device from "expo-device";
+import * as Notifications from "expo-notifications";
+// ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+
+export default function RootLayout() {
+ const colorScheme = useColorScheme();
+ const [loaded] = useFonts({
+ SpaceMono: require("../assets/fonts/SpaceMono-Regular.ttf"),
+ });
+
+ // βββ STATE / REFS FOR PUSH REGISTRATION ββββββββββββββββββββββββββββββββββββββββ
+ const [expoPushToken, setExpoPushToken] = useState("");
+ const notificationListener = useRef(null);
+ const responseListener = useRef(null);
+
+ // Replace this with your actual userβID retrieval logic (e.g. from AuthContext)
+ const fakeUserId = "user123";
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+
+ useEffect(() => {
+ // 1) Configure how notifications are handled when the app is foregrounded:
+ Notifications.setNotificationHandler({
+ handleNotification: async (): Promise => ({
+ shouldShowAlert: true, // (still allowed, but deprecated)
+ shouldShowBanner: true, // required on iOSβ14+ to actually display inβapp banners
+ shouldShowList: true, // required on iOSβ14+ to show in Notification Center
+ shouldPlaySound: true,
+ shouldSetBadge: false,
+ }),
+ });
+
+ // 2) Register for push notifications & get Expo Push Token
+ (async () => {
+ if (!Device.isDevice) {
+ Alert.alert("Push notifications require a physical device.");
+ return;
+ }
+
+ // 2a) Check existing permissions
+ const { status: existingStatus } = await Notifications.getPermissionsAsync();
+ let finalStatus = existingStatus;
+ if (existingStatus !== "granted") {
+ const { status } = await Notifications.requestPermissionsAsync();
+ finalStatus = status;
+ }
+ if (finalStatus !== "granted") {
+ Alert.alert("Failed to get push token for push notifications!");
+ return;
+ }
+
+ // 2b) Get the Expo Push Token
+ const tokenData = await Notifications.getExpoPushTokenAsync();
+ const token = tokenData.data;
+ console.log("Obtained Expo Push Token:", token);
+ setExpoPushToken(token);
+
+ // 2c) Send that token to your FastAPI backend
+ try {
+ await fetch("http://localhost:8000/api/v1/notifications/register_push_token", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ user_id: fakeUserId,
+ expo_token: token,
+ }),
+ });
+ } catch (err) {
+ console.error("Error sending push token to backend:", err);
+ }
+
+ // 2d) (Android only) Create a notification channel
+ if (Platform.OS === "android") {
+ await Notifications.setNotificationChannelAsync("default", {
+ name: "default",
+ importance: Notifications.AndroidImportance.MAX,
+ vibrationPattern: [0, 250, 250, 250],
+ lightColor: "#FF231F7C",
+ });
+ }
+ })();
+
+ // 3) Foregroundβnotification listener
+ notificationListener.current = Notifications.addNotificationReceivedListener(
+ (notification) => {
+ console.log("Notification Received (foreground):", notification);
+ }
+ );
+
+ // 4) Userβtap listener (background or foreground)
+ responseListener.current = Notifications.addNotificationResponseReceivedListener(
+ (response) => {
+ console.log("User tapped on notification:", response);
+ // You can navigate or handle notification.data here
+ }
+ );
+
+ return () => {
+ if (notificationListener.current) {
+ Notifications.removeNotificationSubscription(notificationListener.current);
+ }
+ if (responseListener.current) {
+ Notifications.removeNotificationSubscription(responseListener.current);
+ }
+ };
+ }, []);
+
+ if (!loaded) {
+ return null;
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/KonditionExpo/app/create-post.tsx b/KonditionExpo/app/create-post.tsx
new file mode 100644
index 0000000000..3425efe87a
--- /dev/null
+++ b/KonditionExpo/app/create-post.tsx
@@ -0,0 +1,463 @@
+import React, { useState } from 'react';
+import {
+ View,
+ Text,
+ TextInput,
+ TouchableOpacity,
+ StyleSheet,
+ ScrollView,
+ Alert,
+ Switch,
+ KeyboardAvoidingView,
+ Platform,
+} from 'react-native';
+import { router } from 'expo-router';
+import { useFeed } from '../contexts/FeedContext';
+import { WorkoutPostCreateRequest } from '../services/api';
+import { IconSymbol } from '../components/ui/IconSymbol';
+
+const WORKOUT_TYPES = [
+ 'Running',
+ 'Cycling',
+ 'Swimming',
+ 'Strength Training',
+ 'Yoga',
+ 'Pilates',
+ 'CrossFit',
+ 'Boxing',
+ 'Dancing',
+ 'Walking',
+ 'Hiking',
+ 'Other',
+];
+
+export default function CreatePostScreen() {
+ const { createPost, isLoading } = useFeed();
+
+ const [formData, setFormData] = useState({
+ title: '',
+ description: '',
+ workout_type: 'Running',
+ duration_minutes: 30,
+ calories_burned: undefined,
+ is_public: true,
+ });
+
+ const [showWorkoutTypes, setShowWorkoutTypes] = useState(false);
+ const [isSubmitting, setIsSubmitting] = useState(false);
+
+ const handleSubmit = async () => {
+ // Prevent multiple submissions
+ if (isSubmitting || isLoading) {
+ return;
+ }
+
+ // Validation
+ if (!formData.title.trim()) {
+ Alert.alert('Error', 'Please enter a title for your workout');
+ return;
+ }
+
+ if (formData.duration_minutes <= 0) {
+ Alert.alert('Error', 'Duration must be greater than 0');
+ return;
+ }
+
+ setIsSubmitting(true);
+
+ try {
+ await createPost(formData);
+
+ // Success - navigate back immediately
+ setIsSubmitting(false);
+
+ // Show a brief success message and navigate back
+ Alert.alert(
+ 'Success',
+ 'Your workout post has been created!',
+ [
+ {
+ text: 'OK',
+ onPress: () => router.back()
+ }
+ ]
+ );
+
+ // Fallback: navigate back after a short delay if alert doesn't work
+ setTimeout(() => {
+ router.back();
+ }, 2000);
+
+ } catch (error) {
+ setIsSubmitting(false);
+ console.error('Error creating post:', error);
+ Alert.alert('Error', 'Failed to create post. Please try again.');
+ }
+ };
+
+ const handleCancel = () => {
+ router.back();
+ };
+
+ const formatDuration = (minutes: number): string => {
+ if (minutes < 60) {
+ return `${minutes} min`;
+ }
+ const hours = Math.floor(minutes / 60);
+ const remainingMinutes = minutes % 60;
+ return remainingMinutes > 0 ? `${hours}h ${remainingMinutes}m` : `${hours}h`;
+ };
+
+ return (
+
+ {/* Header */}
+
+
+ Cancel
+
+ Create Post
+
+
+ {(isLoading || isSubmitting) ? 'Posting...' : 'Post'}
+
+
+
+
+
+ {/* Title */}
+
+ Title *
+ setFormData(prev => ({ ...prev, title: text }))}
+ placeholder="What did you do today?"
+ maxLength={255}
+ />
+
+
+ {/* Description */}
+
+ Description
+ setFormData(prev => ({ ...prev, description: text }))}
+ placeholder="Tell us about your workout..."
+ multiline
+ numberOfLines={4}
+ maxLength={1000}
+ />
+
+
+ {/* Workout Type */}
+
+ Workout Type *
+ setShowWorkoutTypes(!showWorkoutTypes)}
+ >
+ {formData.workout_type}
+
+
+
+ {showWorkoutTypes && (
+
+ {WORKOUT_TYPES.map((type) => (
+ {
+ setFormData(prev => ({ ...prev, workout_type: type }));
+ setShowWorkoutTypes(false);
+ }}
+ >
+
+ {type}
+
+
+ ))}
+
+ )}
+
+
+ {/* Duration */}
+
+ Duration (minutes) *
+
+ {
+ const minutes = parseInt(text) || 0;
+ setFormData(prev => ({ ...prev, duration_minutes: minutes }));
+ }}
+ keyboardType="numeric"
+ placeholder="30"
+ />
+
+ = {formatDuration(formData.duration_minutes)}
+
+
+
+
+ {/* Calories */}
+
+ Calories Burned
+ {
+ const calories = text ? parseInt(text) || undefined : undefined;
+ setFormData(prev => ({ ...prev, calories_burned: calories }));
+ }}
+ keyboardType="numeric"
+ placeholder="Optional"
+ />
+
+
+ {/* Privacy Setting */}
+
+
+
+ Privacy Setting
+
+ {formData.is_public
+ ? "Public - Visible to everyone"
+ : "Private - Only visible to mutual followers"
+ }
+
+
+ setFormData(prev => ({ ...prev, is_public: value }))}
+ trackColor={{ false: '#FF9800', true: '#4CAF50' }}
+ thumbColor="#fff"
+ />
+
+
+
+
+
+ {formData.is_public ? "Public Post" : "Private Post"}
+
+
+
+
+ {/* Preview */}
+
+ Preview
+
+ {formData.title || 'Your workout title'}
+ {formData.description && (
+ {formData.description}
+ )}
+
+
+ πββοΈ {formData.workout_type}
+
+
+ β±οΈ {formatDuration(formData.duration_minutes)}
+
+ {formData.calories_burned && (
+
+ π₯ {formData.calories_burned} cal
+
+ )}
+
+
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: '#fff',
+ },
+ header: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ paddingHorizontal: 16,
+ paddingVertical: 12,
+ borderBottomWidth: 1,
+ borderBottomColor: '#e0e0e0',
+ paddingTop: 50, // Account for status bar
+ },
+ headerButton: {
+ paddingVertical: 8,
+ paddingHorizontal: 12,
+ },
+ postButton: {
+ backgroundColor: '#007AFF',
+ borderRadius: 8,
+ },
+ headerTitle: {
+ fontSize: 18,
+ fontWeight: '600',
+ color: '#333',
+ },
+ cancelText: {
+ fontSize: 16,
+ color: '#666',
+ },
+ postText: {
+ fontSize: 16,
+ fontWeight: '600',
+ color: '#fff',
+ },
+ disabledText: {
+ opacity: 0.6,
+ },
+ content: {
+ flex: 1,
+ padding: 16,
+ },
+ section: {
+ marginBottom: 24,
+ },
+ label: {
+ fontSize: 16,
+ fontWeight: '600',
+ color: '#333',
+ marginBottom: 8,
+ },
+ textInput: {
+ borderWidth: 1,
+ borderColor: '#e0e0e0',
+ borderRadius: 8,
+ padding: 12,
+ fontSize: 16,
+ backgroundColor: '#f9f9f9',
+ },
+ textArea: {
+ height: 100,
+ textAlignVertical: 'top',
+ },
+ dropdown: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ borderWidth: 1,
+ borderColor: '#e0e0e0',
+ borderRadius: 8,
+ padding: 12,
+ backgroundColor: '#f9f9f9',
+ },
+ dropdownText: {
+ fontSize: 16,
+ color: '#333',
+ },
+ dropdownOptions: {
+ borderWidth: 1,
+ borderColor: '#e0e0e0',
+ borderRadius: 8,
+ backgroundColor: '#fff',
+ marginTop: 4,
+ maxHeight: 200,
+ },
+ dropdownOption: {
+ padding: 12,
+ borderBottomWidth: 1,
+ borderBottomColor: '#f0f0f0',
+ },
+ dropdownOptionText: {
+ fontSize: 16,
+ color: '#333',
+ },
+ selectedOption: {
+ color: '#007AFF',
+ fontWeight: '600',
+ },
+ durationContainer: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ },
+ numberInput: {
+ borderWidth: 1,
+ borderColor: '#e0e0e0',
+ borderRadius: 8,
+ padding: 12,
+ fontSize: 16,
+ backgroundColor: '#f9f9f9',
+ width: 100,
+ marginRight: 12,
+ },
+ durationDisplay: {
+ fontSize: 16,
+ color: '#666',
+ },
+ privacyContainer: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ },
+ privacyInfo: {
+ flex: 1,
+ },
+ privacyDescription: {
+ fontSize: 14,
+ color: '#666',
+ marginTop: 2,
+ },
+ privacyIndicator: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ marginTop: 8,
+ },
+ privacyText: {
+ fontSize: 14,
+ fontWeight: '500',
+ marginLeft: 6,
+ },
+ preview: {
+ backgroundColor: '#f8f9fa',
+ borderRadius: 8,
+ padding: 16,
+ },
+ previewTitle: {
+ fontSize: 18,
+ fontWeight: '700',
+ color: '#333',
+ marginBottom: 8,
+ },
+ previewDescription: {
+ fontSize: 14,
+ color: '#666',
+ marginBottom: 12,
+ },
+ previewStats: {
+ flexDirection: 'row',
+ flexWrap: 'wrap',
+ gap: 12,
+ },
+ previewStat: {
+ fontSize: 14,
+ color: '#666',
+ },
+});
\ No newline at end of file
diff --git a/KonditionExpo/app/edit-profile.tsx b/KonditionExpo/app/edit-profile.tsx
new file mode 100644
index 0000000000..23677dab38
--- /dev/null
+++ b/KonditionExpo/app/edit-profile.tsx
@@ -0,0 +1,439 @@
+import React, { useState, useEffect } from 'react';
+import { SafeAreaView, View, Text, StyleSheet, ScrollView, TouchableOpacity, Alert, Platform } from 'react-native';
+import { useAuth } from '@/contexts/AuthContext';
+import { router } from 'expo-router';
+import { Input } from '@/components/ui/Input';
+import { Button } from '@/components/ui/Button';
+import DateTimePicker from '@react-native-community/datetimepicker';
+
+const EditProfileScreen = () => {
+ const { user, updateProfile, isLoading } = useAuth();
+ const [isUpdating, setIsUpdating] = useState(false);
+ const [showDatePicker, setShowDatePicker] = useState(false);
+
+ // Form state
+ const [fullName, setFullName] = useState('');
+ const [heightFeet, setHeightFeet] = useState('');
+ const [heightInches, setHeightInches] = useState('');
+ const [weightLbs, setWeightLbs] = useState('');
+ const [dateOfBirth, setDateOfBirth] = useState(new Date());
+ const [gender, setGender] = useState('');
+
+ // Validation errors
+ const [nameError, setNameError] = useState('');
+ const [heightError, setHeightError] = useState('');
+ const [weightError, setWeightError] = useState('');
+ const [dobError, setDobError] = useState('');
+
+ // Unit conversion functions
+ const cmToFeetInches = (cm: number): { feet: number; inches: number } => {
+ const totalInches = cm / 2.54;
+ const feet = Math.floor(totalInches / 12);
+ const inches = Math.round(totalInches % 12);
+ return { feet, inches };
+ };
+
+ const feetInchesToCm = (feet: number, inches: number): number => {
+ return Math.round((feet * 12 + inches) * 2.54);
+ };
+
+ const kgToLbs = (kg: number): number => {
+ return Math.round(kg * 2.20462);
+ };
+
+ const lbsToKg = (lbs: number): number => {
+ return Math.round(lbs / 2.20462 * 100) / 100;
+ };
+
+ // Initialize form with user data
+ useEffect(() => {
+ if (user) {
+ setFullName(user.full_name || '');
+ setGender(user.gender || '');
+
+ if (user.height) {
+ const { feet, inches } = cmToFeetInches(user.height);
+ setHeightFeet(feet.toString());
+ setHeightInches(inches.toString());
+ }
+
+ if (user.weight) {
+ setWeightLbs(kgToLbs(user.weight).toString());
+ }
+
+ if (user.date_of_birth) {
+ setDateOfBirth(new Date(user.date_of_birth));
+ }
+ }
+ }, [user]);
+
+ // Validation functions
+ const validateName = (name: string): boolean => {
+ if (name.trim().length < 2) {
+ setNameError('Name must be at least 2 characters');
+ return false;
+ }
+ setNameError('');
+ return true;
+ };
+
+ const validateHeight = (feet: string, inches: string): boolean => {
+ if (!feet && !inches) {
+ setHeightError('');
+ return true; // Optional field
+ }
+
+ const feetNum = parseInt(feet);
+ const inchesNum = parseInt(inches);
+
+ if (isNaN(feetNum) || isNaN(inchesNum)) {
+ setHeightError('Please enter valid numbers');
+ return false;
+ }
+
+ if (feetNum < 3 || feetNum > 8) {
+ setHeightError('Height must be between 3 and 8 feet');
+ return false;
+ }
+
+ if (inchesNum < 0 || inchesNum >= 12) {
+ setHeightError('Inches must be between 0 and 11');
+ return false;
+ }
+
+ setHeightError('');
+ return true;
+ };
+
+ const validateWeight = (weight: string): boolean => {
+ if (!weight) {
+ setWeightError('');
+ return true; // Optional field
+ }
+
+ const weightNum = parseFloat(weight);
+
+ if (isNaN(weightNum)) {
+ setWeightError('Please enter a valid number');
+ return false;
+ }
+
+ if (weightNum < 50 || weightNum > 1000) {
+ setWeightError('Weight must be between 50 and 1000 lbs');
+ return false;
+ }
+
+ setWeightError('');
+ return true;
+ };
+
+ const validateDateOfBirth = (date: Date): boolean => {
+ const today = new Date();
+ const minDate = new Date(today.getFullYear() - 120, today.getMonth(), today.getDate());
+ const maxDate = new Date(today.getFullYear() - 13, today.getMonth(), today.getDate());
+
+ if (date < minDate || date > maxDate) {
+ setDobError('Age must be between 13 and 120 years');
+ return false;
+ }
+
+ setDobError('');
+ return true;
+ };
+
+ // Save profile
+ const handleSaveProfile = async () => {
+ // Validate all fields
+ const isNameValid = validateName(fullName);
+ const isHeightValid = validateHeight(heightFeet, heightInches);
+ const isWeightValid = validateWeight(weightLbs);
+ const isDobValid = validateDateOfBirth(dateOfBirth);
+
+ if (!isNameValid || !isHeightValid || !isWeightValid || !isDobValid) {
+ return;
+ }
+
+ try {
+ setIsUpdating(true);
+
+ const updateData: any = {
+ full_name: fullName.trim(),
+ gender: gender || undefined,
+ };
+
+ // Add height if provided
+ if (heightFeet && heightInches) {
+ updateData.height = feetInchesToCm(parseInt(heightFeet), parseInt(heightInches));
+ }
+
+ // Add weight if provided
+ if (weightLbs) {
+ updateData.weight = lbsToKg(parseFloat(weightLbs));
+ }
+
+ // Add date of birth
+ updateData.date_of_birth = dateOfBirth.toISOString().split('T')[0];
+
+ await updateProfile(updateData);
+
+ Alert.alert('Success', 'Profile updated successfully!', [
+ { text: 'OK', onPress: () => router.replace('/(tabs)/profile') }
+ ]);
+ } catch (error) {
+ Alert.alert('Error', 'Failed to update profile. Please try again.');
+ } finally {
+ setIsUpdating(false);
+ }
+ };
+
+ // Date picker handler
+ const onDateChange = (event: any, selectedDate?: Date) => {
+ if (Platform.OS === 'android') {
+ setShowDatePicker(false);
+ }
+
+ if (selectedDate) {
+ setDateOfBirth(selectedDate);
+ validateDateOfBirth(selectedDate);
+ }
+ };
+
+ return (
+
+
+ {/* Header */}
+
+ router.replace('/(tabs)/profile')} style={styles.backButton}>
+ β Back
+
+ Edit Profile
+
+
+ {/* Form */}
+
+ {/* Full Name */}
+
+
+ {/* Gender */}
+
+ Gender
+
+ {['Male', 'Female', 'Other'].map((option) => (
+ setGender(option.toLowerCase())}
+ >
+
+ {option}
+
+
+ ))}
+
+
+
+ {/* Height */}
+
+ Height
+
+
+
+ feet
+
+
+
+ inches
+
+
+ {heightError ? {heightError} : null}
+
+
+ {/* Weight */}
+
+
+
+
+ {/* Date of Birth */}
+
+ Date of Birth
+ setShowDatePicker(true)}
+ >
+
+ {dateOfBirth.toLocaleDateString()}
+
+
+ {dobError ? {dobError} : null}
+
+
+ {/* Save Button */}
+
+
+
+
+ {/* Date Picker */}
+ {showDatePicker && (
+
+ )}
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: '#FFFFFF',
+ },
+ content: {
+ padding: 16,
+ paddingBottom: 80,
+ },
+ header: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ marginBottom: 24,
+ },
+ backButton: {
+ marginRight: 16,
+ },
+ backButtonText: {
+ fontSize: 16,
+ color: '#70A1FF',
+ fontWeight: '500',
+ },
+ title: {
+ fontSize: 24,
+ fontWeight: 'bold',
+ color: '#333',
+ },
+ form: {
+ flex: 1,
+ },
+ label: {
+ fontSize: 16,
+ fontWeight: '500',
+ color: '#333',
+ marginBottom: 8,
+ },
+ genderContainer: {
+ marginBottom: 24,
+ },
+ genderButtons: {
+ flexDirection: 'row',
+ gap: 12,
+ },
+ genderButton: {
+ flex: 1,
+ paddingVertical: 12,
+ paddingHorizontal: 16,
+ borderRadius: 8,
+ borderWidth: 1,
+ borderColor: '#E2E8F0',
+ alignItems: 'center',
+ },
+ genderButtonSelected: {
+ backgroundColor: '#70A1FF',
+ borderColor: '#70A1FF',
+ },
+ genderButtonText: {
+ fontSize: 14,
+ color: '#555',
+ fontWeight: '500',
+ },
+ genderButtonTextSelected: {
+ color: '#FFF',
+ },
+ heightContainer: {
+ marginBottom: 24,
+ },
+ heightInputRow: {
+ flexDirection: 'row',
+ gap: 16,
+ },
+ heightInputGroup: {
+ flex: 1,
+ alignItems: 'center',
+ },
+ heightInput: {
+ marginBottom: 8,
+ },
+ heightLabel: {
+ fontSize: 14,
+ color: '#555',
+ },
+ weightContainer: {
+ marginBottom: 24,
+ },
+ dobContainer: {
+ marginBottom: 32,
+ },
+ dateButton: {
+ borderWidth: 1,
+ borderColor: '#E2E8F0',
+ borderRadius: 6,
+ paddingVertical: 12,
+ paddingHorizontal: 16,
+ backgroundColor: '#FFF',
+ },
+ dateButtonText: {
+ fontSize: 16,
+ color: '#333',
+ },
+ errorText: {
+ fontSize: 12,
+ color: '#E53E3E',
+ marginTop: 4,
+ },
+ saveButton: {
+ backgroundColor: '#70A1FF',
+ marginTop: 16,
+ },
+});
+
+export default EditProfileScreen;
\ No newline at end of file
diff --git a/KonditionExpo/app/home.tsx b/KonditionExpo/app/home.tsx
new file mode 100644
index 0000000000..666875cafd
--- /dev/null
+++ b/KonditionExpo/app/home.tsx
@@ -0,0 +1,265 @@
+import React, { useMemo, useState } from 'react';
+import { useAuth } from '@/contexts/AuthContext';
+import { SafeAreaView, View, Text, StyleSheet, ScrollView, TouchableOpacity, Image, Dimensions,} from 'react-native';
+import { LineChart } from 'react-native-chart-kit';
+import { router } from 'expo-router';
+import { usePersonalBests } from "@/hooks/usePersonalBests";
+import { Dialog } from '@/components/ui/Dialog';
+import { Button } from '@/components/ui/Button';
+
+const { width } = Dimensions.get('window');
+//console.log(" 1 HomeScreen about to be rendered rendered");
+const HomeScreen = () => {
+ //console.log("2 HomeScreen rendered");
+ const { user } = useAuth();
+ if (!user) {
+ console.warn("User context is undefined");
+ return null;
+ }
+ // 1) Calculate BMI (kg / mΒ²) whenever height or weight change
+ // - height is in cm -> convert to meters by dividing by 100
+ // - weight is in kg, so we can directly use it in the formula
+ const bmiValue = useMemo(() => {
+ if (!user.height || !user.weight) return null;
+ const heightMeters = user.height / 100;
+ if (heightMeters <= 0) return null;
+ const raw = user.weight / (heightMeters * heightMeters);
+ return Number(raw.toFixed(1));
+ }, [user.height, user.weight]);
+
+ // 2) Derive the status string based on BMI ranges
+ const bmiStatus = useMemo(() => {
+ if (bmiValue === null) return 'β';
+ if (bmiValue < 18.5) return 'Underweight';
+ if (bmiValue < 25) return 'Normal Weight';
+ if (bmiValue < 30) return 'Overweight';
+ if (bmiValue < 35) return 'Obesity Class 1';
+ if (bmiValue < 40) return 'Obesity Class 2';
+ return 'Obesity Class 3';
+ }, [bmiValue]);
+
+ const [showBmiDialog, setShowBmiDialog] = useState(false);
+
+ const workoutProgressData = [20, 40, 30, 60, 90, 80, 70];
+ const workoutDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
+ const latestWorkouts = [
+ { id: '1', type: 'Fullbody Workout', calories: 180, duration: '20 minutes', icon: require('../assets/images/fullbody.png') },
+ { id: '2', type: 'Lowerbody Workout', calories: 200, duration: '30 minutes', icon: require('../assets/images/lowerbody.png') },
+ // ... more workouts
+ ];
+ const { pbs, loading } = usePersonalBests();
+ const testPbs = pbs.length === 0 ? [{ metric: "Deadlift", value: 315, date: "2025-05-27" }] : pbs;
+
+ console.log('PBS LOADED:', pbs);
+ console.log("Rendering personal bests section. pbs:", pbs, "loading:", loading);
+
+ return (
+
+
+ {/* Header */}
+
+ Welcome Back,
+ {user.full_name}
+ router.push('/notification')}>
+
+
+
+
+ {/* ===== BMI Card ===== */}
+
+
+ BMI (Body Mass Index)
+ {bmiStatus}
+ setShowBmiDialog(true)}>
+ View More
+
+
+
+
+ {bmiValue !== null ? bmiValue.toString() : '-'}
+
+
+
+ {/* Today Target */}
+
+ Today Target
+ alert('Targets screen not yet implemented')}>
+ Check
+
+
+
+ {/* Workout Progress */}
+
+
+ Workout Progress
+ {/* toggle weekly/monthly */}}>
+ Weekly βΌ
+
+
+ '#B07FFD',
+ strokeWidth: 2,
+ },
+ ],
+ }}
+ width={width - 64}
+ height={100}
+ chartConfig={{
+ backgroundGradientFrom: '#fff',
+ backgroundGradientTo: '#fff',
+ color: () => '#B07FFD',
+ strokeWidth: 2,
+ }}
+ bezier
+ style={styles.progressChart}
+ />
+
+ {workoutDays.map(day => (
+ {day}
+ ))}
+
+
+
+ {/* Latest Workout */}
+
+
+ Latest Workout
+ alert('Workout List not yet implemented')}>
+ See more
+
+
+ {latestWorkouts.map(w => (
+ alert('Workout Details not yet implemented')}>
+
+
+ {w.type}
+ {w.calories} Calories Burn | {w.duration}
+
+
+
+ ))}
+
+
+ {/* Personal Bests */}
+
+
+
+ Personal Bests
+ alert('Full Personal Bests screen not implemented')}>
+ See more
+
+
+
+ {loading ? (
+ Loading personal bests...
+ ) : pbs.length === 0 ? (
+ No personal bests yet. Start training to set new records!
+ ) : (
+ testPbs.map((pb) => (
+
+
+
+ {pb.metric}
+ {pb.value} β’ {pb.date}
+
+
+ ))
+ )}
+
+
+
+
+ {/* βββ BMI Disclaimer Dialog βββ */}
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: { flex: 1, backgroundColor: '#FFFFFF' },
+ content: { padding: 16, paddingBottom: 80 },
+ header: { flexDirection: 'row', alignItems: 'center', marginBottom: 24 },
+ welcomeText: { fontSize: 16, color: '#777' },
+ userName: { fontSize: 24, fontWeight: 'bold', marginLeft: 8, color: '#333' },
+ notificationBtn: { marginLeft: 'auto' },
+ bellIcon: { width: 24, height: 24 },
+ bmiCard: {
+ flexDirection: 'row',
+ backgroundColor: '#E5F1FF',
+ borderRadius: 20,
+ padding: 16,
+ alignItems: 'center',
+ marginBottom: 24,
+ },
+ bmiInfo: { flex: 2 },
+ bmiLabel: { fontSize: 14, color: '#555' },
+ bmiStatus: { fontSize: 16, marginVertical: 4, color: '#333' },
+ viewMoreBtn: { backgroundColor: '#70A1FF', borderRadius: 12, paddingVertical: 6, paddingHorizontal: 12, marginTop: 8, alignSelf: 'flex-start' },
+ viewMoreText: { color: '#FFF' },
+ bmiPie: { height: 100, width: 100, flex: 1 },
+ bmiOverlay: { position: 'absolute', right: 32, top: 32, alignItems: 'center' },
+ bmiValue: { fontSize: 18, fontWeight: 'bold', color: '#333' },
+ footer: { flexDirection: 'row', justifyContent: 'flex-end' },
+ okButton: { backgroundColor: '#70A1FF' },
+ bodyText: { fontSize: 14, color: '#333', lineHeight: 20 },
+ targetCard: { flexDirection: 'row', backgroundColor: '#F5F8FF', borderRadius: 16, padding: 16, alignItems: 'center', marginBottom: 24 },
+ targetText: { fontSize: 16, color: '#333' },
+ checkBtn: { marginLeft: 'auto', backgroundColor: '#A3C9FD', borderRadius: 12, paddingVertical: 6, paddingHorizontal: 12 },
+ checkText: { color: '#FFF' },
+ sectionTitle: { fontSize: 18, fontWeight: 'bold', color: '#333', marginBottom: 12 },
+ activityCard: { backgroundColor: '#F0F6FF', borderRadius: 16, padding: 16, marginBottom: 24 },
+ activityLabel: { fontSize: 14, color: '#555' },
+ activityValue: { fontSize: 20, fontWeight: 'bold', marginVertical: 8, color: '#333' },
+ lineChart: { height: 80, width: '100%' },
+ timestampBadge: { position: 'absolute', right: 16, top: 16, backgroundColor: '#FFC0CB', borderRadius: 12, paddingVertical: 4, paddingHorizontal: 8 },
+ timestampText: { fontSize: 12, color: '#FFF' },
+ statsGrid: { flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'space-between', marginBottom: 24 },
+ statsCard: { width: '48%', backgroundColor: '#FFF', borderRadius: 16, padding: 16, marginBottom: 16, shadowColor: '#000', shadowOpacity: 0.05, shadowRadius: 10, elevation: 2 },
+ statsCardSmall: { width: '48%', backgroundColor: '#FFF', borderRadius: 16, padding: 16, marginBottom: 16, alignItems: 'center', shadowColor: '#000', shadowOpacity: 0.05, shadowRadius: 10, elevation: 2 },
+ statsLabel: { fontSize: 14, color: '#555' },
+ statsValue: { fontSize: 18, fontWeight: 'bold', marginVertical: 8, color: '#333' },
+ calOverlay: { position: 'absolute', top: 28, alignItems: 'center' },
+ calOverlayText: { fontSize: 14, fontWeight: 'bold', color: '#FFF' },
+ progressSection: { marginBottom: 24 },
+ progressHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 },
+ periodToggle: { fontSize: 14, color: '#777' },
+ progressChart: { height: 100, width: '100%' },
+ daysRow: { flexDirection: 'row', justifyContent: 'space-between', marginTop: 8 },
+ dayText: { fontSize: 12, color: '#777' },
+ latestSection: { marginBottom: 24 },
+ sectionHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 },
+ seeMore: { fontSize: 14, color: '#70A1FF' },
+ workoutItem: { flexDirection: 'row', alignItems: 'center', backgroundColor: '#F9FAFF', borderRadius: 16, padding: 12, marginBottom: 12 },
+ workoutIcon: { width: 40, height: 40, marginRight: 12 },
+ workoutInfo: { flex: 1 },
+ workoutType: { fontSize: 16, fontWeight: 'bold', color: '#333' },
+ workoutMeta: { fontSize: 12, color: '#777', marginTop: 4 },
+ arrowIcon: { width: 24, height: 24, tintColor: '#777' },
+});
+
+export default HomeScreen;
diff --git a/KonditionExpo/app/index.tsx b/KonditionExpo/app/index.tsx
new file mode 100644
index 0000000000..053d10a366
--- /dev/null
+++ b/KonditionExpo/app/index.tsx
@@ -0,0 +1,55 @@
+import React, { useEffect } from 'react';
+import { View, ActivityIndicator, StyleSheet } from 'react-native';
+import { router, useSegments } from 'expo-router';
+import { useAuth } from '@/contexts/AuthContext';
+import { useThemeColor } from '@/hooks/useThemeColor';
+import { ThemedText } from '@/components/ThemedText';
+
+export default function IndexScreen() {
+ const { isAuthenticated, isLoading, isInSignupFlow } = useAuth();
+ const segments = useSegments();
+ const backgroundColor = useThemeColor({}, 'background');
+ const textColor = useThemeColor({}, 'text');
+
+ useEffect(() => {
+ if (!isLoading) {
+ // Don't redirect if we're already on a specific route (like signup2)
+ const currentRoute = segments[0];
+ if (currentRoute) {
+ console.log('IndexScreen: Already on route:', currentRoute, '- not redirecting');
+ return;
+ }
+
+ // Don't redirect if user is in signup flow - let them complete it
+ if (isInSignupFlow) {
+ console.log('IndexScreen: User in signup flow - not redirecting');
+ return;
+ }
+
+ if (isAuthenticated) {
+ router.replace('/(tabs)');
+ } else {
+ router.replace('/login');
+ }
+ }
+ }, [isAuthenticated, isLoading, isInSignupFlow, segments]);
+
+ return (
+
+
+ Loading Kondition...
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ loadingText: {
+ marginTop: 16,
+ fontSize: 16,
+ },
+});
\ No newline at end of file
diff --git a/KonditionExpo/app/login.tsx b/KonditionExpo/app/login.tsx
new file mode 100644
index 0000000000..2ab3d9cc8a
--- /dev/null
+++ b/KonditionExpo/app/login.tsx
@@ -0,0 +1,211 @@
+import React, { useState, useEffect } from 'react';
+import { View, StyleSheet, TouchableOpacity, Text, Image, KeyboardAvoidingView, Platform, ScrollView, Alert } from 'react-native';
+import { useThemeColor } from '../hooks/useThemeColor';
+import { Button } from '../components/ui/Button';
+import { Input } from '../components/ui/Input';
+import { Checkbox } from '../components/ui/Checkbox';
+import { router } from 'expo-router';
+import { useAuth } from '@/contexts/AuthContext';
+
+export default function LoginScreen() {
+ const { login, isLoading, isAuthenticated } = useAuth();
+ const [email, setEmail] = useState('');
+ const [password, setPassword] = useState('');
+ const [rememberMe, setRememberMe] = useState(false);
+ const [showPassword, setShowPassword] = useState(false);
+ const [errors, setErrors] = useState<{ email?: string; password?: string }>({});
+
+ const backgroundColor = useThemeColor({}, 'background');
+ const textColor = useThemeColor({}, 'text');
+ const tintColor = useThemeColor({}, 'tint');
+
+ // Redirect if already authenticated
+ useEffect(() => {
+ if (isAuthenticated) {
+ router.replace('/(tabs)');
+ }
+ }, [isAuthenticated]);
+
+ const handleLogin = async () => {
+ setErrors({});
+
+ const newErrors: { email?: string; password?: string } = {};
+
+ if (!email) newErrors.email = 'Email is required';
+ else if (!/\S+@\S+\.\S+/.test(email)) newErrors.email = 'Please enter a valid email';
+
+ if (!password) newErrors.password = 'Password is required';
+ else if (password.length < 6) newErrors.password = 'Password must be at least 6 characters';
+
+ if (Object.keys(newErrors).length > 0) {
+ setErrors(newErrors);
+ return;
+ }
+
+ try {
+ await login(email, password);
+ // Navigation will happen automatically via useEffect when isAuthenticated becomes true
+ } catch (error) {
+ console.error('Login error:', error);
+ Alert.alert(
+ 'Login Failed',
+ error instanceof Error ? error.message : 'An unexpected error occurred. Please try again.',
+ [{ text: 'OK' }]
+ );
+ }
+ };
+
+ const handleForgotPassword = () => {
+ router.push('/(tabs)');
+ };
+
+ const handleSignUp = () => {
+ router.push('/signup'); // created a new screen for signup (signup.tsx)
+ };
+
+
+ return (
+
+
+
+
+ Kondition
+
+
+
+ Welcome Back
+ Sign in to your account
+
+
+
+ setShowPassword(!showPassword)}
+ accessibilityLabel={showPassword ? 'Hide password' : 'Show password'}
+ accessibilityRole="button"
+ accessible={true}
+ >
+ {showPassword ? 'Hide' : 'Show'}
+
+ }
+ />
+
+
+
+
+
+ Forgot Password?
+
+
+
+
+
+
+ Don't have an account?
+
+ Sign Up
+
+
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ },
+ scrollContent: {
+ flexGrow: 1,
+ padding: 24,
+ justifyContent: 'center',
+ },
+ logoContainer: {
+ alignItems: 'center',
+ marginBottom: 40,
+ },
+ logo: {
+ width: 80,
+ height: 80,
+ marginBottom: 16,
+ },
+ appName: {
+ fontSize: 28,
+ fontWeight: 'bold',
+ },
+ formContainer: {
+ width: '100%',
+ },
+ heading: {
+ fontSize: 24,
+ fontWeight: 'bold',
+ marginBottom: 8,
+ },
+ subheading: {
+ fontSize: 16,
+ marginBottom: 24,
+ opacity: 0.8,
+ },
+ optionsRow: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ marginBottom: 24,
+ },
+ forgotPassword: {
+ fontSize: 14,
+ },
+ loginButton: {
+ marginBottom: 24,
+ },
+ signupContainer: {
+ flexDirection: 'row',
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+});
\ No newline at end of file
diff --git a/KonditionExpo/app/notification.tsx b/KonditionExpo/app/notification.tsx
new file mode 100644
index 0000000000..e00bd9febc
--- /dev/null
+++ b/KonditionExpo/app/notification.tsx
@@ -0,0 +1,160 @@
+// app/screens/NotificationScreen.tsx
+
+import React, { useState, useEffect } from "react";
+import {
+ SafeAreaView,
+ View,
+ Text,
+ StyleSheet,
+ FlatList,
+ Dimensions,
+ ActivityIndicator,
+ TouchableOpacity,
+} from "react-native";
+import { useRouter } from "expo-router";
+import { Ionicons } from "@expo/vector-icons";
+
+// Define the shape of each quote, matching QuoteOut from the backend
+type QuoteItem = {
+ date: string; // "YYYY-MM-DD"
+ text: string;
+};
+
+export default function NotificationScreen() {
+ const router = useRouter();
+ const [notifications, setNotifications] = useState([]);
+ const [loading, setLoading] = useState(true);
+
+ useEffect(() => {
+ // Fetch the last 7 days of quotes from the backend
+ // Replace "localhost" with your LAN IP or actual host when testing on device
+ fetch("http://localhost:8000/api/v1/notifications/quotes")
+ .then((res) => res.json())
+ .then((data: QuoteItem[]) => {
+ setNotifications(data); // data is an array like [{ date: "2025-06-05", text: "..." }, β¦]
+ })
+ .catch((err) => {
+ console.error("Error fetching quotes:", err);
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+ }, []);
+
+ const renderItem = ({ item }: { item: QuoteItem }) => (
+
+
+
+ {item.text}
+
+ {item.date}
+
+
+ );
+
+ if (loading) {
+ return (
+
+
+
+ );
+ }
+
+ return (
+
+
+ {
+ // Go back in the navigation stack (like HomeScreenβs notification button did)
+ router.back();
+ }}
+ >
+
+
+ Daily Quotes
+ {/* Dummy view to balance centering (same width as backButton) */}
+
+
+
+ item.date}
+ renderItem={renderItem}
+ ItemSeparatorComponent={() => }
+ contentContainerStyle={styles.listContent}
+ />
+
+ );
+}
+
+const { width } = Dimensions.get("window");
+
+const styles = StyleSheet.create({
+ // βββ Container now matches HomeScreenβs container style βββ
+ container: {
+ flex: 1,
+ backgroundColor: "#FFFFFF",
+ },
+
+ // βββ Header is identical in structure to the backβarrow version previously shown βββ
+ header: {
+ flexDirection: "row",
+ alignItems: "center",
+ paddingVertical: 12,
+ paddingHorizontal: 8,
+ borderBottomWidth: 1,
+ borderBottomColor: "#ddd",
+ backgroundColor: "#fafafa",
+ width: "100%",
+ },
+ backButton: {
+ padding: 4,
+ },
+ headerTitle: {
+ flex: 1,
+ textAlign: "center",
+ fontSize: 20,
+ fontWeight: "bold",
+ color: "#333",
+ },
+ placeholder: {
+ width: 32, // same width as backButton + icon, to keep title centered
+ },
+
+ // βββ Make the FlatListβs content match HomeScreenβs padding:16 βββ
+ listContent: {
+ padding: 16,
+ paddingBottom: 80, // if you want similar bottom padding (e.g., for any tab bar)
+ width: "100%",
+ },
+
+ // βββ Each item in the quote list βββ
+ item: {
+ flexDirection: "row",
+ alignItems: "center",
+ paddingHorizontal: 16,
+ paddingVertical: 12,
+ backgroundColor: "#fff",
+ borderRadius: 8, // optional, just to match HomeScreenβs card feel
+ marginBottom: 8,
+ },
+ itemText: {
+ flex: 1,
+ },
+ title: {
+ fontSize: 16,
+ color: "#333",
+ },
+ time: {
+ fontSize: 12,
+ color: "#888",
+ marginTop: 4,
+ },
+ separator: {
+ height: 1,
+ backgroundColor: "#eee",
+ marginLeft: 16,
+ width: "100%",
+ },
+});
diff --git a/KonditionExpo/app/profile.tsx b/KonditionExpo/app/profile.tsx
new file mode 100644
index 0000000000..1e2ca61f93
--- /dev/null
+++ b/KonditionExpo/app/profile.tsx
@@ -0,0 +1,112 @@
+import React, { useState } from 'react';
+import { SafeAreaView, View, Text, StyleSheet, ScrollView, TouchableOpacity, Image, Switch } from 'react-native';
+import { useAuth } from '@/contexts/AuthContext';
+import { router } from 'expo-router';
+
+const ProfileScreen = () => {
+ const { user } = useAuth();
+ const [notificationsEnabled, setNotificationsEnabled] = useState(false);
+
+ const toggleNotifications = () => setNotificationsEnabled(prev => !prev);
+
+ // Calculate age from date of birth
+ const calculateAge = (dateOfBirth: string | undefined): number => {
+ if (!dateOfBirth) return 0;
+ const today = new Date();
+ const birthDate = new Date(dateOfBirth);
+ let age = today.getFullYear() - birthDate.getFullYear();
+ const monthDiff = today.getMonth() - birthDate.getMonth();
+ if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
+ age--;
+ }
+ return age;
+ };
+
+ const age = calculateAge(user?.date_of_birth);
+
+ return (
+
+
+ {/* Back Arrow */}
+ router.back()} style={styles.backBtn}>
+
+
+
+ {/* Profile Box */}
+
+ {user?.full_name || user?.email || 'User'}
+
+ Edit
+
+
+
+
+ Height
+ {user?.height ? `${user.height} cm` : '-'}
+
+
+ Weight
+ {user?.weight ? `${user.weight} kg` : '-'}
+
+
+ Age
+ {age > 0 ? age.toString() : '-'}
+
+
+
+
+ {/* Account Box */}
+
+ Account
+ Personal Data
+ Achievement
+ Activity History
+ Workout Progress
+
+
+ {/* Notification Box */}
+
+ Notification
+
+ Pop-up Notifications
+
+
+
+
+ {/* Other Box */}
+
+ Other
+ Privacy Policy
+ Contact Us
+ Settings
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: { flex: 1, backgroundColor: '#FFFFFF' },
+ content: { padding: 16, paddingBottom: 80 },
+ backBtn: { marginBottom: 16 },
+ backIcon: { width: 24, height: 24 },
+ profileBox: { backgroundColor: '#E5F1FF', borderRadius: 20, padding: 16, marginBottom: 24 },
+ profileName: { fontSize: 24, fontWeight: 'bold', color: '#333' },
+ editBtn: { backgroundColor: '#70A1FF', borderRadius: 12, paddingVertical: 6, paddingHorizontal: 12, alignSelf: 'flex-start', marginTop: 8 },
+ editText: { color: '#FFF' },
+ statsRow: { flexDirection: 'row', justifyContent: 'space-between', marginTop: 16 },
+ statBox: { backgroundColor: '#FFF', borderRadius: 12, padding: 12, width: '30%', alignItems: 'center' },
+ statLabel: { fontSize: 12, color: '#555' },
+ statValue: { fontSize: 16, fontWeight: 'bold', color: '#333' },
+ sectionBox: { backgroundColor: '#F5F8FF', borderRadius: 16, padding: 16, marginBottom: 24 },
+ sectionTitle: { fontSize: 16, fontWeight: 'bold', color: '#333', marginBottom: 8 },
+ optionItem: { fontSize: 14, color: '#555', paddingVertical: 6 },
+ toggleRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' },
+});
+
+export default ProfileScreen;
diff --git a/KonditionExpo/app/signup.tsx b/KonditionExpo/app/signup.tsx
new file mode 100644
index 0000000000..b56389082a
--- /dev/null
+++ b/KonditionExpo/app/signup.tsx
@@ -0,0 +1,226 @@
+import React, { useState, useEffect } from 'react';
+import { View, Text, StyleSheet, ScrollView, Alert, TouchableOpacity, KeyboardAvoidingView, Platform } from 'react-native';
+import { Input } from '@/components/ui/Input';
+import { Button } from '@/components/ui/Button';
+import { router } from 'expo-router';
+import { useAuth } from '@/contexts/AuthContext';
+import { useThemeColor } from '../hooks/useThemeColor';
+
+export default function SignUpScreen() {
+ const { signup, isLoading, isAuthenticated } = useAuth();
+ const [email, setEmail] = useState('');
+ const [fullName, setFullName] = useState('');
+ const [password, setPassword] = useState('');
+ const [confirmPassword, setConfirmPassword] = useState('');
+ const [showPassword, setShowPassword] = useState(false);
+ const [showConfirmPassword, setShowConfirmPassword] = useState(false);
+ const [errors, setErrors] = useState<{
+ email?: string;
+ fullName?: string;
+ password?: string;
+ confirmPassword?: string;
+ }>({});
+
+ const backgroundColor = useThemeColor({}, 'background');
+ const textColor = useThemeColor({}, 'text');
+ const tintColor = useThemeColor({}, 'tint');
+
+ // Note: We don't redirect authenticated users here anymore since we want them
+ // to complete the profile setup flow after signup
+
+ const validateForm = () => {
+ const newErrors: typeof errors = {};
+
+ // Full name validation
+ if (!fullName.trim()) {
+ newErrors.fullName = 'Full name is required';
+ } else if (fullName.trim().length < 2) {
+ newErrors.fullName = 'Full name must be at least 2 characters';
+ }
+
+ // Email validation
+ if (!email) {
+ newErrors.email = 'Email is required';
+ } else if (!/\S+@\S+\.\S+/.test(email)) {
+ newErrors.email = 'Please enter a valid email';
+ }
+
+ // Password validation
+ if (!password) {
+ newErrors.password = 'Password is required';
+ } else if (password.length < 8) {
+ newErrors.password = 'Password must be at least 8 characters';
+ } else if (!/(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/.test(password)) {
+ newErrors.password = 'Password must contain at least one uppercase letter, one lowercase letter, and one number';
+ }
+
+ // Confirm password validation
+ if (!confirmPassword) {
+ newErrors.confirmPassword = 'Please confirm your password';
+ } else if (password !== confirmPassword) {
+ newErrors.confirmPassword = "Passwords don't match";
+ }
+
+ setErrors(newErrors);
+ return Object.keys(newErrors).length === 0;
+ };
+
+ const handleCreateAccount = async () => {
+ if (!validateForm()) {
+ return;
+ }
+
+ try {
+ console.log('Starting signup process...');
+ await signup({
+ email: email.trim(),
+ password,
+ full_name: fullName.trim(),
+ });
+
+ console.log('Signup successful, user is now logged in. Navigating to profile completion...');
+ // User is now logged in automatically, redirect to profile completion
+ router.replace('/signup2');
+ console.log('Navigation to profile completion initiated');
+ } catch (error) {
+ console.error('Signup error:', error);
+ Alert.alert(
+ 'Signup Failed',
+ error instanceof Error ? error.message : 'An unexpected error occurred. Please try again.',
+ [{ text: 'OK' }]
+ );
+ }
+ };
+
+ const handleSignIn = () => {
+ router.push('/login');
+ };
+
+ return (
+
+
+
+ Create Your Account
+ Join Kondition and start your fitness journey
+
+
+
+
+
+ setShowPassword(!showPassword)}
+ accessibilityLabel={showPassword ? 'Hide password' : 'Show password'}
+ accessibilityRole="button"
+ accessible={true}
+ >
+ {showPassword ? 'Hide' : 'Show'}
+
+ }
+ />
+
+ setShowConfirmPassword(!showConfirmPassword)}
+ accessibilityLabel={showConfirmPassword ? 'Hide confirm password' : 'Show confirm password'}
+ accessibilityRole="button"
+ accessible={true}
+ >
+ {showConfirmPassword ? 'Hide' : 'Show'}
+
+ }
+ />
+
+
+
+
+ Already have an account?
+
+ Sign In
+
+
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ },
+ scrollContent: {
+ flexGrow: 1,
+ padding: 24,
+ justifyContent: 'center',
+ },
+ formContainer: {
+ width: '100%',
+ },
+ title: {
+ fontSize: 28,
+ fontWeight: 'bold',
+ marginBottom: 8,
+ textAlign: 'center',
+ },
+ subtitle: {
+ fontSize: 16,
+ marginBottom: 32,
+ textAlign: 'center',
+ opacity: 0.8,
+ },
+ signupButton: {
+ marginTop: 8,
+ marginBottom: 24,
+ },
+ signinContainer: {
+ flexDirection: 'row',
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+});
diff --git a/KonditionExpo/app/signup2.tsx b/KonditionExpo/app/signup2.tsx
new file mode 100644
index 0000000000..2cf4cd856d
--- /dev/null
+++ b/KonditionExpo/app/signup2.tsx
@@ -0,0 +1,509 @@
+import React, { useState, useRef, useEffect } from 'react';
+import {
+ View,
+ Text,
+ StyleSheet,
+ TextInput,
+ KeyboardAvoidingView,
+ Platform,
+ ScrollView,
+ Alert,
+ TouchableOpacity,
+} from 'react-native';
+import RNPickerSelect from 'react-native-picker-select';
+import { Button } from '@/components/ui/Button';
+import { router } from 'expo-router';
+import { useAuth } from '@/contexts/AuthContext';
+import { useThemeColor } from '../hooks/useThemeColor';
+
+export default function SignUpStep2() {
+ const { updateProfile, isLoading, isAuthenticated, user, setSignupFlowComplete } = useAuth();
+ const [gender, setGender] = useState('');
+ const [dobMM, setDobMM] = useState('');
+ const [dobDD, setDobDD] = useState('');
+ const [dobYYYY, setDobYYYY] = useState('');
+ const [weight, setWeight] = useState('');
+ const [weightUnit, setWeightUnit] = useState('lbs');
+ const [height, setHeight] = useState('');
+ const [heightUnit, setHeightUnit] = useState('ft/in');
+ const [heightFeet, setHeightFeet] = useState('');
+ const [heightInches, setHeightInches] = useState('');
+ const [errors, setErrors] = useState<{
+ gender?: string;
+ dateOfBirth?: string;
+ weight?: string;
+ height?: string;
+ }>({});
+
+ const backgroundColor = useThemeColor({}, 'background');
+ const textColor = useThemeColor({}, 'text');
+ const tintColor = useThemeColor({}, 'tint');
+
+ // Redirect to login if not authenticated
+ useEffect(() => {
+ console.log('signup2: isAuthenticated:', isAuthenticated);
+ console.log('signup2: user:', user);
+
+ if (!isAuthenticated) {
+ console.log('signup2: User not authenticated, redirecting to login');
+ router.replace('/login');
+ } else {
+ console.log('signup2: User authenticated, staying on signup2');
+ }
+ }, [isAuthenticated, user]);
+
+ const ddRef = useRef(null);
+ const yyyyRef = useRef(null);
+
+ const onChangeDobMM = (text: string) => {
+ const cleanText = text.replace(/[^0-9]/g, '').slice(0, 2);
+ setDobMM(cleanText);
+ if (cleanText.length === 2) ddRef.current?.focus();
+ };
+
+ const onChangeDobDD = (text: string) => {
+ const cleanText = text.replace(/[^0-9]/g, '').slice(0, 2);
+ setDobDD(cleanText);
+ if (cleanText.length === 2) yyyyRef.current?.focus();
+ };
+
+ const onChangeDobYYYY = (text: string) => {
+ const cleanText = text.replace(/[^0-9]/g, '').slice(0, 4);
+ setDobYYYY(cleanText);
+ };
+
+ const validateForm = () => {
+ const newErrors: typeof errors = {};
+
+ // Gender validation
+ if (!gender) {
+ newErrors.gender = 'Please select your gender';
+ }
+
+ // Date of birth validation
+ if (!dobMM || !dobDD || !dobYYYY) {
+ newErrors.dateOfBirth = 'Please enter your complete date of birth';
+ } else {
+ const mm = parseInt(dobMM, 10);
+ const dd = parseInt(dobDD, 10);
+ const yyyy = parseInt(dobYYYY, 10);
+ const currentYear = new Date().getFullYear();
+
+ if (mm < 1 || mm > 12) {
+ newErrors.dateOfBirth = 'Month must be between 1 and 12';
+ } else if (dd < 1 || dd > 31) {
+ newErrors.dateOfBirth = 'Day must be between 1 and 31';
+ } else if (yyyy < 1900 || yyyy > currentYear) {
+ newErrors.dateOfBirth = `Year must be between 1900 and ${currentYear}`;
+ } else {
+ // Check if date is valid
+ const birthDate = new Date(yyyy, mm - 1, dd);
+ if (birthDate.getMonth() !== mm - 1 || birthDate.getDate() !== dd) {
+ newErrors.dateOfBirth = 'Please enter a valid date';
+ }
+ }
+ }
+
+ // Weight validation
+ if (!weight) {
+ newErrors.weight = 'Please enter your weight';
+ } else if (parseFloat(weight) <= 0) {
+ newErrors.weight = 'Weight must be greater than 0';
+ }
+
+ // Height validation
+ if (heightUnit === 'ft/in') {
+ if (!heightFeet || !heightInches) {
+ newErrors.height = 'Please enter both feet and inches';
+ } else if (parseInt(heightFeet) < 0 || parseInt(heightInches) < 0 || parseInt(heightInches) >= 12) {
+ newErrors.height = 'Please enter valid height values';
+ }
+ } else {
+ if (!height) {
+ newErrors.height = 'Please enter your height';
+ } else if (parseFloat(height) <= 0) {
+ newErrors.height = 'Height must be greater than 0';
+ }
+ }
+
+ setErrors(newErrors);
+ return Object.keys(newErrors).length === 0;
+ };
+
+ const handleNext = async () => {
+ if (!validateForm()) {
+ return;
+ }
+
+ try {
+ // Convert height to cm
+ let heightInCm: number;
+ if (heightUnit === 'ft/in') {
+ const feet = parseInt(heightFeet, 10) || 0;
+ const inches = parseInt(heightInches, 10) || 0;
+ heightInCm = Math.round(feet * 30.48 + inches * 2.54);
+ } else {
+ heightInCm = parseFloat(height);
+ }
+
+ // Convert weight to kg
+ let weightInKg: number;
+ if (weightUnit === 'lbs') {
+ weightInKg = parseFloat((parseFloat(weight) * 0.453592).toFixed(1));
+ } else {
+ weightInKg = parseFloat(weight);
+ }
+
+ // Format date as YYYY-MM-DD
+ const formattedDate = `${dobYYYY}-${dobMM.padStart(2, '0')}-${dobDD.padStart(2, '0')}`;
+
+ // Update profile via API (user is already authenticated)
+ await updateProfile({
+ gender,
+ date_of_birth: formattedDate,
+ weight: weightInKg,
+ height: heightInCm,
+ });
+
+ // Mark signup flow as complete
+ setSignupFlowComplete();
+
+ // Navigate to main app
+ router.replace('/(tabs)');
+ } catch (error) {
+ console.error('Profile update error:', error);
+ Alert.alert(
+ 'Profile Update Failed',
+ error instanceof Error ? error.message : 'An unexpected error occurred. Please try again.',
+ [{ text: 'OK' }]
+ );
+ }
+ };
+
+ const handleSkip = () => {
+ Alert.alert(
+ 'Skip Profile Setup?',
+ 'You can always complete your profile later in the settings.',
+ [
+ { text: 'Cancel', style: 'cancel' },
+ {
+ text: 'Skip',
+ onPress: () => {
+ // Mark signup flow as complete
+ setSignupFlowComplete();
+ // User is already authenticated, just navigate to main app
+ router.replace('/(tabs)');
+ },
+ style: 'destructive'
+ }
+ ]
+ );
+ };
+
+ return (
+
+
+
+ Complete Your Profile
+
+ Help us personalize your fitness experience
+
+
+ {/* Gender */}
+ Gender
+
+ {errors.gender && {errors.gender}}
+
+ {/* Date of Birth */}
+ Date of Birth
+ MM / DD / YYYY
+
+
+ /
+
+ /
+
+
+ {errors.dateOfBirth && {errors.dateOfBirth}}
+
+ {/* Weight */}
+ Weight
+
+ setWeight(text.replace(/[^0-9.]/g, ''))}
+ keyboardType="numeric"
+ placeholder="Enter weight"
+ placeholderTextColor="#aaa"
+ />
+
+
+ {errors.weight && {errors.weight}}
+
+ {/* Height */}
+ Height
+
+ {heightUnit === 'ft/in' ? (
+ <>
+ setHeightFeet(text.replace(/[^0-9]/g, ''))}
+ keyboardType="numeric"
+ placeholder="Feet"
+ placeholderTextColor="#aaa"
+ />
+ setHeightInches(text.replace(/[^0-9]/g, ''))}
+ keyboardType="numeric"
+ placeholder="Inches"
+ placeholderTextColor="#aaa"
+ />
+ >
+ ) : (
+ setHeight(text.replace(/[^0-9.]/g, ''))}
+ keyboardType="numeric"
+ placeholder="Height in cm"
+ placeholderTextColor="#aaa"
+ />
+ )}
+ {
+ setHeightUnit(val);
+ setHeight('');
+ setHeightFeet('');
+ setHeightInches('');
+ }}
+ value={heightUnit}
+ items={[
+ { label: 'ft/in', value: 'ft/in' },
+ { label: 'cm', value: 'cm' },
+ ]}
+ style={pickerSelectStylesSmall}
+ />
+
+ {errors.height && {errors.height}}
+
+
+
+
+ Skip for now
+
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ },
+ scrollContent: {
+ flexGrow: 1,
+ padding: 24,
+ justifyContent: 'center',
+ },
+ formContainer: {
+ width: '100%',
+ },
+ title: {
+ fontSize: 28,
+ fontWeight: 'bold',
+ marginBottom: 8,
+ textAlign: 'center',
+ },
+ subtitle: {
+ fontSize: 16,
+ marginBottom: 32,
+ textAlign: 'center',
+ opacity: 0.8,
+ },
+ label: {
+ fontSize: 18,
+ marginTop: 16,
+ marginBottom: 8,
+ fontWeight: '600',
+ },
+ subLabel: {
+ fontSize: 14,
+ marginBottom: 8,
+ opacity: 0.7,
+ },
+ dobRow: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ marginBottom: 8,
+ },
+ dobInput: {
+ borderWidth: 1,
+ borderColor: '#555',
+ backgroundColor: 'rgba(255, 255, 255, 0.1)',
+ padding: 12,
+ borderRadius: 8,
+ fontSize: 16,
+ textAlign: 'center',
+ },
+ dobPart: {
+ width: 60,
+ },
+ dobYear: {
+ flex: 1,
+ },
+ slash: {
+ fontSize: 18,
+ marginHorizontal: 8,
+ },
+ row: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ marginBottom: 8,
+ },
+ textInput: {
+ flex: 1,
+ borderWidth: 1,
+ borderColor: '#555',
+ borderRadius: 8,
+ padding: 12,
+ fontSize: 16,
+ backgroundColor: 'rgba(255, 255, 255, 0.1)',
+ },
+ errorText: {
+ color: '#ff6b6b',
+ fontSize: 14,
+ marginTop: 4,
+ marginBottom: 8,
+ },
+ completeButton: {
+ marginTop: 24,
+ marginBottom: 16,
+ },
+ skipButton: {
+ alignItems: 'center',
+ padding: 12,
+ },
+});
+
+const pickerSelectStyles = {
+ inputIOS: {
+ fontSize: 16,
+ paddingVertical: 12,
+ paddingHorizontal: 12,
+ borderWidth: 1,
+ borderColor: '#555',
+ borderRadius: 8,
+ color: 'white',
+ backgroundColor: 'rgba(255, 255, 255, 0.1)',
+ paddingRight: 30,
+ marginBottom: 8,
+ },
+ inputAndroid: {
+ fontSize: 16,
+ paddingHorizontal: 12,
+ paddingVertical: 8,
+ borderWidth: 1,
+ borderColor: '#555',
+ borderRadius: 8,
+ color: 'white',
+ backgroundColor: 'rgba(255, 255, 255, 0.1)',
+ paddingRight: 30,
+ marginBottom: 8,
+ },
+};
+
+const pickerSelectStylesSmall = {
+ inputIOS: {
+ fontSize: 16,
+ paddingVertical: 12,
+ paddingHorizontal: 8,
+ borderWidth: 1,
+ borderColor: '#555',
+ borderRadius: 8,
+ color: 'white',
+ backgroundColor: 'rgba(255, 255, 255, 0.1)',
+ paddingRight: 30,
+ marginLeft: 10,
+ minWidth: 80,
+ },
+ inputAndroid: {
+ fontSize: 16,
+ paddingHorizontal: 8,
+ paddingVertical: 8,
+ borderWidth: 1,
+ borderColor: '#555',
+ borderRadius: 8,
+ color: 'white',
+ backgroundColor: 'rgba(255, 255, 255, 0.1)',
+ paddingRight: 30,
+ marginLeft: 10,
+ minWidth: 80,
+ },
+};
diff --git a/KonditionExpo/app/workout-detail.tsx b/KonditionExpo/app/workout-detail.tsx
new file mode 100644
index 0000000000..e61f326c28
--- /dev/null
+++ b/KonditionExpo/app/workout-detail.tsx
@@ -0,0 +1,446 @@
+import React, { useState, useEffect } from 'react';
+import {
+ SafeAreaView,
+ View,
+ Text,
+ StyleSheet,
+ ScrollView,
+ TouchableOpacity,
+ ActivityIndicator,
+ Alert,
+} from 'react-native';
+import { useLocalSearchParams, router } from 'expo-router';
+import { apiService, WorkoutResponse } from '@/services/api';
+import { useAuth } from '@/contexts/AuthContext';
+
+const WorkoutDetailScreen = () => {
+ const { workoutId } = useLocalSearchParams<{ workoutId: string }>();
+ const { user } = useAuth();
+ const [workout, setWorkout] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+ useEffect(() => {
+ if (workoutId) {
+ fetchWorkoutDetail();
+ }
+ }, [workoutId]);
+
+ const fetchWorkoutDetail = async () => {
+ try {
+ setLoading(true);
+ setError(null);
+ const workoutData = await apiService.getWorkoutById(workoutId!);
+ setWorkout(workoutData);
+ } catch (err) {
+ console.error('Error fetching workout detail:', err);
+ setError(err instanceof Error ? err.message : 'Failed to load workout details');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const formatDate = (dateString: string) => {
+ const date = new Date(dateString);
+ return date.toLocaleDateString('en-US', {
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric',
+ hour: '2-digit',
+ minute: '2-digit',
+ });
+ };
+
+ const formatDuration = (minutes?: number) => {
+ if (!minutes) return 'N/A';
+ const hours = Math.floor(minutes / 60);
+ const mins = minutes % 60;
+ if (hours > 0) {
+ return `${hours}h ${mins}m`;
+ }
+ return `${mins}m`;
+ };
+
+ const getCompletionStatus = (workout: WorkoutResponse) => {
+ return workout.is_completed ? 'Completed' : 'Incomplete';
+ };
+
+ const getStatusColor = (workout: WorkoutResponse) => {
+ return workout.is_completed ? '#4CAF50' : '#FF9800';
+ };
+
+ const handleGoBack = () => {
+ router.back();
+ };
+
+ const renderExercise = (exercise: any, index: number) => (
+
+
+ {exercise.name}
+
+ {exercise.category}
+
+
+
+ {exercise.description && (
+ {exercise.description}
+ )}
+
+
+ {exercise.sets && (
+
+ Sets
+ {exercise.sets}
+
+ )}
+
+ {exercise.reps && (
+
+ Reps
+ {exercise.reps}
+
+ )}
+
+ {exercise.weight && (
+
+ Weight
+ {exercise.weight} kg
+
+ )}
+
+
+ );
+
+ if (loading) {
+ return (
+
+
+
+ Loading workout details...
+
+
+ );
+ }
+
+ if (error || !workout) {
+ return (
+
+
+ Unable to Load Workout
+ {error || 'Workout not found'}
+
+ Try Again
+
+
+ Go Back
+
+
+
+ );
+ }
+
+ return (
+
+
+
+ β Back
+
+ Workout Details
+
+
+
+ {/* Workout Info Card */}
+
+
+ {workout.name}
+
+ {getCompletionStatus(workout)}
+
+
+
+
+ {workout.completed_date
+ ? `Completed: ${formatDate(workout.completed_date)}`
+ : `Created: ${formatDate(workout.created_at)}`
+ }
+
+
+ {workout.scheduled_date && (
+
+ Scheduled: {formatDate(workout.scheduled_date)}
+
+ )}
+
+
+
+ Duration
+ {formatDuration(workout.duration_minutes)}
+
+
+
+ Exercises
+ {workout.exercises?.length || 0}
+
+
+
+ {workout.description && (
+
+ Description:
+ {workout.description}
+
+ )}
+
+
+ {/* Exercises Section */}
+
+
+ Exercises ({workout.exercises?.length || 0})
+
+
+ {workout.exercises && workout.exercises.length > 0 ? (
+ workout.exercises.map(renderExercise)
+ ) : (
+
+ No exercises recorded for this workout
+
+ )}
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: '#FFFFFF',
+ },
+ header: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ padding: 20,
+ backgroundColor: '#F8F9FA',
+ borderBottomWidth: 1,
+ borderBottomColor: '#E9ECEF',
+ },
+ backButtonHeader: {
+ marginRight: 16,
+ },
+ backButtonText: {
+ fontSize: 16,
+ color: '#70A1FF',
+ fontWeight: '600',
+ },
+ headerTitle: {
+ fontSize: 20,
+ fontWeight: 'bold',
+ color: '#333',
+ },
+ scrollView: {
+ flex: 1,
+ },
+ scrollContent: {
+ padding: 16,
+ paddingBottom: 80,
+ },
+ loadingContainer: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ loadingText: {
+ marginTop: 16,
+ fontSize: 16,
+ color: '#666',
+ },
+ errorContainer: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ padding: 20,
+ },
+ errorTitle: {
+ fontSize: 20,
+ fontWeight: 'bold',
+ color: '#FF6B6B',
+ marginBottom: 8,
+ },
+ errorMessage: {
+ fontSize: 16,
+ color: '#666',
+ textAlign: 'center',
+ marginBottom: 20,
+ lineHeight: 24,
+ },
+ retryButton: {
+ backgroundColor: '#70A1FF',
+ paddingHorizontal: 24,
+ paddingVertical: 12,
+ borderRadius: 12,
+ marginBottom: 12,
+ },
+ retryText: {
+ color: '#FFFFFF',
+ fontSize: 16,
+ fontWeight: '600',
+ },
+ backButton: {
+ backgroundColor: '#E9ECEF',
+ paddingHorizontal: 24,
+ paddingVertical: 12,
+ borderRadius: 12,
+ },
+ backText: {
+ color: '#333',
+ fontSize: 16,
+ fontWeight: '600',
+ },
+ workoutInfoCard: {
+ backgroundColor: '#FFFFFF',
+ borderRadius: 16,
+ padding: 20,
+ marginBottom: 20,
+ shadowColor: '#000',
+ shadowOffset: {
+ width: 0,
+ height: 2,
+ },
+ shadowOpacity: 0.1,
+ shadowRadius: 4,
+ elevation: 3,
+ borderWidth: 1,
+ borderColor: '#F0F0F0',
+ },
+ workoutHeader: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ marginBottom: 12,
+ },
+ workoutName: {
+ fontSize: 24,
+ fontWeight: 'bold',
+ color: '#333',
+ flex: 1,
+ marginRight: 12,
+ },
+ statusBadge: {
+ paddingHorizontal: 12,
+ paddingVertical: 6,
+ borderRadius: 16,
+ },
+ statusText: {
+ color: '#FFFFFF',
+ fontSize: 14,
+ fontWeight: '600',
+ },
+ workoutDate: {
+ fontSize: 16,
+ color: '#666',
+ marginBottom: 8,
+ },
+ scheduledDate: {
+ fontSize: 14,
+ color: '#999',
+ marginBottom: 16,
+ },
+ workoutStats: {
+ flexDirection: 'row',
+ justifyContent: 'space-around',
+ marginBottom: 16,
+ },
+ statItem: {
+ alignItems: 'center',
+ },
+ statLabel: {
+ fontSize: 14,
+ color: '#999',
+ marginBottom: 4,
+ },
+ statValue: {
+ fontSize: 18,
+ fontWeight: 'bold',
+ color: '#333',
+ },
+ descriptionContainer: {
+ marginTop: 16,
+ paddingTop: 16,
+ borderTopWidth: 1,
+ borderTopColor: '#F0F0F0',
+ },
+ descriptionLabel: {
+ fontSize: 16,
+ fontWeight: '600',
+ color: '#333',
+ marginBottom: 8,
+ },
+ workoutDescription: {
+ fontSize: 16,
+ color: '#666',
+ lineHeight: 24,
+ },
+ exercisesSection: {
+ marginBottom: 20,
+ },
+ sectionTitle: {
+ fontSize: 20,
+ fontWeight: 'bold',
+ color: '#333',
+ marginBottom: 16,
+ },
+ exerciseCard: {
+ backgroundColor: '#F8F9FA',
+ borderRadius: 12,
+ padding: 16,
+ marginBottom: 12,
+ borderWidth: 1,
+ borderColor: '#E9ECEF',
+ },
+ exerciseHeader: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ marginBottom: 8,
+ },
+ exerciseName: {
+ fontSize: 18,
+ fontWeight: 'bold',
+ color: '#333',
+ flex: 1,
+ marginRight: 12,
+ },
+ categoryBadge: {
+ backgroundColor: '#70A1FF',
+ paddingHorizontal: 8,
+ paddingVertical: 4,
+ borderRadius: 8,
+ },
+ categoryText: {
+ color: '#FFFFFF',
+ fontSize: 12,
+ fontWeight: '600',
+ },
+ exerciseDescription: {
+ fontSize: 14,
+ color: '#666',
+ marginBottom: 12,
+ fontStyle: 'italic',
+ },
+ exerciseStats: {
+ flexDirection: 'row',
+ justifyContent: 'space-around',
+ },
+ noExercisesContainer: {
+ backgroundColor: '#F8F9FA',
+ borderRadius: 12,
+ padding: 24,
+ alignItems: 'center',
+ },
+ noExercisesText: {
+ fontSize: 16,
+ color: '#666',
+ textAlign: 'center',
+ },
+});
+
+export default WorkoutDetailScreen;
\ No newline at end of file
diff --git a/KonditionExpo/app/workout.tsx b/KonditionExpo/app/workout.tsx
new file mode 100644
index 0000000000..2a9006c02e
--- /dev/null
+++ b/KonditionExpo/app/workout.tsx
@@ -0,0 +1,474 @@
+import React, { useState, useEffect } from 'react';
+import {
+ SafeAreaView,
+ View,
+ Text,
+ StyleSheet,
+ ScrollView,
+ TouchableOpacity,
+ Modal,
+ Alert,
+ TextInput,
+} from 'react-native';
+import { router } from 'expo-router';
+import { useWorkout, Exercise, WorkoutSet } from '@/contexts/WorkoutContext';
+import { Button } from '@/components/ui/Button';
+import { Input } from '@/components/ui/Input';
+
+// Predefined exercises database
+const EXERCISES: Exercise[] = [
+ { id: '1', name: 'Bench Press', muscle_group: 'Chest', type: 'strength' },
+ { id: '2', name: 'Squat', muscle_group: 'Legs', type: 'strength' },
+ { id: '3', name: 'Deadlift', muscle_group: 'Back', type: 'strength' },
+ { id: '4', name: 'Pull-ups', muscle_group: 'Back', type: 'strength' },
+ { id: '5', name: 'Push-ups', muscle_group: 'Chest', type: 'strength' },
+ { id: '6', name: 'Shoulder Press', muscle_group: 'Shoulders', type: 'strength' },
+ { id: '7', name: 'Bicep Curls', muscle_group: 'Arms', type: 'strength' },
+ { id: '8', name: 'Tricep Dips', muscle_group: 'Arms', type: 'strength' },
+ { id: '9', name: 'Lunges', muscle_group: 'Legs', type: 'strength' },
+ { id: '10', name: 'Plank', muscle_group: 'Core', type: 'strength' },
+ { id: '11', name: 'Running', muscle_group: 'Cardio', type: 'cardio' },
+ { id: '12', name: 'Cycling', muscle_group: 'Cardio', type: 'cardio' },
+];
+
+interface SetRowProps {
+ set: WorkoutSet;
+ setNumber: number;
+ onUpdate: (updatedSet: Partial) => void;
+ onRemove: () => void;
+}
+
+const SetRow = ({ set, setNumber, onUpdate, onRemove }: SetRowProps) => {
+ return (
+
+ {setNumber}
+ onUpdate({ weight: parseInt(text) || 0 })}
+ placeholder="Weight"
+ keyboardType="numeric"
+ />
+ kg
+ onUpdate({ reps: parseInt(text) || 0 })}
+ placeholder="Reps"
+ keyboardType="numeric"
+ />
+ reps
+
+ Γ
+
+
+ );
+};
+
+interface ExerciseCardProps {
+ workoutExercise: any;
+ onAddSet: () => void;
+ onUpdateSet: (setId: string, updatedSet: Partial) => void;
+ onRemoveSet: (setId: string) => void;
+}
+
+const ExerciseCard = ({ workoutExercise, onAddSet, onUpdateSet, onRemoveSet }: ExerciseCardProps) => {
+ return (
+
+
+ {workoutExercise.exercise.name}
+ {workoutExercise.exercise.muscle_group}
+
+
+
+
+ Set
+ Weight
+ Reps
+
+
+
+ {workoutExercise.sets.map((set: WorkoutSet, index: number) => (
+ onUpdateSet(set.id, updatedSet)}
+ onRemove={() => onRemoveSet(set.id)}
+ />
+ ))}
+
+
+
+
+ );
+};
+
+const WorkoutScreen = () => {
+ const {
+ currentWorkout,
+ endWorkout,
+ addExerciseToCurrentWorkout,
+ addSetToExercise,
+ removeSetFromExercise,
+ updateSet
+ } = useWorkout();
+
+ const [showExerciseModal, setShowExerciseModal] = useState(false);
+ const [filteredExercises, setFilteredExercises] = useState(EXERCISES);
+ const [searchQuery, setSearchQuery] = useState('');
+ const [startTime] = useState(new Date());
+
+ useEffect(() => {
+ if (!currentWorkout) {
+ router.replace('/(tabs)');
+ }
+ }, [currentWorkout]);
+
+ useEffect(() => {
+ const filtered = EXERCISES.filter(exercise =>
+ exercise.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ exercise.muscle_group.toLowerCase().includes(searchQuery.toLowerCase())
+ );
+ setFilteredExercises(filtered);
+ }, [searchQuery]);
+
+ const handleAddExercise = (exercise: Exercise) => {
+ addExerciseToCurrentWorkout(exercise);
+ setShowExerciseModal(false);
+ setSearchQuery('');
+ };
+
+ const handleAddSet = (exerciseId: string) => {
+ const newSet = { reps: 0, weight: 0 };
+ addSetToExercise(exerciseId, newSet);
+ };
+
+ const handleFinishWorkout = () => {
+ if (!currentWorkout) return;
+
+ Alert.alert(
+ 'Finish Workout',
+ 'Are you sure you want to finish this workout?',
+ [
+ { text: 'Cancel', style: 'cancel' },
+ {
+ text: 'Finish',
+ onPress: () => {
+ const duration = Math.round((new Date().getTime() - startTime.getTime()) / 60000);
+ const updatedWorkout = { ...currentWorkout, duration };
+ endWorkout();
+ router.replace('/(tabs)');
+ },
+ },
+ ]
+ );
+ };
+
+ if (!currentWorkout) {
+ return null;
+ }
+
+ return (
+
+
+ router.replace('/(tabs)')}>
+ β Exit
+
+ {currentWorkout.name}
+
+ Finish
+
+
+
+
+ {currentWorkout.exercises.length === 0 ? (
+
+
+ No exercises added yet. Add your first exercise to get started!
+
+
+ ) : (
+ currentWorkout.exercises.map((workoutExercise) => (
+ handleAddSet(workoutExercise.id)}
+ onUpdateSet={(setId, updatedSet) => updateSet(workoutExercise.id, setId, updatedSet)}
+ onRemoveSet={(setId) => removeSetFromExercise(workoutExercise.id, setId)}
+ />
+ ))
+ )}
+
+ setShowExerciseModal(true)}
+ style={styles.addExerciseButton}
+ size="lg"
+ fullWidth
+ />
+
+
+ {/* Exercise Selection Modal */}
+ setShowExerciseModal(false)}
+ >
+
+
+ setShowExerciseModal(false)}>
+ Cancel
+
+ Select Exercise
+
+
+
+
+
+
+ {filteredExercises.map((exercise) => (
+ handleAddExercise(exercise)}
+ >
+
+ {exercise.name}
+ {exercise.muscle_group}
+
+
+ {exercise.type}
+
+
+ ))}
+
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: '#FFFFFF',
+ },
+ header: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ padding: 16,
+ borderBottomWidth: 1,
+ borderBottomColor: '#E5F1FF',
+ backgroundColor: '#F9FAFF',
+ },
+ backButton: {
+ fontSize: 16,
+ fontWeight: '600',
+ color: '#70A1FF',
+ },
+ workoutTitle: {
+ fontSize: 18,
+ fontWeight: 'bold',
+ color: '#333',
+ },
+ finishButton: {
+ fontSize: 16,
+ fontWeight: '600',
+ color: '#70A1FF',
+ },
+ content: {
+ flex: 1,
+ padding: 16,
+ },
+ emptyState: {
+ padding: 40,
+ alignItems: 'center',
+ backgroundColor: '#F5F8FF',
+ borderRadius: 16,
+ marginVertical: 20,
+ },
+ emptyStateText: {
+ fontSize: 16,
+ textAlign: 'center',
+ opacity: 0.6,
+ color: '#666',
+ },
+ exerciseCard: {
+ backgroundColor: '#E5F1FF',
+ borderRadius: 12,
+ padding: 16,
+ marginBottom: 16,
+ borderWidth: 1,
+ borderColor: '#A3C9FD',
+ },
+ exerciseHeader: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ marginBottom: 16,
+ },
+ exerciseName: {
+ fontSize: 18,
+ fontWeight: 'bold',
+ color: '#333',
+ },
+ muscleGroup: {
+ fontSize: 14,
+ color: '#666',
+ backgroundColor: '#F0F6FF',
+ paddingHorizontal: 8,
+ paddingVertical: 4,
+ borderRadius: 8,
+ },
+ setsContainer: {
+ marginTop: 8,
+ },
+ setHeaderRow: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ paddingVertical: 8,
+ paddingHorizontal: 4,
+ marginBottom: 8,
+ backgroundColor: '#F0F6FF',
+ borderRadius: 8,
+ },
+ setHeaderText: {
+ fontSize: 12,
+ fontWeight: '600',
+ color: '#555',
+ flex: 1,
+ textAlign: 'center',
+ },
+ setRow: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ paddingVertical: 8,
+ paddingHorizontal: 4,
+ marginBottom: 8,
+ backgroundColor: '#FFFFFF',
+ borderRadius: 8,
+ borderWidth: 1,
+ borderColor: '#E5F1FF',
+ },
+ setNumber: {
+ fontSize: 16,
+ fontWeight: '600',
+ color: '#333',
+ width: 30,
+ textAlign: 'center',
+ },
+ setInput: {
+ borderWidth: 1,
+ borderColor: '#A3C9FD',
+ borderRadius: 8,
+ padding: 8,
+ marginHorizontal: 4,
+ textAlign: 'center',
+ fontSize: 16,
+ backgroundColor: 'white',
+ flex: 1,
+ },
+ inputLabel: {
+ fontSize: 12,
+ color: '#666',
+ marginRight: 8,
+ },
+ removeButton: {
+ width: 30,
+ height: 30,
+ borderRadius: 15,
+ backgroundColor: '#FF6B9D',
+ justifyContent: 'center',
+ alignItems: 'center',
+ marginLeft: 8,
+ },
+ removeButtonText: {
+ color: 'white',
+ fontSize: 18,
+ fontWeight: 'bold',
+ },
+ addSetButton: {
+ marginTop: 8,
+ backgroundColor: '#70A1FF',
+ },
+ addExerciseButton: {
+ marginTop: 20,
+ marginBottom: 40,
+ backgroundColor: '#70A1FF',
+ },
+ modalContainer: {
+ flex: 1,
+ backgroundColor: '#FFFFFF',
+ },
+ modalHeader: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ padding: 16,
+ borderBottomWidth: 1,
+ borderBottomColor: '#E5F1FF',
+ backgroundColor: '#F9FAFF',
+ },
+ modalCloseButton: {
+ fontSize: 16,
+ fontWeight: '600',
+ color: '#70A1FF',
+ },
+ modalTitle: {
+ fontSize: 18,
+ fontWeight: 'bold',
+ color: '#333',
+ },
+ searchInput: {
+ margin: 16,
+ },
+ exerciseList: {
+ flex: 1,
+ paddingHorizontal: 16,
+ },
+ exerciseItem: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ padding: 16,
+ backgroundColor: '#F0F6FF',
+ borderRadius: 12,
+ marginBottom: 8,
+ borderWidth: 1,
+ borderColor: '#E5F1FF',
+ },
+ exerciseItemName: {
+ fontSize: 16,
+ fontWeight: '600',
+ color: '#333',
+ },
+ exerciseItemMuscle: {
+ fontSize: 14,
+ color: '#666',
+ marginTop: 4,
+ },
+ exerciseItemType: {
+ fontSize: 12,
+ fontWeight: '600',
+ textTransform: 'uppercase',
+ color: '#B07FFD',
+ backgroundColor: '#F5F8FF',
+ paddingHorizontal: 8,
+ paddingVertical: 4,
+ borderRadius: 6,
+ },
+});
+
+export default WorkoutScreen;
\ No newline at end of file
diff --git a/KonditionExpo/assets/fonts/SpaceMono-Regular.ttf b/KonditionExpo/assets/fonts/SpaceMono-Regular.ttf
new file mode 100755
index 0000000000..28d7ff7177
Binary files /dev/null and b/KonditionExpo/assets/fonts/SpaceMono-Regular.ttf differ
diff --git a/KonditionExpo/assets/images/adaptive-icon.png b/KonditionExpo/assets/images/adaptive-icon.png
new file mode 100644
index 0000000000..03d6f6b6c6
Binary files /dev/null and b/KonditionExpo/assets/images/adaptive-icon.png differ
diff --git a/KonditionExpo/assets/images/arrow-left.png b/KonditionExpo/assets/images/arrow-left.png
new file mode 100644
index 0000000000..aa6c77420b
Binary files /dev/null and b/KonditionExpo/assets/images/arrow-left.png differ
diff --git a/KonditionExpo/assets/images/arrow-right.png b/KonditionExpo/assets/images/arrow-right.png
new file mode 100644
index 0000000000..5b64f703b3
Binary files /dev/null and b/KonditionExpo/assets/images/arrow-right.png differ
diff --git a/KonditionExpo/assets/images/bell.png b/KonditionExpo/assets/images/bell.png
new file mode 100644
index 0000000000..29027b06d5
Binary files /dev/null and b/KonditionExpo/assets/images/bell.png differ
diff --git a/KonditionExpo/assets/images/camera.png b/KonditionExpo/assets/images/camera.png
new file mode 100644
index 0000000000..f626ad26c5
Binary files /dev/null and b/KonditionExpo/assets/images/camera.png differ
diff --git a/KonditionExpo/assets/images/chart.png b/KonditionExpo/assets/images/chart.png
new file mode 100644
index 0000000000..829301fa59
Binary files /dev/null and b/KonditionExpo/assets/images/chart.png differ
diff --git a/KonditionExpo/assets/images/favicon.png b/KonditionExpo/assets/images/favicon.png
new file mode 100644
index 0000000000..e75f697b18
Binary files /dev/null and b/KonditionExpo/assets/images/favicon.png differ
diff --git a/KonditionExpo/assets/images/fullbody.png b/KonditionExpo/assets/images/fullbody.png
new file mode 100644
index 0000000000..90835dd8ce
Binary files /dev/null and b/KonditionExpo/assets/images/fullbody.png differ
diff --git a/KonditionExpo/assets/images/home-active.png b/KonditionExpo/assets/images/home-active.png
new file mode 100644
index 0000000000..3b8ff3d63f
Binary files /dev/null and b/KonditionExpo/assets/images/home-active.png differ
diff --git a/KonditionExpo/assets/images/icon.png b/KonditionExpo/assets/images/icon.png
new file mode 100644
index 0000000000..a0b1526fc7
Binary files /dev/null and b/KonditionExpo/assets/images/icon.png differ
diff --git a/KonditionExpo/assets/images/lowerbody.png b/KonditionExpo/assets/images/lowerbody.png
new file mode 100644
index 0000000000..b56bd10d62
Binary files /dev/null and b/KonditionExpo/assets/images/lowerbody.png differ
diff --git a/KonditionExpo/assets/images/partial-react-logo.png b/KonditionExpo/assets/images/partial-react-logo.png
new file mode 100644
index 0000000000..66fd9570e4
Binary files /dev/null and b/KonditionExpo/assets/images/partial-react-logo.png differ
diff --git a/KonditionExpo/assets/images/react-logo.png b/KonditionExpo/assets/images/react-logo.png
new file mode 100644
index 0000000000..9d72a9ffcb
Binary files /dev/null and b/KonditionExpo/assets/images/react-logo.png differ
diff --git a/KonditionExpo/assets/images/react-logo@2x.png b/KonditionExpo/assets/images/react-logo@2x.png
new file mode 100644
index 0000000000..2229b130ad
Binary files /dev/null and b/KonditionExpo/assets/images/react-logo@2x.png differ
diff --git a/KonditionExpo/assets/images/react-logo@3x.png b/KonditionExpo/assets/images/react-logo@3x.png
new file mode 100644
index 0000000000..a99b203222
Binary files /dev/null and b/KonditionExpo/assets/images/react-logo@3x.png differ
diff --git a/KonditionExpo/assets/images/right-arrow.png b/KonditionExpo/assets/images/right-arrow.png
new file mode 100644
index 0000000000..5b64f703b3
Binary files /dev/null and b/KonditionExpo/assets/images/right-arrow.png differ
diff --git a/KonditionExpo/assets/images/search.png b/KonditionExpo/assets/images/search.png
new file mode 100644
index 0000000000..7176942337
Binary files /dev/null and b/KonditionExpo/assets/images/search.png differ
diff --git a/KonditionExpo/assets/images/splash-icon.png b/KonditionExpo/assets/images/splash-icon.png
new file mode 100644
index 0000000000..03d6f6b6c6
Binary files /dev/null and b/KonditionExpo/assets/images/splash-icon.png differ
diff --git a/KonditionExpo/assets/images/trophy.png b/KonditionExpo/assets/images/trophy.png
new file mode 100644
index 0000000000..2080e286c3
Binary files /dev/null and b/KonditionExpo/assets/images/trophy.png differ
diff --git a/KonditionExpo/assets/images/user.png b/KonditionExpo/assets/images/user.png
new file mode 100644
index 0000000000..34799f68f0
Binary files /dev/null and b/KonditionExpo/assets/images/user.png differ
diff --git a/KonditionExpo/components/AuthNavigator.tsx b/KonditionExpo/components/AuthNavigator.tsx
new file mode 100644
index 0000000000..968a41f0b9
--- /dev/null
+++ b/KonditionExpo/components/AuthNavigator.tsx
@@ -0,0 +1,71 @@
+import React from 'react';
+import { View, ActivityIndicator, StyleSheet } from 'react-native';
+import { Stack, router } from 'expo-router';
+import { useAuth } from '@/contexts/AuthContext';
+import { useThemeColor } from '@/hooks/useThemeColor';
+import { ThemedText } from './ThemedText';
+
+export function AuthNavigator() {
+ const { isAuthenticated, isLoading } = useAuth();
+ const backgroundColor = useThemeColor({}, 'background');
+ const textColor = useThemeColor({}, 'text');
+
+ // Show loading screen while checking authentication
+ if (isLoading) {
+ return (
+
+
+ Loading...
+
+ );
+ }
+
+ // Redirect based on authentication state
+ React.useEffect(() => {
+ if (!isLoading) {
+ if (isAuthenticated) {
+ // User is authenticated, redirect to main app
+ router.replace('/(tabs)');
+ } else {
+ // User is not authenticated, redirect to login
+ router.replace('/login');
+ }
+ }
+ }, [isAuthenticated, isLoading]);
+
+ // Render the stack navigator
+ return (
+
+ {isAuthenticated ? (
+ // Authenticated routes
+ <>
+
+
+
+
+
+ >
+ ) : (
+ // Unauthenticated routes
+ <>
+
+
+
+ >
+ )}
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ loadingContainer: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ loadingText: {
+ marginTop: 16,
+ fontSize: 16,
+ },
+});
\ No newline at end of file
diff --git a/KonditionExpo/components/Collapsible.tsx b/KonditionExpo/components/Collapsible.tsx
new file mode 100644
index 0000000000..55bff2f914
--- /dev/null
+++ b/KonditionExpo/components/Collapsible.tsx
@@ -0,0 +1,45 @@
+import { PropsWithChildren, useState } from 'react';
+import { StyleSheet, TouchableOpacity } from 'react-native';
+
+import { ThemedText } from '@/components/ThemedText';
+import { ThemedView } from '@/components/ThemedView';
+import { IconSymbol } from '@/components/ui/IconSymbol';
+import { Colors } from '@/constants/Colors';
+import { useColorScheme } from '@/hooks/useColorScheme';
+
+export function Collapsible({ children, title }: PropsWithChildren & { title: string }) {
+ const [isOpen, setIsOpen] = useState(false);
+ const theme = useColorScheme() ?? 'light';
+
+ return (
+
+ setIsOpen((value) => !value)}
+ activeOpacity={0.8}>
+
+
+ {title}
+
+ {isOpen && {children}}
+
+ );
+}
+
+const styles = StyleSheet.create({
+ heading: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ gap: 6,
+ },
+ content: {
+ marginTop: 6,
+ marginLeft: 24,
+ },
+});
diff --git a/KonditionExpo/components/DevTools.tsx b/KonditionExpo/components/DevTools.tsx
new file mode 100644
index 0000000000..a158cc7a79
--- /dev/null
+++ b/KonditionExpo/components/DevTools.tsx
@@ -0,0 +1,203 @@
+import React, { useState } from 'react';
+import { View, Text, StyleSheet, TouchableOpacity, Alert, ScrollView, TextInput } from 'react-native';
+import { getApiBaseUrl, setCustomApiUrl, getNetworkSetupInstructions } from '../config/api';
+
+interface DevToolsProps {
+ visible: boolean;
+ onClose: () => void;
+}
+
+export const DevTools: React.FC = ({ visible, onClose }) => {
+ const [customUrl, setCustomUrl] = useState('');
+ const currentUrl = getApiBaseUrl();
+ const instructions = getNetworkSetupInstructions();
+
+ if (!visible) return null;
+
+ const handleSetCustomUrl = () => {
+ if (customUrl.trim()) {
+ setCustomApiUrl(customUrl.trim());
+ Alert.alert('Success', `API URL updated to: ${customUrl.trim()}`);
+ setCustomUrl('');
+ }
+ };
+
+ const showInstructions = () => {
+ Alert.alert(
+ instructions.title,
+ instructions.instructions.join('\n'),
+ [{ text: 'OK' }]
+ );
+ };
+
+ const testConnection = async () => {
+ try {
+ const response = await fetch(`${currentUrl}/docs`);
+ if (response.ok) {
+ Alert.alert('Success', 'API connection successful!');
+ } else {
+ Alert.alert('Error', `API returned status: ${response.status}`);
+ }
+ } catch (error) {
+ Alert.alert('Error', `Connection failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
+ }
+ };
+
+ return (
+
+
+
+ Development Tools
+
+
+ Current API URL
+ {currentUrl}
+
+
+
+ Test Connection
+
+ Test API Connection
+
+
+
+
+ Custom API URL
+
+
+ Set Custom URL
+
+
+
+
+
+ Show Setup Instructions
+
+
+
+
+ Quick URLs
+ setCustomUrl('http://localhost:8000/api/v1')}
+ >
+ Localhost (Web/iOS Sim)
+
+ setCustomUrl('http://10.0.2.2:8000/api/v1')}
+ >
+ Android Emulator
+
+
+
+
+
+
+ Close
+
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ overlay: {
+ position: 'absolute',
+ top: 0,
+ left: 0,
+ right: 0,
+ bottom: 0,
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
+ justifyContent: 'center',
+ alignItems: 'center',
+ zIndex: 1000,
+ },
+ container: {
+ backgroundColor: '#fff',
+ borderRadius: 16,
+ margin: 20,
+ maxHeight: '80%',
+ width: '90%',
+ },
+ content: {
+ padding: 20,
+ },
+ title: {
+ fontSize: 20,
+ fontWeight: 'bold',
+ marginBottom: 20,
+ textAlign: 'center',
+ },
+ section: {
+ marginBottom: 20,
+ },
+ sectionTitle: {
+ fontSize: 16,
+ fontWeight: '600',
+ marginBottom: 8,
+ color: '#333',
+ },
+ urlText: {
+ fontSize: 14,
+ color: '#666',
+ backgroundColor: '#f5f5f5',
+ padding: 10,
+ borderRadius: 8,
+ fontFamily: 'monospace',
+ },
+ input: {
+ borderWidth: 1,
+ borderColor: '#ddd',
+ borderRadius: 8,
+ padding: 12,
+ fontSize: 14,
+ marginBottom: 10,
+ fontFamily: 'monospace',
+ },
+ button: {
+ backgroundColor: '#70A1FF',
+ borderRadius: 8,
+ padding: 12,
+ alignItems: 'center',
+ },
+ buttonText: {
+ color: '#fff',
+ fontSize: 14,
+ fontWeight: '600',
+ },
+ quickButton: {
+ backgroundColor: '#f0f0f0',
+ borderRadius: 8,
+ padding: 10,
+ marginBottom: 8,
+ alignItems: 'center',
+ },
+ quickButtonText: {
+ color: '#333',
+ fontSize: 12,
+ },
+ footer: {
+ borderTopWidth: 1,
+ borderTopColor: '#eee',
+ padding: 15,
+ },
+ closeButton: {
+ backgroundColor: '#ff6b6b',
+ borderRadius: 8,
+ padding: 12,
+ alignItems: 'center',
+ },
+ closeButtonText: {
+ color: '#fff',
+ fontSize: 14,
+ fontWeight: '600',
+ },
+});
\ No newline at end of file
diff --git a/KonditionExpo/components/ExternalLink.tsx b/KonditionExpo/components/ExternalLink.tsx
new file mode 100644
index 0000000000..dfbd23ea26
--- /dev/null
+++ b/KonditionExpo/components/ExternalLink.tsx
@@ -0,0 +1,24 @@
+import { Href, Link } from 'expo-router';
+import { openBrowserAsync } from 'expo-web-browser';
+import { type ComponentProps } from 'react';
+import { Platform } from 'react-native';
+
+type Props = Omit, 'href'> & { href: Href & string };
+
+export function ExternalLink({ href, ...rest }: Props) {
+ return (
+ {
+ if (Platform.OS !== 'web') {
+ // Prevent the default behavior of linking to the default browser on native.
+ event.preventDefault();
+ // Open the link in an in-app browser.
+ await openBrowserAsync(href);
+ }
+ }}
+ />
+ );
+}
diff --git a/KonditionExpo/components/HapticTab.tsx b/KonditionExpo/components/HapticTab.tsx
new file mode 100644
index 0000000000..7f3981cb94
--- /dev/null
+++ b/KonditionExpo/components/HapticTab.tsx
@@ -0,0 +1,18 @@
+import { BottomTabBarButtonProps } from '@react-navigation/bottom-tabs';
+import { PlatformPressable } from '@react-navigation/elements';
+import * as Haptics from 'expo-haptics';
+
+export function HapticTab(props: BottomTabBarButtonProps) {
+ return (
+ {
+ if (process.env.EXPO_OS === 'ios') {
+ // Add a soft haptic feedback when pressing down on the tabs.
+ Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
+ }
+ props.onPressIn?.(ev);
+ }}
+ />
+ );
+}
diff --git a/KonditionExpo/components/HelloWave.tsx b/KonditionExpo/components/HelloWave.tsx
new file mode 100644
index 0000000000..eb6ea61a0f
--- /dev/null
+++ b/KonditionExpo/components/HelloWave.tsx
@@ -0,0 +1,40 @@
+import { useEffect } from 'react';
+import { StyleSheet } from 'react-native';
+import Animated, {
+ useAnimatedStyle,
+ useSharedValue,
+ withRepeat,
+ withSequence,
+ withTiming,
+} from 'react-native-reanimated';
+
+import { ThemedText } from '@/components/ThemedText';
+
+export function HelloWave() {
+ const rotationAnimation = useSharedValue(0);
+
+ useEffect(() => {
+ rotationAnimation.value = withRepeat(
+ withSequence(withTiming(25, { duration: 150 }), withTiming(0, { duration: 150 })),
+ 4 // Run the animation 4 times
+ );
+ }, [rotationAnimation]);
+
+ const animatedStyle = useAnimatedStyle(() => ({
+ transform: [{ rotate: `${rotationAnimation.value}deg` }],
+ }));
+
+ return (
+
+ π
+
+ );
+}
+
+const styles = StyleSheet.create({
+ text: {
+ fontSize: 28,
+ lineHeight: 32,
+ marginTop: -6,
+ },
+});
diff --git a/KonditionExpo/components/ParallaxScrollView.tsx b/KonditionExpo/components/ParallaxScrollView.tsx
new file mode 100644
index 0000000000..5df1d75fd8
--- /dev/null
+++ b/KonditionExpo/components/ParallaxScrollView.tsx
@@ -0,0 +1,82 @@
+import type { PropsWithChildren, ReactElement } from 'react';
+import { StyleSheet } from 'react-native';
+import Animated, {
+ interpolate,
+ useAnimatedRef,
+ useAnimatedStyle,
+ useScrollViewOffset,
+} from 'react-native-reanimated';
+
+import { ThemedView } from '@/components/ThemedView';
+import { useBottomTabOverflow } from '@/components/ui/TabBarBackground';
+import { useColorScheme } from '@/hooks/useColorScheme';
+
+const HEADER_HEIGHT = 250;
+
+type Props = PropsWithChildren<{
+ headerImage: ReactElement;
+ headerBackgroundColor: { dark: string; light: string };
+}>;
+
+export default function ParallaxScrollView({
+ children,
+ headerImage,
+ headerBackgroundColor,
+}: Props) {
+ const colorScheme = useColorScheme() ?? 'light';
+ const scrollRef = useAnimatedRef();
+ const scrollOffset = useScrollViewOffset(scrollRef);
+ const bottom = useBottomTabOverflow();
+ const headerAnimatedStyle = useAnimatedStyle(() => {
+ return {
+ transform: [
+ {
+ translateY: interpolate(
+ scrollOffset.value,
+ [-HEADER_HEIGHT, 0, HEADER_HEIGHT],
+ [-HEADER_HEIGHT / 2, 0, HEADER_HEIGHT * 0.75]
+ ),
+ },
+ {
+ scale: interpolate(scrollOffset.value, [-HEADER_HEIGHT, 0, HEADER_HEIGHT], [2, 1, 1]),
+ },
+ ],
+ };
+ });
+
+ return (
+
+
+
+ {headerImage}
+
+ {children}
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ },
+ header: {
+ height: HEADER_HEIGHT,
+ overflow: 'hidden',
+ },
+ content: {
+ flex: 1,
+ padding: 32,
+ gap: 16,
+ overflow: 'hidden',
+ },
+});
diff --git a/KonditionExpo/components/ProtectedRoute.tsx b/KonditionExpo/components/ProtectedRoute.tsx
new file mode 100644
index 0000000000..6511c2b877
--- /dev/null
+++ b/KonditionExpo/components/ProtectedRoute.tsx
@@ -0,0 +1,77 @@
+import React, { useEffect } from 'react';
+import { View, ActivityIndicator, StyleSheet } from 'react-native';
+import { router, useSegments } from 'expo-router';
+import { useAuth } from '@/contexts/AuthContext';
+import { useThemeColor } from '@/hooks/useThemeColor';
+import { ThemedText } from './ThemedText';
+
+interface ProtectedRouteProps {
+ children: React.ReactNode;
+}
+
+export function ProtectedRoute({ children }: ProtectedRouteProps) {
+ const { isAuthenticated, isLoading, isInSignupFlow } = useAuth();
+ const segments = useSegments();
+ const backgroundColor = useThemeColor({}, 'background');
+ const textColor = useThemeColor({}, 'text');
+
+ useEffect(() => {
+ if (!isLoading) {
+ const currentRoute = segments[0];
+ const inAuthGroup = currentRoute === '(tabs)' || currentRoute === 'workout' || currentRoute === 'notification' || currentRoute === 'profile' || currentRoute === 'home';
+ const inPublicGroup = currentRoute === 'login' || currentRoute === 'signup';
+ const isSignup2Route = currentRoute === 'signup2';
+
+ console.log('ProtectedRoute: Current route:', currentRoute);
+ console.log('ProtectedRoute: Segments:', segments);
+ console.log('ProtectedRoute: isAuthenticated:', isAuthenticated);
+ console.log('ProtectedRoute: isLoading:', isLoading);
+ console.log('ProtectedRoute: isInSignupFlow:', isInSignupFlow);
+ console.log('ProtectedRoute: inAuthGroup:', inAuthGroup);
+ console.log('ProtectedRoute: inPublicGroup:', inPublicGroup);
+ console.log('ProtectedRoute: isSignup2Route:', isSignup2Route);
+
+ // Allow signup2 route for authenticated users during profile completion
+ if (isSignup2Route) {
+ console.log('ProtectedRoute: Allowing access to signup2 route');
+ return;
+ }
+
+ if (!isAuthenticated && inAuthGroup) {
+ // User is not authenticated but trying to access protected route
+ console.log('ProtectedRoute: Redirecting to /login (not authenticated, trying to access protected route)');
+ router.replace('/login');
+ } else if (isAuthenticated && inPublicGroup && !isInSignupFlow) {
+ // User is authenticated but on login/signup screens - redirect to main app
+ // But don't redirect if they're in the signup flow (going to signup2)
+ console.log('ProtectedRoute: Redirecting to /(tabs) (authenticated user on public route, not in signup flow)');
+ router.replace('/(tabs)');
+ }
+ // For index route, let the index component handle the redirect
+ }
+ }, [isAuthenticated, isLoading, segments]);
+
+ // Show loading screen while checking authentication
+ if (isLoading) {
+ return (
+
+
+ Loading...
+
+ );
+ }
+
+ return <>{children}>;
+}
+
+const styles = StyleSheet.create({
+ loadingContainer: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ loadingText: {
+ marginTop: 16,
+ fontSize: 16,
+ },
+});
\ No newline at end of file
diff --git a/KonditionExpo/components/ThemedText.tsx b/KonditionExpo/components/ThemedText.tsx
new file mode 100644
index 0000000000..9d214a2b83
--- /dev/null
+++ b/KonditionExpo/components/ThemedText.tsx
@@ -0,0 +1,60 @@
+import { StyleSheet, Text, type TextProps } from 'react-native';
+
+import { useThemeColor } from '@/hooks/useThemeColor';
+
+export type ThemedTextProps = TextProps & {
+ lightColor?: string;
+ darkColor?: string;
+ type?: 'default' | 'title' | 'defaultSemiBold' | 'subtitle' | 'link';
+};
+
+export function ThemedText({
+ style,
+ lightColor,
+ darkColor,
+ type = 'default',
+ ...rest
+}: ThemedTextProps) {
+ const color = useThemeColor({ light: lightColor, dark: darkColor }, 'text');
+
+ return (
+
+ );
+}
+
+const styles = StyleSheet.create({
+ default: {
+ fontSize: 16,
+ lineHeight: 24,
+ },
+ defaultSemiBold: {
+ fontSize: 16,
+ lineHeight: 24,
+ fontWeight: '600',
+ },
+ title: {
+ fontSize: 32,
+ fontWeight: 'bold',
+ lineHeight: 32,
+ },
+ subtitle: {
+ fontSize: 20,
+ fontWeight: 'bold',
+ },
+ link: {
+ lineHeight: 30,
+ fontSize: 16,
+ color: '#0a7ea4',
+ },
+});
diff --git a/KonditionExpo/components/ThemedView.tsx b/KonditionExpo/components/ThemedView.tsx
new file mode 100644
index 0000000000..4d2cb09d4f
--- /dev/null
+++ b/KonditionExpo/components/ThemedView.tsx
@@ -0,0 +1,14 @@
+import { View, type ViewProps } from 'react-native';
+
+import { useThemeColor } from '@/hooks/useThemeColor';
+
+export type ThemedViewProps = ViewProps & {
+ lightColor?: string;
+ darkColor?: string;
+};
+
+export function ThemedView({ style, lightColor, darkColor, ...otherProps }: ThemedViewProps) {
+ const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, 'background');
+
+ return ;
+}
diff --git a/KonditionExpo/components/feed/CreatePostButton.tsx b/KonditionExpo/components/feed/CreatePostButton.tsx
new file mode 100644
index 0000000000..73005a2ec1
--- /dev/null
+++ b/KonditionExpo/components/feed/CreatePostButton.tsx
@@ -0,0 +1,43 @@
+import React from 'react';
+import { TouchableOpacity, StyleSheet, View } from 'react-native';
+import { IconSymbol } from '../ui/IconSymbol';
+
+interface CreatePostButtonProps {
+ onPress: () => void;
+}
+
+export function CreatePostButton({ onPress }: CreatePostButtonProps) {
+ return (
+
+
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ position: 'absolute',
+ bottom: 20,
+ right: 20,
+ zIndex: 1000,
+ },
+ button: {
+ width: 56,
+ height: 56,
+ borderRadius: 28,
+ backgroundColor: '#007AFF',
+ justifyContent: 'center',
+ alignItems: 'center',
+ shadowColor: '#000',
+ shadowOffset: { width: 0, height: 4 },
+ shadowOpacity: 0.3,
+ shadowRadius: 8,
+ elevation: 8,
+ },
+});
\ No newline at end of file
diff --git a/KonditionExpo/components/feed/FeedToggle.tsx b/KonditionExpo/components/feed/FeedToggle.tsx
new file mode 100644
index 0000000000..baa50865c3
--- /dev/null
+++ b/KonditionExpo/components/feed/FeedToggle.tsx
@@ -0,0 +1,119 @@
+import React from 'react';
+import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
+import { FeedType } from '../../contexts/FeedContext';
+
+interface FeedToggleProps {
+ currentFeedType: FeedType;
+ onFeedTypeChange: (feedType: FeedType) => void;
+ postCounts?: {
+ personal: number;
+ public: number;
+ combined: number;
+ };
+}
+
+export function FeedToggle({ currentFeedType, onFeedTypeChange, postCounts }: FeedToggleProps) {
+ const feedOptions: { type: FeedType; label: string; description: string }[] = [
+ { type: 'personal', label: 'Personal', description: 'Following' },
+ { type: 'public', label: 'Public', description: 'Discover' },
+ { type: 'combined', label: 'Combined', description: 'Mixed' },
+ ];
+
+ return (
+
+ {feedOptions.map((option) => {
+ const isActive = currentFeedType === option.type;
+ const count = postCounts?.[option.type] || 0;
+
+ return (
+ onFeedTypeChange(option.type)}
+ activeOpacity={0.7}
+ >
+
+ {option.label}
+
+
+ {option.description}
+
+ {count > 0 && (
+
+
+ {count > 99 ? '99+' : count}
+
+
+ )}
+
+ );
+ })}
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flexDirection: 'row',
+ backgroundColor: '#f8f9fa',
+ borderRadius: 12,
+ padding: 4,
+ marginHorizontal: 16,
+ marginVertical: 8,
+ },
+ tab: {
+ flex: 1,
+ paddingVertical: 12,
+ paddingHorizontal: 8,
+ borderRadius: 8,
+ alignItems: 'center',
+ position: 'relative',
+ },
+ activeTab: {
+ backgroundColor: '#007AFF',
+ shadowColor: '#007AFF',
+ shadowOffset: { width: 0, height: 2 },
+ shadowOpacity: 0.2,
+ shadowRadius: 4,
+ elevation: 3,
+ },
+ tabLabel: {
+ fontSize: 14,
+ fontWeight: '600',
+ color: '#666',
+ marginBottom: 2,
+ },
+ activeTabLabel: {
+ color: '#fff',
+ },
+ tabDescription: {
+ fontSize: 11,
+ color: '#999',
+ },
+ activeTabDescription: {
+ color: '#E3F2FD',
+ },
+ badge: {
+ position: 'absolute',
+ top: 4,
+ right: 4,
+ backgroundColor: '#FF3B30',
+ borderRadius: 10,
+ minWidth: 20,
+ height: 20,
+ justifyContent: 'center',
+ alignItems: 'center',
+ paddingHorizontal: 6,
+ },
+ activeBadge: {
+ backgroundColor: '#fff',
+ },
+ badgeText: {
+ fontSize: 10,
+ fontWeight: '700',
+ color: '#fff',
+ },
+ activeBadgeText: {
+ color: '#007AFF',
+ },
+});
\ No newline at end of file
diff --git a/KonditionExpo/components/feed/PostCard.tsx b/KonditionExpo/components/feed/PostCard.tsx
new file mode 100644
index 0000000000..c6165b8e9b
--- /dev/null
+++ b/KonditionExpo/components/feed/PostCard.tsx
@@ -0,0 +1,378 @@
+import React, { useState } from 'react';
+import { View, Text, StyleSheet, TouchableOpacity, Alert, ActivityIndicator, Platform } from 'react-native';
+import { WorkoutPostResponse } from '../../services/api';
+import { useFeed } from '../../contexts/FeedContext';
+import { useAuth } from '../../contexts/AuthContext';
+import { IconSymbol } from '../ui/IconSymbol';
+
+interface PostCardProps {
+ post: WorkoutPostResponse;
+ onPress?: () => void;
+}
+
+export function PostCard({ post, onPress }: PostCardProps) {
+ const { deletePost } = useFeed();
+ const { user } = useAuth();
+ const [isDeleting, setIsDeleting] = useState(false);
+
+ const isOwnPost = user?.id === post.user_id;
+
+ // Enhanced debug logging to help identify the issue
+ console.log('PostCard Debug:', {
+ currentUserId: user?.id,
+ currentUserIdType: typeof user?.id,
+ postUserId: post.user_id,
+ postUserIdType: typeof post.user_id,
+ isOwnPost,
+ userFullName: user?.full_name,
+ postUserName: post.user_full_name,
+ postTitle: post.title,
+ strictEquality: user?.id === post.user_id,
+ looseEquality: user?.id == post.user_id
+ });
+
+ const formatDuration = (minutes: number): string => {
+ 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 formatTimeAgo = (dateString: string): string => {
+ const date = new Date(dateString);
+ const now = new Date();
+ const diffInMs = now.getTime() - date.getTime();
+ const diffInMinutes = Math.floor(diffInMs / (1000 * 60));
+ const diffInHours = Math.floor(diffInMinutes / 60);
+ const diffInDays = Math.floor(diffInHours / 24);
+
+ if (diffInMinutes < 1) return 'Just now';
+ if (diffInMinutes < 60) return `${diffInMinutes}m ago`;
+ if (diffInHours < 24) return `${diffInHours}h ago`;
+ if (diffInDays < 7) return `${diffInDays}d ago`;
+
+ return date.toLocaleDateString();
+ };
+
+ const handleDelete = async () => {
+ console.log('Delete button pressed for post:', post.id, 'by user:', user?.id);
+
+ if (isDeleting) {
+ console.log('Delete already in progress, ignoring click');
+ return; // Prevent multiple delete attempts
+ }
+
+ console.log('Showing delete confirmation dialog');
+
+ // Use web-compatible confirmation for web platform
+ if (Platform.OS === 'web') {
+ const confirmed = window.confirm('Are you sure you want to delete this post?');
+ if (!confirmed) {
+ console.log('User cancelled delete');
+ return;
+ }
+ } else {
+ // Use Alert.alert for native platforms
+ return new Promise((resolve) => {
+ Alert.alert(
+ 'Delete Post',
+ 'Are you sure you want to delete this post?',
+ [
+ {
+ text: 'Cancel',
+ style: 'cancel',
+ onPress: () => {
+ console.log('User cancelled delete');
+ resolve(false);
+ }
+ },
+ {
+ text: 'Delete',
+ style: 'destructive',
+ onPress: () => {
+ console.log('User confirmed delete via Alert');
+ resolve(true);
+ },
+ },
+ ]
+ );
+ }).then((confirmed) => {
+ if (!confirmed) return;
+ return performDelete();
+ });
+ }
+
+ // For web, proceed directly after confirmation
+ console.log('User confirmed delete, starting deletion process...');
+ await performDelete();
+ };
+
+ const performDelete = async () => {
+ try {
+ setIsDeleting(true);
+ console.log('Calling deletePost with ID:', post.id);
+ await deletePost(post.id);
+ console.log('Delete post successful');
+ // Success feedback is handled by optimistic update in FeedContext
+ } catch (error) {
+ console.error('Delete post error:', error);
+ const errorMessage = error instanceof Error ? error.message : 'Failed to delete post';
+ console.log('Showing error alert:', errorMessage);
+
+ if (Platform.OS === 'web') {
+ window.alert(`Delete Failed: ${errorMessage}`);
+ } else {
+ Alert.alert(
+ 'Delete Failed',
+ errorMessage,
+ [{ text: 'OK', style: 'default' }]
+ );
+ }
+ } finally {
+ console.log('Delete process completed, resetting isDeleting state');
+ setIsDeleting(false);
+ }
+ };
+
+ return (
+
+ {/* Header */}
+
+
+
+
+
+
+ {post.user_full_name || 'Unknown User'}
+
+ {formatTimeAgo(post.created_at)}
+
+
+
+ {post.is_public ? "Public" : "Private"}
+
+
+
+
+
+ {/* Only show delete button for user's own posts */}
+ {isOwnPost && (
+
+ {isDeleting ? (
+
+ ) : (
+
+ )}
+
+ )}
+
+
+ {/* Content */}
+
+ {post.title}
+ {post.description && (
+ {post.description}
+ )}
+
+ {/* Workout Details */}
+
+
+
+ {post.workout_type}
+
+
+
+
+
+ {formatDuration(post.duration_minutes)}
+
+
+ {post.calories_burned && (
+
+
+ {post.calories_burned} cal
+
+ )}
+
+
+
+
+ {/* Footer - Placeholder for future interactions */}
+
+
+
+ Like
+
+
+
+
+ Comment
+
+
+
+
+ Share
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ backgroundColor: '#fff',
+ marginHorizontal: 16,
+ marginVertical: 8,
+ borderRadius: 12,
+ padding: 16,
+ shadowColor: '#000',
+ shadowOffset: { width: 0, height: 2 },
+ shadowOpacity: 0.1,
+ shadowRadius: 4,
+ elevation: 3,
+ },
+ header: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'flex-start',
+ marginBottom: 12,
+ },
+ userInfo: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ flex: 1,
+ },
+ avatar: {
+ width: 40,
+ height: 40,
+ borderRadius: 20,
+ backgroundColor: '#f0f0f0',
+ justifyContent: 'center',
+ alignItems: 'center',
+ marginRight: 12,
+ },
+ userDetails: {
+ flex: 1,
+ },
+ userName: {
+ fontSize: 16,
+ fontWeight: '600',
+ color: '#333',
+ marginBottom: 2,
+ },
+ metaInfo: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ },
+ timestamp: {
+ fontSize: 12,
+ color: '#666',
+ marginRight: 8,
+ },
+ privacyIndicator: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ },
+ privacyText: {
+ fontSize: 11,
+ fontWeight: '500',
+ marginLeft: 4,
+ },
+ deleteButton: {
+ padding: 10,
+ borderRadius: 8,
+ backgroundColor: '#FF3B30',
+ borderWidth: 2,
+ borderColor: '#FF1F1F',
+ minWidth: 40,
+ minHeight: 40,
+ justifyContent: 'center',
+ alignItems: 'center',
+ shadowColor: '#FF3B30',
+ shadowOffset: { width: 0, height: 2 },
+ shadowOpacity: 0.3,
+ shadowRadius: 4,
+ elevation: 4,
+ },
+ deleteButtonDisabled: {
+ opacity: 0.6,
+ backgroundColor: '#FF8A80',
+ borderColor: '#FF8A80',
+ },
+ content: {
+ marginBottom: 12,
+ },
+ title: {
+ fontSize: 18,
+ fontWeight: '700',
+ color: '#333',
+ marginBottom: 6,
+ },
+ description: {
+ fontSize: 14,
+ color: '#666',
+ lineHeight: 20,
+ marginBottom: 12,
+ },
+ workoutDetails: {
+ backgroundColor: '#f8f9fa',
+ borderRadius: 8,
+ padding: 12,
+ },
+ workoutType: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ marginBottom: 8,
+ },
+ workoutTypeText: {
+ fontSize: 16,
+ fontWeight: '600',
+ color: '#007AFF',
+ marginLeft: 6,
+ },
+ stats: {
+ flexDirection: 'row',
+ gap: 16,
+ },
+ stat: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ },
+ statText: {
+ fontSize: 14,
+ color: '#666',
+ marginLeft: 4,
+ fontWeight: '500',
+ },
+ footer: {
+ flexDirection: 'row',
+ justifyContent: 'space-around',
+ paddingTop: 12,
+ borderTopWidth: 1,
+ borderTopColor: '#f0f0f0',
+ },
+ actionButton: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ paddingVertical: 8,
+ paddingHorizontal: 12,
+ },
+ actionText: {
+ fontSize: 14,
+ color: '#666',
+ marginLeft: 4,
+ fontWeight: '500',
+ },
+});
\ No newline at end of file
diff --git a/KonditionExpo/components/feed/PostList.tsx b/KonditionExpo/components/feed/PostList.tsx
new file mode 100644
index 0000000000..3aa1296abd
--- /dev/null
+++ b/KonditionExpo/components/feed/PostList.tsx
@@ -0,0 +1,125 @@
+import React from 'react';
+import { FlatList, View, Text, StyleSheet, RefreshControl, ActivityIndicator } from 'react-native';
+import { WorkoutPostResponse } from '../../services/api';
+import { PostCard } from './PostCard';
+
+interface PostListProps {
+ posts: WorkoutPostResponse[];
+ isLoading: boolean;
+ isRefreshing: boolean;
+ hasMore: boolean;
+ onRefresh: () => void;
+ onLoadMore: () => void;
+ onPostPress?: (post: WorkoutPostResponse) => void;
+}
+
+export function PostList({
+ posts,
+ isLoading,
+ isRefreshing,
+ hasMore,
+ onRefresh,
+ onLoadMore,
+ onPostPress,
+}: PostListProps) {
+ const renderPost = ({ item }: { item: WorkoutPostResponse }) => (
+ onPostPress?.(item)}
+ />
+ );
+
+ const renderFooter = () => {
+ if (!isLoading || isRefreshing) return null;
+
+ return (
+
+
+ Loading more posts...
+
+ );
+ };
+
+ const renderEmpty = () => {
+ if (isLoading && !isRefreshing) {
+ return (
+
+
+ Loading posts...
+
+ );
+ }
+
+ return (
+
+ No posts yet
+
+ Be the first to share your workout or follow some users to see their posts!
+
+
+ );
+ };
+
+ const handleEndReached = () => {
+ if (hasMore && !isLoading) {
+ onLoadMore();
+ }
+ };
+
+ return (
+ item.id}
+ refreshControl={
+
+ }
+ onEndReached={handleEndReached}
+ onEndReachedThreshold={0.1}
+ ListFooterComponent={renderFooter}
+ ListEmptyComponent={renderEmpty}
+ showsVerticalScrollIndicator={false}
+ contentContainerStyle={posts.length === 0 ? styles.emptyList : undefined}
+ />
+ );
+}
+
+const styles = StyleSheet.create({
+ footer: {
+ paddingVertical: 20,
+ alignItems: 'center',
+ },
+ loadingText: {
+ marginTop: 8,
+ fontSize: 14,
+ color: '#666',
+ },
+ emptyList: {
+ flex: 1,
+ },
+ emptyContainer: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ paddingHorizontal: 32,
+ paddingVertical: 64,
+ },
+ emptyTitle: {
+ fontSize: 20,
+ fontWeight: '600',
+ color: '#333',
+ marginBottom: 8,
+ textAlign: 'center',
+ },
+ emptyText: {
+ fontSize: 16,
+ color: '#666',
+ textAlign: 'center',
+ lineHeight: 24,
+ },
+});
\ No newline at end of file
diff --git a/KonditionExpo/components/feed/index.ts b/KonditionExpo/components/feed/index.ts
new file mode 100644
index 0000000000..224f16c025
--- /dev/null
+++ b/KonditionExpo/components/feed/index.ts
@@ -0,0 +1,4 @@
+export { PostCard } from './PostCard';
+export { PostList } from './PostList';
+export { FeedToggle } from './FeedToggle';
+export { CreatePostButton } from './CreatePostButton';
\ No newline at end of file
diff --git a/KonditionExpo/components/social/FollowButton.tsx b/KonditionExpo/components/social/FollowButton.tsx
new file mode 100644
index 0000000000..48f1811227
--- /dev/null
+++ b/KonditionExpo/components/social/FollowButton.tsx
@@ -0,0 +1,128 @@
+import React from 'react';
+import {
+ TouchableOpacity,
+ Text,
+ StyleSheet,
+ ActivityIndicator,
+ ViewStyle,
+} from 'react-native';
+
+interface FollowButtonProps {
+ isFollowing: boolean;
+ loading?: boolean;
+ onPress: () => void;
+ disabled?: boolean;
+ size?: 'small' | 'medium' | 'large';
+ style?: ViewStyle;
+}
+
+export const FollowButton: React.FC = ({
+ isFollowing,
+ loading = false,
+ onPress,
+ disabled = false,
+ size = 'medium',
+ style,
+}) => {
+ const buttonStyle = [
+ styles.button,
+ styles[size],
+ isFollowing ? styles.followingButton : styles.followButton,
+ disabled && styles.disabledButton,
+ style,
+ ];
+
+ const textStyle = [
+ styles.text,
+ styles[`${size}Text`],
+ isFollowing ? styles.followingText : styles.followText,
+ disabled && styles.disabledText,
+ ];
+
+ return (
+
+ {loading ? (
+
+ ) : (
+
+ {isFollowing ? 'Unfollow' : 'Follow'}
+
+ )}
+
+ );
+};
+
+const styles = StyleSheet.create({
+ button: {
+ borderRadius: 8,
+ alignItems: 'center',
+ justifyContent: 'center',
+ borderWidth: 1,
+ },
+
+ // Sizes
+ small: {
+ paddingHorizontal: 12,
+ paddingVertical: 6,
+ minWidth: 70,
+ },
+ medium: {
+ paddingHorizontal: 16,
+ paddingVertical: 8,
+ minWidth: 80,
+ },
+ large: {
+ paddingHorizontal: 20,
+ paddingVertical: 12,
+ minWidth: 100,
+ },
+
+ // Follow state styles
+ followButton: {
+ backgroundColor: '#70A1FF',
+ borderColor: '#70A1FF',
+ },
+ followingButton: {
+ backgroundColor: '#FFFFFF',
+ borderColor: '#70A1FF',
+ },
+
+ // Disabled state
+ disabledButton: {
+ backgroundColor: '#F8F9FA',
+ borderColor: '#E9ECEF',
+ },
+
+ // Text styles
+ text: {
+ fontWeight: '600',
+ },
+ smallText: {
+ fontSize: 12,
+ },
+ mediumText: {
+ fontSize: 14,
+ },
+ largeText: {
+ fontSize: 16,
+ },
+
+ // Text colors
+ followText: {
+ color: '#FFFFFF',
+ },
+ followingText: {
+ color: '#70A1FF',
+ },
+ disabledText: {
+ color: '#999',
+ },
+});
\ No newline at end of file
diff --git a/KonditionExpo/components/social/SearchBar.tsx b/KonditionExpo/components/social/SearchBar.tsx
new file mode 100644
index 0000000000..c3855be056
--- /dev/null
+++ b/KonditionExpo/components/social/SearchBar.tsx
@@ -0,0 +1,124 @@
+import React, { useState, useEffect, useRef } from 'react';
+import {
+ View,
+ TextInput,
+ StyleSheet,
+ TouchableOpacity,
+ Text,
+} from 'react-native';
+import { Ionicons } from '@expo/vector-icons';
+
+interface SearchBarProps {
+ onSearch: (query: string) => void;
+ onClear: () => void;
+ placeholder?: string;
+ debounceMs?: number;
+ loading?: boolean;
+}
+
+export const SearchBar: React.FC = ({
+ onSearch,
+ onClear,
+ placeholder = "Search users...",
+ debounceMs = 300,
+ loading = false,
+}) => {
+ const [query, setQuery] = useState('');
+ const debounceRef = useRef | null>(null);
+
+ useEffect(() => {
+ // Clear previous timeout
+ if (debounceRef.current) {
+ clearTimeout(debounceRef.current);
+ }
+
+ // Set new timeout for debounced search
+ if (query.trim()) {
+ debounceRef.current = setTimeout(() => {
+ onSearch(query.trim());
+ }, debounceMs);
+ } else {
+ // Clear results immediately when query is empty
+ onClear();
+ }
+
+ // Cleanup timeout on unmount
+ return () => {
+ if (debounceRef.current) {
+ clearTimeout(debounceRef.current);
+ }
+ };
+ }, [query, debounceMs]); // Removed onSearch and onClear from dependencies
+
+ const handleClear = () => {
+ setQuery('');
+ onClear();
+ };
+
+ return (
+
+
+
+
+ {query.length > 0 && (
+
+ {loading ? (
+
+ ) : (
+
+ )}
+
+ )}
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ paddingHorizontal: 16,
+ paddingVertical: 8,
+ },
+ searchContainer: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ backgroundColor: '#F8F9FA',
+ borderRadius: 12,
+ paddingHorizontal: 12,
+ paddingVertical: 8,
+ borderWidth: 1,
+ borderColor: '#E9ECEF',
+ },
+ searchIcon: {
+ marginRight: 8,
+ },
+ input: {
+ flex: 1,
+ fontSize: 16,
+ color: '#333',
+ paddingVertical: 4,
+ },
+ clearButton: {
+ padding: 4,
+ marginLeft: 8,
+ },
+});
\ No newline at end of file
diff --git a/KonditionExpo/components/social/UserCard.tsx b/KonditionExpo/components/social/UserCard.tsx
new file mode 100644
index 0000000000..2c4e4fedf5
--- /dev/null
+++ b/KonditionExpo/components/social/UserCard.tsx
@@ -0,0 +1,224 @@
+import React from 'react';
+import {
+ View,
+ Text,
+ StyleSheet,
+ TouchableOpacity,
+ Alert,
+} from 'react-native';
+import { Ionicons } from '@expo/vector-icons';
+import { UserSearchResult, UserProfile } from '../../services/api';
+import { FollowButton } from './FollowButton';
+import { useSocial } from '../../contexts/SocialContext';
+
+interface UserCardProps {
+ user: UserSearchResult | UserProfile;
+ onPress?: () => void;
+ showFollowButton?: boolean;
+ size?: 'compact' | 'normal';
+ context?: 'following' | 'followers' | 'search';
+}
+
+export const UserCard: React.FC = ({
+ user,
+ onPress,
+ showFollowButton = true,
+ size = 'normal',
+ context = 'search',
+}) => {
+ const { followUser, unfollowUser, followActionLoading } = useSocial();
+
+ // Determine follow status based on context
+ const isFollowing = (() => {
+ if (context === 'following') {
+ // Users in Following tab should always show as following (for Unfollow button)
+ console.log(`[UserCard] Following context - User: ${user.full_name || user.email}, forcing isFollowing=true`);
+ return true;
+ } else if (context === 'followers') {
+ // Users in Followers tab show follow status based on whether we follow them back
+ const followStatus = 'is_following' in user ? (user.is_following ?? false) : false;
+ console.log(`[UserCard] Followers context - User: ${user.full_name || user.email}, is_following: ${followStatus}`);
+ return followStatus;
+ } else {
+ // Search results use the is_following property
+ const followStatus = 'is_following' in user ? (user.is_following ?? false) : false;
+ console.log(`[UserCard] Search context - User: ${user.full_name || user.email}, is_following: ${followStatus}`);
+ return followStatus;
+ }
+ })();
+ const followerCount = 'follower_count' in user ? (user.follower_count ?? 0) : 0;
+ const followingCount = 'following_count' in user ? (user.following_count ?? 0) : 0;
+ const isLoading = followActionLoading[user.id] || false;
+
+ const handleFollowPress = async () => {
+ try {
+ if (isFollowing) {
+ await unfollowUser(user.id);
+ } else {
+ await followUser(user.id);
+ }
+ } catch (error) {
+ Alert.alert(
+ 'Error',
+ error instanceof Error ? error.message : 'Failed to update follow status',
+ [{ text: 'OK' }]
+ );
+ }
+ };
+
+ const formatCount = (count: number): string => {
+ if (count >= 1000000) {
+ return `${(count / 1000000).toFixed(1)}M`;
+ } else if (count >= 1000) {
+ return `${(count / 1000).toFixed(1)}K`;
+ }
+ return count.toString();
+ };
+
+ const cardStyle = [
+ styles.card,
+ size === 'compact' && styles.compactCard,
+ ];
+
+ const avatarStyle = [
+ styles.avatar,
+ size === 'compact' && styles.compactAvatar,
+ ];
+
+ return (
+
+
+
+
+
+
+
+
+ {user.full_name || 'Unknown User'}
+
+
+ {user.email}
+
+
+ {size === 'normal' && (followerCount > 0 || followingCount > 0) && (
+
+
+ {formatCount(followerCount)}
+ followers
+
+
+ {formatCount(followingCount)}
+ following
+
+
+ )}
+
+
+
+ {showFollowButton && (
+
+ )}
+
+ );
+};
+
+const styles = StyleSheet.create({
+ card: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'space-between',
+ backgroundColor: '#FFFFFF',
+ padding: 16,
+ marginHorizontal: 16,
+ marginVertical: 4,
+ borderRadius: 12,
+ shadowColor: '#000',
+ shadowOffset: {
+ width: 0,
+ height: 1,
+ },
+ shadowOpacity: 0.1,
+ shadowRadius: 2,
+ elevation: 2,
+ borderWidth: 1,
+ borderColor: '#F0F0F0',
+ },
+ compactCard: {
+ padding: 12,
+ marginVertical: 2,
+ },
+ leftSection: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ flex: 1,
+ marginRight: 12,
+ },
+ avatar: {
+ width: 48,
+ height: 48,
+ borderRadius: 24,
+ backgroundColor: '#F8F9FA',
+ alignItems: 'center',
+ justifyContent: 'center',
+ marginRight: 12,
+ borderWidth: 1,
+ borderColor: '#E9ECEF',
+ },
+ compactAvatar: {
+ width: 40,
+ height: 40,
+ borderRadius: 20,
+ marginRight: 10,
+ },
+ userInfo: {
+ flex: 1,
+ },
+ name: {
+ fontSize: 16,
+ fontWeight: '600',
+ color: '#333',
+ marginBottom: 2,
+ },
+ compactName: {
+ fontSize: 14,
+ marginBottom: 1,
+ },
+ email: {
+ fontSize: 14,
+ color: '#666',
+ marginBottom: 4,
+ },
+ compactEmail: {
+ fontSize: 12,
+ },
+ statsContainer: {
+ flexDirection: 'row',
+ marginTop: 4,
+ },
+ stat: {
+ marginRight: 16,
+ },
+ statNumber: {
+ fontSize: 14,
+ fontWeight: '600',
+ color: '#333',
+ },
+ statLabel: {
+ fontSize: 12,
+ color: '#666',
+ },
+});
\ No newline at end of file
diff --git a/KonditionExpo/components/social/UserList.tsx b/KonditionExpo/components/social/UserList.tsx
new file mode 100644
index 0000000000..313fcd65cb
--- /dev/null
+++ b/KonditionExpo/components/social/UserList.tsx
@@ -0,0 +1,166 @@
+import React from 'react';
+import {
+ FlatList,
+ View,
+ Text,
+ StyleSheet,
+ ActivityIndicator,
+ RefreshControl,
+ ListRenderItem,
+} from 'react-native';
+import { UserSearchResult, UserProfile } from '../../services/api';
+import { UserCard } from './UserCard';
+
+interface UserListProps {
+ users: (UserSearchResult | UserProfile)[];
+ loading?: boolean;
+ refreshing?: boolean;
+ onRefresh?: () => void;
+ onLoadMore?: () => void;
+ onUserPress?: (user: UserSearchResult | UserProfile) => void;
+ showFollowButton?: boolean;
+ emptyTitle?: string;
+ emptyMessage?: string;
+ size?: 'compact' | 'normal';
+ hasMore?: boolean;
+ context?: 'following' | 'followers' | 'search';
+}
+
+export const UserList: React.FC = ({
+ users,
+ loading = false,
+ refreshing = false,
+ onRefresh,
+ onLoadMore,
+ onUserPress,
+ showFollowButton = true,
+ emptyTitle = "No Users Found",
+ emptyMessage = "Try searching for users or check back later.",
+ size = 'normal',
+ hasMore = false,
+ context = 'search',
+}) => {
+ const renderUser: ListRenderItem = ({ item }) => (
+ onUserPress(item) : undefined}
+ showFollowButton={showFollowButton}
+ size={size}
+ context={context}
+ />
+ );
+
+ const renderEmptyState = () => (
+
+ {emptyTitle}
+ {emptyMessage}
+
+ );
+
+ const renderFooter = () => {
+ if (!loading || users.length === 0) return null;
+
+ return (
+
+
+ Loading more users...
+
+ );
+ };
+
+ const handleEndReached = () => {
+ if (!loading && hasMore && onLoadMore) {
+ onLoadMore();
+ }
+ };
+
+ if (loading && users.length === 0) {
+ return (
+
+
+ Loading users...
+
+ );
+ }
+
+ return (
+ item.id}
+ contentContainerStyle={[
+ styles.container,
+ users.length === 0 && styles.emptyContainer,
+ ]}
+ showsVerticalScrollIndicator={false}
+ refreshControl={
+ onRefresh ? (
+
+ ) : undefined
+ }
+ ListEmptyComponent={renderEmptyState}
+ ListFooterComponent={renderFooter}
+ onEndReached={handleEndReached}
+ onEndReachedThreshold={0.1}
+ removeClippedSubviews={true}
+ maxToRenderPerBatch={10}
+ windowSize={10}
+ initialNumToRender={10}
+ />
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ paddingVertical: 8,
+ },
+ emptyContainer: {
+ flex: 1,
+ justifyContent: 'center',
+ },
+ loadingContainer: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ paddingVertical: 40,
+ },
+ loadingText: {
+ marginTop: 12,
+ fontSize: 16,
+ color: '#666',
+ },
+ emptyState: {
+ alignItems: 'center',
+ paddingVertical: 40,
+ paddingHorizontal: 32,
+ },
+ emptyTitle: {
+ fontSize: 20,
+ fontWeight: '600',
+ color: '#333',
+ marginBottom: 8,
+ textAlign: 'center',
+ },
+ emptyMessage: {
+ fontSize: 16,
+ color: '#666',
+ textAlign: 'center',
+ lineHeight: 24,
+ },
+ footer: {
+ flexDirection: 'row',
+ justifyContent: 'center',
+ alignItems: 'center',
+ paddingVertical: 16,
+ },
+ footerText: {
+ marginLeft: 8,
+ fontSize: 14,
+ color: '#666',
+ },
+});
\ No newline at end of file
diff --git a/KonditionExpo/components/social/index.ts b/KonditionExpo/components/social/index.ts
new file mode 100644
index 0000000000..4b626496c0
--- /dev/null
+++ b/KonditionExpo/components/social/index.ts
@@ -0,0 +1,4 @@
+export { SearchBar } from './SearchBar';
+export { FollowButton } from './FollowButton';
+export { UserCard } from './UserCard';
+export { UserList } from './UserList';
\ No newline at end of file
diff --git a/KonditionExpo/components/ui/Button.tsx b/KonditionExpo/components/ui/Button.tsx
new file mode 100644
index 0000000000..6e767f339b
--- /dev/null
+++ b/KonditionExpo/components/ui/Button.tsx
@@ -0,0 +1,159 @@
+import React from 'react';
+import { TouchableOpacity, ActivityIndicator, Text, StyleSheet, View } from 'react-native';
+import { useThemeColor } from '../../hooks/useThemeColor';
+
+interface ButtonProps {
+ onPress?: () => void;
+ title: string;
+ loading?: boolean;
+ loadingText?: string;
+ disabled?: boolean;
+ variant?: 'solid' | 'outline' | 'ghost';
+ size?: 'sm' | 'md' | 'lg';
+ colorScheme?: string;
+ leftIcon?: React.ReactNode;
+ rightIcon?: React.ReactNode;
+ fullWidth?: boolean;
+ style?: any;
+ textStyle?: any;
+}
+
+export function Button({
+ onPress,
+ title,
+ loading = false,
+ loadingText,
+ disabled = false,
+ variant = 'solid',
+ size = 'md',
+ colorScheme = 'primary',
+ leftIcon,
+ rightIcon,
+ fullWidth = false,
+ style,
+ textStyle
+}: ButtonProps) {
+ const tintColor = useThemeColor({}, 'tint');
+ const textColor = useThemeColor({}, 'text');
+ const backgroundColor = tintColor;
+ const disabledBg = '#A0AEC0';
+
+ // Apply variant styles
+ let variantStyle = {};
+ let variantTextStyle = {};
+
+ if (variant === 'outline') {
+ variantStyle = {
+ backgroundColor: 'transparent',
+ borderWidth: 1,
+ borderColor: backgroundColor,
+ };
+ variantTextStyle = {
+ color: backgroundColor,
+ };
+ } else if (variant === 'ghost') {
+ variantStyle = {
+ backgroundColor: 'transparent',
+ };
+ variantTextStyle = {
+ color: backgroundColor,
+ };
+ }
+
+ // Apply size styles
+ let sizeStyle = styles.buttonMd;
+ let sizeTextStyle = styles.textMd;
+
+ if (size === 'sm') {
+ sizeStyle = styles.buttonSm;
+ sizeTextStyle = styles.textSm;
+ } else if (size === 'lg') {
+ sizeStyle = styles.buttonLg;
+ sizeTextStyle = styles.textLg;
+ }
+
+ // Apply width style
+ const widthStyle = fullWidth ? { width: '100%' } : {};
+
+ return (
+
+ {loading ? (
+
+
+ {loadingText && {loadingText}}
+
+ ) : (
+
+ {leftIcon && {leftIcon}}
+ {title}
+ {rightIcon && {rightIcon}}
+
+ )}
+
+ );
+}
+
+const styles = StyleSheet.create({
+ button: {
+ alignItems: 'center',
+ justifyContent: 'center',
+ borderRadius: 6,
+ },
+ buttonSm: {
+ paddingVertical: 8,
+ paddingHorizontal: 16,
+ },
+ buttonMd: {
+ paddingVertical: 10,
+ paddingHorizontal: 20,
+ },
+ buttonLg: {
+ paddingVertical: 12,
+ paddingHorizontal: 24,
+ },
+ text: {
+ fontWeight: '600',
+ color: 'white',
+ },
+ textSm: {
+ fontSize: 14,
+ },
+ textMd: {
+ fontSize: 16,
+ },
+ textLg: {
+ fontSize: 18,
+ },
+ loadingContainer: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ contentContainer: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ iconLeft: {
+ marginRight: 8,
+ },
+ iconRight: {
+ marginLeft: 8,
+ }
+});
\ No newline at end of file
diff --git a/KonditionExpo/components/ui/Checkbox.tsx b/KonditionExpo/components/ui/Checkbox.tsx
new file mode 100644
index 0000000000..f97ddae5bf
--- /dev/null
+++ b/KonditionExpo/components/ui/Checkbox.tsx
@@ -0,0 +1,112 @@
+import React from 'react';
+import { TouchableOpacity, Text, StyleSheet, View } from 'react-native';
+import { useThemeColor } from '../../hooks/useThemeColor';
+
+interface CheckboxProps {
+ isChecked: boolean;
+ onChange: (isChecked: boolean) => void;
+ label?: string;
+ disabled?: boolean;
+ size?: 'sm' | 'md' | 'lg';
+ style?: any;
+}
+
+export function Checkbox({
+ isChecked,
+ onChange,
+ label,
+ disabled = false,
+ size = 'md',
+ style,
+}: CheckboxProps) {
+ const tintColor = useThemeColor({}, 'tint');
+ const textColor = useThemeColor({}, 'text');
+ const iconColor = useThemeColor({}, 'icon');
+
+ // Determine checkbox size
+ let checkboxSize = 20;
+ if (size === 'sm') checkboxSize = 16;
+ if (size === 'lg') checkboxSize = 24;
+
+ // Calculate internal checkmark size
+ const checkmarkSize = checkboxSize - 8;
+
+ return (
+ {
+ if (!disabled) {
+ onChange(!isChecked);
+ }
+ }}
+ activeOpacity={0.7}
+ disabled={disabled}
+ accessibilityLabel={label || 'Checkbox'}
+ accessibilityRole="checkbox"
+ accessibilityState={{ checked: isChecked, disabled }}
+ accessible={true}
+ >
+
+ {isChecked && (
+
+ )}
+
+
+ {label && (
+
+ {label}
+
+ )}
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ marginVertical: 4,
+ },
+ checkbox: {
+ borderWidth: 2,
+ borderRadius: 4,
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ checkmark: {
+ backgroundColor: 'white',
+ borderRadius: 2,
+ },
+ label: {
+ marginLeft: 8,
+ },
+});
\ No newline at end of file
diff --git a/KonditionExpo/components/ui/Dialog.tsx b/KonditionExpo/components/ui/Dialog.tsx
new file mode 100644
index 0000000000..240c23f679
--- /dev/null
+++ b/KonditionExpo/components/ui/Dialog.tsx
@@ -0,0 +1,148 @@
+import React from 'react';
+import { Modal, View, Text, StyleSheet, TouchableOpacity, TouchableWithoutFeedback, Dimensions } from 'react-native';
+import { useThemeColor } from '../../hooks/useThemeColor';
+
+interface DialogProps {
+ isOpen: boolean;
+ onClose: () => void;
+ title?: string;
+ children: React.ReactNode;
+ closeOnOverlayClick?: boolean;
+ size?: 'sm' | 'md' | 'lg' | 'full';
+ footer?: React.ReactNode;
+ position?: 'center' | 'bottom';
+}
+
+export function Dialog({
+ isOpen,
+ onClose,
+ title,
+ children,
+ closeOnOverlayClick = true,
+ size = 'md',
+ footer,
+ position = 'center',
+}: DialogProps) {
+ const backgroundColor = useThemeColor({}, 'background');
+ const textColor = useThemeColor({}, 'text');
+
+ // Determine width based on size
+ let dialogWidth = 0.9 * Dimensions.get('window').width;
+ let maxHeight = 0.8 * Dimensions.get('window').height;
+ if (size === 'sm') dialogWidth = 0.7 * Dimensions.get('window').width;
+ if (size === 'lg') dialogWidth = 0.95 * Dimensions.get('window').width;
+ if (size === 'full') {
+ dialogWidth = Dimensions.get('window').width;
+ maxHeight = Dimensions.get('window').height;
+ }
+
+ return (
+
+ {
+ if (closeOnOverlayClick) {
+ onClose();
+ }
+ }}
+ >
+
+ e.stopPropagation()}>
+
+ {title && (
+
+ {title}
+
+
+ Γ
+
+
+ )}
+
+
+ {children}
+
+
+ {footer && (
+
+ {footer}
+
+ )}
+
+
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ overlay: {
+ flex: 1,
+ backgroundColor: 'rgba(0, 0, 0, 0.4)',
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ dialogContainer: {
+ borderRadius: 8,
+ overflow: 'hidden',
+ shadowColor: '#000',
+ shadowOffset: { width: 0, height: 2 },
+ shadowOpacity: 0.25,
+ shadowRadius: 3.84,
+ elevation: 5,
+ },
+ centerPosition: {
+ alignSelf: 'center',
+ },
+ bottomPosition: {
+ position: 'absolute',
+ bottom: 0,
+ borderBottomLeftRadius: 0,
+ borderBottomRightRadius: 0,
+ alignSelf: 'center',
+ },
+ header: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ paddingHorizontal: 16,
+ paddingVertical: 12,
+ borderBottomWidth: 1,
+ borderBottomColor: '#E2E8F0',
+ },
+ title: {
+ fontSize: 18,
+ fontWeight: '600',
+ },
+ closeButton: {
+ width: 30,
+ height: 30,
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ content: {
+ padding: 16,
+ },
+ footer: {
+ padding: 16,
+ borderTopWidth: 1,
+ borderTopColor: '#E2E8F0',
+ flexDirection: 'row',
+ justifyContent: 'flex-end',
+ },
+});
\ No newline at end of file
diff --git a/KonditionExpo/components/ui/Get_Started.tsx b/KonditionExpo/components/ui/Get_Started.tsx
new file mode 100644
index 0000000000..50a44ec251
--- /dev/null
+++ b/KonditionExpo/components/ui/Get_Started.tsx
@@ -0,0 +1,50 @@
+import { useStyles, createStyleSheet } from 'styles';
+import { View, Text } from 'react-native';
+
+export interface Component1Props {
+ /** Used to locate this view in end-to-end tests. */
+ testID?: string;
+}
+
+export function Component1(props: Component1Props) {
+ const styles = useStyles(stylesheet);
+
+ return (
+
+
+
+ Get Started
+
+
+ );
+}
+
+const stylesheet = createStyleSheet((theme) => ({
+ root: {
+ width: 315,
+ height: 60,
+ flexShrink: 0,
+ },
+ rectangle5853: {
+ width: 315,
+ height: 60,
+ flexShrink: 0,
+ borderBottomLeftRadius: 99,
+ borderBottomRightRadius: 99,
+ borderTopLeftRadius: 99,
+ borderTopRightRadius: 99,
+ backgroundColor: theme.colors.white, // Example usage
+ shadowColor: "rgba(0, 0, 0, 0.25)",
+ shadowOffset: { width: 0, height: 4 },
+ shadowOpacity: 1,
+ shadowRadius: 4,
+ elevation: 4, // For Android
+ },
+ getStarted: {
+ fontFamily: 'Poppins',
+ fontSize: 16,
+ fontStyle: 'normal',
+ fontWeight: '700',
+ backgroundColor: theme.colors.blue, // Example usage
+ },
+}));
diff --git a/KonditionExpo/components/ui/IconSymbol.ios.tsx b/KonditionExpo/components/ui/IconSymbol.ios.tsx
new file mode 100644
index 0000000000..9177f4daf2
--- /dev/null
+++ b/KonditionExpo/components/ui/IconSymbol.ios.tsx
@@ -0,0 +1,32 @@
+import { SymbolView, SymbolViewProps, SymbolWeight } from 'expo-symbols';
+import { StyleProp, ViewStyle } from 'react-native';
+
+export function IconSymbol({
+ name,
+ size = 24,
+ color,
+ style,
+ weight = 'regular',
+}: {
+ name: SymbolViewProps['name'];
+ size?: number;
+ color: string;
+ style?: StyleProp;
+ weight?: SymbolWeight;
+}) {
+ return (
+
+ );
+}
diff --git a/KonditionExpo/components/ui/IconSymbol.tsx b/KonditionExpo/components/ui/IconSymbol.tsx
new file mode 100644
index 0000000000..82bc868c0a
--- /dev/null
+++ b/KonditionExpo/components/ui/IconSymbol.tsx
@@ -0,0 +1,50 @@
+// Fallback for using MaterialIcons on Android and web.
+
+import MaterialIcons from '@expo/vector-icons/MaterialIcons';
+import { SymbolWeight, SymbolViewProps } from 'expo-symbols';
+import { ComponentProps } from 'react';
+import { OpaqueColorValue, type StyleProp, type TextStyle } from 'react-native';
+
+type IconMapping = Record['name']>;
+type IconSymbolName = keyof typeof MAPPING;
+
+/**
+ * Add your SF Symbols to Material Icons mappings here.
+ * - see Material Icons in the [Icons Directory](https://icons.expo.fyi).
+ * - see SF Symbols in the [SF Symbols](https://developer.apple.com/sf-symbols/) app.
+ */
+const MAPPING = {
+ 'house.fill': 'home',
+ 'paperplane.fill': 'send',
+ 'chevron.left.forwardslash.chevron.right': 'code',
+ 'chevron.right': 'chevron-right',
+ 'chevron.up': 'keyboard-arrow-up',
+ 'chevron.down': 'keyboard-arrow-down',
+ 'chart.line.uptrend.xyaxis': 'trending-up',
+ 'clock.fill': 'history',
+ 'person.2.fill': 'people',
+ 'person.fill': 'person',
+ 'plus': 'add',
+ 'globe': 'public',
+ 'lock.fill': 'lock',
+} as IconMapping;
+
+/**
+ * An icon component that uses native SF Symbols on iOS, and Material Icons on Android and web.
+ * This ensures a consistent look across platforms, and optimal resource usage.
+ * Icon `name`s are based on SF Symbols and require manual mapping to Material Icons.
+ */
+export function IconSymbol({
+ name,
+ size = 24,
+ color,
+ style,
+}: {
+ name: IconSymbolName;
+ size?: number;
+ color: string | OpaqueColorValue;
+ style?: StyleProp;
+ weight?: SymbolWeight;
+}) {
+ return ;
+}
diff --git a/KonditionExpo/components/ui/Input.tsx b/KonditionExpo/components/ui/Input.tsx
new file mode 100644
index 0000000000..e7cbd7b3f7
--- /dev/null
+++ b/KonditionExpo/components/ui/Input.tsx
@@ -0,0 +1,159 @@
+import React, { useState } from 'react';
+import { View, TextInput, Text, StyleSheet, TouchableOpacity } from 'react-native';
+import { useThemeColor } from '../../hooks/useThemeColor';
+
+interface InputProps {
+ placeholder?: string;
+ value: string;
+ onChangeText: (text: string) => void;
+ label?: string;
+ error?: string;
+ isRequired?: boolean;
+ autoCapitalize?: 'none' | 'sentences' | 'words' | 'characters';
+ keyboardType?: 'default' | 'email-address' | 'numeric' | 'phone-pad' | 'number-pad';
+ secureTextEntry?: boolean;
+ multiline?: boolean;
+ numberOfLines?: number;
+ style?: any;
+ inputStyle?: any;
+ leftIcon?: React.ReactNode;
+ rightIcon?: React.ReactNode;
+ onRightIconPress?: () => void;
+}
+
+export function Input({
+ placeholder,
+ value,
+ onChangeText,
+ label,
+ error,
+ isRequired = false,
+ autoCapitalize = 'none',
+ keyboardType = 'default',
+ secureTextEntry = false,
+ multiline = false,
+ numberOfLines = 1,
+ style,
+ inputStyle,
+ leftIcon,
+ rightIcon,
+ onRightIconPress,
+}: InputProps) {
+ const [isFocused, setIsFocused] = useState(false);
+
+ const textColor = useThemeColor({}, 'text');
+ const tintColor = useThemeColor({}, 'tint');
+ const backgroundColor = useThemeColor({}, 'background');
+
+ const borderColor = error ? '#E53E3E' : isFocused ? tintColor : '#E2E8F0';
+
+ return (
+
+ {label && (
+
+
+ {label}
+ {isRequired && *}
+
+
+ )}
+
+
+ {leftIcon && {leftIcon}}
+
+ setIsFocused(true)}
+ onBlur={() => setIsFocused(false)}
+ placeholderTextColor="#A0AEC0"
+ accessibilityLabel={label || placeholder}
+ accessibilityHint={error ? `Error: ${error}` : undefined}
+ accessibilityRole="text"
+ accessible={true}
+ />
+
+ {rightIcon && (
+ onRightIconPress ? (
+
+ {rightIcon}
+
+ ) : (
+
+ {rightIcon}
+
+ )
+ )}
+
+
+ {error && (
+ {error}
+ )}
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ marginBottom: 16,
+ },
+ labelContainer: {
+ marginBottom: 4,
+ },
+ label: {
+ fontSize: 14,
+ fontWeight: '500',
+ },
+ inputContainer: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ borderWidth: 1,
+ borderRadius: 6,
+ overflow: 'hidden',
+ },
+ input: {
+ flex: 1,
+ paddingVertical: 10,
+ paddingHorizontal: 12,
+ fontSize: 16,
+ },
+ leftIcon: {
+ paddingLeft: 12,
+ },
+ rightIcon: {
+ paddingRight: 12,
+ },
+ error: {
+ fontSize: 12,
+ color: '#E53E3E',
+ marginTop: 4,
+ },
+});
\ No newline at end of file
diff --git a/KonditionExpo/components/ui/TabBarBackground.ios.tsx b/KonditionExpo/components/ui/TabBarBackground.ios.tsx
new file mode 100644
index 0000000000..495b2d4e5a
--- /dev/null
+++ b/KonditionExpo/components/ui/TabBarBackground.ios.tsx
@@ -0,0 +1,19 @@
+import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs';
+import { BlurView } from 'expo-blur';
+import { StyleSheet } from 'react-native';
+
+export default function BlurTabBarBackground() {
+ return (
+
+ );
+}
+
+export function useBottomTabOverflow() {
+ return useBottomTabBarHeight();
+}
diff --git a/KonditionExpo/components/ui/TabBarBackground.tsx b/KonditionExpo/components/ui/TabBarBackground.tsx
new file mode 100644
index 0000000000..70d1c3c0b8
--- /dev/null
+++ b/KonditionExpo/components/ui/TabBarBackground.tsx
@@ -0,0 +1,6 @@
+// This is a shim for web and Android where the tab bar is generally opaque.
+export default undefined;
+
+export function useBottomTabOverflow() {
+ return 0;
+}
diff --git a/KonditionExpo/config/api.ts b/KonditionExpo/config/api.ts
new file mode 100644
index 0000000000..52c820c303
--- /dev/null
+++ b/KonditionExpo/config/api.ts
@@ -0,0 +1,117 @@
+import { Platform } from 'react-native';
+import Constants from 'expo-constants';
+
+// API Configuration
+export const API_CONFIG = {
+ // Timeout for API requests (in milliseconds)
+ TIMEOUT: 10000,
+
+ // Token storage key
+ TOKEN_KEY: 'access_token',
+
+ // API endpoints
+ ENDPOINTS: {
+ LOGIN: '/login/access-token',
+ SIGNUP: '/users/signup',
+ TEST_TOKEN: '/login/test-token',
+ USER_PROFILE: '/users/me',
+ WORKOUTS: '/workouts',
+ WORKOUT_BY_ID: (id: string) => `/workouts/${id}`,
+ // Social endpoints
+ SEARCH_USERS: '/social/users/search',
+ FOLLOW_USER: (userId: string) => `/social/follow/${userId}`,
+ UNFOLLOW_USER: (userId: string) => `/social/unfollow/${userId}`,
+ GET_FOLLOWERS: '/social/followers',
+ GET_FOLLOWING: '/social/following',
+ IS_FOLLOWING: (userId: string) => `/social/is-following/${userId}`,
+ USER_PROFILE_EXTENDED: (userId: string) => `/social/user/${userId}/profile`,
+ },
+};
+
+// Platform-specific base URLs
+const API_URLS = {
+ // Web platform - works with localhost
+ web: 'http://localhost:8000/api/v1',
+
+ // iOS Simulator - works with localhost
+ ios_simulator: 'http://localhost:8000/api/v1',
+
+ // Android Emulator - needs special IP
+ android_emulator: 'http://10.0.2.2:8000/api/v1',
+
+ // Physical device/Expo Go - needs computer's IP address
+ // You'll need to replace this with your computer's actual IP address
+ physical_device: 'http://192.168.1.100:8000/api/v1', // Replace with your IP
+};
+
+// Detect platform and environment
+const getPlatformType = () => {
+ if (Platform.OS === 'web') {
+ return 'web';
+ }
+
+ if (Platform.OS === 'ios') {
+ // Check if running on simulator
+ if (Constants.platform?.ios?.simulator) {
+ return 'ios_simulator';
+ }
+ return 'physical_device';
+ }
+
+ if (Platform.OS === 'android') {
+ // Check if running on emulator
+ if (Constants.platform?.android?.isDevice === false) {
+ return 'android_emulator';
+ }
+ return 'physical_device';
+ }
+
+ // Default fallback
+ return 'web';
+};
+
+// Environment-specific configuration
+export const getApiBaseUrl = () => {
+ const platformType = getPlatformType();
+ const baseUrl = API_URLS[platformType];
+
+ // Log the detected platform and URL for debugging
+ console.log(`Platform detected: ${Platform.OS} (${platformType})`);
+ console.log(`Using API URL: ${baseUrl}`);
+
+ return baseUrl;
+};
+
+// Helper function to manually set API URL for testing
+export const setCustomApiUrl = (url: string) => {
+ // This can be used for testing different endpoints
+ API_URLS.physical_device = url;
+ console.log(`Custom API URL set: ${url}`);
+};
+
+// Get current computer's IP address instructions
+export const getNetworkSetupInstructions = () => {
+ return {
+ title: "Network Setup for Mobile Testing",
+ instructions: [
+ "1. Find your computer's IP address:",
+ " - macOS: System Preferences > Network > Advanced > TCP/IP",
+ " - Windows: ipconfig in Command Prompt",
+ " - Linux: ifconfig or ip addr",
+ "",
+ "2. Update the physical_device URL in config/api.ts:",
+ " Replace '192.168.1.100' with your computer's IP address",
+ "",
+ "3. Make sure your backend is running on 0.0.0.0:8000:",
+ " uvicorn app.main:app --host 0.0.0.0 --port 8000",
+ "",
+ "4. Ensure your computer and mobile device are on the same network",
+ "",
+ "Platform-specific URLs:",
+ `- Web: ${API_URLS.web}`,
+ `- iOS Simulator: ${API_URLS.ios_simulator}`,
+ `- Android Emulator: ${API_URLS.android_emulator}`,
+ `- Physical Device: ${API_URLS.physical_device}`,
+ ]
+ };
+};
\ No newline at end of file
diff --git a/KonditionExpo/constants/Colors.ts b/KonditionExpo/constants/Colors.ts
new file mode 100644
index 0000000000..25fa784cb9
--- /dev/null
+++ b/KonditionExpo/constants/Colors.ts
@@ -0,0 +1,30 @@
+/**
+ * Below are the colors that are used in the app. The colors are defined in the light and dark mode.
+ * There are many other ways to style your app. For example, [Nativewind](https://www.nativewind.dev/), [Tamagui](https://tamagui.dev/), [unistyles](https://reactnativeunistyles.vercel.app), etc.
+ */
+
+const tintColorLight = '#FF6B00'; // Vibrant orange
+const tintColorDark = '#FF8A3D'; // Lighter orange for dark mode
+const accentColorLight = '#FF9F45'; // Secondary orange
+const accentColorDark = '#FFB067'; // Lighter secondary orange
+
+export const Colors = {
+ light: {
+ text: '#11181C',
+ background: '#fff',
+ tint: tintColorLight,
+ accent: accentColorLight,
+ icon: '#687076',
+ tabIconDefault: '#687076',
+ tabIconSelected: tintColorLight,
+ },
+ dark: {
+ text: '#ECEDEE',
+ background: '#151718',
+ tint: tintColorDark,
+ accent: accentColorDark,
+ icon: '#9BA1A6',
+ tabIconDefault: '#9BA1A6',
+ tabIconSelected: tintColorDark,
+ },
+};
diff --git a/KonditionExpo/contexts/AuthContext.tsx b/KonditionExpo/contexts/AuthContext.tsx
new file mode 100644
index 0000000000..89d7f44a1c
--- /dev/null
+++ b/KonditionExpo/contexts/AuthContext.tsx
@@ -0,0 +1,207 @@
+import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';
+import AsyncStorage from '@react-native-async-storage/async-storage';
+import { apiService, UserProfile, SignupRequest, ProfileUpdateRequest } from '../services/api';
+
+type AuthContextType = {
+ // Authentication state
+ isAuthenticated: boolean;
+ isLoading: boolean;
+ user: UserProfile | null;
+ token: string | null;
+ isInSignupFlow: boolean;
+
+ // Authentication methods
+ login: (email: string, password: string) => Promise;
+ signup: (userData: SignupRequest) => Promise;
+ signupWithoutLogin: (userData: SignupRequest) => Promise;
+ logout: () => Promise;
+
+ // Profile methods
+ updateProfile: (profileData: ProfileUpdateRequest) => Promise;
+
+ // Utility methods
+ checkAuthState: () => Promise;
+ setSignupFlowComplete: () => void;
+};
+
+const AuthContext = createContext(undefined);
+
+export const AuthProvider = ({ children }: { children: ReactNode }) => {
+ const [isAuthenticated, setIsAuthenticated] = useState(false);
+ const [isLoading, setIsLoading] = useState(true);
+ const [user, setUser] = useState(null);
+ const [token, setToken] = useState(null);
+ const [isInSignupFlow, setIsInSignupFlow] = useState(false);
+
+ // Check authentication state on app start
+ useEffect(() => {
+ checkAuthState();
+ }, []);
+
+ const checkAuthState = async () => {
+ try {
+ setIsLoading(true);
+
+ // Get stored token
+ const storedToken = await AsyncStorage.getItem('access_token');
+
+ if (storedToken) {
+ // Verify token is still valid by getting user profile
+ const userProfile = await apiService.testToken();
+
+ setToken(storedToken);
+ setUser(userProfile);
+ setIsAuthenticated(true);
+ } else {
+ // No token found
+ setIsAuthenticated(false);
+ setUser(null);
+ setToken(null);
+ }
+ } catch (error) {
+ console.error('Auth check failed:', error);
+ // Token is invalid, clear everything
+ await logout();
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ const login = async (email: string, password: string) => {
+ try {
+ setIsLoading(true);
+
+ // Call login API
+ const authResponse = await apiService.login(email, password);
+
+ // Store token
+ await apiService.storeToken(authResponse.access_token);
+
+ // Get user profile
+ const userProfile = await apiService.getCurrentUser();
+
+ // Update state
+ setToken(authResponse.access_token);
+ setUser(userProfile);
+ setIsAuthenticated(true);
+ } catch (error) {
+ console.error('Login failed:', error);
+ throw error;
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ const signup = async (userData: SignupRequest) => {
+ try {
+ setIsLoading(true);
+ setIsInSignupFlow(true);
+
+ // Call signup API
+ const userProfile = await apiService.signup(userData);
+
+ // After successful signup, automatically log the user in
+ await login(userData.email, userData.password);
+
+ return userProfile;
+ } catch (error) {
+ console.error('Signup failed:', error);
+ setIsInSignupFlow(false);
+ throw error;
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ const setSignupFlowComplete = () => {
+ setIsInSignupFlow(false);
+ };
+
+ const logout = async () => {
+ try {
+ setIsLoading(true);
+
+ // Remove token from storage
+ await apiService.removeToken();
+
+ // Clear state
+ setToken(null);
+ setUser(null);
+ setIsAuthenticated(false);
+ } catch (error) {
+ console.error('Logout failed:', error);
+ // Even if logout fails, clear local state
+ setToken(null);
+ setUser(null);
+ setIsAuthenticated(false);
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ const signupWithoutLogin = async (userData: SignupRequest) => {
+ try {
+ console.log('AuthContext: Starting signupWithoutLogin...');
+ setIsLoading(true);
+
+ // Call signup API only, don't log in automatically
+ console.log('AuthContext: Calling apiService.signup...');
+ const userProfile = await apiService.signup(userData);
+ console.log('AuthContext: Signup API call successful, user profile:', userProfile);
+
+ return userProfile;
+ } catch (error) {
+ console.error('AuthContext: Signup failed:', error);
+ throw error;
+ } finally {
+ console.log('AuthContext: Setting isLoading to false');
+ setIsLoading(false);
+ }
+ };
+
+ const updateProfile = async (profileData: ProfileUpdateRequest) => {
+ try {
+ setIsLoading(true);
+
+ // Call profile update API
+ const updatedUser = await apiService.updateProfile(profileData);
+
+ // Update user state
+ setUser(updatedUser);
+ } catch (error) {
+ console.error('Profile update failed:', error);
+ throw error;
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ const value: AuthContextType = {
+ isAuthenticated,
+ isLoading,
+ user,
+ token,
+ isInSignupFlow,
+ login,
+ signup,
+ signupWithoutLogin,
+ logout,
+ updateProfile,
+ checkAuthState,
+ setSignupFlowComplete,
+ };
+
+ return (
+
+ {children}
+
+ );
+};
+
+export const useAuth = () => {
+ const context = useContext(AuthContext);
+ if (!context) {
+ throw new Error('useAuth must be used within an AuthProvider');
+ }
+ return context;
+};
diff --git a/KonditionExpo/contexts/FeedContext.tsx b/KonditionExpo/contexts/FeedContext.tsx
new file mode 100644
index 0000000000..0b3fa388ba
--- /dev/null
+++ b/KonditionExpo/contexts/FeedContext.tsx
@@ -0,0 +1,236 @@
+import React, { createContext, useContext, useState, useCallback, ReactNode } from 'react';
+import { apiService, WorkoutPostResponse, WorkoutPostsResponse, WorkoutPostCreateRequest } from '../services/api';
+
+export type FeedType = 'personal' | 'public' | 'combined';
+
+interface FeedState {
+ personalFeed: WorkoutPostResponse[];
+ publicFeed: WorkoutPostResponse[];
+ combinedFeed: WorkoutPostResponse[];
+ isLoading: boolean;
+ error: string | null;
+ hasMore: boolean;
+ currentFeedType: FeedType;
+}
+
+interface FeedContextType extends FeedState {
+ // Feed operations
+ loadFeed: (feedType: FeedType, refresh?: boolean) => Promise;
+ loadMorePosts: () => Promise;
+ refreshFeed: () => Promise;
+ setCurrentFeedType: (feedType: FeedType) => void;
+
+ // Post operations
+ createPost: (postData: WorkoutPostCreateRequest) => Promise;
+ deletePost: (postId: string) => Promise;
+
+ // Utility functions
+ getCurrentFeed: () => WorkoutPostResponse[];
+ clearError: () => void;
+}
+
+const FeedContext = createContext(undefined);
+
+interface FeedProviderProps {
+ children: ReactNode;
+}
+
+export function FeedProvider({ children }: FeedProviderProps) {
+ const [state, setState] = useState({
+ personalFeed: [],
+ publicFeed: [],
+ combinedFeed: [],
+ isLoading: false,
+ error: null,
+ hasMore: true,
+ currentFeedType: 'personal',
+ });
+
+ const setLoading = useCallback((isLoading: boolean) => {
+ setState(prev => ({ ...prev, isLoading }));
+ }, []);
+
+ const setError = useCallback((error: string | null) => {
+ setState(prev => ({ ...prev, error }));
+ }, []);
+
+ const clearError = useCallback(() => {
+ setError(null);
+ }, [setError]);
+
+ const setCurrentFeedType = useCallback((feedType: FeedType) => {
+ setState(prev => ({ ...prev, currentFeedType: feedType }));
+ }, []);
+
+ const getCurrentFeedForType = useCallback((feedType: FeedType): WorkoutPostResponse[] => {
+ switch (feedType) {
+ case 'personal':
+ return state.personalFeed;
+ case 'public':
+ return state.publicFeed;
+ case 'combined':
+ return state.combinedFeed;
+ default:
+ return state.personalFeed;
+ }
+ }, [state.personalFeed, state.publicFeed, state.combinedFeed]);
+
+ const getCurrentFeed = useCallback((): WorkoutPostResponse[] => {
+ switch (state.currentFeedType) {
+ case 'personal':
+ return state.personalFeed;
+ case 'public':
+ return state.publicFeed;
+ case 'combined':
+ return state.combinedFeed;
+ default:
+ return state.personalFeed;
+ }
+ }, [state.personalFeed, state.publicFeed, state.combinedFeed, state.currentFeedType]);
+
+ const loadFeed = useCallback(async (feedType: FeedType, refresh: boolean = false) => {
+ try {
+ setLoading(true);
+ setError(null);
+
+ const currentFeed = getCurrentFeedForType(feedType);
+ const skip = refresh ? 0 : currentFeed.length;
+
+ let response: WorkoutPostsResponse;
+
+ switch (feedType) {
+ case 'personal':
+ response = await apiService.getPersonalFeed(skip, 20);
+ break;
+ case 'public':
+ response = await apiService.getPublicFeed(skip, 20);
+ break;
+ case 'combined':
+ response = await apiService.getFeed('combined', skip, 20);
+ break;
+ default:
+ response = await apiService.getPersonalFeed(skip, 20);
+ }
+
+ setState(prev => {
+ const newState = { ...prev };
+ const feedKey = `${feedType}Feed` as keyof Pick;
+
+ if (refresh) {
+ newState[feedKey] = response.data;
+ } else {
+ newState[feedKey] = [...prev[feedKey], ...response.data];
+ }
+
+ newState.hasMore = response.data.length === 20; // Assume no more if less than requested
+ return newState;
+ });
+ } catch (error) {
+ console.error('Error loading feed:', error);
+ setError(error instanceof Error ? error.message : 'Failed to load feed');
+ } finally {
+ setLoading(false);
+ }
+ }, [getCurrentFeedForType, setLoading, setError]);
+
+ const loadMorePosts = useCallback(async () => {
+ if (!state.hasMore || state.isLoading) return;
+ await loadFeed(state.currentFeedType, false);
+ }, [state.hasMore, state.isLoading, state.currentFeedType, loadFeed]);
+
+ const refreshFeed = useCallback(async () => {
+ await loadFeed(state.currentFeedType, true);
+ }, [state.currentFeedType, loadFeed]);
+
+ const createPost = useCallback(async (postData: WorkoutPostCreateRequest) => {
+ try {
+ setLoading(true);
+ setError(null);
+
+ const newPost = await apiService.createWorkoutPost(postData);
+
+ // Add the new post to the beginning of relevant feeds
+ setState(prev => ({
+ ...prev,
+ personalFeed: [newPost, ...prev.personalFeed],
+ combinedFeed: [newPost, ...prev.combinedFeed],
+ // Add to public feed only if it's a public post
+ publicFeed: newPost.is_public ? [newPost, ...prev.publicFeed] : prev.publicFeed,
+ }));
+ } catch (error) {
+ console.error('Error creating post:', error);
+ setError(error instanceof Error ? error.message : 'Failed to create post');
+ throw error; // Re-throw so the UI can handle it
+ } finally {
+ setLoading(false);
+ }
+ }, []);
+
+ const deletePost = useCallback(async (postId: string) => {
+ // Store the post data for potential restoration
+ let deletedPost: WorkoutPostResponse | null = null;
+ let previousState: FeedState | null = null;
+
+ try {
+ setError(null);
+
+ // Store current state and find the post to delete
+ setState(prev => {
+ previousState = { ...prev };
+ deletedPost = prev.personalFeed.find(post => post.id === postId) ||
+ prev.publicFeed.find(post => post.id === postId) ||
+ prev.combinedFeed.find(post => post.id === postId) ||
+ null;
+
+ // Optimistic update: Remove the post immediately from all feeds
+ return {
+ ...prev,
+ personalFeed: prev.personalFeed.filter(post => post.id !== postId),
+ publicFeed: prev.publicFeed.filter(post => post.id !== postId),
+ combinedFeed: prev.combinedFeed.filter(post => post.id !== postId),
+ };
+ });
+
+ // Attempt to delete the post on the server
+ await apiService.deleteWorkoutPost(postId);
+
+ // Success - the optimistic update stands
+ } catch (error) {
+ console.error('Error deleting post:', error);
+
+ // Restore the post to its previous position if deletion failed
+ if (previousState && deletedPost) {
+ setState(previousState);
+ }
+
+ setError(error instanceof Error ? error.message : 'Failed to delete post');
+ throw error;
+ }
+ }, []);
+
+ const contextValue: FeedContextType = {
+ ...state,
+ loadFeed,
+ loadMorePosts,
+ refreshFeed,
+ setCurrentFeedType,
+ createPost,
+ deletePost,
+ getCurrentFeed,
+ clearError,
+ };
+
+ return (
+
+ {children}
+
+ );
+}
+
+export function useFeed() {
+ const context = useContext(FeedContext);
+ if (context === undefined) {
+ throw new Error('useFeed must be used within a FeedProvider');
+ }
+ return context;
+}
\ No newline at end of file
diff --git a/KonditionExpo/contexts/SocialContext.tsx b/KonditionExpo/contexts/SocialContext.tsx
new file mode 100644
index 0000000000..0c5e391798
--- /dev/null
+++ b/KonditionExpo/contexts/SocialContext.tsx
@@ -0,0 +1,312 @@
+import React, { createContext, useContext, useState, useCallback, ReactNode } from 'react';
+import { apiService, UserSearchResult, UserProfile, UserPublicExtended } from '../services/api';
+
+type SocialContextType = {
+ // Search state
+ searchResults: UserSearchResult[];
+ searchLoading: boolean;
+ searchError: string | null;
+
+ // Followers/Following state
+ followers: UserSearchResult[];
+ following: UserSearchResult[];
+ followersLoading: boolean;
+ followingLoading: boolean;
+ followersError: string | null;
+ followingError: string | null;
+
+ // Follow action state
+ followActionLoading: { [userId: string]: boolean };
+
+ // Methods
+ searchUsers: (query: string, skip?: number, limit?: number) => Promise;
+ clearSearchResults: () => void;
+ followUser: (userId: string) => Promise;
+ unfollowUser: (userId: string) => Promise;
+ getFollowers: (skip?: number, limit?: number) => Promise;
+ getFollowing: (skip?: number, limit?: number) => Promise;
+ refreshFollowers: () => Promise;
+ refreshFollowing: () => Promise;
+ isFollowing: (userId: string) => Promise;
+ getUserProfile: (userId: string) => Promise;
+};
+
+const SocialContext = createContext(undefined);
+
+export const SocialProvider = ({ children }: { children: ReactNode }) => {
+ // Search state
+ const [searchResults, setSearchResults] = useState([]);
+ const [searchLoading, setSearchLoading] = useState(false);
+ const [searchError, setSearchError] = useState(null);
+
+ // Followers/Following state
+ const [followers, setFollowers] = useState([]);
+ const [following, setFollowing] = useState([]);
+ const [followersLoading, setFollowersLoading] = useState(false);
+ const [followingLoading, setFollowingLoading] = useState(false);
+ const [followersError, setFollowersError] = useState(null);
+ const [followingError, setFollowingError] = useState(null);
+
+ // Follow action state
+ const [followActionLoading, setFollowActionLoading] = useState<{ [userId: string]: boolean }>({});
+
+ const searchUsers = useCallback(async (query: string, skip: number = 0, limit: number = 20) => {
+ if (!query.trim()) {
+ setSearchResults([]);
+ setSearchLoading(false); // Ensure loading is false when clearing
+ return;
+ }
+
+ try {
+ setSearchLoading(true);
+ setSearchError(null);
+
+ const response = await apiService.searchUsers(query.trim(), skip, limit);
+
+ if (skip === 0) {
+ setSearchResults(response.data);
+ } else {
+ // Append for pagination
+ setSearchResults(prev => [...prev, ...response.data]);
+ }
+ } catch (error) {
+ console.error('Search users failed:', error);
+ setSearchError(error instanceof Error ? error.message : 'Failed to search users');
+ if (skip === 0) {
+ setSearchResults([]);
+ }
+ } finally {
+ setSearchLoading(false);
+ }
+ }, []);
+
+ const clearSearchResults = useCallback(() => {
+ setSearchResults([]);
+ setSearchError(null);
+ setSearchLoading(false); // Ensure loading is false when clearing
+ }, []);
+
+ const followUser = useCallback(async (userId: string) => {
+ try {
+ setFollowActionLoading(prev => ({ ...prev, [userId]: true }));
+
+ await apiService.followUser(userId);
+
+ // Update local state in search results and followers
+ setSearchResults(prev =>
+ prev.map(user =>
+ user.id === userId
+ ? {
+ ...user,
+ is_following: true,
+ follower_count: user.follower_count + 1
+ }
+ : user
+ )
+ );
+
+ setFollowers(prev =>
+ prev.map(user =>
+ user.id === userId
+ ? {
+ ...user,
+ is_following: true
+ }
+ : user
+ )
+ );
+
+ // Immediately refresh the following list to show the new user
+ console.log('[SocialContext] Follow successful, refreshing following list...');
+ try {
+ setFollowingLoading(true);
+ setFollowingError(null);
+
+ const response = await apiService.getFollowing(0, 100);
+ console.log(`[SocialContext] Refreshed following list:`, response.data.length, 'users');
+ setFollowing(response.data);
+ } catch (error) {
+ console.error('Failed to refresh following list after follow:', error);
+ setFollowingError(error instanceof Error ? error.message : 'Failed to refresh following list');
+ } finally {
+ setFollowingLoading(false);
+ }
+
+ } catch (error) {
+ console.error('Follow user failed:', error);
+ throw error;
+ } finally {
+ setFollowActionLoading(prev => ({ ...prev, [userId]: false }));
+ }
+ }, []);
+
+ const unfollowUser = useCallback(async (userId: string) => {
+ try {
+ setFollowActionLoading(prev => ({ ...prev, [userId]: true }));
+
+ await apiService.unfollowUser(userId);
+
+ // Update local state in search results and followers
+ setSearchResults(prev =>
+ prev.map(user =>
+ user.id === userId
+ ? {
+ ...user,
+ is_following: false,
+ follower_count: Math.max(0, user.follower_count - 1)
+ }
+ : user
+ )
+ );
+
+ setFollowers(prev =>
+ prev.map(user =>
+ user.id === userId
+ ? {
+ ...user,
+ is_following: false
+ }
+ : user
+ )
+ );
+
+ // Remove user from following list immediately
+ setFollowing(prev => prev.filter(user => user.id !== userId));
+
+ console.log('[SocialContext] Unfollow successful, user removed from following list');
+
+ } catch (error) {
+ console.error('Unfollow user failed:', error);
+ throw error;
+ } finally {
+ setFollowActionLoading(prev => ({ ...prev, [userId]: false }));
+ }
+ }, []);
+
+ const getFollowers = useCallback(async (skip: number = 0, limit: number = 100) => {
+ try {
+ setFollowersLoading(true);
+ setFollowersError(null);
+
+ const response = await apiService.getFollowers(skip, limit);
+
+ if (skip === 0) {
+ setFollowers(response.data);
+ } else {
+ // Append for pagination
+ setFollowers(prev => [...prev, ...response.data]);
+ }
+ } catch (error) {
+ console.error('Get followers failed:', error);
+ setFollowersError(error instanceof Error ? error.message : 'Failed to load followers');
+ if (skip === 0) {
+ setFollowers([]);
+ }
+ } finally {
+ setFollowersLoading(false);
+ }
+ }, []);
+
+ const getFollowing = useCallback(async (skip: number = 0, limit: number = 100) => {
+ try {
+ setFollowingLoading(true);
+ setFollowingError(null);
+
+ const response = await apiService.getFollowing(skip, limit);
+
+ // Debug logging
+ console.log(`[SocialContext] getFollowing response:`, response);
+ console.log(`[SocialContext] Following users:`, response.data.map(u => ({
+ name: u.full_name || u.email,
+ is_following: u.is_following
+ })));
+
+ if (skip === 0) {
+ setFollowing(response.data);
+ } else {
+ // Append for pagination
+ setFollowing(prev => [...prev, ...response.data]);
+ }
+ } catch (error) {
+ console.error('Get following failed:', error);
+ setFollowingError(error instanceof Error ? error.message : 'Failed to load following');
+ if (skip === 0) {
+ setFollowing([]);
+ }
+ } finally {
+ setFollowingLoading(false);
+ }
+ }, []);
+
+ const refreshFollowers = useCallback(async () => {
+ await getFollowers(0, 100);
+ }, [getFollowers]);
+
+ const refreshFollowing = useCallback(async () => {
+ await getFollowing(0, 100);
+ }, [getFollowing]);
+
+ const isFollowing = useCallback(async (userId: string): Promise => {
+ try {
+ const response = await apiService.isFollowing(userId);
+ return response.is_following;
+ } catch (error) {
+ console.error('Check following status failed:', error);
+ return false;
+ }
+ }, []);
+
+ const getUserProfile = useCallback(async (userId: string): Promise => {
+ try {
+ return await apiService.getUserProfileExtended(userId);
+ } catch (error) {
+ console.error('Get user profile failed:', error);
+ throw error;
+ }
+ }, []);
+
+
+ const value: SocialContextType = {
+ // Search state
+ searchResults,
+ searchLoading,
+ searchError,
+
+ // Followers/Following state
+ followers,
+ following,
+ followersLoading,
+ followingLoading,
+ followersError,
+ followingError,
+
+ // Follow action state
+ followActionLoading,
+
+ // Methods
+ searchUsers,
+ clearSearchResults,
+ followUser,
+ unfollowUser,
+ getFollowers,
+ getFollowing,
+ refreshFollowers,
+ refreshFollowing,
+ isFollowing,
+ getUserProfile,
+ };
+
+ return (
+
+ {children}
+
+ );
+};
+
+export const useSocial = () => {
+ const context = useContext(SocialContext);
+ if (!context) {
+ throw new Error('useSocial must be used within a SocialProvider');
+ }
+ return context;
+};
\ No newline at end of file
diff --git a/KonditionExpo/contexts/UserContext.tsx b/KonditionExpo/contexts/UserContext.tsx
new file mode 100644
index 0000000000..2ca61a54e6
--- /dev/null
+++ b/KonditionExpo/contexts/UserContext.tsx
@@ -0,0 +1,42 @@
+import React, { createContext, useContext, useState, ReactNode } from 'react';
+
+type UserContextType = {
+ name: string;
+ setName: (name: string) => void;
+ height: string;
+ setHeight: (height: string) => void;
+ weight: string;
+ setWeight: (weight: string) => void;
+ age: number;
+ setAge: (age: number) => void;
+};
+
+const UserContext = createContext(undefined);
+
+export const UserProvider = ({ children }: { children: ReactNode }) => {
+ const [name, setName] = useState('');
+ const [height, setHeight] = useState('');
+ const [weight, setWeight] = useState('');
+ const [age, setAge] = useState(0);
+
+ return (
+
+ {children}
+
+ );
+};
+
+//Added default fallback
+export const useUser = () => {
+ return useContext(UserContext) ?? {
+ name: '',
+ setName: () => {},
+ height: '',
+ setHeight: () => {},
+ weight: '',
+ setWeight: () => {},
+ age: 0,
+ setAge: () => {},
+ };
+};
+
diff --git a/KonditionExpo/contexts/WorkoutContext.tsx b/KonditionExpo/contexts/WorkoutContext.tsx
new file mode 100644
index 0000000000..d86652b408
--- /dev/null
+++ b/KonditionExpo/contexts/WorkoutContext.tsx
@@ -0,0 +1,173 @@
+import React, { createContext, useContext, useState, ReactNode } from 'react';
+
+export interface Exercise {
+ id: string;
+ name: string;
+ muscle_group: string;
+ type: 'strength' | 'cardio' | 'flexibility';
+}
+
+export interface WorkoutSet {
+ id: string;
+ reps: number;
+ weight: number; // in kg
+ duration?: number; // in seconds for cardio
+ distance?: number; // in km for cardio
+}
+
+export interface WorkoutExercise {
+ id: string;
+ exercise: Exercise;
+ sets: WorkoutSet[];
+ notes?: string;
+}
+
+export interface Workout {
+ id: string;
+ date: Date;
+ name: string;
+ exercises: WorkoutExercise[];
+ duration: number; // in minutes
+ notes?: string;
+}
+
+interface WorkoutContextType {
+ workouts: Workout[];
+ currentWorkout: Workout | null;
+ addWorkout: (workout: Workout) => void;
+ updateWorkout: (workoutId: string, workout: Workout) => void;
+ deleteWorkout: (workoutId: string) => void;
+ startWorkout: (name: string) => void;
+ endWorkout: () => void;
+ addExerciseToCurrentWorkout: (exercise: Exercise) => void;
+ addSetToExercise: (exerciseId: string, set: Omit) => void;
+ removeSetFromExercise: (exerciseId: string, setId: string) => void;
+ updateSet: (exerciseId: string, setId: string, updatedSet: Partial) => void;
+}
+
+const WorkoutContext = createContext(undefined);
+
+export const WorkoutProvider = ({ children }: { children: ReactNode }) => {
+ const [workouts, setWorkouts] = useState([]);
+ const [currentWorkout, setCurrentWorkout] = useState(null);
+
+ const generateId = () => Math.random().toString(36).substr(2, 9);
+
+ const addWorkout = (workout: Workout) => {
+ setWorkouts(prev => [...prev, workout]);
+ };
+
+ const updateWorkout = (workoutId: string, updatedWorkout: Workout) => {
+ setWorkouts(prev => prev.map(w => w.id === workoutId ? updatedWorkout : w));
+ };
+
+ const deleteWorkout = (workoutId: string) => {
+ setWorkouts(prev => prev.filter(w => w.id !== workoutId));
+ };
+
+ const startWorkout = (name: string) => {
+ const newWorkout: Workout = {
+ id: generateId(),
+ date: new Date(),
+ name,
+ exercises: [],
+ duration: 0,
+ };
+ setCurrentWorkout(newWorkout);
+ };
+
+ const endWorkout = () => {
+ if (currentWorkout) {
+ addWorkout(currentWorkout);
+ setCurrentWorkout(null);
+ }
+ };
+
+ const addExerciseToCurrentWorkout = (exercise: Exercise) => {
+ if (!currentWorkout) return;
+
+ const workoutExercise: WorkoutExercise = {
+ id: generateId(),
+ exercise,
+ sets: [],
+ };
+
+ setCurrentWorkout(prev => prev ? {
+ ...prev,
+ exercises: [...prev.exercises, workoutExercise]
+ } : null);
+ };
+
+ const addSetToExercise = (exerciseId: string, set: Omit) => {
+ if (!currentWorkout) return;
+
+ const newSet: WorkoutSet = {
+ ...set,
+ id: generateId(),
+ };
+
+ setCurrentWorkout(prev => prev ? {
+ ...prev,
+ exercises: prev.exercises.map(ex =>
+ ex.id === exerciseId
+ ? { ...ex, sets: [...ex.sets, newSet] }
+ : ex
+ )
+ } : null);
+ };
+
+ const removeSetFromExercise = (exerciseId: string, setId: string) => {
+ if (!currentWorkout) return;
+
+ setCurrentWorkout(prev => prev ? {
+ ...prev,
+ exercises: prev.exercises.map(ex =>
+ ex.id === exerciseId
+ ? { ...ex, sets: ex.sets.filter(s => s.id !== setId) }
+ : ex
+ )
+ } : null);
+ };
+
+ const updateSet = (exerciseId: string, setId: string, updatedSet: Partial) => {
+ if (!currentWorkout) return;
+
+ setCurrentWorkout(prev => prev ? {
+ ...prev,
+ exercises: prev.exercises.map(ex =>
+ ex.id === exerciseId
+ ? {
+ ...ex,
+ sets: ex.sets.map(s =>
+ s.id === setId ? { ...s, ...updatedSet } : s
+ )
+ }
+ : ex
+ )
+ } : null);
+ };
+
+ return (
+
+ {children}
+
+ );
+};
+
+export const useWorkout = () => {
+ const context = useContext(WorkoutContext);
+ if (!context) throw new Error('useWorkout must be used within a WorkoutProvider');
+ return context;
+};
\ No newline at end of file
diff --git a/KonditionExpo/docs/AUTHENTICATION.md b/KonditionExpo/docs/AUTHENTICATION.md
new file mode 100644
index 0000000000..4ef8094d00
--- /dev/null
+++ b/KonditionExpo/docs/AUTHENTICATION.md
@@ -0,0 +1,199 @@
+# Authentication System Documentation
+
+## Overview
+
+This document describes the authentication system implementation for the Kondition Expo app, including recent fixes for signout functionality and network connectivity issues.
+
+## Architecture
+
+### Components
+
+1. **AuthContext** (`contexts/AuthContext.tsx`)
+ - Manages authentication state
+ - Provides login, signup, logout functions
+ - Handles token storage and validation
+
+2. **ProtectedRoute** (`components/ProtectedRoute.tsx`)
+ - Controls navigation based on authentication state
+ - Redirects unauthenticated users to login
+ - Redirects authenticated users away from auth screens
+
+3. **API Service** (`services/api.ts`)
+ - Handles all API communication
+ - Manages token storage in AsyncStorage
+ - Provides platform-aware error handling
+
+4. **API Configuration** (`config/api.ts`)
+ - Platform-specific API URL configuration
+ - Automatic platform detection
+ - Development tools for testing
+
+## Recent Fixes
+
+### 1. Signout Functionality
+
+**Issue**: Signout button was not working properly
+**Solution**:
+- Enhanced error handling in logout function
+- Added detailed logging for debugging
+- Improved user feedback with specific error messages
+- Ensured proper token cleanup
+
+**Implementation**:
+```typescript
+const handleLogout = () => {
+ Alert.alert(
+ 'Sign Out',
+ 'Are you sure you want to sign out?',
+ [
+ {
+ text: 'Cancel',
+ style: 'cancel',
+ },
+ {
+ text: 'Sign Out',
+ style: 'destructive',
+ onPress: async () => {
+ try {
+ console.log('Starting logout process...');
+ await logout();
+ console.log('Logout successful');
+ // Navigation handled by ProtectedRoute
+ } catch (error) {
+ console.error('Logout error:', error);
+ Alert.alert(
+ 'Logout Error',
+ `Failed to sign out: ${error instanceof Error ? error.message : 'Unknown error'}. Please try again.`
+ );
+ }
+ },
+ },
+ ]
+ );
+};
+```
+
+### 2. Network Connectivity Issues
+
+**Issue**: App worked on web but failed on mobile devices with "network failed" errors
+**Solution**:
+- Implemented platform-specific API URLs
+- Added automatic platform detection
+- Created development tools for easy testing
+
+**Platform-Specific URLs**:
+- **Web**: `http://localhost:8000/api/v1`
+- **iOS Simulator**: `http://localhost:8000/api/v1`
+- **Android Emulator**: `http://10.0.2.2:8000/api/v1`
+- **Physical Device**: `http://[YOUR_IP]:8000/api/v1`
+
+### 3. Development Tools
+
+Added a DevTools component accessible from the profile screen that provides:
+- Current API URL display
+- Connection testing
+- Custom URL configuration
+- Quick URL presets
+- Setup instructions
+
+## Testing Instructions
+
+### Web Platform
+1. Start the backend: `uvicorn app.main:app --host 0.0.0.0 --port 8000`
+2. Start the web app: `npm run web`
+3. Authentication should work with `localhost:8000`
+
+### iOS Simulator
+1. Start the backend: `uvicorn app.main:app --host 0.0.0.0 --port 8000`
+2. Start the iOS simulator: `npm run ios`
+3. Authentication should work with `localhost:8000`
+
+### Android Emulator
+1. Start the backend: `uvicorn app.main:app --host 0.0.0.0 --port 8000`
+2. Start the Android emulator: `npm run android`
+3. The app automatically uses `10.0.2.2:8000` for Android emulator
+
+### Physical Device / Expo Go
+
+1. **Find your computer's IP address**:
+ - **macOS**: System Preferences > Network > Advanced > TCP/IP
+ - **Windows**: Run `ipconfig` in Command Prompt
+ - **Linux**: Run `ifconfig` or `ip addr`
+
+2. **Update the API configuration**:
+ - Open the app and go to Profile > Developer Tools
+ - Enter your computer's IP: `http://[YOUR_IP]:8000/api/v1`
+ - Or manually edit `config/api.ts` and update the `physical_device` URL
+
+3. **Start the backend with external access**:
+ ```bash
+ uvicorn app.main:app --host 0.0.0.0 --port 8000
+ ```
+
+4. **Ensure network connectivity**:
+ - Computer and mobile device must be on the same network
+ - Firewall should allow connections on port 8000
+
+## API Endpoints
+
+- **Login**: `POST /api/v1/login/access-token`
+- **Signup**: `POST /api/v1/users/signup`
+- **Test Token**: `POST /api/v1/login/test-token`
+- **User Profile**: `GET /api/v1/users/me`
+
+## Error Handling
+
+### Network Errors
+- Connection failures show user-friendly messages
+- Automatic retry suggestions
+- Platform-specific troubleshooting
+
+### Authentication Errors
+- Invalid credentials handled gracefully
+- Token expiration triggers automatic logout
+- Clear error messages for users
+
+### Development Debugging
+- Console logging for all authentication actions
+- Network request logging with URLs
+- Error details preserved for debugging
+
+## Troubleshooting
+
+### Common Issues
+
+1. **"Network request failed" on mobile**
+ - Check if backend is running with `--host 0.0.0.0`
+ - Verify IP address in DevTools
+ - Ensure devices are on same network
+
+2. **Signout not working**
+ - Check console logs for error details
+ - Verify AsyncStorage permissions
+ - Try force-closing and reopening the app
+
+3. **Login works but user data not loading**
+ - Check token storage in AsyncStorage
+ - Verify API endpoints are accessible
+ - Check network connectivity
+
+### Development Tips
+
+1. **Use DevTools**: Access via Profile > Developer Tools for easy testing
+2. **Check Console**: All authentication actions are logged
+3. **Test Connection**: Use the "Test API Connection" button in DevTools
+4. **Network Setup**: Follow the setup instructions in DevTools
+
+## Security Considerations
+
+- Tokens stored securely in AsyncStorage
+- Automatic token validation on app start
+- Secure token transmission with Bearer authentication
+- Proper cleanup on logout
+
+## Future Improvements
+
+1. **Refresh Token Implementation**: Add automatic token refresh
+2. **Biometric Authentication**: Add fingerprint/face ID support
+3. **Offline Support**: Cache user data for offline access
+4. **Enhanced Security**: Add certificate pinning for production
\ No newline at end of file
diff --git a/KonditionExpo/eslint.config.js b/KonditionExpo/eslint.config.js
new file mode 100644
index 0000000000..5025da683d
--- /dev/null
+++ b/KonditionExpo/eslint.config.js
@@ -0,0 +1,10 @@
+// https://docs.expo.dev/guides/using-eslint/
+const { defineConfig } = require('eslint/config');
+const expoConfig = require('eslint-config-expo/flat');
+
+module.exports = defineConfig([
+ expoConfig,
+ {
+ ignores: ['dist/*'],
+ },
+]);
diff --git a/KonditionExpo/hooks/useColorScheme.ts b/KonditionExpo/hooks/useColorScheme.ts
new file mode 100644
index 0000000000..17e3c63e1a
--- /dev/null
+++ b/KonditionExpo/hooks/useColorScheme.ts
@@ -0,0 +1 @@
+export { useColorScheme } from 'react-native';
diff --git a/KonditionExpo/hooks/useColorScheme.web.ts b/KonditionExpo/hooks/useColorScheme.web.ts
new file mode 100644
index 0000000000..7eb1c1b707
--- /dev/null
+++ b/KonditionExpo/hooks/useColorScheme.web.ts
@@ -0,0 +1,21 @@
+import { useEffect, useState } from 'react';
+import { useColorScheme as useRNColorScheme } from 'react-native';
+
+/**
+ * To support static rendering, this value needs to be re-calculated on the client side for web
+ */
+export function useColorScheme() {
+ const [hasHydrated, setHasHydrated] = useState(false);
+
+ useEffect(() => {
+ setHasHydrated(true);
+ }, []);
+
+ const colorScheme = useRNColorScheme();
+
+ if (hasHydrated) {
+ return colorScheme;
+ }
+
+ return 'light';
+}
diff --git a/KonditionExpo/hooks/usePersonalBests.ts b/KonditionExpo/hooks/usePersonalBests.ts
new file mode 100644
index 0000000000..5bdbe4c5ee
--- /dev/null
+++ b/KonditionExpo/hooks/usePersonalBests.ts
@@ -0,0 +1,50 @@
+import { useEffect, useState } from "react";
+import { getAccessToken } from "@/scripts/auth"; // adjust based on your token handling
+
+type PersonalBest = {
+ metric: string;
+ value: number;
+ date: string;
+};
+
+export const usePersonalBests = () => {
+
+ console.log("usePersonalBests hook loaded");
+
+ const [pbs, setPbs] = useState([]);
+ const [loading, setLoading] = useState( true);
+
+ useEffect(() => {
+
+ const fetchPBs = async () => {
+ console.log("Fetching personal bests...");
+ setLoading(true);
+ try {
+ console.log("Attempting to get token...");
+ const token = await getAccessToken();
+ console.log("Got token:", token);
+
+ const res = await fetch("http://localhost:8000/api/v1/personal-bests", {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ });
+
+ const data = await res.json();
+ console.log("Fetched PBs:", data);
+ setPbs(data?.data ?? []); // Adjust if shape differs
+ } catch (err) {
+ console.error("Failed to fetch personal bests", err);
+ setPbs([]);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchPBs();
+ }, []);
+
+
+
+ return { pbs, loading };
+};
diff --git a/KonditionExpo/hooks/useThemeColor.ts b/KonditionExpo/hooks/useThemeColor.ts
new file mode 100644
index 0000000000..0608e7315a
--- /dev/null
+++ b/KonditionExpo/hooks/useThemeColor.ts
@@ -0,0 +1,21 @@
+/**
+ * Learn more about light and dark modes:
+ * https://docs.expo.dev/guides/color-schemes/
+ */
+
+import { Colors } from '@/constants/Colors';
+import { useColorScheme } from '@/hooks/useColorScheme';
+
+export function useThemeColor(
+ props: { light?: string; dark?: string },
+ colorName: keyof typeof Colors.light & keyof typeof Colors.dark
+) {
+ const theme = useColorScheme() ?? 'light';
+ const colorFromProps = props[theme];
+
+ if (colorFromProps) {
+ return colorFromProps;
+ } else {
+ return Colors[theme][colorName];
+ }
+}
diff --git a/KonditionExpo/package-lock.json b/KonditionExpo/package-lock.json
new file mode 100644
index 0000000000..594280fdde
--- /dev/null
+++ b/KonditionExpo/package-lock.json
@@ -0,0 +1,13658 @@
+{
+ "name": "konditionexpo",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "konditionexpo",
+ "version": "1.0.0",
+ "dependencies": {
+ "@expo/ngrok": "^4.1.3",
+ "@expo/vector-icons": "^14.1.0",
+ "@react-native-async-storage/async-storage": "2.1.2",
+ "@react-native-community/datetimepicker": "^8.4.1",
+ "@react-native-picker/picker": "^2.11.0",
+ "@react-navigation/bottom-tabs": "^7.3.10",
+ "@react-navigation/elements": "^2.3.8",
+ "@react-navigation/native": "^7.1.6",
+ "d3-shape": "^3.2.0",
+ "expo": "^53.0.9",
+ "expo-blur": "~14.1.4",
+ "expo-constants": "~17.1.6",
+ "expo-device": "~7.1.4",
+ "expo-font": "~13.3.1",
+ "expo-haptics": "~14.1.4",
+ "expo-image": "~2.1.7",
+ "expo-linking": "~7.1.5",
+ "expo-notifications": "~0.31.2",
+ "expo-permissions": "^14.4.0",
+ "expo-router": "~5.0.7",
+ "expo-splash-screen": "~0.30.8",
+ "expo-status-bar": "~2.2.3",
+ "expo-symbols": "~0.4.4",
+ "expo-system-ui": "~5.0.7",
+ "expo-web-browser": "~14.1.6",
+ "react": "19.0.0",
+ "react-dom": "19.0.0",
+ "react-native": "0.79.2",
+ "react-native-chart-kit": "^6.12.0",
+ "react-native-gesture-handler": "~2.24.0",
+ "react-native-picker-select": "^9.3.1",
+ "react-native-reanimated": "~3.17.4",
+ "react-native-safe-area-context": "5.4.0",
+ "react-native-screens": "~4.10.0",
+ "react-native-svg": "15.11.2",
+ "react-native-web": "~0.20.0",
+ "react-native-webview": "13.13.5"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.25.2",
+ "@types/react": "~19.0.10",
+ "eslint": "^9.25.0",
+ "eslint-config-expo": "~9.2.0",
+ "typescript": "~5.8.3"
+ }
+ },
+ "node_modules/@0no-co/graphql.web": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@0no-co/graphql.web/-/graphql.web-1.1.2.tgz",
+ "integrity": "sha512-N2NGsU5FLBhT8NZ+3l2YrzZSHITjNXNuDhC4iDiikv0IujaJ0Xc6xIxQZ/Ek3Cb+rgPjnLHYyJm11tInuJn+cw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0"
+ },
+ "peerDependenciesMeta": {
+ "graphql": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@ampproject/remapping": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
+ "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "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/compat-data": {
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.2.tgz",
+ "integrity": "sha512-TUtMJYRPyUb/9aU8f3K0mjmjf6M9N5Woshn2CS6nqJSeJtTtQcpLUXjGt9vbF8ZGff0El99sWkLgzwW3VXnxZQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.1.tgz",
+ "integrity": "sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@ampproject/remapping": "^2.2.0",
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.27.1",
+ "@babel/helper-compilation-targets": "^7.27.1",
+ "@babel/helper-module-transforms": "^7.27.1",
+ "@babel/helpers": "^7.27.1",
+ "@babel/parser": "^7.27.1",
+ "@babel/template": "^7.27.1",
+ "@babel/traverse": "^7.27.1",
+ "@babel/types": "^7.27.1",
+ "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/generator": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.1.tgz",
+ "integrity": "sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.27.1",
+ "@babel/types": "^7.27.1",
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.25",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-annotate-as-pure": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.1.tgz",
+ "integrity": "sha512-WnuuDILl9oOBbKnb4L+DyODx7iC47XfzmNCpTttFsSp6hTG7XZxu60+4IO+2/hPfcGOoKbFiwoI/+zwARbNQow==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.27.1"
+ },
+ "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-create-class-features-plugin": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz",
+ "integrity": "sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.27.1",
+ "@babel/helper-member-expression-to-functions": "^7.27.1",
+ "@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.27.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-create-regexp-features-plugin": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz",
+ "integrity": "sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.27.1",
+ "regexpu-core": "^6.2.0",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-define-polyfill-provider": {
+ "version": "0.6.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.4.tgz",
+ "integrity": "sha512-jljfR1rGnXXNWnmQg2K3+bvhkxB51Rl32QRaOTuwwjviGrHzIbSc8+x9CpraDtbT7mfyjXObULP4w/adunNwAw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-compilation-targets": "^7.22.6",
+ "@babel/helper-plugin-utils": "^7.22.5",
+ "debug": "^4.1.1",
+ "lodash.debounce": "^4.0.8",
+ "resolve": "^1.14.2"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
+ }
+ },
+ "node_modules/@babel/helper-member-expression-to-functions": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz",
+ "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.27.1",
+ "@babel/types": "^7.27.1"
+ },
+ "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.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.1.tgz",
+ "integrity": "sha512-9yHn519/8KvTU5BjTVEEeIM3w9/2yXNKoD82JifINImhpKkARMJKPP59kLo+BafpdN5zgNeIcS4jsGDmd3l58g==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.27.1",
+ "@babel/traverse": "^7.27.1"
+ },
+ "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.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
+ "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
+ "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.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.27.1.tgz",
+ "integrity": "sha512-NFJK2sHUvrjo8wAU/nQTWU890/zB2jj0qBcCbZbbf+005cAsv6tMjXz31fBign6M5ov1o0Bllu+9nbqkfsjjJQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.27.1",
+ "@babel/traverse": "^7.27.1",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.1.tgz",
+ "integrity": "sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.27.1",
+ "@babel/types": "^7.27.1"
+ },
+ "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/highlight/node_modules/ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "1.1.3"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
+ "license": "MIT"
+ },
+ "node_modules/@babel/highlight/node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.2.tgz",
+ "integrity": "sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.27.1"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-decorators": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.27.1.tgz",
+ "integrity": "sha512-DTxe4LBPrtFdsWzgpmbBKevg3e9PBy+dXRt19kSbucbZvL2uqtdqwwpluL1jfxYE0wIDTFp1nTy/q6gNLsxXrg==",
+ "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.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.27.1.tgz",
+ "integrity": "sha512-eST9RrwlpaoJBDHShc+DS2SG4ATTi2MYNb4OxYkf3n+7eb49LWpnS+HSpVfW4x927qQwgk8A2hGNVaajAEw0EA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-remap-async-to-generator": "^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-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.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.27.1.tgz",
+ "integrity": "sha512-QEcFlMl9nGTgh1rn2nIeU5bkfb9BAjaQcWbiP4LvKxUot52ABcTkpcyJ7f2Q2U2RuQ84BNLgts3jRme2dTx6Fw==",
+ "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-classes": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.27.1.tgz",
+ "integrity": "sha512-7iLhfFAubmpeJe/Wo2TVuDrykh/zlWXLzPNdL0Jqn/Xu8R3QQ8h9ff8FQoISZOsw74/HFqFI7NX63HN7QFIHKA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.27.1",
+ "@babel/helper-compilation-targets": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-replace-supers": "^7.27.1",
+ "@babel/traverse": "^7.27.1",
+ "globals": "^11.1.0"
+ },
+ "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.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.27.1.tgz",
+ "integrity": "sha512-ttDCqhfvpE9emVkXbPD8vyxxh4TWYACVybGkDj+oReOGwnp066ITEivDlLwe0b1R0+evJ13IXQuLNB5w1fhC5Q==",
+ "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-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.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.27.1.tgz",
+ "integrity": "sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw==",
+ "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.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.27.2.tgz",
+ "integrity": "sha512-AIUHD7xJ1mCrj3uPozvtngY3s0xpv7Nu7DoUSnzNY6Xam1Cy4rUznR//pvMHOhQ4AvbCexhbqXCtpxGHOGOO6g==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-compilation-targets": "^7.27.2",
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/plugin-transform-destructuring": "^7.27.1",
+ "@babel/plugin-transform-parameters": "^7.27.1"
+ },
+ "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.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz",
+ "integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==",
+ "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.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.1.tgz",
+ "integrity": "sha512-018KRk76HWKeZ5l4oTj2zPpSh+NbGdt0st5S6x0pga6HgrjBOJb24mMDHorFopOOd6YHkLgOZ+zaCjZGPO4aKg==",
+ "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.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.27.1.tgz",
+ "integrity": "sha512-p9+Vl3yuHPmkirRrg021XiP+EETmPMQTLr6Ayjj85RLNEbb3Eya/4VI0vAdzQG9SEAl2Lnt7fy5lZyMzjYoZQQ==",
+ "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.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.27.1.tgz",
+ "integrity": "sha512-B19lbbL7PMrKr52BNPjCqg1IyNUIjTcxKj8uX9zHO+PmWN93s19NDr/f69mIkEp2x9nmDJ08a7lgHaTTzvW7mw==",
+ "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.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.27.1.tgz",
+ "integrity": "sha512-TqGF3desVsTcp3WrJGj4HfKokfCXCLcHpt4PJF0D8/iT6LPd9RS82Upw3KPeyr6B22Lfd3DO8MVrmp0oRkUDdw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "babel-plugin-polyfill-corejs2": "^0.4.10",
+ "babel-plugin-polyfill-corejs3": "^0.11.0",
+ "babel-plugin-polyfill-regenerator": "^0.6.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "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.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.27.1.tgz",
+ "integrity": "sha512-Q5sT5+O4QUebHdbwKedFBEwRLb02zJ7r4A5Gg2hUoLuU3FjdMcyqcywqUrLCaDsFCxzokf7u9kuy7qz51YUuAg==",
+ "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",
+ "@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.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.27.1.tgz",
+ "integrity": "sha512-oJHWh2gLhU9dW9HHr42q0cI0/iHHXTLGe39qvpAZZzagHy0MzYLCnCVV0symeRvzmjHyVU7mw2K06E6u/JwbhA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-validator-option": "^7.27.1",
+ "@babel/plugin-transform-react-display-name": "^7.27.1",
+ "@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.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.27.1.tgz",
+ "integrity": "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==",
+ "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.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.1.tgz",
+ "integrity": "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==",
+ "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/traverse": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.1.tgz",
+ "integrity": "sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.27.1",
+ "@babel/parser": "^7.27.1",
+ "@babel/template": "^7.27.1",
+ "@babel/types": "^7.27.1",
+ "debug": "^4.3.1",
+ "globals": "^11.1.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse--for-generate-function-map": {
+ "name": "@babel/traverse",
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.1.tgz",
+ "integrity": "sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.27.1",
+ "@babel/parser": "^7.27.1",
+ "@babel/template": "^7.27.1",
+ "@babel/types": "^7.27.1",
+ "debug": "^4.3.1",
+ "globals": "^11.1.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz",
+ "integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "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/@emnapi/core": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.3.tgz",
+ "integrity": "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.0.2",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz",
+ "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/wasi-threads": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.2.tgz",
+ "integrity": "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz",
+ "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz",
+ "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/config-array": {
+ "version": "0.20.0",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz",
+ "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.6",
+ "debug": "^4.3.1",
+ "minimatch": "^3.1.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/config-helpers": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.2.tgz",
+ "integrity": "sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/core": {
+ "version": "0.14.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz",
+ "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz",
+ "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/globals": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "9.27.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.27.0.tgz",
+ "integrity": "sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ }
+ },
+ "node_modules/@eslint/object-schema": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz",
+ "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/plugin-kit": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz",
+ "integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.14.0",
+ "levn": "^0.4.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@expo/cli": {
+ "version": "0.24.13",
+ "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-0.24.13.tgz",
+ "integrity": "sha512-2LSdbvYs+WmUljnplQXMCUyNzyX4H+F4l8uExfA1hud25Bl5kyaGrx1jjtgNxMTXmfmMjvgBdK798R50imEhkA==",
+ "license": "MIT",
+ "dependencies": {
+ "@0no-co/graphql.web": "^1.0.8",
+ "@babel/runtime": "^7.20.0",
+ "@expo/code-signing-certificates": "^0.0.5",
+ "@expo/config": "~11.0.10",
+ "@expo/config-plugins": "~10.0.2",
+ "@expo/devcert": "^1.1.2",
+ "@expo/env": "~1.0.5",
+ "@expo/image-utils": "^0.7.4",
+ "@expo/json-file": "^9.1.4",
+ "@expo/metro-config": "~0.20.14",
+ "@expo/osascript": "^2.2.4",
+ "@expo/package-manager": "^1.8.4",
+ "@expo/plist": "^0.3.4",
+ "@expo/prebuild-config": "^9.0.6",
+ "@expo/spawn-async": "^1.7.2",
+ "@expo/ws-tunnel": "^1.0.1",
+ "@expo/xcpretty": "^4.3.0",
+ "@react-native/dev-middleware": "0.79.2",
+ "@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",
+ "freeport-async": "^2.0.0",
+ "getenv": "^1.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"
+ }
+ },
+ "node_modules/@expo/cli/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@expo/cli/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/@expo/cli/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/@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": "11.0.10",
+ "resolved": "https://registry.npmjs.org/@expo/config/-/config-11.0.10.tgz",
+ "integrity": "sha512-8S8Krr/c5lnl0eF03tA2UGY9rGBhZcbWKz2UWw5dpL/+zstwUmog8oyuuC8aRcn7GiTQLlbBkxcMeT8sOGlhbA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "~7.10.4",
+ "@expo/config-plugins": "~10.0.2",
+ "@expo/config-types": "^53.0.4",
+ "@expo/json-file": "^9.1.4",
+ "deepmerge": "^4.3.1",
+ "getenv": "^1.0.0",
+ "glob": "^10.4.2",
+ "require-from-string": "^2.0.2",
+ "resolve-from": "^5.0.0",
+ "resolve-workspace-root": "^2.0.0",
+ "semver": "^7.6.0",
+ "slugify": "^1.3.4",
+ "sucrase": "3.35.0"
+ }
+ },
+ "node_modules/@expo/config-plugins": {
+ "version": "10.0.2",
+ "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-10.0.2.tgz",
+ "integrity": "sha512-TzUn3pPdpwCS0yYaSlZOClgDmCX8N4I2lfgitX5oStqmvpPtB+vqtdyqsVM02fQ2tlJIAqwBW+NHaHqqy8Jv7g==",
+ "license": "MIT",
+ "dependencies": {
+ "@expo/config-types": "^53.0.3",
+ "@expo/json-file": "~9.1.4",
+ "@expo/plist": "^0.3.4",
+ "@expo/sdk-runtime-versions": "^1.0.0",
+ "chalk": "^4.1.2",
+ "debug": "^4.3.5",
+ "getenv": "^1.0.0",
+ "glob": "^10.4.2",
+ "resolve-from": "^5.0.0",
+ "semver": "^7.5.4",
+ "slash": "^3.0.0",
+ "slugify": "^1.6.6",
+ "xcode": "^3.0.1",
+ "xml2js": "0.6.0"
+ }
+ },
+ "node_modules/@expo/config-plugins/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/@expo/config-types": {
+ "version": "53.0.4",
+ "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-53.0.4.tgz",
+ "integrity": "sha512-0s+9vFx83WIToEr0Iwy4CcmiUXa5BgwBmEjylBB2eojX5XAMm9mJvw9KpjAb8m7zq2G0Q6bRbeufkzgbipuNQg==",
+ "license": "MIT"
+ },
+ "node_modules/@expo/config/node_modules/@babel/code-frame": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
+ "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/highlight": "^7.10.4"
+ }
+ },
+ "node_modules/@expo/config/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/@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/env": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@expo/env/-/env-1.0.5.tgz",
+ "integrity": "sha512-dtEZ4CAMaVrFu2+tezhU3FoGWtbzQl50xV+rNJE5lYVRjUflWiZkVHlHkWUlPAwDPifLy4TuissVfScGGPWR5g==",
+ "license": "MIT",
+ "dependencies": {
+ "chalk": "^4.0.0",
+ "debug": "^4.3.4",
+ "dotenv": "~16.4.5",
+ "dotenv-expand": "~11.0.6",
+ "getenv": "^1.0.0"
+ }
+ },
+ "node_modules/@expo/fingerprint": {
+ "version": "0.12.4",
+ "resolved": "https://registry.npmjs.org/@expo/fingerprint/-/fingerprint-0.12.4.tgz",
+ "integrity": "sha512-HOJVvjiQYVHIouCOfFf4JRrQvBDIV/12GVG2iwbw1iGwmpQVkPgEXa9lN0f2yuS4J3QXHs73wr9jvuCjMmJlfw==",
+ "license": "MIT",
+ "dependencies": {
+ "@expo/spawn-async": "^1.7.2",
+ "arg": "^5.0.2",
+ "chalk": "^4.1.2",
+ "debug": "^4.3.4",
+ "find-up": "^5.0.0",
+ "getenv": "^1.0.0",
+ "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/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@expo/fingerprint/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/@expo/fingerprint/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/@expo/image-utils": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/@expo/image-utils/-/image-utils-0.7.4.tgz",
+ "integrity": "sha512-LcZ82EJy/t/a1avwIboeZbO6hlw8CvsIRh2k6SWPcAOvW0RqynyKFzUJsvnjWlhUzfBEn4oI7y/Pu5Xkw3KkkA==",
+ "license": "MIT",
+ "dependencies": {
+ "@expo/spawn-async": "^1.7.2",
+ "chalk": "^4.0.0",
+ "getenv": "^1.0.0",
+ "jimp-compact": "0.16.1",
+ "parse-png": "^2.1.0",
+ "resolve-from": "^5.0.0",
+ "semver": "^7.6.0",
+ "temp-dir": "~2.0.0",
+ "unique-string": "~2.0.0"
+ }
+ },
+ "node_modules/@expo/image-utils/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/@expo/json-file": {
+ "version": "9.1.4",
+ "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-9.1.4.tgz",
+ "integrity": "sha512-7Bv86X27fPERGhw8aJEZvRcH9sk+9BenDnEmrI3ZpywKodYSBgc8lX9Y32faNVQ/p0YbDK9zdJ0BfAKNAOyi0A==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "~7.10.4",
+ "json5": "^2.2.3"
+ }
+ },
+ "node_modules/@expo/json-file/node_modules/@babel/code-frame": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
+ "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/highlight": "^7.10.4"
+ }
+ },
+ "node_modules/@expo/metro-config": {
+ "version": "0.20.14",
+ "resolved": "https://registry.npmjs.org/@expo/metro-config/-/metro-config-0.20.14.tgz",
+ "integrity": "sha512-tYDDubuZycK+NX00XN7BMu73kBur/evOPcKfxc+UBeFfgN2EifOITtdwSUDdRsbtJ2OnXwMY1HfRUG3Lq3l4cw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.20.0",
+ "@babel/generator": "^7.20.5",
+ "@babel/parser": "^7.20.0",
+ "@babel/types": "^7.20.0",
+ "@expo/config": "~11.0.9",
+ "@expo/env": "~1.0.5",
+ "@expo/json-file": "~9.1.4",
+ "@expo/spawn-async": "^1.7.2",
+ "chalk": "^4.1.0",
+ "debug": "^4.3.2",
+ "dotenv": "~16.4.5",
+ "dotenv-expand": "~11.0.6",
+ "getenv": "^1.0.0",
+ "glob": "^10.4.2",
+ "jsc-safe-url": "^0.2.4",
+ "lightningcss": "~1.27.0",
+ "minimatch": "^9.0.0",
+ "postcss": "~8.4.32",
+ "resolve-from": "^5.0.0"
+ }
+ },
+ "node_modules/@expo/metro-config/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@expo/metro-config/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/@expo/metro-runtime": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/@expo/metro-runtime/-/metro-runtime-5.0.4.tgz",
+ "integrity": "sha512-r694MeO+7Vi8IwOsDIDzH/Q5RPMt1kUDYbiTJwnO15nIqiDwlE8HU55UlRhffKZy6s5FmxQsZ8HA+T8DqUW8cQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react-native": "*"
+ }
+ },
+ "node_modules/@expo/ngrok": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/@expo/ngrok/-/ngrok-4.1.3.tgz",
+ "integrity": "sha512-AESYaROGIGKWwWmUyQoUXcbvaUZjmpecC5buArXxYou+RID813F8T0Y5jQ2HUY49mZpYfJiy9oh4VSN37GgrXA==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@expo/ngrok-bin": "2.3.42",
+ "got": "^11.5.1",
+ "uuid": "^3.3.2",
+ "yaml": "^1.10.0"
+ },
+ "engines": {
+ "node": ">=10.19.0"
+ }
+ },
+ "node_modules/@expo/ngrok-bin": {
+ "version": "2.3.42",
+ "resolved": "https://registry.npmjs.org/@expo/ngrok-bin/-/ngrok-bin-2.3.42.tgz",
+ "integrity": "sha512-kyhORGwv9XpbPeNIrX6QZ9wDVCDOScyTwxeS+ScNmUqYoZqD9LRmEqF7bpDh5VonTsrXgWrGl7wD2++oSHcaTQ==",
+ "bin": {
+ "ngrok": "bin/ngrok.js"
+ },
+ "optionalDependencies": {
+ "@expo/ngrok-bin-darwin-arm64": "2.3.41",
+ "@expo/ngrok-bin-darwin-x64": "2.3.41",
+ "@expo/ngrok-bin-freebsd-ia32": "2.3.41",
+ "@expo/ngrok-bin-freebsd-x64": "2.3.41",
+ "@expo/ngrok-bin-linux-arm": "2.3.41",
+ "@expo/ngrok-bin-linux-arm64": "2.3.41",
+ "@expo/ngrok-bin-linux-ia32": "2.3.41",
+ "@expo/ngrok-bin-linux-x64": "2.3.41",
+ "@expo/ngrok-bin-sunos-x64": "2.3.41",
+ "@expo/ngrok-bin-win32-ia32": "2.3.41",
+ "@expo/ngrok-bin-win32-x64": "2.3.41"
+ }
+ },
+ "node_modules/@expo/ngrok-bin-darwin-arm64": {
+ "version": "2.3.41",
+ "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-darwin-arm64/-/ngrok-bin-darwin-arm64-2.3.41.tgz",
+ "integrity": "sha512-TPf95xp6SkvbRONZjltTOFcCJbmzAH7lrQ36Dv+djrOckWGPVq4HCur48YAeiGDqspmFEmqZ7ykD5c/bDfRFOA==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@expo/ngrok-bin-darwin-x64": {
+ "version": "2.3.41",
+ "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-darwin-x64/-/ngrok-bin-darwin-x64-2.3.41.tgz",
+ "integrity": "sha512-29QZHfX4Ec0p0pQF5UrqiP2/Qe7t2rI96o+5b8045VCEl9AEAKHceGuyo+jfUDR4FSQBGFLSDb06xy8ghL3ZYA==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@expo/ngrok-bin-freebsd-ia32": {
+ "version": "2.3.41",
+ "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-freebsd-ia32/-/ngrok-bin-freebsd-ia32-2.3.41.tgz",
+ "integrity": "sha512-YYXgwNZ+p0aIrwgb+1/RxJbsWhGEzBDBhZulKg1VB7tKDAd2C8uGnbK1rOCuZy013iOUsJDXaj9U5QKc13iIXw==",
+ "cpu": [
+ "ia32"
+ ],
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@expo/ngrok-bin-freebsd-x64": {
+ "version": "2.3.41",
+ "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-freebsd-x64/-/ngrok-bin-freebsd-x64-2.3.41.tgz",
+ "integrity": "sha512-1Ei6K8BB+3etmmBT0tXYC4dyVkJMigT4ELbRTF5jKfw1pblqeXM9Qpf3p8851PTlH142S3bockCeO39rSkOnkg==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@expo/ngrok-bin-linux-arm": {
+ "version": "2.3.41",
+ "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-linux-arm/-/ngrok-bin-linux-arm-2.3.41.tgz",
+ "integrity": "sha512-B6+rW/+tEi7ZrKWQGkRzlwmKo7c1WJhNODFBSgkF/Sj9PmmNhBz67mer91S2+6nNt5pfcwLLd61CjtWfR1LUHQ==",
+ "cpu": [
+ "arm"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@expo/ngrok-bin-linux-arm64": {
+ "version": "2.3.41",
+ "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-linux-arm64/-/ngrok-bin-linux-arm64-2.3.41.tgz",
+ "integrity": "sha512-eC8GA/xPcmQJy4h+g2FlkuQB3lf5DjITy8Y6GyydmPYMByjUYAGEXe0brOcP893aalAzRqbNOAjSuAw1lcCLSQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@expo/ngrok-bin-linux-ia32": {
+ "version": "2.3.41",
+ "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-linux-ia32/-/ngrok-bin-linux-ia32-2.3.41.tgz",
+ "integrity": "sha512-w5Cy31wSz4jYnygEHS7eRizR1yt8s9TX6kHlkjzayIiRTFRb2E1qD2l0/4T2w0LJpBjM5ZFPaaKqsNWgCUIEow==",
+ "cpu": [
+ "ia32"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@expo/ngrok-bin-linux-x64": {
+ "version": "2.3.41",
+ "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-linux-x64/-/ngrok-bin-linux-x64-2.3.41.tgz",
+ "integrity": "sha512-LcU3MbYHv7Sn2eFz8Yzo2rXduufOvX1/hILSirwCkH+9G8PYzpwp2TeGqVWuO+EmvtBe6NEYwgdQjJjN6I4L1A==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@expo/ngrok-bin-sunos-x64": {
+ "version": "2.3.41",
+ "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-sunos-x64/-/ngrok-bin-sunos-x64-2.3.41.tgz",
+ "integrity": "sha512-bcOj45BLhiV2PayNmLmEVZlFMhEiiGpOr36BXC0XSL+cHUZHd6uNaS28AaZdz95lrRzGpeb0hAF8cuJjo6nq4g==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "sunos"
+ ]
+ },
+ "node_modules/@expo/ngrok-bin-win32-ia32": {
+ "version": "2.3.41",
+ "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-win32-ia32/-/ngrok-bin-win32-ia32-2.3.41.tgz",
+ "integrity": "sha512-0+vPbKvUA+a9ERgiAknmZCiWA3AnM5c6beI+51LqmjKEM4iAAlDmfXNJ89aAbvZMUtBNwEPHzJHnaM4s2SeBhA==",
+ "cpu": [
+ "ia32"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@expo/ngrok-bin-win32-x64": {
+ "version": "2.3.41",
+ "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-win32-x64/-/ngrok-bin-win32-x64-2.3.41.tgz",
+ "integrity": "sha512-mncsPRaG462LiYrM8mQT8OYe3/i44m3N/NzUeieYpGi8+pCOo8TIC23kR9P93CVkbM9mmXsy3X6hq91a8FWBdA==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@expo/osascript": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/@expo/osascript/-/osascript-2.2.4.tgz",
+ "integrity": "sha512-Q+Oyj+1pdRiHHpev9YjqfMZzByFH8UhKvSszxa0acTveijjDhQgWrq4e9T/cchBHi0GWZpGczWyiyJkk1wM1dg==",
+ "license": "MIT",
+ "dependencies": {
+ "@expo/spawn-async": "^1.7.2",
+ "exec-async": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@expo/package-manager": {
+ "version": "1.8.4",
+ "resolved": "https://registry.npmjs.org/@expo/package-manager/-/package-manager-1.8.4.tgz",
+ "integrity": "sha512-8H8tLga/NS3iS7QaX/NneRPqbObnHvVCfMCo0ShudreOFmvmgqhYjRlkZTRstSyFqefai8ONaT4VmnLHneRYYg==",
+ "license": "MIT",
+ "dependencies": {
+ "@expo/json-file": "^9.1.4",
+ "@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/plist": {
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/@expo/plist/-/plist-0.3.4.tgz",
+ "integrity": "sha512-MhBLaUJNe9FQDDU2xhSNS4SAolr6K2wuyi4+A79vYuXLkAoICsbTwcGEQJN5jPY6D9izO/jsXh5k0h+mIWQMdw==",
+ "license": "MIT",
+ "dependencies": {
+ "@xmldom/xmldom": "^0.8.8",
+ "base64-js": "^1.2.3",
+ "xmlbuilder": "^15.1.1"
+ }
+ },
+ "node_modules/@expo/prebuild-config": {
+ "version": "9.0.6",
+ "resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-9.0.6.tgz",
+ "integrity": "sha512-HDTdlMkTQZ95rd6EpvuLM+xkZV03yGLc38FqI37qKFLJtUN1WnYVaWsuXKoljd1OrVEVsHe6CfqKwaPZ52D56Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@expo/config": "~11.0.9",
+ "@expo/config-plugins": "~10.0.2",
+ "@expo/config-types": "^53.0.4",
+ "@expo/image-utils": "^0.7.4",
+ "@expo/json-file": "^9.1.4",
+ "@react-native/normalize-colors": "0.79.2",
+ "debug": "^4.3.1",
+ "resolve-from": "^5.0.0",
+ "semver": "^7.6.0",
+ "xml2js": "0.6.0"
+ }
+ },
+ "node_modules/@expo/prebuild-config/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/@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/server": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/@expo/server/-/server-0.6.2.tgz",
+ "integrity": "sha512-ko+dq+1WEC126/iGVv3g+ChFCs9wGyKtGlnYphwrOQbFBBqX19sn6UV0oUks6UdhD+MyzUv+w/TOdktdcI0Cgg==",
+ "license": "MIT",
+ "dependencies": {
+ "abort-controller": "^3.0.0",
+ "debug": "^4.3.4",
+ "source-map-support": "~0.5.21",
+ "undici": "^6.18.2 || ^7.0.0"
+ }
+ },
+ "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/vector-icons": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/@expo/vector-icons/-/vector-icons-14.1.0.tgz",
+ "integrity": "sha512-7T09UE9h8QDTsUeMGymB4i+iqvtEeaO5VvUjryFB4tugDTG/bkzViWA74hm5pfjjDEhYMXWaX112mcvhccmIwQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "expo-font": "*",
+ "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/@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/@humanfs/core": {
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
+ "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node": {
+ "version": "0.16.6",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz",
+ "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/core": "^0.19.1",
+ "@humanwhocodes/retry": "^0.3.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz",
+ "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
+ "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@ide/backoff": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@ide/backoff/-/backoff-1.0.0.tgz",
+ "integrity": "sha512-F0YfUDjvT+Mtt/R4xdl2X0EYCHMMiJqNLdxHD++jDT5ydEFIyqbCHh51Qx2E211dgZprPKhV7sHmnXKpLuvc5g==",
+ "license": "MIT"
+ },
+ "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/cliui/node_modules/ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/@isaacs/cliui/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/@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/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/@istanbuljs/load-nyc-config/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/@istanbuljs/load-nyc-config/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/@istanbuljs/load-nyc-config/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/@istanbuljs/load-nyc-config/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/@istanbuljs/load-nyc-config/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/@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/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/@jridgewell/gen-mapping": {
+ "version": "0.3.8",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
+ "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/set-array": "^1.2.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "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/set-array": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
+ "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/source-map": {
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
+ "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.25"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
+ "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.25",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
+ "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@napi-rs/wasm-runtime": {
+ "version": "0.2.10",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.10.tgz",
+ "integrity": "sha512-bCsCyeZEwVErsGmyPNSzwfwFn4OdxBj0mmv6hOFucB/k81Ojdu68RbZdxYsRQUPc9l6SU5F/cG+bXgWs3oUgsQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "^1.4.3",
+ "@emnapi/runtime": "^1.4.3",
+ "@tybys/wasm-util": "^0.9.0"
+ }
+ },
+ "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==",
+ "dev": true,
+ "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==",
+ "dev": true,
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nolyfill/is-core-module": {
+ "version": "1.0.39",
+ "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz",
+ "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.4.0"
+ }
+ },
+ "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/@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-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/@react-native-async-storage/async-storage": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-2.1.2.tgz",
+ "integrity": "sha512-dvlNq4AlGWC+ehtH12p65+17V0Dx7IecOWl6WanF2ja38O1Dcjjvn7jVzkUHJ5oWkQBlyASurTPlTHgKXyYiow==",
+ "license": "MIT",
+ "dependencies": {
+ "merge-options": "^3.0.4"
+ },
+ "peerDependencies": {
+ "react-native": "^0.0.0-0 || >=0.65 <1.0"
+ }
+ },
+ "node_modules/@react-native-community/datetimepicker": {
+ "version": "8.4.1",
+ "resolved": "https://registry.npmjs.org/@react-native-community/datetimepicker/-/datetimepicker-8.4.1.tgz",
+ "integrity": "sha512-DrK+CUS5fZnz8dhzBezirkzQTcNDdaXer3oDLh0z4nc2tbdIdnzwvXCvi8IEOIvleoc9L95xS5tKUl0/Xv71Mg==",
+ "license": "MIT",
+ "dependencies": {
+ "invariant": "^2.2.4"
+ },
+ "peerDependencies": {
+ "expo": ">=52.0.0",
+ "react": "*",
+ "react-native": "*",
+ "react-native-windows": "*"
+ },
+ "peerDependenciesMeta": {
+ "expo": {
+ "optional": true
+ },
+ "react-native-windows": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@react-native-picker/picker": {
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/@react-native-picker/picker/-/picker-2.11.0.tgz",
+ "integrity": "sha512-QuZU6gbxmOID5zZgd/H90NgBnbJ3VV6qVzp6c7/dDrmWdX8S0X5YFYgDcQFjE3dRen9wB9FWnj2VVdPU64adSg==",
+ "license": "MIT",
+ "workspaces": [
+ "example"
+ ],
+ "peerDependencies": {
+ "react": "*",
+ "react-native": "*"
+ }
+ },
+ "node_modules/@react-native/assets-registry": {
+ "version": "0.79.2",
+ "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.79.2.tgz",
+ "integrity": "sha512-5h2Z7/+/HL/0h88s0JHOdRCW4CXMCJoROxqzHqxdrjGL6EBD1DdaB4ZqkCOEVSW4Vjhir5Qb97C8i/MPWEYPtg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@react-native/babel-plugin-codegen": {
+ "version": "0.79.2",
+ "resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.79.2.tgz",
+ "integrity": "sha512-d+NB7Uosn2ZWd4O4+7ZkB6q1a+0z2opD/4+Bzhk/Tv6fc5FrSftK2Noqxvo3/bhbdGFVPxf0yvLE8et4W17x/Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.25.3",
+ "@react-native/codegen": "0.79.2"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@react-native/babel-preset": {
+ "version": "0.79.2",
+ "resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.79.2.tgz",
+ "integrity": "sha512-/HNu869oUq4FUXizpiNWrIhucsYZqu0/0spudJEzk9SEKar0EjVDP7zkg/sKK+KccNypDQGW7nFXT8onzvQ3og==",
+ "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.79.2",
+ "babel-plugin-syntax-hermes-parser": "0.25.1",
+ "babel-plugin-transform-flow-enums": "^0.0.2",
+ "react-refresh": "^0.14.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@babel/core": "*"
+ }
+ },
+ "node_modules/@react-native/codegen": {
+ "version": "0.79.2",
+ "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.79.2.tgz",
+ "integrity": "sha512-8JTlGLuLi1p8Jx2N/enwwEd7/2CfrqJpv90Cp77QLRX3VHF2hdyavRIxAmXMwN95k+Me7CUuPtqn2X3IBXOWYg==",
+ "license": "MIT",
+ "dependencies": {
+ "glob": "^7.1.1",
+ "hermes-parser": "0.25.1",
+ "invariant": "^2.2.4",
+ "nullthrows": "^1.1.1",
+ "yargs": "^17.6.2"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@babel/core": "*"
+ }
+ },
+ "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/community-cli-plugin": {
+ "version": "0.79.2",
+ "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.79.2.tgz",
+ "integrity": "sha512-E+YEY2dL+68HyR2iahsZdyBKBUi9QyPyaN9vsnda1jNgCjNpSPk2yAF5cXsho+zKK5ZQna3JSeE1Kbi2IfGJbw==",
+ "license": "MIT",
+ "dependencies": {
+ "@react-native/dev-middleware": "0.79.2",
+ "chalk": "^4.0.0",
+ "debug": "^2.2.0",
+ "invariant": "^2.2.4",
+ "metro": "^0.82.0",
+ "metro-config": "^0.82.0",
+ "metro-core": "^0.82.0",
+ "semver": "^7.1.3"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@react-native-community/cli": "*"
+ },
+ "peerDependenciesMeta": {
+ "@react-native-community/cli": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@react-native/community-cli-plugin/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/@react-native/community-cli-plugin/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/@react-native/community-cli-plugin/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/debugger-frontend": {
+ "version": "0.79.2",
+ "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.79.2.tgz",
+ "integrity": "sha512-cGmC7X6kju76DopSBNc+PRAEetbd7TWF9J9o84hOp/xL3ahxR2kuxJy0oJX8Eg8oehhGGEXTuMKHzNa3rDBeSg==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@react-native/dev-middleware": {
+ "version": "0.79.2",
+ "resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.79.2.tgz",
+ "integrity": "sha512-9q4CpkklsAs1L0Bw8XYCoqqyBSrfRALGEw4/r0EkR38Y/6fVfNfdsjSns0pTLO6h0VpxswK34L/hm4uK3MoLHw==",
+ "license": "MIT",
+ "dependencies": {
+ "@isaacs/ttlcache": "^1.4.1",
+ "@react-native/debugger-frontend": "0.79.2",
+ "chrome-launcher": "^0.15.2",
+ "chromium-edge-launcher": "^0.2.0",
+ "connect": "^3.6.5",
+ "debug": "^2.2.0",
+ "invariant": "^2.2.4",
+ "nullthrows": "^1.1.1",
+ "open": "^7.0.3",
+ "serve-static": "^1.16.2",
+ "ws": "^6.2.3"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@react-native/dev-middleware/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/@react-native/dev-middleware/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/@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.79.2",
+ "resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.79.2.tgz",
+ "integrity": "sha512-6MJFemrwR0bOT0QM+2BxX9k3/pvZQNmJ3Js5pF/6owsA0cUDiCO57otiEU8Fz+UywWEzn1FoQfOfQ8vt2GYmoA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@react-native/js-polyfills": {
+ "version": "0.79.2",
+ "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.79.2.tgz",
+ "integrity": "sha512-IaY87Ckd4GTPMkO1/Fe8fC1IgIx3vc3q9Tyt/6qS3Mtk9nC0x9q4kSR5t+HHq0/MuvGtu8HpdxXGy5wLaM+zUw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@react-native/normalize-colors": {
+ "version": "0.79.2",
+ "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.79.2.tgz",
+ "integrity": "sha512-+b+GNrupWrWw1okHnEENz63j7NSMqhKeFMOyzYLBwKcprG8fqJQhDIGXfizKdxeIa5NnGSAevKL1Ev1zJ56X8w==",
+ "license": "MIT"
+ },
+ "node_modules/@react-native/virtualized-lists": {
+ "version": "0.79.2",
+ "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.79.2.tgz",
+ "integrity": "sha512-9G6ROJeP+rdw9Bvr5ruOlag11ET7j1z/En1riFFNo6W3xZvJY+alCuH1ttm12y9+zBm4n8jwCk4lGhjYaV4dKw==",
+ "license": "MIT",
+ "dependencies": {
+ "invariant": "^2.2.4",
+ "nullthrows": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@types/react": "^19.0.0",
+ "react": "*",
+ "react-native": "*"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@react-navigation/bottom-tabs": {
+ "version": "7.3.13",
+ "resolved": "https://registry.npmjs.org/@react-navigation/bottom-tabs/-/bottom-tabs-7.3.13.tgz",
+ "integrity": "sha512-J3MWXBJc3y6hefZNRqdj/JD4nzIDLzZL5GIYj89pR6oRf2Iibz9t1qV7yzxEc1KOaNDkXVZ/5U16PArEJFfykQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@react-navigation/elements": "^2.4.2",
+ "color": "^4.2.3"
+ },
+ "peerDependencies": {
+ "@react-navigation/native": "^7.1.9",
+ "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.9.2",
+ "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-7.9.2.tgz",
+ "integrity": "sha512-lqCyKMWWaSwGK4VV3wRXXEKvl5IKrVH207Kp77TLCnITnd4KQIdgjzzJ/Pr62ugki3VTAErq1vg0yRlcXciCbg==",
+ "license": "MIT",
+ "dependencies": {
+ "@react-navigation/routers": "^7.3.7",
+ "escape-string-regexp": "^4.0.0",
+ "nanoid": "^3.3.11",
+ "query-string": "^7.1.3",
+ "react-is": "^19.1.0",
+ "use-latest-callback": "^0.2.3",
+ "use-sync-external-store": "^1.5.0"
+ },
+ "peerDependencies": {
+ "react": ">= 18.2.0"
+ }
+ },
+ "node_modules/@react-navigation/elements": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-2.4.2.tgz",
+ "integrity": "sha512-cudKLsRtOB+i8iDzfBKypdqiHsDy1ruqCfYAtwKEclDmLsxu3/90YXoBtoPyFNyIpsn3GtsJzZsrYWQh78xSWg==",
+ "license": "MIT",
+ "dependencies": {
+ "color": "^4.2.3"
+ },
+ "peerDependencies": {
+ "@react-native-masked-view/masked-view": ">= 0.2.0",
+ "@react-navigation/native": "^7.1.9",
+ "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.9",
+ "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-7.1.9.tgz",
+ "integrity": "sha512-/A0oBwZIeD23o4jsnB0fEyKmKS+l4LAbJP/ioVvsGEubGp+sc5ouQNranOh7JwR0R1eX0MjcsLKprEwB+nztdw==",
+ "license": "MIT",
+ "dependencies": {
+ "@react-navigation/core": "^7.9.2",
+ "escape-string-regexp": "^4.0.0",
+ "fast-deep-equal": "^3.1.3",
+ "nanoid": "^3.3.11",
+ "use-latest-callback": "^0.2.3"
+ },
+ "peerDependencies": {
+ "react": ">= 18.2.0",
+ "react-native": "*"
+ }
+ },
+ "node_modules/@react-navigation/native-stack": {
+ "version": "7.3.13",
+ "resolved": "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-7.3.13.tgz",
+ "integrity": "sha512-udH+HumX0PmaT6QQTqjU3ciiCwifBGtnw1+6B1bVEDw83q80WHotlMitaf8Enbuf7oWrxwB+Eow4tV5MJXgQtQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@react-navigation/elements": "^2.4.2",
+ "warn-once": "^0.1.1"
+ },
+ "peerDependencies": {
+ "@react-navigation/native": "^7.1.9",
+ "react": ">= 18.2.0",
+ "react-native": "*",
+ "react-native-safe-area-context": ">= 4.0.0",
+ "react-native-screens": ">= 4.0.0"
+ }
+ },
+ "node_modules/@react-navigation/routers": {
+ "version": "7.3.7",
+ "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-7.3.7.tgz",
+ "integrity": "sha512-5ffgrefOs2zWqcCVX+OKn+RDx0puopQtxqetegFrTfWQ6pGXdY/5v4kBpPwaOFrNEeE/LPbHt9IJaJuvyhB7RA==",
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.11"
+ }
+ },
+ "node_modules/@rtsao/scc": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
+ "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@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/@sindresorhus/is": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
+ "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/is?sponsor=1"
+ }
+ },
+ "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/@szmarczak/http-timer": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
+ "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==",
+ "license": "MIT",
+ "dependencies": {
+ "defer-to-connect": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@tybys/wasm-util": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz",
+ "integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.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.20.7",
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz",
+ "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.20.7"
+ }
+ },
+ "node_modules/@types/cacheable-request": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz",
+ "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/http-cache-semantics": "*",
+ "@types/keyv": "^3.1.4",
+ "@types/node": "*",
+ "@types/responselike": "^1.0.0"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
+ "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==",
+ "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/http-cache-semantics": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz",
+ "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==",
+ "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/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/json5": {
+ "version": "0.0.29",
+ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/keyv": {
+ "version": "3.1.4",
+ "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz",
+ "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "22.15.21",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.21.tgz",
+ "integrity": "sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==",
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.21.0"
+ }
+ },
+ "node_modules/@types/react": {
+ "version": "19.0.14",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.14.tgz",
+ "integrity": "sha512-ixLZ7zG7j1fM0DijL9hDArwhwcCb4vqmePgwtV0GfnkHRSCUEv4LvzarcTdhoqgyMznUx/EhoTUv31CKZzkQlw==",
+ "devOptional": true,
+ "license": "MIT",
+ "dependencies": {
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@types/responselike": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz",
+ "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/stack-utils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz",
+ "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==",
+ "license": "MIT"
+ },
+ "node_modules/@types/yargs": {
+ "version": "17.0.33",
+ "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz",
+ "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==",
+ "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/@typescript-eslint/eslint-plugin": {
+ "version": "8.32.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.1.tgz",
+ "integrity": "sha512-6u6Plg9nP/J1GRpe/vcjjabo6Uc5YQPAMxsgQyGC/I0RuukiG1wIe3+Vtg3IrSCVJDmqK3j8adrtzXSENRtFgg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.10.0",
+ "@typescript-eslint/scope-manager": "8.32.1",
+ "@typescript-eslint/type-utils": "8.32.1",
+ "@typescript-eslint/utils": "8.32.1",
+ "@typescript-eslint/visitor-keys": "8.32.1",
+ "graphemer": "^1.4.0",
+ "ignore": "^7.0.0",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^2.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.4.tgz",
+ "integrity": "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "8.32.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.32.1.tgz",
+ "integrity": "sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "8.32.1",
+ "@typescript-eslint/types": "8.32.1",
+ "@typescript-eslint/typescript-estree": "8.32.1",
+ "@typescript-eslint/visitor-keys": "8.32.1",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.32.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.32.1.tgz",
+ "integrity": "sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.32.1",
+ "@typescript-eslint/visitor-keys": "8.32.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "8.32.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.32.1.tgz",
+ "integrity": "sha512-mv9YpQGA8iIsl5KyUPi+FGLm7+bA4fgXaeRcFKRDRwDMu4iwrSHeDPipwueNXhdIIZltwCJv+NkxftECbIZWfA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/typescript-estree": "8.32.1",
+ "@typescript-eslint/utils": "8.32.1",
+ "debug": "^4.3.4",
+ "ts-api-utils": "^2.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "8.32.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.32.1.tgz",
+ "integrity": "sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.32.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.32.1.tgz",
+ "integrity": "sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.32.1",
+ "@typescript-eslint/visitor-keys": "8.32.1",
+ "debug": "^4.3.4",
+ "fast-glob": "^3.3.2",
+ "is-glob": "^4.0.3",
+ "minimatch": "^9.0.4",
+ "semver": "^7.6.0",
+ "ts-api-utils": "^2.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/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==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "8.32.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.32.1.tgz",
+ "integrity": "sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.7.0",
+ "@typescript-eslint/scope-manager": "8.32.1",
+ "@typescript-eslint/types": "8.32.1",
+ "@typescript-eslint/typescript-estree": "8.32.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.32.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.32.1.tgz",
+ "integrity": "sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.32.1",
+ "eslint-visitor-keys": "^4.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@unrs/resolver-binding-darwin-arm64": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.7.2.tgz",
+ "integrity": "sha512-vxtBno4xvowwNmO/ASL0Y45TpHqmNkAaDtz4Jqb+clmcVSSl8XCG/PNFFkGsXXXS6AMjP+ja/TtNCFFa1QwLRg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-darwin-x64": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.7.2.tgz",
+ "integrity": "sha512-qhVa8ozu92C23Hsmv0BF4+5Dyyd5STT1FolV4whNgbY6mj3kA0qsrGPe35zNR3wAN7eFict3s4Rc2dDTPBTuFQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-freebsd-x64": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.7.2.tgz",
+ "integrity": "sha512-zKKdm2uMXqLFX6Ac7K5ElnnG5VIXbDlFWzg4WJ8CGUedJryM5A3cTgHuGMw1+P5ziV8CRhnSEgOnurTI4vpHpg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.7.2.tgz",
+ "integrity": "sha512-8N1z1TbPnHH+iDS/42GJ0bMPLiGK+cUqOhNbMKtWJ4oFGzqSJk/zoXFzcQkgtI63qMcUI7wW1tq2usZQSb2jxw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.7.2.tgz",
+ "integrity": "sha512-tjYzI9LcAXR9MYd9rO45m1s0B/6bJNuZ6jeOxo1pq1K6OBuRMMmfyvJYval3s9FPPGmrldYA3mi4gWDlWuTFGA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm64-gnu": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.7.2.tgz",
+ "integrity": "sha512-jon9M7DKRLGZ9VYSkFMflvNqu9hDtOCEnO2QAryFWgT6o6AXU8du56V7YqnaLKr6rAbZBWYsYpikF226v423QA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm64-musl": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.7.2.tgz",
+ "integrity": "sha512-c8Cg4/h+kQ63pL43wBNaVMmOjXI/X62wQmru51qjfTvI7kmCy5uHTJvK/9LrF0G8Jdx8r34d019P1DVJmhXQpA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.7.2.tgz",
+ "integrity": "sha512-A+lcwRFyrjeJmv3JJvhz5NbcCkLQL6Mk16kHTNm6/aGNc4FwPHPE4DR9DwuCvCnVHvF5IAd9U4VIs/VvVir5lg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.7.2.tgz",
+ "integrity": "sha512-hQQ4TJQrSQW8JlPm7tRpXN8OCNP9ez7PajJNjRD1ZTHQAy685OYqPrKjfaMw/8LiHCt8AZ74rfUVHP9vn0N69Q==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-riscv64-musl": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.7.2.tgz",
+ "integrity": "sha512-NoAGbiqrxtY8kVooZ24i70CjLDlUFI7nDj3I9y54U94p+3kPxwd2L692YsdLa+cqQ0VoqMWoehDFp21PKRUoIQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-s390x-gnu": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.7.2.tgz",
+ "integrity": "sha512-KaZByo8xuQZbUhhreBTW+yUnOIHUsv04P8lKjQ5otiGoSJ17ISGYArc+4vKdLEpGaLbemGzr4ZeUbYQQsLWFjA==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-x64-gnu": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.7.2.tgz",
+ "integrity": "sha512-dEidzJDubxxhUCBJ/SHSMJD/9q7JkyfBMT77Px1npl4xpg9t0POLvnWywSk66BgZS/b2Hy9Y1yFaoMTFJUe9yg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-x64-musl": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.7.2.tgz",
+ "integrity": "sha512-RvP+Ux3wDjmnZDT4XWFfNBRVG0fMsc+yVzNFUqOflnDfZ9OYujv6nkh+GOr+watwrW4wdp6ASfG/e7bkDradsw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-wasm32-wasi": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.7.2.tgz",
+ "integrity": "sha512-y797JBmO9IsvXVRCKDXOxjyAE4+CcZpla2GSoBQ33TVb3ILXuFnMrbR/QQZoauBYeOFuu4w3ifWLw52sdHGz6g==",
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@napi-rs/wasm-runtime": "^0.2.9"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@unrs/resolver-binding-win32-arm64-msvc": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.7.2.tgz",
+ "integrity": "sha512-gtYTh4/VREVSLA+gHrfbWxaMO/00y+34htY7XpioBTy56YN2eBjkPrY1ML1Zys89X3RJDKVaogzwxlM1qU7egg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-win32-ia32-msvc": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.7.2.tgz",
+ "integrity": "sha512-Ywv20XHvHTDRQs12jd3MY8X5C8KLjDbg/jyaal/QLKx3fAShhJyD4blEANInsjxW3P7isHx1Blt56iUDDJO3jg==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-win32-x64-msvc": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.7.2.tgz",
+ "integrity": "sha512-friS8NEQfHaDbkThxopGk+LuE5v3iY0StruifjQEt7SLbA46OnfgMO15sOTkbpJkol6RB+1l1TYPXh0sCddpvA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@urql/core": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@urql/core/-/core-5.1.1.tgz",
+ "integrity": "sha512-aGh024z5v2oINGD/In6rAtVKTm4VmQ2TxKQBAtk2ZSME5dunZFcjltw4p5ENQg+5CBhZ3FHMzl0Oa+rwqiWqlg==",
+ "license": "MIT",
+ "dependencies": {
+ "@0no-co/graphql.web": "^1.0.5",
+ "wonka": "^6.3.2"
+ }
+ },
+ "node_modules/@urql/exchange-retry": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@urql/exchange-retry/-/exchange-retry-1.3.1.tgz",
+ "integrity": "sha512-EEmtFu8JTuwsInqMakhLq+U3qN8ZMd5V3pX44q0EqD2imqTDsa8ikZqJ1schVrN8HljOdN+C08cwZ1/r5uIgLw==",
+ "license": "MIT",
+ "dependencies": {
+ "@urql/core": "^5.1.1",
+ "wonka": "^6.3.2"
+ },
+ "peerDependencies": {
+ "@urql/core": "^5.0.0"
+ }
+ },
+ "node_modules/@xmldom/xmldom": {
+ "version": "0.8.10",
+ "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz",
+ "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==",
+ "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.14.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
+ "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/agent-base": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz",
+ "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ajv-formats": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
+ "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "ajv": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/ajv-formats/node_modules/ajv": {
+ "version": "8.17.1",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
+ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "fast-uri": "^3.0.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ajv-formats/node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "license": "MIT"
+ },
+ "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-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": "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/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/anymatch/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/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": "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/array-buffer-byte-length": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz",
+ "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "is-array-buffer": "^3.0.5"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array-includes": {
+ "version": "3.1.8",
+ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz",
+ "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.4",
+ "is-string": "^1.0.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.findlast": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz",
+ "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.findlastindex": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz",
+ "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.9",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "es-shim-unscopables": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.flat": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz",
+ "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.flatmap": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz",
+ "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.tosorted": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz",
+ "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.3",
+ "es-errors": "^1.3.0",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/arraybuffer.prototype.slice": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz",
+ "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.1",
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "is-array-buffer": "^3.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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/assert": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz",
+ "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "is-nan": "^1.3.2",
+ "object-is": "^1.1.5",
+ "object.assign": "^4.1.4",
+ "util": "^0.12.5"
+ }
+ },
+ "node_modules/async-function": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz",
+ "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "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/available-typed-arrays": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
+ "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
+ "license": "MIT",
+ "dependencies": {
+ "possible-typed-array-names": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/babel-jest": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz",
+ "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==",
+ "license": "MIT",
+ "dependencies": {
+ "@jest/transform": "^29.7.0",
+ "@types/babel__core": "^7.1.14",
+ "babel-plugin-istanbul": "^6.1.1",
+ "babel-preset-jest": "^29.6.3",
+ "chalk": "^4.0.0",
+ "graceful-fs": "^4.2.9",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.8.0"
+ }
+ },
+ "node_modules/babel-plugin-istanbul": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz",
+ "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@istanbuljs/load-nyc-config": "^1.0.0",
+ "@istanbuljs/schema": "^0.1.2",
+ "istanbul-lib-instrument": "^5.0.4",
+ "test-exclude": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/babel-plugin-jest-hoist": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz",
+ "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.3.3",
+ "@babel/types": "^7.3.3",
+ "@types/babel__core": "^7.1.14",
+ "@types/babel__traverse": "^7.0.6"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/babel-plugin-polyfill-corejs2": {
+ "version": "0.4.13",
+ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.13.tgz",
+ "integrity": "sha512-3sX/eOms8kd3q2KZ6DAhKPc0dgm525Gqq5NtWKZ7QYYZEv57OQ54KtblzJzH1lQF/eQxO8KjWGIK9IPUJNus5g==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.22.6",
+ "@babel/helper-define-polyfill-provider": "^0.6.4",
+ "semver": "^6.3.1"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
+ }
+ },
+ "node_modules/babel-plugin-polyfill-corejs3": {
+ "version": "0.11.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz",
+ "integrity": "sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-define-polyfill-provider": "^0.6.3",
+ "core-js-compat": "^3.40.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
+ }
+ },
+ "node_modules/babel-plugin-polyfill-regenerator": {
+ "version": "0.6.4",
+ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.4.tgz",
+ "integrity": "sha512-7gD3pRadPrbjhjLyxebmx/WrFYcuSjZ0XbdUujQMZ/fcE9oeewk2U/7PCvez84UeuK3oSjmPZ0Ch0dlupQvGzw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-define-polyfill-provider": "^0.6.4"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
+ }
+ },
+ "node_modules/babel-plugin-react-native-web": {
+ "version": "0.19.13",
+ "resolved": "https://registry.npmjs.org/babel-plugin-react-native-web/-/babel-plugin-react-native-web-0.19.13.tgz",
+ "integrity": "sha512-4hHoto6xaN23LCyZgL9LJZc3olmAxd7b6jDzlZnKXAh4rRAbZRKNBJoOOdp46OBqgy+K0t0guTj5/mhA8inymQ==",
+ "license": "MIT"
+ },
+ "node_modules/babel-plugin-syntax-hermes-parser": {
+ "version": "0.25.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.25.1.tgz",
+ "integrity": "sha512-IVNpGzboFLfXZUAwkLFcI/bnqVbwky0jP3eBno4HKtqvQJAHBLdgxiG6lQ4to0+Q/YCN3PO0od5NZwIKyY4REQ==",
+ "license": "MIT",
+ "dependencies": {
+ "hermes-parser": "0.25.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.1.0",
+ "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz",
+ "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==",
+ "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"
+ }
+ },
+ "node_modules/babel-preset-expo": {
+ "version": "13.1.11",
+ "resolved": "https://registry.npmjs.org/babel-preset-expo/-/babel-preset-expo-13.1.11.tgz",
+ "integrity": "sha512-jigWjvhRVdm9UTPJ1wjLYJ0OJvD5vLZ8YYkEknEl6+9S1JWORO/y3xtHr/hNj5n34nOilZqdXrmNFcqKc8YTsg==",
+ "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-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.79.2",
+ "babel-plugin-react-native-web": "~0.19.13",
+ "babel-plugin-syntax-hermes-parser": "^0.25.1",
+ "babel-plugin-transform-flow-enums": "^0.0.2",
+ "debug": "^4.3.4",
+ "react-refresh": "^0.14.2",
+ "resolve-from": "^5.0.0"
+ },
+ "peerDependencies": {
+ "babel-plugin-react-compiler": "^19.0.0-beta-e993439-20250405"
+ },
+ "peerDependenciesMeta": {
+ "babel-plugin-react-compiler": {
+ "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/badgin": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/badgin/-/badgin-1.2.3.tgz",
+ "integrity": "sha512-NQGA7LcfCpSzIbGRbkgjgdWkjy7HI+Th5VLxTJfW5EeaAf3fnS+xWQaQOCYiny+q6QSvxqoSO04vCx+4u++EJw==",
+ "license": "MIT"
+ },
+ "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/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/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/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.2",
+ "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.2.tgz",
+ "integrity": "sha512-apC2+fspHGI3mMKj+dGevkGo/tCqVB8jMb6i+OX+E29p0Iposz07fABkRIfVUPNd5A5VbuOz1bZbnmkKLYF+wQ==",
+ "license": "MIT",
+ "dependencies": {
+ "big-integer": "1.6.x"
+ },
+ "engines": {
+ "node": ">= 5.10.0"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "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.24.5",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.5.tgz",
+ "integrity": "sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==",
+ "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": {
+ "caniuse-lite": "^1.0.30001716",
+ "electron-to-chromium": "^1.5.149",
+ "node-releases": "^2.0.19",
+ "update-browserslist-db": "^1.1.3"
+ },
+ "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/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/cacheable-lookup": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz",
+ "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.6.0"
+ }
+ },
+ "node_modules/cacheable-request": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz",
+ "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==",
+ "license": "MIT",
+ "dependencies": {
+ "clone-response": "^1.0.2",
+ "get-stream": "^5.1.0",
+ "http-cache-semantics": "^4.0.0",
+ "keyv": "^4.0.0",
+ "lowercase-keys": "^2.0.0",
+ "normalize-url": "^6.0.1",
+ "responselike": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/call-bind": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
+ "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.0",
+ "es-define-property": "^1.0.0",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/caller-callsite": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz",
+ "integrity": "sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==",
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/caller-callsite/node_modules/callsites": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz",
+ "integrity": "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/caller-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz",
+ "integrity": "sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==",
+ "license": "MIT",
+ "dependencies": {
+ "caller-callsite": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "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/caniuse-lite": {
+ "version": "1.0.30001718",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001718.tgz",
+ "integrity": "sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==",
+ "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": "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/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/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/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/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/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/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/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/clone-response": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz",
+ "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==",
+ "license": "MIT",
+ "dependencies": {
+ "mimic-response": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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": "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-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/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/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/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.0",
+ "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.0.tgz",
+ "integrity": "sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==",
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "3.1.2",
+ "compressible": "~2.0.18",
+ "debug": "2.6.9",
+ "negotiator": "~0.6.4",
+ "on-headers": "~1.0.2",
+ "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/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/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.42.0",
+ "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.42.0.tgz",
+ "integrity": "sha512-bQasjMfyDGyaeWKBIu33lHh9qlSR0MFE/Nmc6nMjf/iU9b3rSMdAYz1Baxrv4lPdGUsTqZudHA4jIGSJy0SWZQ==",
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.24.4"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/core-js"
+ }
+ },
+ "node_modules/cosmiconfig": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz",
+ "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==",
+ "license": "MIT",
+ "dependencies": {
+ "import-fresh": "^2.0.0",
+ "is-directory": "^0.3.1",
+ "js-yaml": "^3.13.1",
+ "parse-json": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/cosmiconfig/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/cosmiconfig/node_modules/import-fresh": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
+ "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==",
+ "license": "MIT",
+ "dependencies": {
+ "caller-path": "^2.0.0",
+ "resolve-from": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/cosmiconfig/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/cosmiconfig/node_modules/resolve-from": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
+ "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "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.1.0",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
+ "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
+ "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.1.0",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
+ "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">= 6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
+ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
+ "devOptional": true,
+ "license": "MIT"
+ },
+ "node_modules/d3-path": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
+ "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-shape": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
+ "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-path": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/data-view-buffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz",
+ "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/data-view-byte-length": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz",
+ "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/inspect-js"
+ }
+ },
+ "node_modules/data-view-byte-offset": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz",
+ "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
+ "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+ "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/decompress-response": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
+ "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
+ "license": "MIT",
+ "dependencies": {
+ "mimic-response": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/decompress-response/node_modules/mimic-response": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
+ "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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/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/defer-to-connect": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
+ "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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/define-properties": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
+ "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.0.1",
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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/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": "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/doctrine": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "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/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/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "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.157",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.157.tgz",
+ "integrity": "sha512-/0ybgsQd1muo8QlnuTpKwtl0oX5YMlUGbm8xyqgDU00motRkKFFbUJySAQBWcY79rVqNLWIWa87BGVGClwAB2w==",
+ "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/end-of-stream": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+ "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+ "license": "MIT",
+ "dependencies": {
+ "once": "^1.4.0"
+ }
+ },
+ "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/error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "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-abstract": {
+ "version": "1.23.10",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.10.tgz",
+ "integrity": "sha512-MtUbM072wlJNyeYAe0mhzrD+M6DIJa96CZAOBBrhDbgKnB4MApIKefcyAB1eOdYn8cUNZgvwBvEzdoAYsxgEIw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.2",
+ "arraybuffer.prototype.slice": "^1.0.4",
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "data-view-buffer": "^1.0.2",
+ "data-view-byte-length": "^1.0.2",
+ "data-view-byte-offset": "^1.0.1",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "es-set-tostringtag": "^2.1.0",
+ "es-to-primitive": "^1.3.0",
+ "function.prototype.name": "^1.1.8",
+ "get-intrinsic": "^1.3.0",
+ "get-proto": "^1.0.1",
+ "get-symbol-description": "^1.1.0",
+ "globalthis": "^1.0.4",
+ "gopd": "^1.2.0",
+ "has-property-descriptors": "^1.0.2",
+ "has-proto": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "internal-slot": "^1.1.0",
+ "is-array-buffer": "^3.0.5",
+ "is-callable": "^1.2.7",
+ "is-data-view": "^1.0.2",
+ "is-regex": "^1.2.1",
+ "is-shared-array-buffer": "^1.0.4",
+ "is-string": "^1.1.1",
+ "is-typed-array": "^1.1.15",
+ "is-weakref": "^1.1.1",
+ "math-intrinsics": "^1.1.0",
+ "object-inspect": "^1.13.4",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.7",
+ "own-keys": "^1.0.1",
+ "regexp.prototype.flags": "^1.5.4",
+ "safe-array-concat": "^1.1.3",
+ "safe-push-apply": "^1.0.0",
+ "safe-regex-test": "^1.1.0",
+ "set-proto": "^1.0.0",
+ "string.prototype.trim": "^1.2.10",
+ "string.prototype.trimend": "^1.0.9",
+ "string.prototype.trimstart": "^1.0.8",
+ "typed-array-buffer": "^1.0.3",
+ "typed-array-byte-length": "^1.0.3",
+ "typed-array-byte-offset": "^1.0.4",
+ "typed-array-length": "^1.0.7",
+ "unbox-primitive": "^1.1.0",
+ "which-typed-array": "^1.1.19"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-iterator-helpers": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz",
+ "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.6",
+ "es-errors": "^1.3.0",
+ "es-set-tostringtag": "^2.0.3",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.6",
+ "globalthis": "^1.0.4",
+ "gopd": "^1.2.0",
+ "has-property-descriptors": "^1.0.2",
+ "has-proto": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "internal-slot": "^1.1.0",
+ "iterator.prototype": "^1.1.4",
+ "safe-array-concat": "^1.1.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-shim-unscopables": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz",
+ "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-to-primitive": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz",
+ "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-callable": "^1.2.7",
+ "is-date-object": "^1.0.5",
+ "is-symbol": "^1.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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-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": "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/eslint": {
+ "version": "9.27.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.27.0.tgz",
+ "integrity": "sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.12.1",
+ "@eslint/config-array": "^0.20.0",
+ "@eslint/config-helpers": "^0.2.1",
+ "@eslint/core": "^0.14.0",
+ "@eslint/eslintrc": "^3.3.1",
+ "@eslint/js": "9.27.0",
+ "@eslint/plugin-kit": "^0.3.1",
+ "@humanfs/node": "^0.16.6",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.4.2",
+ "@types/estree": "^1.0.6",
+ "@types/json-schema": "^7.0.15",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.6",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^8.3.0",
+ "eslint-visitor-keys": "^4.2.0",
+ "espree": "^10.3.0",
+ "esquery": "^1.5.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^8.0.0",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-config-expo": {
+ "version": "9.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-expo/-/eslint-config-expo-9.2.0.tgz",
+ "integrity": "sha512-TQgmSx+2mRM7qUS0hB5kTDrHcSC35rA1UzOSgK5YRLmSkSMlKLmXkUrhwOpnyo9D/nHdf4ERRAySRYxgA6dlrw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/eslint-plugin": "^8.18.2",
+ "@typescript-eslint/parser": "^8.18.2",
+ "eslint-import-resolver-typescript": "^3.6.3",
+ "eslint-plugin-expo": "^0.1.4",
+ "eslint-plugin-import": "^2.30.0",
+ "eslint-plugin-react": "^7.37.3",
+ "eslint-plugin-react-hooks": "^5.1.0",
+ "globals": "^16.0.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=8.10"
+ }
+ },
+ "node_modules/eslint-config-expo/node_modules/globals": {
+ "version": "16.2.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-16.2.0.tgz",
+ "integrity": "sha512-O+7l9tPdHCU320IigZZPj5zmRCFG9xHmx9cU8FqU2Rp+JN714seHV+2S9+JslCpY4gJwU2vOGox0wzgae/MCEg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint-import-resolver-node": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz",
+ "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^3.2.7",
+ "is-core-module": "^2.13.0",
+ "resolve": "^1.22.4"
+ }
+ },
+ "node_modules/eslint-import-resolver-node/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-import-resolver-typescript": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz",
+ "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@nolyfill/is-core-module": "1.0.39",
+ "debug": "^4.4.0",
+ "get-tsconfig": "^4.10.0",
+ "is-bun-module": "^2.0.0",
+ "stable-hash": "^0.0.5",
+ "tinyglobby": "^0.2.13",
+ "unrs-resolver": "^1.6.2"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint-import-resolver-typescript"
+ },
+ "peerDependencies": {
+ "eslint": "*",
+ "eslint-plugin-import": "*",
+ "eslint-plugin-import-x": "*"
+ },
+ "peerDependenciesMeta": {
+ "eslint-plugin-import": {
+ "optional": true
+ },
+ "eslint-plugin-import-x": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-module-utils": {
+ "version": "2.12.0",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz",
+ "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^3.2.7"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependenciesMeta": {
+ "eslint": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-module-utils/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-plugin-expo": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-expo/-/eslint-plugin-expo-0.1.4.tgz",
+ "integrity": "sha512-YA7yiMacQbLJySuyJA0Eb5V65obqp6fVOWtw1JdYDRWC5MeToPrnNvhGDpk01Bv3Vm4ownuzUfvi89MXi1d6cg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "^8.29.1",
+ "@typescript-eslint/utils": "^8.29.1",
+ "eslint": "^9.24.0"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=8.10"
+ }
+ },
+ "node_modules/eslint-plugin-import": {
+ "version": "2.31.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz",
+ "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rtsao/scc": "^1.1.0",
+ "array-includes": "^3.1.8",
+ "array.prototype.findlastindex": "^1.2.5",
+ "array.prototype.flat": "^1.3.2",
+ "array.prototype.flatmap": "^1.3.2",
+ "debug": "^3.2.7",
+ "doctrine": "^2.1.0",
+ "eslint-import-resolver-node": "^0.3.9",
+ "eslint-module-utils": "^2.12.0",
+ "hasown": "^2.0.2",
+ "is-core-module": "^2.15.1",
+ "is-glob": "^4.0.3",
+ "minimatch": "^3.1.2",
+ "object.fromentries": "^2.0.8",
+ "object.groupby": "^1.0.3",
+ "object.values": "^1.2.0",
+ "semver": "^6.3.1",
+ "string.prototype.trimend": "^1.0.8",
+ "tsconfig-paths": "^3.15.0"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependencies": {
+ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-plugin-react": {
+ "version": "7.37.5",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz",
+ "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-includes": "^3.1.8",
+ "array.prototype.findlast": "^1.2.5",
+ "array.prototype.flatmap": "^1.3.3",
+ "array.prototype.tosorted": "^1.1.4",
+ "doctrine": "^2.1.0",
+ "es-iterator-helpers": "^1.2.1",
+ "estraverse": "^5.3.0",
+ "hasown": "^2.0.2",
+ "jsx-ast-utils": "^2.4.1 || ^3.0.0",
+ "minimatch": "^3.1.2",
+ "object.entries": "^1.1.9",
+ "object.fromentries": "^2.0.8",
+ "object.values": "^1.2.1",
+ "prop-types": "^15.8.1",
+ "resolve": "^2.0.0-next.5",
+ "semver": "^6.3.1",
+ "string.prototype.matchall": "^4.0.12",
+ "string.prototype.repeat": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependencies": {
+ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7"
+ }
+ },
+ "node_modules/eslint-plugin-react-hooks": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz",
+ "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-react/node_modules/resolve": {
+ "version": "2.0.0-next.5",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz",
+ "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-core-module": "^2.13.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz",
+ "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
+ "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree": {
+ "version": "10.3.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz",
+ "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.14.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "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/esquery": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
+ "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.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/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/expo": {
+ "version": "53.0.9",
+ "resolved": "https://registry.npmjs.org/expo/-/expo-53.0.9.tgz",
+ "integrity": "sha512-UFG68aVOpccg3s++S3pbtI3YCQCnlu/TFvhnQ5vaD3vhOox1Uk/f2O2T95jmwA/EvKvetqGj34lys3DNXvPqgQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.20.0",
+ "@expo/cli": "0.24.13",
+ "@expo/config": "~11.0.10",
+ "@expo/config-plugins": "~10.0.2",
+ "@expo/fingerprint": "0.12.4",
+ "@expo/metro-config": "0.20.14",
+ "@expo/vector-icons": "^14.0.0",
+ "babel-preset-expo": "~13.1.11",
+ "expo-asset": "~11.1.5",
+ "expo-constants": "~17.1.6",
+ "expo-file-system": "~18.1.10",
+ "expo-font": "~13.3.1",
+ "expo-keep-awake": "~14.1.4",
+ "expo-modules-autolinking": "2.1.10",
+ "expo-modules-core": "2.3.13",
+ "react-native-edge-to-edge": "1.6.0",
+ "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": "6.1.4",
+ "resolved": "https://registry.npmjs.org/expo-application/-/expo-application-6.1.4.tgz",
+ "integrity": "sha512-jXVZb3llTQ5j4C/I03GxKjujmhKex9Xo5JDZo/pRjScHSr4NoeMjPKWThyWVlWDM1v5YSEcsRJebVfTvq9SR5Q==",
+ "license": "MIT",
+ "peerDependencies": {
+ "expo": "*"
+ }
+ },
+ "node_modules/expo-asset": {
+ "version": "11.1.5",
+ "resolved": "https://registry.npmjs.org/expo-asset/-/expo-asset-11.1.5.tgz",
+ "integrity": "sha512-GEQDCqC25uDBoXHEnXeBuwpeXvI+3fRGvtzwwt0ZKKzWaN+TgeF8H7c76p3Zi4DfBMFDcduM0CmOvJX+yCCLUQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@expo/image-utils": "^0.7.4",
+ "expo-constants": "~17.1.5"
+ },
+ "peerDependencies": {
+ "expo": "*",
+ "react": "*",
+ "react-native": "*"
+ }
+ },
+ "node_modules/expo-blur": {
+ "version": "14.1.4",
+ "resolved": "https://registry.npmjs.org/expo-blur/-/expo-blur-14.1.4.tgz",
+ "integrity": "sha512-55P9tK/RjJZEcu2tU7BqX3wmIOrGMOOkmHztJMMws+ZGHzvtjnPmT7dsQxhOU9vPj77oHnKetYHU2sik3iBcCw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "expo": "*",
+ "react": "*",
+ "react-native": "*"
+ }
+ },
+ "node_modules/expo-constants": {
+ "version": "17.1.6",
+ "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-17.1.6.tgz",
+ "integrity": "sha512-q5mLvJiLtPcaZ7t2diSOlQ2AyxIO8YMVEJsEfI/ExkGj15JrflNQ7CALEW6IF/uNae/76qI/XcjEuuAyjdaCNw==",
+ "license": "MIT",
+ "dependencies": {
+ "@expo/config": "~11.0.9",
+ "@expo/env": "~1.0.5"
+ },
+ "peerDependencies": {
+ "expo": "*",
+ "react-native": "*"
+ }
+ },
+ "node_modules/expo-device": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/expo-device/-/expo-device-7.1.4.tgz",
+ "integrity": "sha512-HS04IiE1Fy0FRjBLurr9e5A6yj3kbmQB+2jCZvbSGpsjBnCLdSk/LCii4f5VFhPIBWJLyYuN5QqJyEAw6BcS4Q==",
+ "license": "MIT",
+ "dependencies": {
+ "ua-parser-js": "^0.7.33"
+ },
+ "peerDependencies": {
+ "expo": "*"
+ }
+ },
+ "node_modules/expo-device/node_modules/ua-parser-js": {
+ "version": "0.7.40",
+ "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.40.tgz",
+ "integrity": "sha512-us1E3K+3jJppDBa3Tl0L3MOJiGhe1C6P0+nIvQAFYbxlMAx0h81eOwLmU57xgqToduDDPx3y5QsdjPfDu+FgOQ==",
+ "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-file-system": {
+ "version": "18.1.10",
+ "resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-18.1.10.tgz",
+ "integrity": "sha512-SyaWg+HitScLuyEeSG9gMSDT0hIxbM9jiZjSBP9l9zMnwZjmQwsusE6+7qGiddxJzdOhTP4YGUfvEzeeS0YL3Q==",
+ "license": "MIT",
+ "peerDependencies": {
+ "expo": "*",
+ "react-native": "*"
+ }
+ },
+ "node_modules/expo-font": {
+ "version": "13.3.1",
+ "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-13.3.1.tgz",
+ "integrity": "sha512-d+xrHYvSM9WB42wj8vP9OOFWyxed5R1evphfDb6zYBmC1dA9Hf89FpT7TNFtj2Bk3clTnpmVqQTCYbbA2P3CLg==",
+ "license": "MIT",
+ "dependencies": {
+ "fontfaceobserver": "^2.1.0"
+ },
+ "peerDependencies": {
+ "expo": "*",
+ "react": "*"
+ }
+ },
+ "node_modules/expo-haptics": {
+ "version": "14.1.4",
+ "resolved": "https://registry.npmjs.org/expo-haptics/-/expo-haptics-14.1.4.tgz",
+ "integrity": "sha512-QZdE3NMX74rTuIl82I+n12XGwpDWKb8zfs5EpwsnGi/D/n7O2Jd4tO5ivH+muEG/OCJOMq5aeaVDqqaQOhTkcA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "expo": "*"
+ }
+ },
+ "node_modules/expo-image": {
+ "version": "2.1.7",
+ "resolved": "https://registry.npmjs.org/expo-image/-/expo-image-2.1.7.tgz",
+ "integrity": "sha512-p2Gr8fP/YakFHHo4rbpJbRWwKNrZp1GzSD91WEG3ZYAbTVdTjheJ6gUxXgggFaxEbaY+4WeQ0c5j9tZq8+3cEg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "expo": "*",
+ "react": "*",
+ "react-native": "*",
+ "react-native-web": "*"
+ },
+ "peerDependenciesMeta": {
+ "react-native-web": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/expo-keep-awake": {
+ "version": "14.1.4",
+ "resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-14.1.4.tgz",
+ "integrity": "sha512-wU9qOnosy4+U4z/o4h8W9PjPvcFMfZXrlUoKTMBW7F4pLqhkkP/5G4EviPZixv4XWFMjn1ExQ5rV6BX8GwJsWA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "expo": "*",
+ "react": "*"
+ }
+ },
+ "node_modules/expo-linking": {
+ "version": "7.1.5",
+ "resolved": "https://registry.npmjs.org/expo-linking/-/expo-linking-7.1.5.tgz",
+ "integrity": "sha512-8g20zOpROW78bF+bLI4a3ZWj4ntLgM0rCewKycPL0jk9WGvBrBtFtwwADJgOiV1EurNp3lcquerXGlWS+SOQyA==",
+ "license": "MIT",
+ "dependencies": {
+ "expo-constants": "~17.1.6",
+ "invariant": "^2.2.4"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-native": "*"
+ }
+ },
+ "node_modules/expo-modules-autolinking": {
+ "version": "2.1.10",
+ "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-2.1.10.tgz",
+ "integrity": "sha512-k93fzoszrYTKbZ51DSVnewYIGUV6Gi22Su8qySXPFJEfvtDs2NUUNRHBZNKgLHvwc6xPzVC5j7JYbrpXNuY44A==",
+ "license": "MIT",
+ "dependencies": {
+ "@expo/spawn-async": "^1.7.2",
+ "chalk": "^4.1.0",
+ "commander": "^7.2.0",
+ "find-up": "^5.0.0",
+ "glob": "^10.4.2",
+ "require-from-string": "^2.0.2",
+ "resolve-from": "^5.0.0"
+ },
+ "bin": {
+ "expo-modules-autolinking": "bin/expo-modules-autolinking.js"
+ }
+ },
+ "node_modules/expo-modules-core": {
+ "version": "2.3.13",
+ "resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-2.3.13.tgz",
+ "integrity": "sha512-vmKHv7tEo2wUQoYDV6grhsLsQfD3DUnew5Up3yNnOE1gHGQE+zhV1SBYqaPMPB12OvpyD1mlfzGhu6r9PODnng==",
+ "license": "MIT",
+ "dependencies": {
+ "invariant": "^2.2.4"
+ }
+ },
+ "node_modules/expo-notifications": {
+ "version": "0.31.2",
+ "resolved": "https://registry.npmjs.org/expo-notifications/-/expo-notifications-0.31.2.tgz",
+ "integrity": "sha512-p73VIECCULm9XkHE6WNweZDw9G62Tz9OpOOQ2tCF/QLpDW8W1q4PEUZUdbrTJREB4Q0VHegVNDJmKUkSOp7bfA==",
+ "license": "MIT",
+ "dependencies": {
+ "@expo/image-utils": "^0.7.4",
+ "@ide/backoff": "^1.0.0",
+ "abort-controller": "^3.0.0",
+ "assert": "^2.0.0",
+ "badgin": "^1.1.5",
+ "expo-application": "~6.1.4",
+ "expo-constants": "~17.1.6"
+ },
+ "peerDependencies": {
+ "expo": "*",
+ "react": "*",
+ "react-native": "*"
+ }
+ },
+ "node_modules/expo-permissions": {
+ "version": "14.4.0",
+ "resolved": "https://registry.npmjs.org/expo-permissions/-/expo-permissions-14.4.0.tgz",
+ "integrity": "sha512-oAcnJ7dlZhpBydK73cwomA2xofizayVUz+FW5REl7dMu7MYyeN/3aqhlpZ3mYddrxvG161bqu97MQr01UixUnw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "expo": "*"
+ }
+ },
+ "node_modules/expo-router": {
+ "version": "5.0.7",
+ "resolved": "https://registry.npmjs.org/expo-router/-/expo-router-5.0.7.tgz",
+ "integrity": "sha512-NlEgRXCKtseDuIHBp87UfkvqsuVrc0MYG+zg33dopaN6wik4RkrWWxUYdNPHub0s/7qMye6zZBY4ZCrXwd/xpA==",
+ "license": "MIT",
+ "dependencies": {
+ "@expo/metro-runtime": "5.0.4",
+ "@expo/server": "^0.6.2",
+ "@radix-ui/react-slot": "1.2.0",
+ "@react-navigation/bottom-tabs": "^7.3.10",
+ "@react-navigation/native": "^7.1.6",
+ "@react-navigation/native-stack": "^7.3.10",
+ "client-only": "^0.0.1",
+ "invariant": "^2.2.4",
+ "react-fast-compare": "^3.2.2",
+ "react-native-is-edge-to-edge": "^1.1.6",
+ "schema-utils": "^4.0.1",
+ "semver": "~7.6.3",
+ "server-only": "^0.0.1",
+ "shallowequal": "^1.1.0"
+ },
+ "peerDependencies": {
+ "@react-navigation/drawer": "^7.3.9",
+ "expo": "*",
+ "expo-constants": "*",
+ "expo-linking": "*",
+ "react-native-reanimated": "*",
+ "react-native-safe-area-context": "*",
+ "react-native-screens": "*"
+ },
+ "peerDependenciesMeta": {
+ "@react-navigation/drawer": {
+ "optional": true
+ },
+ "@testing-library/jest-native": {
+ "optional": true
+ },
+ "react-native-reanimated": {
+ "optional": true
+ }
+ }
+ },
+ "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-splash-screen": {
+ "version": "0.30.8",
+ "resolved": "https://registry.npmjs.org/expo-splash-screen/-/expo-splash-screen-0.30.8.tgz",
+ "integrity": "sha512-2eh+uA543brfeG5HILXmtNKA7E2/pfywKzNumzy3Ef6OtDjYy6zJUGNSbhnZRbVEjUZo3/QNRs0JRBfY80okZg==",
+ "license": "MIT",
+ "dependencies": {
+ "@expo/prebuild-config": "^9.0.5"
+ },
+ "peerDependencies": {
+ "expo": "*"
+ }
+ },
+ "node_modules/expo-status-bar": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/expo-status-bar/-/expo-status-bar-2.2.3.tgz",
+ "integrity": "sha512-+c8R3AESBoduunxTJ8353SqKAKpxL6DvcD8VKBuh81zzJyUUbfB4CVjr1GufSJEKsMzNPXZU+HJwXx7Xh7lx8Q==",
+ "license": "MIT",
+ "dependencies": {
+ "react-native-edge-to-edge": "1.6.0",
+ "react-native-is-edge-to-edge": "^1.1.6"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-native": "*"
+ }
+ },
+ "node_modules/expo-symbols": {
+ "version": "0.4.4",
+ "resolved": "https://registry.npmjs.org/expo-symbols/-/expo-symbols-0.4.4.tgz",
+ "integrity": "sha512-ZVTBdm48MUZsO/sRLrxezB37aazynn8pzpsIUwMqI7V5JtBPPb2gU7LRVPITRc0CqOA+OL01/PqFE3ifBUIP4A==",
+ "license": "MIT",
+ "dependencies": {
+ "sf-symbols-typescript": "^2.0.0"
+ },
+ "peerDependencies": {
+ "expo": "*"
+ }
+ },
+ "node_modules/expo-system-ui": {
+ "version": "5.0.7",
+ "resolved": "https://registry.npmjs.org/expo-system-ui/-/expo-system-ui-5.0.7.tgz",
+ "integrity": "sha512-ijSnSFA4VfuQc84N6WyCUNsKKTIyQb6QuC8q2zGvYC/sBXTMrOtZg0zrisQGzCRW+WhritQTiVqHlp3Ix9xDmQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@react-native/normalize-colors": "0.79.2",
+ "debug": "^4.3.2"
+ },
+ "peerDependencies": {
+ "expo": "*",
+ "react-native": "*",
+ "react-native-web": "*"
+ },
+ "peerDependenciesMeta": {
+ "react-native-web": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/expo-web-browser": {
+ "version": "14.1.6",
+ "resolved": "https://registry.npmjs.org/expo-web-browser/-/expo-web-browser-14.1.6.tgz",
+ "integrity": "sha512-/4P8eWqRyfXIMZna3acg320LXNA+P2cwyEVbjDX8vHnWU+UnOtyRKWy3XaAIyMPQ9hVjBNUQTh4MPvtnPRzakw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "expo": "*",
+ "react-native": "*"
+ }
+ },
+ "node_modules/exponential-backoff": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.2.tgz",
+ "integrity": "sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==",
+ "license": "Apache-2.0"
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.8"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-glob/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/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-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-uri": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz",
+ "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/fastq": {
+ "version": "1.19.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
+ "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
+ "dev": true,
+ "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/fdir": {
+ "version": "6.4.4",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
+ "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.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": "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/flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
+ "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "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/for-each": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
+ "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
+ "license": "MIT",
+ "dependencies": {
+ "is-callable": "^1.2.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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/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.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/function.prototype.name": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz",
+ "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "functions-have-names": "^1.2.3",
+ "hasown": "^2.0.2",
+ "is-callable": "^1.2.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/functions-have-names": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
+ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/get-stream": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+ "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+ "license": "MIT",
+ "dependencies": {
+ "pump": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/get-symbol-description": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz",
+ "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-tsconfig": {
+ "version": "4.10.1",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz",
+ "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "resolve-pkg-maps": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+ }
+ },
+ "node_modules/getenv": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/getenv/-/getenv-1.0.0.tgz",
+ "integrity": "sha512-7yetJWqbS9sbn0vIfliPsFgoXMKn/YMF+Wuiog97x+urnSRRRZ7xB+uVkwGKzRgq9CDFfMQnE9ruL5DHv9c6Xg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "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": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/glob/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/glob/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/globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/globalthis": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
+ "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-properties": "^1.2.1",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/got": {
+ "version": "11.8.6",
+ "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz",
+ "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==",
+ "license": "MIT",
+ "dependencies": {
+ "@sindresorhus/is": "^4.0.0",
+ "@szmarczak/http-timer": "^4.0.5",
+ "@types/cacheable-request": "^6.0.1",
+ "@types/responselike": "^1.0.0",
+ "cacheable-lookup": "^5.0.3",
+ "cacheable-request": "^7.0.2",
+ "decompress-response": "^6.0.0",
+ "http2-wrapper": "^1.0.0-beta.5.2",
+ "lowercase-keys": "^2.0.0",
+ "p-cancelable": "^2.0.0",
+ "responselike": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10.19.0"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/got?sponsor=1"
+ }
+ },
+ "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/graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/has-bigints": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz",
+ "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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/has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-proto": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz",
+ "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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.25.1",
+ "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz",
+ "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==",
+ "license": "MIT"
+ },
+ "node_modules/hermes-parser": {
+ "version": "0.25.1",
+ "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz",
+ "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==",
+ "license": "MIT",
+ "dependencies": {
+ "hermes-estree": "0.25.1"
+ }
+ },
+ "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/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/http-cache-semantics": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
+ "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==",
+ "license": "BSD-2-Clause"
+ },
+ "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/http2-wrapper": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz",
+ "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==",
+ "license": "MIT",
+ "dependencies": {
+ "quick-lru": "^5.1.1",
+ "resolve-alpn": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=10.19.0"
+ }
+ },
+ "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/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==",
+ "dev": true,
+ "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/import-fresh": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/import-fresh/node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "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/internal-slot": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz",
+ "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "hasown": "^2.0.2",
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "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-arguments": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz",
+ "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-array-buffer": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
+ "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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==",
+ "license": "MIT"
+ },
+ "node_modules/is-async-function": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz",
+ "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "async-function": "^1.0.0",
+ "call-bound": "^1.0.3",
+ "get-proto": "^1.0.1",
+ "has-tostringtag": "^1.0.2",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-bigint": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz",
+ "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-bigints": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-boolean-object": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz",
+ "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-bun-module": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz",
+ "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^7.7.1"
+ }
+ },
+ "node_modules/is-bun-module/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==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/is-callable": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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-data-view": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz",
+ "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "get-intrinsic": "^1.2.6",
+ "is-typed-array": "^1.1.13"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-date-object": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz",
+ "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-directory": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz",
+ "integrity": "sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-finalizationregistry": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz",
+ "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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-generator-function": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz",
+ "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "get-proto": "^1.0.0",
+ "has-tostringtag": "^1.0.2",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-map": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz",
+ "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-nan": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz",
+ "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.0",
+ "define-properties": "^1.1.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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-number-object": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz",
+ "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-plain-obj": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
+ "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-regex": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
+ "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-set": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz",
+ "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-shared-array-buffer": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz",
+ "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-string": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz",
+ "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-symbol": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz",
+ "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "has-symbols": "^1.1.0",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-typed-array": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
+ "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
+ "license": "MIT",
+ "dependencies": {
+ "which-typed-array": "^1.1.16"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakmap": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz",
+ "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakref": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz",
+ "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakset": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz",
+ "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+ "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/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/iterator.prototype": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz",
+ "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.6",
+ "get-proto": "^1.0.0",
+ "has-symbols": "^1.1.0",
+ "set-function-name": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "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-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/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/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/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/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/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/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": "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/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-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "license": "MIT"
+ },
+ "node_modules/json-parse-better-errors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
+ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
+ "license": "MIT"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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/jsx-ast-utils": {
+ "version": "3.3.5",
+ "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
+ "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-includes": "^3.1.6",
+ "array.prototype.flat": "^1.3.1",
+ "object.assign": "^4.1.4",
+ "object.values": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "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/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/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/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "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.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/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/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/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/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/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/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/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/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/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/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/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/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/lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.debounce": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
+ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isequal": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
+ "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
+ "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isobject": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz",
+ "integrity": "sha512-3/Qptq2vr7WeJbB4KHUSKlq8Pl7ASXi3UG6CMbBm8WRtXi8+GHm7mKaU3urfpSEzWe2wCIChs6/sdocUsTKJiA==",
+ "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-symbols/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/log-symbols/node_modules/chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/log-symbols/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/log-symbols/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/log-symbols/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/log-symbols/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/log-symbols/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/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/lowercase-keys": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
+ "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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/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/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/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "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-options": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/merge-options/-/merge-options-3.0.4.tgz",
+ "integrity": "sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==",
+ "license": "MIT",
+ "dependencies": {
+ "is-plain-obj": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/merge-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/metro": {
+ "version": "0.82.4",
+ "resolved": "https://registry.npmjs.org/metro/-/metro-0.82.4.tgz",
+ "integrity": "sha512-/gFmw3ux9CPG5WUmygY35hpyno28zi/7OUn6+OFfbweA8l0B+PPqXXLr0/T6cf5nclCcH0d22o+02fICaShVxw==",
+ "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.28.1",
+ "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.82.4",
+ "metro-cache": "0.82.4",
+ "metro-cache-key": "0.82.4",
+ "metro-config": "0.82.4",
+ "metro-core": "0.82.4",
+ "metro-file-map": "0.82.4",
+ "metro-resolver": "0.82.4",
+ "metro-runtime": "0.82.4",
+ "metro-source-map": "0.82.4",
+ "metro-symbolicate": "0.82.4",
+ "metro-transform-plugins": "0.82.4",
+ "metro-transform-worker": "0.82.4",
+ "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": ">=18.18"
+ }
+ },
+ "node_modules/metro-babel-transformer": {
+ "version": "0.82.4",
+ "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.82.4.tgz",
+ "integrity": "sha512-4juJahGRb1gmNbQq48lNinB6WFNfb6m0BQqi/RQibEltNiqTCxew/dBspI2EWA4xVCd3mQWGfw0TML4KurQZnQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.25.2",
+ "flow-enums-runtime": "^0.0.6",
+ "hermes-parser": "0.28.1",
+ "nullthrows": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=18.18"
+ }
+ },
+ "node_modules/metro-babel-transformer/node_modules/hermes-estree": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.28.1.tgz",
+ "integrity": "sha512-w3nxl/RGM7LBae0v8LH2o36+8VqwOZGv9rX1wyoWT6YaKZLqpJZ0YQ5P0LVr3tuRpf7vCx0iIG4i/VmBJejxTQ==",
+ "license": "MIT"
+ },
+ "node_modules/metro-babel-transformer/node_modules/hermes-parser": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.28.1.tgz",
+ "integrity": "sha512-nf8o+hE8g7UJWParnccljHumE9Vlq8F7MqIdeahl+4x0tvCUJYRrT0L7h0MMg/X9YJmkNwsfbaNNrzPtFXOscg==",
+ "license": "MIT",
+ "dependencies": {
+ "hermes-estree": "0.28.1"
+ }
+ },
+ "node_modules/metro-cache": {
+ "version": "0.82.4",
+ "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.82.4.tgz",
+ "integrity": "sha512-vX0ylSMGtORKiZ4G8uP6fgfPdDiCWvLZUGZ5zIblSGylOX6JYhvExl0Zg4UA9pix/SSQu5Pnp9vdODMFsNIxhw==",
+ "license": "MIT",
+ "dependencies": {
+ "exponential-backoff": "^3.1.1",
+ "flow-enums-runtime": "^0.0.6",
+ "https-proxy-agent": "^7.0.5",
+ "metro-core": "0.82.4"
+ },
+ "engines": {
+ "node": ">=18.18"
+ }
+ },
+ "node_modules/metro-cache-key": {
+ "version": "0.82.4",
+ "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.82.4.tgz",
+ "integrity": "sha512-2JCTqcpF+f2OghOpe/+x+JywfzDkrHdAqinPFWmK2ezNAU/qX0jBFaTETogPibFivxZJil37w9Yp6syX8rFUng==",
+ "license": "MIT",
+ "dependencies": {
+ "flow-enums-runtime": "^0.0.6"
+ },
+ "engines": {
+ "node": ">=18.18"
+ }
+ },
+ "node_modules/metro-config": {
+ "version": "0.82.4",
+ "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.82.4.tgz",
+ "integrity": "sha512-Ki3Wumr3hKHGDS7RrHsygmmRNc/PCJrvkLn0+BWWxmbOmOcMMJDSmSI+WRlT8jd5VPZFxIi4wg+sAt5yBXAK0g==",
+ "license": "MIT",
+ "dependencies": {
+ "connect": "^3.6.5",
+ "cosmiconfig": "^5.0.5",
+ "flow-enums-runtime": "^0.0.6",
+ "jest-validate": "^29.7.0",
+ "metro": "0.82.4",
+ "metro-cache": "0.82.4",
+ "metro-core": "0.82.4",
+ "metro-runtime": "0.82.4"
+ },
+ "engines": {
+ "node": ">=18.18"
+ }
+ },
+ "node_modules/metro-core": {
+ "version": "0.82.4",
+ "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.82.4.tgz",
+ "integrity": "sha512-Xo4ozbxPg2vfgJGCgXZ8sVhC2M0lhTqD+tsKO2q9aelq/dCjnnSb26xZKcQO80CQOQUL7e3QWB7pLFGPjZm31A==",
+ "license": "MIT",
+ "dependencies": {
+ "flow-enums-runtime": "^0.0.6",
+ "lodash.throttle": "^4.1.1",
+ "metro-resolver": "0.82.4"
+ },
+ "engines": {
+ "node": ">=18.18"
+ }
+ },
+ "node_modules/metro-file-map": {
+ "version": "0.82.4",
+ "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.82.4.tgz",
+ "integrity": "sha512-eO7HD1O3aeNsbEe6NBZvx1lLJUrxgyATjnDmb7bm4eyF6yWOQot9XVtxTDLNifECuvsZ4jzRiTInrbmIHkTdGA==",
+ "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": ">=18.18"
+ }
+ },
+ "node_modules/metro-minify-terser": {
+ "version": "0.82.4",
+ "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.82.4.tgz",
+ "integrity": "sha512-W79Mi6BUwWVaM8Mc5XepcqkG+TSsCyyo//dmTsgYfJcsmReQorRFodil3bbJInETvjzdnS1mCsUo9pllNjT1Hg==",
+ "license": "MIT",
+ "dependencies": {
+ "flow-enums-runtime": "^0.0.6",
+ "terser": "^5.15.0"
+ },
+ "engines": {
+ "node": ">=18.18"
+ }
+ },
+ "node_modules/metro-resolver": {
+ "version": "0.82.4",
+ "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.82.4.tgz",
+ "integrity": "sha512-uWoHzOBGQTPT5PjippB8rRT3iI9CTgFA9tRiLMzrseA5o7YAlgvfTdY9vFk2qyk3lW3aQfFKWkmqENryPRpu+Q==",
+ "license": "MIT",
+ "dependencies": {
+ "flow-enums-runtime": "^0.0.6"
+ },
+ "engines": {
+ "node": ">=18.18"
+ }
+ },
+ "node_modules/metro-runtime": {
+ "version": "0.82.4",
+ "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.82.4.tgz",
+ "integrity": "sha512-vVyFO7H+eLXRV2E7YAUYA7aMGBECGagqxmFvC2hmErS7oq90BbPVENfAHbUWq1vWH+MRiivoRxdxlN8gBoF/dw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.25.0",
+ "flow-enums-runtime": "^0.0.6"
+ },
+ "engines": {
+ "node": ">=18.18"
+ }
+ },
+ "node_modules/metro-source-map": {
+ "version": "0.82.4",
+ "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.82.4.tgz",
+ "integrity": "sha512-9jzDQJ0FPas1FuQFtwmBHsez2BfhFNufMowbOMeG3ZaFvzeziE8A0aJwILDS3U+V5039ssCQFiQeqDgENWvquA==",
+ "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.82.4",
+ "nullthrows": "^1.1.1",
+ "ob1": "0.82.4",
+ "source-map": "^0.5.6",
+ "vlq": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18.18"
+ }
+ },
+ "node_modules/metro-symbolicate": {
+ "version": "0.82.4",
+ "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.82.4.tgz",
+ "integrity": "sha512-LwEwAtdsx7z8rYjxjpLWxuFa2U0J6TS6ljlQM4WAATKa4uzV8unmnRuN2iNBWTmRqgNR77mzmI2vhwD4QSCo+w==",
+ "license": "MIT",
+ "dependencies": {
+ "flow-enums-runtime": "^0.0.6",
+ "invariant": "^2.2.4",
+ "metro-source-map": "0.82.4",
+ "nullthrows": "^1.1.1",
+ "source-map": "^0.5.6",
+ "vlq": "^1.0.0"
+ },
+ "bin": {
+ "metro-symbolicate": "src/index.js"
+ },
+ "engines": {
+ "node": ">=18.18"
+ }
+ },
+ "node_modules/metro-transform-plugins": {
+ "version": "0.82.4",
+ "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.82.4.tgz",
+ "integrity": "sha512-NoWQRPHupVpnDgYguiEcm7YwDhnqW02iWWQjO2O8NsNP09rEMSq99nPjARWfukN7+KDh6YjLvTIN20mj3dk9kw==",
+ "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": ">=18.18"
+ }
+ },
+ "node_modules/metro-transform-worker": {
+ "version": "0.82.4",
+ "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.82.4.tgz",
+ "integrity": "sha512-kPI7Ad/tdAnI9PY4T+2H0cdgGeSWWdiPRKuytI806UcN4VhFL6OmYa19/4abYVYF+Cd2jo57CDuwbaxRfmXDhw==",
+ "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.82.4",
+ "metro-babel-transformer": "0.82.4",
+ "metro-cache": "0.82.4",
+ "metro-cache-key": "0.82.4",
+ "metro-minify-terser": "0.82.4",
+ "metro-source-map": "0.82.4",
+ "metro-transform-plugins": "0.82.4",
+ "nullthrows": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=18.18"
+ }
+ },
+ "node_modules/metro/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/metro/node_modules/hermes-estree": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.28.1.tgz",
+ "integrity": "sha512-w3nxl/RGM7LBae0v8LH2o36+8VqwOZGv9rX1wyoWT6YaKZLqpJZ0YQ5P0LVr3tuRpf7vCx0iIG4i/VmBJejxTQ==",
+ "license": "MIT"
+ },
+ "node_modules/metro/node_modules/hermes-parser": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.28.1.tgz",
+ "integrity": "sha512-nf8o+hE8g7UJWParnccljHumE9Vlq8F7MqIdeahl+4x0tvCUJYRrT0L7h0MMg/X9YJmkNwsfbaNNrzPtFXOscg==",
+ "license": "MIT",
+ "dependencies": {
+ "hermes-estree": "0.28.1"
+ }
+ },
+ "node_modules/metro/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/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/micromatch/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/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.54.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
+ "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
+ "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/mime-types/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/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-response": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
+ "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "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/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.0.2",
+ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz",
+ "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==",
+ "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/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/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/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/napi-postinstall": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.2.4.tgz",
+ "integrity": "sha512-ZEzHJwBhZ8qQSbknHqYcdtQVr8zUgGyM/q6h6qAyhtyVMNrSgDhrC4disf03dYW0e+czXyLnZINnCTEkWy0eJg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "napi-postinstall": "lib/cli.js"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/napi-postinstall"
+ }
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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-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-releases": {
+ "version": "2.0.19",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
+ "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
+ "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/normalize-url": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
+ "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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-package-arg/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/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/ob1": {
+ "version": "0.82.4",
+ "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.82.4.tgz",
+ "integrity": "sha512-n9S8e4l5TvkrequEAMDidl4yXesruWTNTzVkeaHSGywoTOIwTzZzKw7Z670H3eaXDZui5MJXjWGNzYowVZIxCA==",
+ "license": "MIT",
+ "dependencies": {
+ "flow-enums-runtime": "^0.0.6"
+ },
+ "engines": {
+ "node": ">=18.18"
+ }
+ },
+ "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-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object-is": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz",
+ "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.assign": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz",
+ "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0",
+ "has-symbols": "^1.1.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.entries": {
+ "version": "1.1.9",
+ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz",
+ "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.fromentries": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz",
+ "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.groupby": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz",
+ "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.values": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz",
+ "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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.0.2",
+ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
+ "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
+ "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/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "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/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/ora/node_modules/chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/ora/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/ora/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/ora/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/ora/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/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/ora/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/own-keys": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz",
+ "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-intrinsic": "^1.2.6",
+ "object-keys": "^1.1.1",
+ "safe-push-apply": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/p-cancelable": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz",
+ "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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": "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/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-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/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/parse-json": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+ "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==",
+ "license": "MIT",
+ "dependencies": {
+ "error-ex": "^1.3.1",
+ "json-parse-better-errors": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/parse-png": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/parse-png/-/parse-png-2.1.0.tgz",
+ "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/paths-js": {
+ "version": "0.4.11",
+ "resolved": "https://registry.npmjs.org/paths-js/-/paths-js-0.4.11.tgz",
+ "integrity": "sha512-3mqcLomDBXOo7Fo+UlaenG6f71bk1ZezPQy2JCmYHy2W2k5VKpP+Jbin9H0bjXynelTbglCqdFhSEkeIkKTYUA==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=0.11.0"
+ }
+ },
+ "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": "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/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/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/point-in-polygon": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/point-in-polygon/-/point-in-polygon-1.1.0.tgz",
+ "integrity": "sha512-3ojrFwjnnw8Q9242TzgXuTD+eKiutbzyslcq1ydfu82Db2y+Ogbmyrkpv0Hgj31qwT3lbS9+QAAO/pIQM35XRw==",
+ "license": "MIT"
+ },
+ "node_modules/possible-typed-array-names": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
+ "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "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-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/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "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/pretty-format/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/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/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/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/prop-types": {
+ "version": "15.8.1",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
+ "node_modules/prop-types/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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/pump": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz",
+ "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==",
+ "license": "MIT",
+ "dependencies": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "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/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/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==",
+ "dev": true,
+ "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-lru": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
+ "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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/rc/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/react": {
+ "version": "19.0.0",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz",
+ "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-devtools-core": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-6.1.2.tgz",
+ "integrity": "sha512-ldFwzufLletzCikNJVYaxlxMLu7swJ3T2VrGfzXlMsVhZhPDKXA38DEROidaYZVgMAmQnIjymrmqto5pyfrwPA==",
+ "license": "MIT",
+ "dependencies": {
+ "shell-quote": "^1.6.1",
+ "ws": "^7"
+ }
+ },
+ "node_modules/react-devtools-core/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/react-dom": {
+ "version": "19.0.0",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz",
+ "integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==",
+ "license": "MIT",
+ "dependencies": {
+ "scheduler": "^0.25.0"
+ },
+ "peerDependencies": {
+ "react": "^19.0.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",
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "react": ">=17.0.0"
+ }
+ },
+ "node_modules/react-is": {
+ "version": "19.1.0",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.0.tgz",
+ "integrity": "sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg==",
+ "license": "MIT"
+ },
+ "node_modules/react-native": {
+ "version": "0.79.2",
+ "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.79.2.tgz",
+ "integrity": "sha512-AnGzb56JvU5YCL7cAwg10+ewDquzvmgrMddiBM0GAWLwQM/6DJfGd2ZKrMuKKehHerpDDZgG+EY64gk3x3dEkw==",
+ "license": "MIT",
+ "dependencies": {
+ "@jest/create-cache-key-function": "^29.7.0",
+ "@react-native/assets-registry": "0.79.2",
+ "@react-native/codegen": "0.79.2",
+ "@react-native/community-cli-plugin": "0.79.2",
+ "@react-native/gradle-plugin": "0.79.2",
+ "@react-native/js-polyfills": "0.79.2",
+ "@react-native/normalize-colors": "0.79.2",
+ "@react-native/virtualized-lists": "0.79.2",
+ "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.25.1",
+ "base64-js": "^1.5.1",
+ "chalk": "^4.0.0",
+ "commander": "^12.0.0",
+ "event-target-shim": "^5.0.1",
+ "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.82.0",
+ "metro-source-map": "^0.82.0",
+ "nullthrows": "^1.1.1",
+ "pretty-format": "^29.7.0",
+ "promise": "^8.3.0",
+ "react-devtools-core": "^6.1.1",
+ "react-refresh": "^0.14.0",
+ "regenerator-runtime": "^0.13.2",
+ "scheduler": "0.25.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": ">=18"
+ },
+ "peerDependencies": {
+ "@types/react": "^19.0.0",
+ "react": "^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-native-chart-kit": {
+ "version": "6.12.0",
+ "resolved": "https://registry.npmjs.org/react-native-chart-kit/-/react-native-chart-kit-6.12.0.tgz",
+ "integrity": "sha512-nZLGyCFzZ7zmX0KjYeeSV1HKuPhl1wOMlTAqa0JhlyW62qV/1ZPXHgT8o9s8mkFaGxdqbspOeuaa6I9jUQDgnA==",
+ "license": "MIT",
+ "dependencies": {
+ "lodash": "^4.17.13",
+ "paths-js": "^0.4.10",
+ "point-in-polygon": "^1.0.1"
+ },
+ "peerDependencies": {
+ "react": "> 16.7.0",
+ "react-native": ">= 0.50.0",
+ "react-native-svg": "> 6.4.1"
+ }
+ },
+ "node_modules/react-native-edge-to-edge": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/react-native-edge-to-edge/-/react-native-edge-to-edge-1.6.0.tgz",
+ "integrity": "sha512-2WCNdE3Qd6Fwg9+4BpbATUxCLcouF6YRY7K+J36KJ4l3y+tWN6XCqAC4DuoGblAAbb2sLkhEDp4FOlbOIot2Og==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "*",
+ "react-native": "*"
+ }
+ },
+ "node_modules/react-native-gesture-handler": {
+ "version": "2.24.0",
+ "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.24.0.tgz",
+ "integrity": "sha512-ZdWyOd1C8axKJHIfYxjJKCcxjWEpUtUWgTOVY2wynbiveSQDm8X/PDyAKXSer/GOtIpjudUbACOndZXCN3vHsw==",
+ "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.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-picker-select": {
+ "version": "9.3.1",
+ "resolved": "https://registry.npmjs.org/react-native-picker-select/-/react-native-picker-select-9.3.1.tgz",
+ "integrity": "sha512-o621HcsKJfJkpYeP/PZQiZTKbf8W7FT08niLFL0v1pGkIQyak5IfzfinV2t+/l1vktGwAH2Tt29LrP/Hc5fk3A==",
+ "license": "MIT",
+ "dependencies": {
+ "lodash.isequal": "^4.5.0",
+ "lodash.isobject": "^3.0.2"
+ },
+ "peerDependencies": {
+ "@react-native-picker/picker": "^2.4.0"
+ }
+ },
+ "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-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.10.0",
+ "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.10.0.tgz",
+ "integrity": "sha512-Tw21NGuXm3PbiUGtZd0AnXirUixaAbPXDjNR0baBH7/WJDaDTTELLcQ7QRXuqAWbmr/EVCrKj1348ei1KFIr8A==",
+ "license": "MIT",
+ "dependencies": {
+ "react-freeze": "^1.0.0",
+ "warn-once": "^0.1.0"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-native": "*"
+ }
+ },
+ "node_modules/react-native-svg": {
+ "version": "15.11.2",
+ "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.11.2.tgz",
+ "integrity": "sha512-+YfF72IbWQUKzCIydlijV1fLuBsQNGMT6Da2kFlo1sh+LE3BIm/2Q7AR1zAAR6L0BFLi1WaQPLfFUC9bNZpOmw==",
+ "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.20.0",
+ "resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.20.0.tgz",
+ "integrity": "sha512-OOSgrw+aON6R3hRosCau/xVxdLzbjEcsLysYedka0ZON4ZZe6n9xgeN9ZkoejhARM36oTlUgHIQqxGutEJ9Wxg==",
+ "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-webview": {
+ "version": "13.13.5",
+ "resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-13.13.5.tgz",
+ "integrity": "sha512-MfC2B+woL4Hlj2WCzcb1USySKk+SteXnUKmKktOk/H/AQy5+LuVdkPKm8SknJ0/RxaxhZ48WBoTRGaqgR137hw==",
+ "license": "MIT",
+ "dependencies": {
+ "escape-string-regexp": "^4.0.0",
+ "invariant": "2.2.4"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-native": "*"
+ }
+ },
+ "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/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/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/reflect.getprototypeof": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
+ "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.9",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.7",
+ "get-proto": "^1.0.1",
+ "which-builtin-type": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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.0",
+ "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz",
+ "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==",
+ "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/regexp.prototype.flags": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
+ "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-errors": "^1.3.0",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "set-function-name": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/regexpu-core": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz",
+ "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==",
+ "license": "MIT",
+ "dependencies": {
+ "regenerate": "^1.4.2",
+ "regenerate-unicode-properties": "^10.2.0",
+ "regjsgen": "^0.8.0",
+ "regjsparser": "^0.12.0",
+ "unicode-match-property-ecmascript": "^2.0.0",
+ "unicode-match-property-value-ecmascript": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "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.12.0",
+ "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz",
+ "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "jsesc": "~3.0.2"
+ },
+ "bin": {
+ "regjsparser": "bin/parser"
+ }
+ },
+ "node_modules/regjsparser/node_modules/jsesc": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz",
+ "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==",
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "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.10",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
+ "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
+ "license": "MIT",
+ "dependencies": {
+ "is-core-module": "^2.16.0",
+ "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-alpn": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
+ "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==",
+ "license": "MIT"
+ },
+ "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-pkg-maps": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+ }
+ },
+ "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/responselike": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz",
+ "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==",
+ "license": "MIT",
+ "dependencies": {
+ "lowercase-keys": "^2.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "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/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/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==",
+ "dev": true,
+ "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-array-concat": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz",
+ "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "get-intrinsic": "^1.2.6",
+ "has-symbols": "^1.1.0",
+ "isarray": "^2.0.5"
+ },
+ "engines": {
+ "node": ">=0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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-push-apply": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz",
+ "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "isarray": "^2.0.5"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/safe-regex-test": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
+ "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "is-regex": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/sax": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz",
+ "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==",
+ "license": "ISC"
+ },
+ "node_modules/scheduler": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz",
+ "integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==",
+ "license": "MIT"
+ },
+ "node_modules/schema-utils": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz",
+ "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/json-schema": "^7.0.9",
+ "ajv": "^8.9.0",
+ "ajv-formats": "^2.1.1",
+ "ajv-keywords": "^5.1.0"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/schema-utils/node_modules/ajv": {
+ "version": "8.17.1",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
+ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "fast-uri": "^3.0.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/schema-utils/node_modules/ajv-keywords": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
+ "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3"
+ },
+ "peerDependencies": {
+ "ajv": "^8.8.2"
+ }
+ },
+ "node_modules/schema-utils/node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "license": "MIT"
+ },
+ "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/send": {
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.19.1.tgz",
+ "integrity": "sha512-p4rRk4f23ynFEfcD9LA0xRYngj+IyGiEYyqqOak8kaN0TvNmuxC2dcVeBn62GpCeR2CpWqyHCNScTP91QbAVFg==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "1.2.0",
+ "encodeurl": "~2.0.0",
+ "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/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/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/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/serve-static/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/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/serve-static/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/serve-static/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/serve-static/node_modules/send/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/serve-static/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/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-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/set-function-name": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
+ "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "functions-have-names": "^1.2.3",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/set-proto": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz",
+ "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "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.2",
+ "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz",
+ "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+ "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3",
+ "side-channel-list": "^1.0.0",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
+ "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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-plist/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/simple-swizzle": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
+ "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
+ "license": "MIT",
+ "dependencies": {
+ "is-arrayish": "^0.3.1"
+ }
+ },
+ "node_modules/simple-swizzle/node_modules/is-arrayish": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
+ "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
+ "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/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/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/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/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/stable-hash": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz",
+ "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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/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-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/string.prototype.matchall": {
+ "version": "4.0.12",
+ "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz",
+ "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.6",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.6",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "internal-slot": "^1.1.0",
+ "regexp.prototype.flags": "^1.5.3",
+ "set-function-name": "^2.0.2",
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.repeat": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz",
+ "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5"
+ }
+ },
+ "node_modules/string.prototype.trim": {
+ "version": "1.2.10",
+ "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz",
+ "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "define-data-property": "^1.1.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-object-atoms": "^1.0.0",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimend": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz",
+ "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimstart": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz",
+ "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "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.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
+ "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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/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": "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-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-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/tar": {
+ "version": "7.4.3",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz",
+ "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==",
+ "license": "ISC",
+ "dependencies": {
+ "@isaacs/fs-minipass": "^4.0.0",
+ "chownr": "^3.0.0",
+ "minipass": "^7.1.2",
+ "minizlib": "^3.0.1",
+ "mkdirp": "^3.0.1",
+ "yallist": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tar/node_modules/mkdirp": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz",
+ "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==",
+ "license": "MIT",
+ "bin": {
+ "mkdirp": "dist/cjs/src/bin.js"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "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.39.2",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.2.tgz",
+ "integrity": "sha512-yEPUmWve+VA78bI71BW70Dh0TuV4HHd+I5SHOAfS1+QBOmvmCiiffgjR8ryyEd3KIfvPGFqoADt8LdQ6XpXIvg==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@jridgewell/source-map": "^0.3.3",
+ "acorn": "^8.14.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/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/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/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/tinyglobby": {
+ "version": "0.2.14",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
+ "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/tinyglobby/node_modules/picomatch": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
+ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "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-api-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
+ "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.12"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4"
+ }
+ },
+ "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/tsconfig-paths": {
+ "version": "3.15.0",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
+ "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/json5": "^0.0.29",
+ "json5": "^1.0.2",
+ "minimist": "^1.2.6",
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "node_modules/tsconfig-paths/node_modules/json5": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
+ "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minimist": "^1.2.0"
+ },
+ "bin": {
+ "json5": "lib/cli.js"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "0BSD",
+ "optional": true
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "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/typed-array-buffer": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
+ "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/typed-array-byte-length": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz",
+ "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "for-each": "^0.3.3",
+ "gopd": "^1.2.0",
+ "has-proto": "^1.2.0",
+ "is-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-byte-offset": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz",
+ "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "for-each": "^0.3.3",
+ "gopd": "^1.2.0",
+ "has-proto": "^1.2.0",
+ "is-typed-array": "^1.1.15",
+ "reflect.getprototypeof": "^1.0.9"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-length": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz",
+ "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "is-typed-array": "^1.1.13",
+ "possible-typed-array-names": "^1.0.0",
+ "reflect.getprototypeof": "^1.0.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.8.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
+ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/ua-parser-js": {
+ "version": "1.0.40",
+ "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.40.tgz",
+ "integrity": "sha512-z6PJ8Lml+v3ichVojCiB8toQJBuwR42ySM4ezjXIqXK3M0HczmKQ3LF4rhU55PfD99KEEXQG6yb7iOMyvYuHew==",
+ "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/unbox-primitive": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",
+ "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-bigints": "^1.0.2",
+ "has-symbols": "^1.1.0",
+ "which-boxed-primitive": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/undici": {
+ "version": "6.21.3",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz",
+ "integrity": "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "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.0",
+ "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz",
+ "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/unicode-property-aliases-ecmascript": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz",
+ "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "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/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/unrs-resolver": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.7.2.tgz",
+ "integrity": "sha512-BBKpaylOW8KbHsu378Zky/dGh4ckT/4NW/0SHRABdqRLcQJ2dAOjDo9g97p04sWflm0kqPqpUatxReNV/dqI5A==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "napi-postinstall": "^0.2.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/JounQin"
+ },
+ "optionalDependencies": {
+ "@unrs/resolver-binding-darwin-arm64": "1.7.2",
+ "@unrs/resolver-binding-darwin-x64": "1.7.2",
+ "@unrs/resolver-binding-freebsd-x64": "1.7.2",
+ "@unrs/resolver-binding-linux-arm-gnueabihf": "1.7.2",
+ "@unrs/resolver-binding-linux-arm-musleabihf": "1.7.2",
+ "@unrs/resolver-binding-linux-arm64-gnu": "1.7.2",
+ "@unrs/resolver-binding-linux-arm64-musl": "1.7.2",
+ "@unrs/resolver-binding-linux-ppc64-gnu": "1.7.2",
+ "@unrs/resolver-binding-linux-riscv64-gnu": "1.7.2",
+ "@unrs/resolver-binding-linux-riscv64-musl": "1.7.2",
+ "@unrs/resolver-binding-linux-s390x-gnu": "1.7.2",
+ "@unrs/resolver-binding-linux-x64-gnu": "1.7.2",
+ "@unrs/resolver-binding-linux-x64-musl": "1.7.2",
+ "@unrs/resolver-binding-wasm32-wasi": "1.7.2",
+ "@unrs/resolver-binding-win32-arm64-msvc": "1.7.2",
+ "@unrs/resolver-binding-win32-ia32-msvc": "1.7.2",
+ "@unrs/resolver-binding-win32-x64-msvc": "1.7.2"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
+ "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==",
+ "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/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/use-latest-callback": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.2.3.tgz",
+ "integrity": "sha512-7vI3fBuyRcP91pazVboc4qu+6ZqM8izPWX9k7cRnT8hbD5svslcknsh3S9BUhaK11OmgTV4oWZZVSeQAiV53SQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": ">=16.8"
+ }
+ },
+ "node_modules/use-sync-external-store": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz",
+ "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/util": {
+ "version": "0.12.5",
+ "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz",
+ "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==",
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "is-arguments": "^1.0.4",
+ "is-generator-function": "^1.0.7",
+ "is-typed-array": "^1.1.3",
+ "which-typed-array": "^1.1.2"
+ }
+ },
+ "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": "3.4.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+ "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
+ "license": "MIT",
+ "bin": {
+ "uuid": "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/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/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/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/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-without-unicode/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/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/which-boxed-primitive": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz",
+ "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-bigint": "^1.1.0",
+ "is-boolean-object": "^1.2.1",
+ "is-number-object": "^1.1.1",
+ "is-string": "^1.1.1",
+ "is-symbol": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-builtin-type": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz",
+ "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "function.prototype.name": "^1.1.6",
+ "has-tostringtag": "^1.0.2",
+ "is-async-function": "^2.0.0",
+ "is-date-object": "^1.1.0",
+ "is-finalizationregistry": "^1.1.0",
+ "is-generator-function": "^1.0.10",
+ "is-regex": "^1.2.1",
+ "is-weakref": "^1.0.2",
+ "isarray": "^2.0.5",
+ "which-boxed-primitive": "^1.1.0",
+ "which-collection": "^1.0.2",
+ "which-typed-array": "^1.1.16"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-collection": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz",
+ "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-map": "^2.0.3",
+ "is-set": "^2.0.3",
+ "is-weakmap": "^2.0.2",
+ "is-weakset": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-typed-array": {
+ "version": "1.1.19",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz",
+ "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==",
+ "license": "MIT",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "for-each": "^0.3.5",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "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/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/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/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/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/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/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": "8.18.2",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz",
+ "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==",
+ "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/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/xcode/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/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": "1.10.2",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
+ "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">= 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"
+ }
+ }
+ }
+}
diff --git a/KonditionExpo/package.json b/KonditionExpo/package.json
new file mode 100644
index 0000000000..18fd4706e2
--- /dev/null
+++ b/KonditionExpo/package.json
@@ -0,0 +1,60 @@
+{
+ "name": "konditionexpo",
+ "main": "expo-router/entry",
+ "version": "1.0.0",
+ "scripts": {
+ "start": "expo start",
+ "reset-project": "node ./scripts/reset-project.js",
+ "android": "expo start --android",
+ "ios": "expo start --ios",
+ "web": "expo start --web",
+ "lint": "expo lint"
+ },
+ "dependencies": {
+ "@expo/ngrok": "^4.1.3",
+ "@expo/vector-icons": "^14.1.0",
+ "@react-native-async-storage/async-storage": "2.1.2",
+ "@react-native-community/datetimepicker": "^8.4.1",
+ "@react-native-picker/picker": "^2.11.0",
+ "@react-navigation/bottom-tabs": "^7.3.10",
+ "@react-navigation/elements": "^2.3.8",
+ "@react-navigation/native": "^7.1.6",
+ "d3-shape": "^3.2.0",
+ "expo": "^53.0.9",
+ "expo-blur": "~14.1.4",
+ "expo-constants": "~17.1.6",
+ "expo-device": "~7.1.4",
+ "expo-font": "~13.3.1",
+ "expo-haptics": "~14.1.4",
+ "expo-image": "~2.1.7",
+ "expo-linking": "~7.1.5",
+ "expo-notifications": "~0.31.2",
+ "expo-permissions": "^14.4.0",
+ "expo-router": "~5.0.7",
+ "expo-splash-screen": "~0.30.8",
+ "expo-status-bar": "~2.2.3",
+ "expo-symbols": "~0.4.4",
+ "expo-system-ui": "~5.0.7",
+ "expo-web-browser": "~14.1.6",
+ "react": "19.0.0",
+ "react-dom": "19.0.0",
+ "react-native": "0.79.2",
+ "react-native-chart-kit": "^6.12.0",
+ "react-native-gesture-handler": "~2.24.0",
+ "react-native-picker-select": "^9.3.1",
+ "react-native-reanimated": "~3.17.4",
+ "react-native-safe-area-context": "5.4.0",
+ "react-native-screens": "~4.10.0",
+ "react-native-svg": "15.11.2",
+ "react-native-web": "~0.20.0",
+ "react-native-webview": "13.13.5"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.25.2",
+ "@types/react": "~19.0.10",
+ "eslint": "^9.25.0",
+ "eslint-config-expo": "~9.2.0",
+ "typescript": "~5.8.3"
+ },
+ "private": true
+}
diff --git a/KonditionExpo/public/manifest.json b/KonditionExpo/public/manifest.json
new file mode 100644
index 0000000000..b5d3f10e1a
--- /dev/null
+++ b/KonditionExpo/public/manifest.json
@@ -0,0 +1,26 @@
+{
+ "name": "Kondition",
+ "short_name": "Kondition",
+ "description": "Your personal fitness tracking app",
+ "start_url": "/",
+ "display": "standalone",
+ "background_color": "#ffffff",
+ "theme_color": "#007bff",
+ "orientation": "portrait",
+ "icons": [
+ {
+ "src": "/assets/images/icon-192.png",
+ "sizes": "192x192",
+ "type": "image/png",
+ "purpose": "any maskable"
+ },
+ {
+ "src": "/assets/images/icon-512.png",
+ "sizes": "512x512",
+ "type": "image/png",
+ "purpose": "any maskable"
+ }
+ ],
+ "gcm_sender_id": "103953800507",
+ "vapid_public_key": "BHIYUSgOOx7MC1EXPT5HSZ6snrLWCe4uV2tloytm3uB8rZtTiNl5234SLOWPzdBa8TcIr1CubVkh_8jXipesK8E"
+}
\ No newline at end of file
diff --git a/KonditionExpo/public/service-worker.js b/KonditionExpo/public/service-worker.js
new file mode 100644
index 0000000000..6ccb3a5044
--- /dev/null
+++ b/KonditionExpo/public/service-worker.js
@@ -0,0 +1,17 @@
+self.addEventListener('push', function (event) {
+ let data = {};
+ try {
+ data = event.data.json();
+ } catch (e) {
+ data = { title: 'Notification', body: 'You have a new message.' };
+ }
+
+ const title = data.title || 'New Notification';
+ const options = {
+ body: data.body || '',
+ icon: '/assets/images/icon.png',
+ };
+
+ event.waitUntil(self.registration.showNotification(title, options));
+});
+
diff --git a/KonditionExpo/scripts/auth.ts b/KonditionExpo/scripts/auth.ts
new file mode 100644
index 0000000000..f78ae9cd67
--- /dev/null
+++ b/KonditionExpo/scripts/auth.ts
@@ -0,0 +1,12 @@
+import AsyncStorage from '@react-native-async-storage/async-storage';
+
+export const getAccessToken = async (): Promise => {
+ try {
+ const token = await AsyncStorage.getItem('access_token');
+ console.log("Token from AsyncStorage:", token);
+ return token;
+ } catch (err) {
+ console.error("Error getting token", err);
+ return null;
+ }
+};
diff --git a/KonditionExpo/scripts/reset-project.js b/KonditionExpo/scripts/reset-project.js
new file mode 100755
index 0000000000..51dff15acb
--- /dev/null
+++ b/KonditionExpo/scripts/reset-project.js
@@ -0,0 +1,112 @@
+#!/usr/bin/env node
+
+/**
+ * This script is used to reset the project to a blank state.
+ * It deletes or moves the /app, /components, /hooks, /scripts, and /constants directories to /app-example based on user input and creates a new /app directory with an index.tsx and _layout.tsx file.
+ * You can remove the `reset-project` script from package.json and safely delete this file after running it.
+ */
+
+const fs = require("fs");
+const path = require("path");
+const readline = require("readline");
+
+const root = process.cwd();
+const oldDirs = ["app", "components", "hooks", "constants", "scripts"];
+const exampleDir = "app-example";
+const newAppDir = "app";
+const exampleDirPath = path.join(root, exampleDir);
+
+const indexContent = `import { Text, View } from "react-native";
+
+export default function Index() {
+ return (
+
+ Edit app/index.tsx to edit this screen.
+
+ );
+}
+`;
+
+const layoutContent = `import { Stack } from "expo-router";
+
+export default function RootLayout() {
+ return ;
+}
+`;
+
+const rl = readline.createInterface({
+ input: process.stdin,
+ output: process.stdout,
+});
+
+const moveDirectories = async (userInput) => {
+ try {
+ if (userInput === "y") {
+ // Create the app-example directory
+ await fs.promises.mkdir(exampleDirPath, { recursive: true });
+ console.log(`π /${exampleDir} directory created.`);
+ }
+
+ // Move old directories to new app-example directory or delete them
+ for (const dir of oldDirs) {
+ const oldDirPath = path.join(root, dir);
+ if (fs.existsSync(oldDirPath)) {
+ if (userInput === "y") {
+ const newDirPath = path.join(root, exampleDir, dir);
+ await fs.promises.rename(oldDirPath, newDirPath);
+ console.log(`β‘οΈ /${dir} moved to /${exampleDir}/${dir}.`);
+ } else {
+ await fs.promises.rm(oldDirPath, { recursive: true, force: true });
+ console.log(`β /${dir} deleted.`);
+ }
+ } else {
+ console.log(`β‘οΈ /${dir} does not exist, skipping.`);
+ }
+ }
+
+ // Create new /app directory
+ const newAppDirPath = path.join(root, newAppDir);
+ await fs.promises.mkdir(newAppDirPath, { recursive: true });
+ console.log("\nπ New /app directory created.");
+
+ // Create index.tsx
+ const indexPath = path.join(newAppDirPath, "index.tsx");
+ await fs.promises.writeFile(indexPath, indexContent);
+ console.log("π app/index.tsx created.");
+
+ // Create _layout.tsx
+ const layoutPath = path.join(newAppDirPath, "_layout.tsx");
+ await fs.promises.writeFile(layoutPath, layoutContent);
+ console.log("π app/_layout.tsx created.");
+
+ console.log("\nβ
Project reset complete. Next steps:");
+ console.log(
+ `1. Run \`npx expo start\` to start a development server.\n2. Edit app/index.tsx to edit the main screen.${
+ userInput === "y"
+ ? `\n3. Delete the /${exampleDir} directory when you're done referencing it.`
+ : ""
+ }`
+ );
+ } catch (error) {
+ console.error(`β Error during script execution: ${error.message}`);
+ }
+};
+
+rl.question(
+ "Do you want to move existing files to /app-example instead of deleting them? (Y/n): ",
+ (answer) => {
+ const userInput = answer.trim().toLowerCase() || "y";
+ if (userInput === "y" || userInput === "n") {
+ moveDirectories(userInput).finally(() => rl.close());
+ } else {
+ console.log("β Invalid input. Please enter 'Y' or 'N'.");
+ rl.close();
+ }
+ }
+);
diff --git a/KonditionExpo/services/api.ts b/KonditionExpo/services/api.ts
new file mode 100644
index 0000000000..ba11a05835
--- /dev/null
+++ b/KonditionExpo/services/api.ts
@@ -0,0 +1,455 @@
+import AsyncStorage from '@react-native-async-storage/async-storage';
+import { API_CONFIG, getApiBaseUrl } from '../config/api';
+
+// Types for API requests and responses
+export interface LoginRequest {
+ username: string; // FastAPI OAuth2 expects 'username' field for email
+ password: string;
+}
+
+export interface SignupRequest {
+ email: string;
+ password: string;
+ full_name?: string;
+}
+
+export interface ProfileUpdateRequest {
+ full_name?: string;
+ email?: string;
+ gender?: string;
+ date_of_birth?: string;
+ weight?: number;
+ height?: number;
+}
+
+// Workout-related types
+export interface WorkoutResponse {
+ id: string;
+ user_id: string;
+ name: string;
+ description?: string;
+ scheduled_date?: string;
+ completed_date?: string;
+ duration_minutes?: number;
+ is_completed: boolean;
+ created_at: string;
+ updated_at?: string;
+ exercises?: ExerciseResponse[];
+ exercise_count?: number;
+}
+
+export interface ExerciseResponse {
+ id: string;
+ workout_id: string;
+ name: string;
+ description?: string;
+ category: string;
+ sets?: number;
+ reps?: number;
+ weight?: number;
+ created_at: string;
+ updated_at?: string;
+}
+
+export interface WorkoutsPublicResponse {
+ data: WorkoutResponse[];
+ count: number;
+}
+
+export interface AuthResponse {
+ access_token: string;
+ token_type: string;
+}
+
+export interface UserProfile {
+ id: string;
+ email: string;
+ full_name?: string;
+ is_active: boolean;
+ is_superuser: boolean;
+ // Profile fields
+ gender?: string;
+ date_of_birth?: string;
+ weight?: number;
+ height?: number;
+}
+
+// Social-related types
+export interface UserSearchResult {
+ id: string;
+ email: string;
+ full_name?: string;
+ is_active: boolean;
+ is_superuser: boolean;
+ gender?: string;
+ date_of_birth?: string;
+ weight?: number;
+ height?: number;
+ follower_count: number;
+ following_count: number;
+ is_following: boolean;
+}
+
+export interface UserSearchResultsResponse {
+ data: UserSearchResult[];
+ count: number;
+}
+
+export interface UsersPublicResponse {
+ data: UserProfile[];
+ count: number;
+}
+
+export interface UserPublicExtended extends UserProfile {
+ follower_count: number;
+ following_count: number;
+}
+
+export interface FollowStatusResponse {
+ is_following: boolean;
+}
+
+// Workout Post types
+export interface WorkoutPostResponse {
+ id: string;
+ user_id: string;
+ title: string;
+ description?: string;
+ workout_type: string;
+ duration_minutes: number;
+ calories_burned?: number;
+ is_public: boolean;
+ created_at: string;
+ updated_at?: string;
+ user_full_name?: string;
+ is_mutual_follow?: boolean;
+}
+
+export interface WorkoutPostsResponse {
+ data: WorkoutPostResponse[];
+ count: number;
+}
+
+export interface WorkoutPostCreateRequest {
+ title: string;
+ description?: string;
+ workout_type: string;
+ duration_minutes: number;
+ calories_burned?: number;
+ is_public?: boolean;
+}
+
+export interface WorkoutPostUpdateRequest {
+ title?: string;
+ description?: string;
+ workout_type?: string;
+ duration_minutes?: number;
+ calories_burned?: number;
+ is_public?: boolean;
+}
+
+export interface ApiError {
+ detail: string;
+}
+
+// API Service Class
+class ApiService {
+ private baseUrl: string;
+
+ constructor(baseUrl: string = getApiBaseUrl()) {
+ this.baseUrl = baseUrl;
+ }
+
+ // Helper method to get stored token
+ private async getToken(): Promise {
+ try {
+ return await AsyncStorage.getItem(API_CONFIG.TOKEN_KEY);
+ } catch (error) {
+ console.error('Error getting token:', error);
+ return null;
+ }
+ }
+
+ // Helper method to make authenticated requests
+ private async makeRequest(
+ endpoint: string,
+ options: RequestInit = {}
+ ): Promise {
+ const url = `${this.baseUrl}${endpoint}`;
+ const token = await this.getToken();
+
+ const headers: Record = {
+ 'Content-Type': 'application/json',
+ ...(options.headers as Record),
+ };
+
+ // Add authorization header if token exists
+ if (token) {
+ headers.Authorization = `Bearer ${token}`;
+ }
+
+ const config: RequestInit = {
+ ...options,
+ headers,
+ };
+
+ try {
+ console.log(`Making request to: ${url}`);
+ const response = await fetch(url, config);
+
+ if (!response.ok) {
+ let errorMessage = `HTTP error! status: ${response.status}`;
+ try {
+ const errorData: ApiError = await response.json();
+ errorMessage = errorData.detail || errorMessage;
+ } catch {
+ // If we can't parse error response, use status message
+ }
+ throw new Error(errorMessage);
+ }
+
+ return await response.json();
+ } catch (error) {
+ console.error(`Request failed for ${url}:`, error);
+
+ if (error instanceof TypeError && error.message.includes('Network request failed')) {
+ throw new Error('Network connection failed. Please check your internet connection and API server.');
+ }
+
+ if (error instanceof Error) {
+ throw error;
+ }
+
+ throw new Error('Network request failed');
+ }
+ }
+
+ // Authentication Methods
+ async login(email: string, password: string): Promise {
+ // FastAPI OAuth2 expects form data with 'username' field
+ const formData = new FormData();
+ formData.append('username', email);
+ formData.append('password', password);
+
+ const response = await fetch(`${this.baseUrl}${API_CONFIG.ENDPOINTS.LOGIN}`, {
+ method: 'POST',
+ body: formData,
+ });
+
+ if (!response.ok) {
+ const errorData: ApiError = await response.json();
+ throw new Error(errorData.detail || 'Login failed');
+ }
+
+ return await response.json();
+ }
+
+ async signup(userData: SignupRequest): Promise {
+ return this.makeRequest(API_CONFIG.ENDPOINTS.SIGNUP, {
+ method: 'POST',
+ body: JSON.stringify(userData),
+ });
+ }
+
+ async getCurrentUser(): Promise {
+ return this.makeRequest(API_CONFIG.ENDPOINTS.USER_PROFILE);
+ }
+
+ async testToken(): Promise {
+ return this.makeRequest(API_CONFIG.ENDPOINTS.TEST_TOKEN, {
+ method: 'POST',
+ });
+ }
+
+ async updateProfile(profileData: ProfileUpdateRequest): Promise {
+ return this.makeRequest(API_CONFIG.ENDPOINTS.USER_PROFILE, {
+ method: 'PATCH',
+ body: JSON.stringify(profileData),
+ });
+ }
+
+ // Workout Methods
+ async getWorkouts(): Promise {
+ const response = await this.makeRequest(API_CONFIG.ENDPOINTS.WORKOUTS);
+ return response.data;
+ }
+
+ async getWorkoutById(id: string): Promise {
+ return this.makeRequest(API_CONFIG.ENDPOINTS.WORKOUT_BY_ID(id));
+ }
+
+ // Token Management
+ async storeToken(token: string): Promise {
+ try {
+ await AsyncStorage.setItem(API_CONFIG.TOKEN_KEY, token);
+ } catch (error) {
+ console.error('Error storing token:', error);
+ throw new Error('Failed to store authentication token');
+ }
+ }
+
+ async removeToken(): Promise {
+ try {
+ await AsyncStorage.removeItem(API_CONFIG.TOKEN_KEY);
+ } catch (error) {
+ console.error('Error removing token:', error);
+ throw new Error('Failed to remove authentication token');
+ }
+ }
+
+ async hasValidToken(): Promise {
+ try {
+ const token = await this.getToken();
+ if (!token) return false;
+
+ // Test if token is still valid
+ await this.testToken();
+ return true;
+ } catch (error) {
+ // Token is invalid or expired
+ await this.removeToken();
+ return false;
+ }
+ }
+
+ // Social Methods
+ async searchUsers(query: string, skip: number = 0, limit: number = 20): Promise {
+ const params = new URLSearchParams({
+ q: query,
+ skip: skip.toString(),
+ limit: limit.toString(),
+ });
+
+ return this.makeRequest(
+ `${API_CONFIG.ENDPOINTS.SEARCH_USERS}?${params.toString()}`
+ );
+ }
+
+ async followUser(userId: string): Promise<{ message: string }> {
+ return this.makeRequest<{ message: string }>(API_CONFIG.ENDPOINTS.FOLLOW_USER(userId), {
+ method: 'POST',
+ });
+ }
+
+ async unfollowUser(userId: string): Promise<{ message: string }> {
+ return this.makeRequest<{ message: string }>(API_CONFIG.ENDPOINTS.UNFOLLOW_USER(userId), {
+ method: 'DELETE',
+ });
+ }
+
+ async getFollowers(skip: number = 0, limit: number = 100): Promise {
+ const params = new URLSearchParams({
+ skip: skip.toString(),
+ limit: limit.toString(),
+ });
+
+ return this.makeRequest(
+ `${API_CONFIG.ENDPOINTS.GET_FOLLOWERS}?${params.toString()}`
+ );
+ }
+
+ async getFollowing(skip: number = 0, limit: number = 100): Promise {
+ const params = new URLSearchParams({
+ skip: skip.toString(),
+ limit: limit.toString(),
+ });
+
+ return this.makeRequest(
+ `${API_CONFIG.ENDPOINTS.GET_FOLLOWING}?${params.toString()}`
+ );
+ }
+
+ async isFollowing(userId: string): Promise {
+ return this.makeRequest(API_CONFIG.ENDPOINTS.IS_FOLLOWING(userId));
+ }
+
+ async getUserProfileExtended(userId: string): Promise {
+ return this.makeRequest(API_CONFIG.ENDPOINTS.USER_PROFILE_EXTENDED(userId));
+ }
+
+ // Feed Methods
+ async getFeed(feedType: 'personal' | 'public' | 'combined' = 'personal', skip: number = 0, limit: number = 20): Promise {
+ const params = new URLSearchParams({
+ feed_type: feedType,
+ skip: skip.toString(),
+ limit: limit.toString(),
+ });
+
+ return this.makeRequest(
+ `/social/feed?${params.toString()}`
+ );
+ }
+
+ async getPublicFeed(skip: number = 0, limit: number = 20): Promise {
+ const params = new URLSearchParams({
+ skip: skip.toString(),
+ limit: limit.toString(),
+ });
+
+ return this.makeRequest(
+ `/social/feed/public?${params.toString()}`
+ );
+ }
+
+ async getPersonalFeed(skip: number = 0, limit: number = 20): Promise {
+ const params = new URLSearchParams({
+ skip: skip.toString(),
+ limit: limit.toString(),
+ });
+
+ return this.makeRequest(
+ `/social/feed/personal?${params.toString()}`
+ );
+ }
+
+ // Workout Post Methods
+ async createWorkoutPost(postData: WorkoutPostCreateRequest): Promise {
+ return this.makeRequest('/social/workout-posts', {
+ method: 'POST',
+ body: JSON.stringify(postData),
+ });
+ }
+
+ async getMyWorkoutPosts(skip: number = 0, limit: number = 20): Promise {
+ const params = new URLSearchParams({
+ skip: skip.toString(),
+ limit: limit.toString(),
+ });
+
+ return this.makeRequest(
+ `/social/workout-posts?${params.toString()}`
+ );
+ }
+
+ async getUserWorkoutPosts(userId: string, skip: number = 0, limit: number = 20): Promise {
+ const params = new URLSearchParams({
+ skip: skip.toString(),
+ limit: limit.toString(),
+ });
+
+ return this.makeRequest(
+ `/social/user/${userId}/workout-posts?${params.toString()}`
+ );
+ }
+
+ async getWorkoutPost(postId: string): Promise {
+ return this.makeRequest(`/social/workout-posts/${postId}`);
+ }
+
+ async updateWorkoutPost(postId: string, postData: WorkoutPostUpdateRequest): Promise {
+ return this.makeRequest(`/social/workout-posts/${postId}`, {
+ method: 'PUT',
+ body: JSON.stringify(postData),
+ });
+ }
+
+ async deleteWorkoutPost(postId: string): Promise<{ message: string }> {
+ return this.makeRequest<{ message: string }>(`/social/workout-posts/${postId}`, {
+ method: 'DELETE',
+ });
+ }
+}
+
+// Export singleton instance
+export const apiService = new ApiService();
+export default apiService;
\ No newline at end of file
diff --git a/KonditionExpo/tsconfig.json b/KonditionExpo/tsconfig.json
new file mode 100644
index 0000000000..909e901086
--- /dev/null
+++ b/KonditionExpo/tsconfig.json
@@ -0,0 +1,17 @@
+{
+ "extends": "expo/tsconfig.base",
+ "compilerOptions": {
+ "strict": true,
+ "paths": {
+ "@/*": [
+ "./*"
+ ]
+ }
+ },
+ "include": [
+ "**/*.ts",
+ "**/*.tsx",
+ ".expo/types/**/*.ts",
+ "expo-env.d.ts"
+ ]
+}
diff --git a/README.md b/README.md
index afe124f3fb..236d0e2458 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Full Stack FastAPI Template
+# Full Stack FastAPI Template :)
@@ -237,3 +237,7 @@ Check the file [release-notes.md](./release-notes.md).
## License
The Full Stack FastAPI Template is licensed under the terms of the MIT license.
+
+
+## git push test
+-test
\ No newline at end of file
diff --git a/backend/README.md b/backend/README.md
index 17210a2f2c..dc984fc3d5 100644
--- a/backend/README.md
+++ b/backend/README.md
@@ -1,172 +1,528 @@
-# FastAPI Project - Backend
+# Kondition Backend - FastAPI Documentation
-## Requirements
+## Overview
-* [Docker](https://www.docker.com/).
-* [uv](https://docs.astral.sh/uv/) for Python package and environment management.
+This is the FastAPI backend for the Kondition fitness motivator application. It provides a robust API for user management, authentication, and data storage to support the mobile frontend.
-## Docker Compose
+## Tech Stack
-Start the local development environment with Docker Compose following the guide in [../development.md](../development.md).
+- **FastAPI**: High-performance Python web framework
+- **SQLModel**: SQL database ORM combining SQLAlchemy and Pydantic
+- **PostgreSQL**: Relational database
+- **Alembic**: Database migration tool
+- **JWT**: JSON Web Tokens for authentication
+- **Docker**: Containerization for deployment
+- **Sentry**: Error tracking and monitoring
-## General Workflow
+## Project Structure
-By default, the dependencies are managed with [uv](https://docs.astral.sh/uv/), go there and install it.
+```
+backend/
+βββ app/ # Main application package
+β βββ __init__.py # Package initialization
+β βββ main.py # FastAPI application entry point
+β βββ crud.py # CRUD operations
+β βββ models.py # Data models
+β βββ utils.py # Utility functions
+β βββ alembic/ # Database migrations
+β βββ api/ # API endpoints
+β β βββ routes/ # Route definitions
+β β β βββ login.py # Authentication endpoints
+β β β βββ users.py # User management endpoints
+β β β βββ items.py # Item endpoints
+β β β βββ ... # Other endpoint modules
+β β βββ deps.py # Dependency injection
+β β βββ main.py # API router configuration
+β βββ core/ # Core functionality
+β β βββ config.py # Application configuration
+β β βββ security.py # Security utilities
+β β βββ db.py # Database connection
+β βββ tests/ # Test suite
+βββ Dockerfile # Docker configuration
+βββ pyproject.toml # Python project metadata
+βββ alembic.ini # Alembic configuration
+```
+
+## API Endpoints
+
+### Authentication
+
+#### Login
+
+```
+POST /api/v1/login/access-token
+```
+
+Authenticates a user and returns a JWT access token.
+
+**Request Body:**
+```json
+{
+ "username": "user@example.com",
+ "password": "password123"
+}
+```
+
+**Response:**
+```json
+{
+ "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
+}
+```
+
+#### Test Token
+
+```
+POST /api/v1/login/test-token
+```
+
+Tests if the current token is valid and returns the user information.
+
+**Headers:**
+```
+Authorization: Bearer {token}
+```
+
+**Response:**
+```json
+{
+ "id": "123e4567-e89b-12d3-a456-426614174000",
+ "email": "user@example.com",
+ "is_active": true,
+ "is_superuser": false,
+ "full_name": "John Doe"
+}
+```
+
+#### Password Recovery
+
+```
+POST /api/v1/password-recovery/{email}
+```
+
+Initiates the password recovery process for a user.
+
+**Response:**
+```json
+{
+ "message": "Password recovery email sent"
+}
+```
-From `./backend/` you can install all the dependencies with:
+#### Reset Password
-```console
-$ uv sync
+```
+POST /api/v1/reset-password/
+```
+
+Resets a user's password using a token.
+
+**Request Body:**
+```json
+{
+ "token": "recovery-token",
+ "new_password": "new-password123"
+}
+```
+
+**Response:**
+```json
+{
+ "message": "Password updated successfully"
+}
+```
+
+### User Management
+
+#### Create User (Admin)
+
+```
+POST /api/v1/users/
+```
+
+Creates a new user (admin only).
+
+**Request Body:**
+```json
+{
+ "email": "new-user@example.com",
+ "password": "password123",
+ "full_name": "New User",
+ "is_superuser": false
+}
+```
+
+**Response:**
+```json
+{
+ "id": "123e4567-e89b-12d3-a456-426614174000",
+ "email": "new-user@example.com",
+ "is_active": true,
+ "is_superuser": false,
+ "full_name": "New User"
+}
```
-Then you can activate the virtual environment with:
+#### Get Current User
-```console
-$ source .venv/bin/activate
+```
+GET /api/v1/users/me
```
-Make sure your editor is using the correct Python virtual environment, with the interpreter at `backend/.venv/bin/python`.
+Returns the current user's information.
-Modify or add SQLModel models for data and SQL tables in `./backend/app/models.py`, API endpoints in `./backend/app/api/`, CRUD (Create, Read, Update, Delete) utils in `./backend/app/crud.py`.
+**Headers:**
+```
+Authorization: Bearer {token}
+```
-## VS Code
+**Response:**
+```json
+{
+ "id": "123e4567-e89b-12d3-a456-426614174000",
+ "email": "user@example.com",
+ "is_active": true,
+ "is_superuser": false,
+ "full_name": "John Doe"
+}
+```
-There are already configurations in place to run the backend through the VS Code debugger, so that you can use breakpoints, pause and explore variables, etc.
+#### Update Current User
-The setup is also already configured so you can run the tests through the VS Code Python tests tab.
+```
+PATCH /api/v1/users/me
+```
-## Docker Compose Override
+Updates the current user's information.
-During development, you can change Docker Compose settings that will only affect the local development environment in the file `docker-compose.override.yml`.
+**Headers:**
+```
+Authorization: Bearer {token}
+```
-The changes to that file only affect the local development environment, not the production environment. So, you can add "temporary" changes that help the development workflow.
+**Request Body:**
+```json
+{
+ "full_name": "Updated Name",
+ "email": "updated-email@example.com"
+}
+```
-For example, the directory with the backend code is synchronized in the Docker container, copying the code you change live to the directory inside the container. That allows you to test your changes right away, without having to build the Docker image again. It should only be done during development, for production, you should build the Docker image with a recent version of the backend code. But during development, it allows you to iterate very fast.
+**Response:**
+```json
+{
+ "id": "123e4567-e89b-12d3-a456-426614174000",
+ "email": "updated-email@example.com",
+ "is_active": true,
+ "is_superuser": false,
+ "full_name": "Updated Name"
+}
+```
-There is also a command override that runs `fastapi run --reload` instead of the default `fastapi run`. It starts a single server process (instead of multiple, as would be for production) and reloads the process whenever the code changes. Have in mind that if you have a syntax error and save the Python file, it will break and exit, and the container will stop. After that, you can restart the container by fixing the error and running again:
+#### Update Password
-```console
-$ docker compose watch
+```
+PATCH /api/v1/users/me/password
```
-There is also a commented out `command` override, you can uncomment it and comment the default one. It makes the backend container run a process that does "nothing", but keeps the container alive. That allows you to get inside your running container and execute commands inside, for example a Python interpreter to test installed dependencies, or start the development server that reloads when it detects changes.
+Updates the current user's password.
+
+**Headers:**
+```
+Authorization: Bearer {token}
+```
-To get inside the container with a `bash` session you can start the stack with:
+**Request Body:**
+```json
+{
+ "current_password": "current-password",
+ "new_password": "new-password123"
+}
+```
-```console
-$ docker compose watch
+**Response:**
+```json
+{
+ "message": "Password updated successfully"
+}
```
-and then in another terminal, `exec` inside the running container:
+#### Delete Current User
-```console
-$ docker compose exec backend bash
+```
+DELETE /api/v1/users/me
```
-You should see an output like:
+Deletes the current user's account.
-```console
-root@7f2607af31c3:/app#
+**Headers:**
+```
+Authorization: Bearer {token}
```
-that means that you are in a `bash` session inside your container, as a `root` user, under the `/app` directory, this directory has another directory called "app" inside, that's where your code lives inside the container: `/app/app`.
+**Response:**
+```json
+{
+ "message": "User deleted successfully"
+}
+```
-There you can use the `fastapi run --reload` command to run the debug live reloading server.
+#### User Registration
-```console
-$ fastapi run --reload app/main.py
+```
+POST /api/v1/users/signup
```
-...it will look like:
+Registers a new user without admin privileges.
-```console
-root@7f2607af31c3:/app# fastapi run --reload app/main.py
+**Request Body:**
+```json
+{
+ "email": "new-user@example.com",
+ "password": "password123",
+ "full_name": "New User"
+}
```
-and then hit enter. That runs the live reloading server that auto reloads when it detects code changes.
+**Response:**
+```json
+{
+ "id": "123e4567-e89b-12d3-a456-426614174000",
+ "email": "new-user@example.com",
+ "is_active": true,
+ "is_superuser": false,
+ "full_name": "New User"
+}
+```
-Nevertheless, if it doesn't detect a change but a syntax error, it will just stop with an error. But as the container is still alive and you are in a Bash session, you can quickly restart it after fixing the error, running the same command ("up arrow" and "Enter").
+### Additional Endpoints
-...this previous detail is what makes it useful to have the container alive doing nothing and then, in a Bash session, make it run the live reload server.
+The API includes additional endpoints for item management and other features that will be implemented in future sprints.
-## Backend tests
+## Data Models
-To test the backend run:
+### User
-```console
-$ bash ./scripts/test.sh
+```python
+class User(SQLModel, table=True):
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
+ email: str = Field(unique=True, index=True)
+ hashed_password: str
+ full_name: str | None = None
+ is_active: bool = True
+ is_superuser: bool = False
+ items: list["Item"] = Relationship(back_populates="owner")
```
-The tests run with Pytest, modify and add tests to `./backend/app/tests/`.
+### Item
+
+```python
+class Item(SQLModel, table=True):
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
+ title: str
+ description: str | None = None
+ owner_id: uuid.UUID = Field(foreign_key="user.id")
+ owner: User = Relationship(back_populates="items")
+```
+
+## Authentication Flow
+
+1. **User Registration**: Users register with email and password
+2. **Password Hashing**: Passwords are securely hashed before storage
+3. **Login**: Users authenticate with credentials to receive JWT token
+4. **Token Usage**: JWT token is included in API requests for authorization
+5. **Token Validation**: Backend validates token for protected endpoints
+6. **Password Recovery**: Email-based password reset flow
+
+## Security Features
+
+- **Password Hashing**: Secure password storage using bcrypt
+- **JWT Authentication**: Stateless authentication with expiring tokens
+- **Role-Based Access Control**: Superuser and regular user permissions
+- **CORS Protection**: Configurable CORS settings
+- **Rate Limiting**: Protection against brute force attacks (to be implemented)
+- **Input Validation**: Request validation using Pydantic models
+
+## Development Setup
-If you use GitHub Actions the tests will run automatically.
+### Prerequisites
-### Test running stack
+- Python 3.9+
+- PostgreSQL
+- Docker and Docker Compose (optional)
-If your stack is already up and you just want to run the tests, you can use:
+### Local Setup
+
+1. **Clone the repository**
```bash
-docker compose exec backend bash scripts/tests-start.sh
+git clone
+cd PROJECT/KonditionFastAPI
```
-That `/app/scripts/tests-start.sh` script just calls `pytest` after making sure that the rest of the stack is running. If you need to pass extra arguments to `pytest`, you can pass them to that command and they will be forwarded.
+2. **Set up a virtual environment**
+
+```bash
+python -m venv venv
+source venv/bin/activate # On Windows: venv\Scripts\activate
+```
-For example, to stop on first error:
+3. **Install dependencies**
```bash
-docker compose exec backend bash scripts/tests-start.sh -x
+cd backend
+pip install -e .
```
-### Test Coverage
+4. **Set up environment variables**
-When the tests are run, a file `htmlcov/index.html` is generated, you can open it in your browser to see the coverage of the tests.
+Create a `.env` file in the backend directory:
-## Migrations
+```
+POSTGRES_SERVER=localhost
+POSTGRES_USER=postgres
+POSTGRES_PASSWORD=password
+POSTGRES_DB=kondition
+FIRST_SUPERUSER=admin@example.com
+FIRST_SUPERUSER_PASSWORD=admin
+```
-As during local development your app directory is mounted as a volume inside the container, you can also run the migrations with `alembic` commands inside the container and the migration code will be in your app directory (instead of being only inside the container). So you can add it to your git repository.
+5. **Run database migrations**
-Make sure you create a "revision" of your models and that you "upgrade" your database with that revision every time you change them. As this is what will update the tables in your database. Otherwise, your application will have errors.
+```bash
+alembic upgrade head
+```
-* Start an interactive session in the backend container:
+6. **Start the development server**
-```console
-$ docker compose exec backend bash
+```bash
+uvicorn app.main:app --reload
```
-* Alembic is already configured to import your SQLModel models from `./backend/app/models.py`.
+### Docker Setup
-* After changing a model (for example, adding a column), inside the container, create a revision, e.g.:
+1. **Build and start the containers**
-```console
-$ alembic revision --autogenerate -m "Add column last_name to User model"
+```bash
+docker-compose up -d
```
-* Commit to the git repository the files generated in the alembic directory.
+2. **Run migrations in the container**
-* After creating the revision, run the migration in the database (this is what will actually change the database):
+```bash
+docker-compose exec backend alembic upgrade head
+```
-```console
-$ alembic upgrade head
+3. **Create initial data**
+
+```bash
+docker-compose exec backend python -m app.initial_data
```
-If you don't want to use migrations at all, uncomment the lines in the file at `./backend/app/core/db.py` that end in:
+## Testing
-```python
-SQLModel.metadata.create_all(engine)
+### Running Tests
+
+```bash
+# Run all tests
+pytest
+
+# Run with coverage report
+pytest --cov=app
```
-and comment the line in the file `scripts/prestart.sh` that contains:
+### Test Structure
+
+- **Unit Tests**: Test individual functions and methods
+- **Integration Tests**: Test API endpoints and database interactions
+- **Fixtures**: Reusable test components
+
+## Database Migrations
+
+### Creating a Migration
+
+```bash
+# Generate a migration script
+alembic revision --autogenerate -m "description of changes"
+
+# Apply migrations
+alembic upgrade head
+
+# Revert migrations
+alembic downgrade -1
+```
+
+## Deployment
+
+### Production Deployment
+
+1. **Configure environment variables for production**
+
+2. **Build and deploy with Docker Compose**
-```console
-$ alembic upgrade head
+```bash
+docker-compose -f docker-compose.yml -f docker-compose.traefik.yml up -d
```
-If you don't want to start with the default models and want to remove them / modify them, from the beginning, without having any previous revision, you can remove the revision files (`.py` Python files) under `./backend/app/alembic/versions/`. And then create a first migration as described above.
+3. **Set up a reverse proxy (Traefik, Nginx, etc.)**
+
+4. **Configure SSL certificates**
+
+### Monitoring
+
+- **Sentry Integration**: Error tracking and performance monitoring
+- **Logging**: Structured logs for debugging and auditing
+- **Health Checks**: Endpoint for monitoring service health
+
+## Future Implementations
+
+### Planned Features
+
+1. **Workout Data Models**
+ - Workout sessions
+ - Exercise types
+ - User progress tracking
+
+2. **Scheduling System**
+ - Workout scheduling
+ - Reminder notifications
+
+3. **Social Features**
+ - Friend connections
+ - Activity sharing
+ - Leaderboards
+
+4. **Analytics**
+ - Progress visualization
+ - Achievement tracking
+ - Performance metrics
+
+## Best Practices
+
+### Code Style
+
+- Follow PEP 8 guidelines
+- Use type hints for better IDE support
+- Document functions and classes with docstrings
+- Use meaningful variable and function names
+
+### API Design
+
+- Follow RESTful principles
+- Use appropriate HTTP methods and status codes
+- Implement proper error handling
+- Version the API for backward compatibility
-## Email Templates
+### Security
-The email templates are in `./backend/app/email-templates/`. Here, there are two directories: `build` and `src`. The `src` directory contains the source files that are used to build the final email templates. The `build` directory contains the final email templates that are used by the application.
+- Keep dependencies updated
+- Implement proper input validation
+- Use secure password hashing
+- Protect against common vulnerabilities (CSRF, XSS, etc.)
-Before continuing, ensure you have the [MJML extension](https://marketplace.visualstudio.com/items?itemName=attilabuti.vscode-mjml) installed in your VS Code.
+## Resources
-Once you have the MJML extension installed, you can create a new email template in the `src` directory. After creating the new email template and with the `.mjml` file open in your editor, open the command palette with `Ctrl+Shift+P` and search for `MJML: Export to HTML`. This will convert the `.mjml` file to a `.html` file and now you can save it in the build directory.
+- [FastAPI Documentation](https://fastapi.tiangolo.com/)
+- [SQLModel Documentation](https://sqlmodel.tiangolo.com/)
+- [Alembic Documentation](https://alembic.sqlalchemy.org/)
+- [JWT Documentation](https://jwt.io/)
diff --git a/backend/app/alembic/env.py b/backend/app/alembic/env.py
index 7f29c04680..655c9e8b0e 100755
--- a/backend/app/alembic/env.py
+++ b/backend/app/alembic/env.py
@@ -21,6 +21,13 @@
from app.models import SQLModel # noqa
from app.core.config import settings # noqa
+# Import all models to ensure they are registered with SQLModel metadata
+import app.models.user # noqa
+import app.models.item # noqa
+import app.models.social # noqa
+import app.models.token # noqa
+import app.models.workout # noqa
+
target_metadata = SQLModel.metadata
# other values from the config, defined by the needs of env.py,
diff --git a/backend/app/alembic/versions/20250515_add_social_models.py b/backend/app/alembic/versions/20250515_add_social_models.py
new file mode 100644
index 0000000000..ac3102f4f7
--- /dev/null
+++ b/backend/app/alembic/versions/20250515_add_social_models.py
@@ -0,0 +1,64 @@
+"""Add social models for user follows and workout posts
+
+Revision ID: 20250515_social
+Revises:
+Create Date: 2025-05-15 15:56:00.000000
+
+"""
+from alembic import op
+import sqlalchemy as sa
+import sqlmodel
+from sqlalchemy.dialects import postgresql
+
+# revision identifiers, used by Alembic.
+revision = '20250515_social'
+down_revision = 'd98dd8ec85a3' # Updated to point to the previous migration
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # Create UserFollow table
+ op.create_table(
+ 'userfollow',
+ sa.Column('follower_id', postgresql.UUID(), nullable=False),
+ sa.Column('followed_id', postgresql.UUID(), nullable=False),
+ sa.Column('created_at', sa.DateTime(), nullable=False),
+ sa.ForeignKeyConstraint(['followed_id'], ['user.id'], ),
+ sa.ForeignKeyConstraint(['follower_id'], ['user.id'], ),
+ sa.PrimaryKeyConstraint('follower_id', 'followed_id')
+ )
+
+ # Create WorkoutPost table
+ op.create_table(
+ 'workoutpost',
+ sa.Column('id', postgresql.UUID(), nullable=False),
+ sa.Column('user_id', postgresql.UUID(), nullable=False),
+ sa.Column('title', sa.String(), nullable=False),
+ sa.Column('description', sa.String(), nullable=True),
+ sa.Column('workout_type', sa.String(), nullable=False),
+ sa.Column('duration_minutes', sa.Integer(), nullable=False),
+ sa.Column('calories_burned', sa.Integer(), nullable=True),
+ sa.Column('created_at', sa.DateTime(), nullable=False),
+ sa.Column('updated_at', sa.DateTime(), nullable=True),
+ sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+
+ # Add indexes for better query performance
+ op.create_index(op.f('ix_workoutpost_user_id'), 'workoutpost', ['user_id'], unique=False)
+ op.create_index(op.f('ix_workoutpost_created_at'), 'workoutpost', ['created_at'], unique=False)
+ op.create_index(op.f('ix_userfollow_follower_id'), 'userfollow', ['follower_id'], unique=False)
+ op.create_index(op.f('ix_userfollow_followed_id'), 'userfollow', ['followed_id'], unique=False)
+
+
+def downgrade():
+ # Drop indexes
+ op.drop_index(op.f('ix_userfollow_followed_id'), table_name='userfollow')
+ op.drop_index(op.f('ix_userfollow_follower_id'), table_name='userfollow')
+ op.drop_index(op.f('ix_workoutpost_created_at'), table_name='workoutpost')
+ op.drop_index(op.f('ix_workoutpost_user_id'), table_name='workoutpost')
+
+ # Drop tables
+ op.drop_table('workoutpost')
+ op.drop_table('userfollow')
\ No newline at end of file
diff --git a/backend/app/alembic/versions/20250520_add_workout_models.py b/backend/app/alembic/versions/20250520_add_workout_models.py
new file mode 100644
index 0000000000..4d48763455
--- /dev/null
+++ b/backend/app/alembic/versions/20250520_add_workout_models.py
@@ -0,0 +1,73 @@
+"""Add workout models for fitness tracking
+
+Revision ID: 20250520_workout
+Revises: 20250515_social
+Create Date: 2025-05-20 10:38:00.000000
+
+"""
+from alembic import op
+import sqlalchemy as sa
+import sqlmodel
+from sqlalchemy.dialects import postgresql
+
+# revision identifiers, used by Alembic.
+revision = '20250520_workout'
+down_revision = '20250515_social' # Points to the previous migration
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # Create Workout table
+ op.create_table(
+ 'workout',
+ sa.Column('id', postgresql.UUID(), nullable=False),
+ sa.Column('user_id', postgresql.UUID(), nullable=False),
+ sa.Column('name', sa.String(), nullable=False),
+ sa.Column('description', sa.String(), nullable=True),
+ sa.Column('scheduled_date', sa.DateTime(), nullable=True),
+ sa.Column('completed_date', sa.DateTime(), nullable=True),
+ sa.Column('duration_minutes', sa.Integer(), nullable=True),
+ sa.Column('is_completed', sa.Boolean(), nullable=False),
+ sa.Column('created_at', sa.DateTime(), nullable=False),
+ sa.Column('updated_at', sa.DateTime(), nullable=True),
+ sa.ForeignKeyConstraint(['user_id'], ['user.id'], ondelete='CASCADE'),
+ sa.PrimaryKeyConstraint('id')
+ )
+
+ # Create Exercise table
+ op.create_table(
+ 'exercise',
+ sa.Column('id', postgresql.UUID(), nullable=False),
+ sa.Column('workout_id', postgresql.UUID(), nullable=False),
+ sa.Column('name', sa.String(), nullable=False),
+ sa.Column('description', sa.String(), nullable=True),
+ sa.Column('category', sa.String(), nullable=False),
+ sa.Column('sets', sa.Integer(), nullable=True),
+ sa.Column('reps', sa.Integer(), nullable=True),
+ sa.Column('weight', sa.Float(), nullable=True),
+ sa.Column('created_at', sa.DateTime(), nullable=False),
+ sa.Column('updated_at', sa.DateTime(), nullable=True),
+ sa.ForeignKeyConstraint(['workout_id'], ['workout.id'], ondelete='CASCADE'),
+ sa.PrimaryKeyConstraint('id')
+ )
+
+ # Add indexes for better query performance
+ op.create_index(op.f('ix_workout_user_id'), 'workout', ['user_id'], unique=False)
+ op.create_index(op.f('ix_workout_scheduled_date'), 'workout', ['scheduled_date'], unique=False)
+ op.create_index(op.f('ix_workout_is_completed'), 'workout', ['is_completed'], unique=False)
+ op.create_index(op.f('ix_exercise_workout_id'), 'exercise', ['workout_id'], unique=False)
+ op.create_index(op.f('ix_exercise_category'), 'exercise', ['category'], unique=False)
+
+
+def downgrade():
+ # Drop indexes
+ op.drop_index(op.f('ix_exercise_category'), table_name='exercise')
+ op.drop_index(op.f('ix_exercise_workout_id'), table_name='exercise')
+ op.drop_index(op.f('ix_workout_is_completed'), table_name='workout')
+ op.drop_index(op.f('ix_workout_scheduled_date'), table_name='workout')
+ op.drop_index(op.f('ix_workout_user_id'), table_name='workout')
+
+ # Drop tables
+ op.drop_table('exercise')
+ op.drop_table('workout')
\ No newline at end of file
diff --git a/backend/app/alembic/versions/20250601_add_user_profile_fields.py b/backend/app/alembic/versions/20250601_add_user_profile_fields.py
new file mode 100644
index 0000000000..2289e9d56f
--- /dev/null
+++ b/backend/app/alembic/versions/20250601_add_user_profile_fields.py
@@ -0,0 +1,34 @@
+"""Add user profile fields
+
+Revision ID: 20250601_add_user_profile_fields
+Revises: 20250520_workout
+Create Date: 2025-06-01 19:17:00.000000
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = '20250601_add_user_profile_fields'
+down_revision = '20250520_workout'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.add_column('user', sa.Column('gender', sa.String(length=50), nullable=True))
+ op.add_column('user', sa.Column('date_of_birth', sa.String(length=10), nullable=True))
+ op.add_column('user', sa.Column('weight', sa.Float(), nullable=True))
+ op.add_column('user', sa.Column('height', sa.Float(), nullable=True))
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_column('user', 'height')
+ op.drop_column('user', 'weight')
+ op.drop_column('user', 'date_of_birth')
+ op.drop_column('user', 'gender')
+ # ### end Alembic commands ###
\ No newline at end of file
diff --git a/backend/app/alembic/versions/20250604_add_post_privacy.py b/backend/app/alembic/versions/20250604_add_post_privacy.py
new file mode 100644
index 0000000000..d672ad04f3
--- /dev/null
+++ b/backend/app/alembic/versions/20250604_add_post_privacy.py
@@ -0,0 +1,26 @@
+"""Add is_public field to WorkoutPost
+
+Revision ID: 20250604_add_post_privacy
+Revises: 20250601_add_user_profile_fields
+Create Date: 2025-06-04 16:35:00.000000
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = '20250604_add_post_privacy'
+down_revision = '20250601_add_user_profile_fields'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # Add is_public column to workoutpost table
+ op.add_column('workoutpost', sa.Column('is_public', sa.Boolean(), nullable=False, server_default='true'))
+
+
+def downgrade():
+ # Remove is_public column from workoutpost table
+ op.drop_column('workoutpost', 'is_public')
\ No newline at end of file
diff --git a/backend/app/api/main.py b/backend/app/api/main.py
index eac18c8e8f..59f9fda72b 100644
--- a/backend/app/api/main.py
+++ b/backend/app/api/main.py
@@ -1,6 +1,6 @@
from fastapi import APIRouter
-from app.api.routes import items, login, private, users, utils
+from app.api.routes import items, login, private, social, users, utils, workouts, notifications
from app.core.config import settings
api_router = APIRouter()
@@ -8,7 +8,9 @@
api_router.include_router(users.router)
api_router.include_router(utils.router)
api_router.include_router(items.router)
-
+api_router.include_router(social.router)
+api_router.include_router(workouts.router)
+api_router.include_router(notifications.router, prefix="/notifications")
if settings.ENVIRONMENT == "local":
api_router.include_router(private.router)
diff --git a/backend/app/api/routes/items.py b/backend/app/api/routes/items.py
index 177dc1e476..aeac1b125c 100644
--- a/backend/app/api/routes/items.py
+++ b/backend/app/api/routes/items.py
@@ -12,10 +12,22 @@
@router.get("/", response_model=ItemsPublic)
def read_items(
- session: SessionDep, current_user: CurrentUser, skip: int = 0, limit: int = 100
+ session: SessionDep,
+ current_user: CurrentUser,
+ skip: int = 0,
+ limit: int = 100
) -> Any:
"""
Retrieve items.
+
+ This endpoint retrieves a list of items. For regular users, it returns only their own items.
+ For superusers, it returns all items in the system.
+
+ Parameters:
+ - **skip**: Number of records to skip for pagination
+ - **limit**: Maximum number of records to return
+
+ Returns a list of items and the total count.
"""
if current_user.is_superuser:
@@ -42,9 +54,24 @@ def read_items(
@router.get("/{id}", response_model=ItemPublic)
-def read_item(session: SessionDep, current_user: CurrentUser, id: uuid.UUID) -> Any:
+def read_item(
+ session: SessionDep,
+ current_user: CurrentUser,
+ id: uuid.UUID
+) -> Any:
"""
Get item by ID.
+
+ This endpoint retrieves a specific item by its ID.
+
+ Parameters:
+ - **id**: The UUID of the item to retrieve
+
+ Returns the item details.
+
+ Raises:
+ - 404: If the item is not found
+ - 400: If the user doesn't have permission to access this item
"""
item = session.get(Item, id)
if not item:
@@ -56,10 +83,29 @@ def read_item(session: SessionDep, current_user: CurrentUser, id: uuid.UUID) ->
@router.post("/", response_model=ItemPublic)
def create_item(
- *, session: SessionDep, current_user: CurrentUser, item_in: ItemCreate
+ *,
+ session: SessionDep,
+ current_user: CurrentUser,
+ item_in: ItemCreate
) -> Any:
"""
Create new item.
+
+ This endpoint allows users to create a new item.
+
+ Parameters:
+ - **title**: Required. The title of the item
+ - **description**: Optional. A description of the item
+
+ Returns the created item with its ID and owner information.
+
+ Example request body:
+ ```json
+ {
+ "title": "My Item",
+ "description": "This is a description of my item"
+ }
+ ```
"""
item = Item.model_validate(item_in, update={"owner_id": current_user.id})
session.add(item)
@@ -78,6 +124,27 @@ def update_item(
) -> Any:
"""
Update an item.
+
+ This endpoint allows users to update an existing item.
+
+ Parameters:
+ - **id**: The UUID of the item to update
+ - **title**: Optional. The updated title
+ - **description**: Optional. The updated description
+
+ Returns the updated item.
+
+ Raises:
+ - 404: If the item is not found
+ - 400: If the user doesn't have permission to update this item
+
+ Example request body:
+ ```json
+ {
+ "title": "Updated Title",
+ "description": "Updated description"
+ }
+ ```
"""
item = session.get(Item, id)
if not item:
@@ -94,10 +161,23 @@ def update_item(
@router.delete("/{id}")
def delete_item(
- session: SessionDep, current_user: CurrentUser, id: uuid.UUID
+ session: SessionDep,
+ current_user: CurrentUser,
+ id: uuid.UUID
) -> Message:
"""
Delete an item.
+
+ This endpoint allows users to delete an item.
+
+ Parameters:
+ - **id**: The UUID of the item to delete
+
+ Returns a success message upon successful deletion.
+
+ Raises:
+ - 404: If the item is not found
+ - 400: If the user doesn't have permission to delete this item
"""
item = session.get(Item, id)
if not item:
diff --git a/backend/app/api/routes/login.py b/backend/app/api/routes/login.py
index 980c66f86f..ffeac73800 100644
--- a/backend/app/api/routes/login.py
+++ b/backend/app/api/routes/login.py
@@ -26,7 +26,25 @@ def login_access_token(
session: SessionDep, form_data: Annotated[OAuth2PasswordRequestForm, Depends()]
) -> Token:
"""
- OAuth2 compatible token login, get an access token for future requests
+ OAuth2 compatible token login.
+
+ This endpoint authenticates a user and returns an access token for future requests.
+
+ Parameters:
+ - **username**: Required. The user's email address
+ - **password**: Required. The user's password
+
+ Returns an access token that should be included in the Authorization header
+ of subsequent requests as a Bearer token.
+
+ Example:
+ ```
+ Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
+ ```
+
+ Raises:
+ - 400: If the email or password is incorrect
+ - 400: If the user account is inactive
"""
user = crud.authenticate(
session=session, email=form_data.username, password=form_data.password
@@ -46,7 +64,15 @@ def login_access_token(
@router.post("/login/test-token", response_model=UserPublic)
def test_token(current_user: CurrentUser) -> Any:
"""
- Test access token
+ Test access token validity.
+
+ This endpoint verifies that the provided access token is valid and returns
+ the user's profile information.
+
+ Returns the user profile associated with the provided token.
+
+ Note: This endpoint can be used to validate tokens or to retrieve the current
+ user's information.
"""
return current_user
@@ -54,7 +80,18 @@ def test_token(current_user: CurrentUser) -> Any:
@router.post("/password-recovery/{email}")
def recover_password(email: str, session: SessionDep) -> Message:
"""
- Password Recovery
+ Initiate password recovery process.
+
+ This endpoint sends a password recovery email to the specified email address
+ if it belongs to a registered user.
+
+ Parameters:
+ - **email**: Required. The email address of the user requesting password recovery
+
+ Returns a message indicating that the recovery email has been sent.
+
+ Note: For security reasons, the same success message is returned even if the
+ email doesn't exist in the system.
"""
user = crud.get_user_by_email(session=session, email=email)
@@ -78,7 +115,20 @@ def recover_password(email: str, session: SessionDep) -> Message:
@router.post("/reset-password/")
def reset_password(session: SessionDep, body: NewPassword) -> Message:
"""
- Reset password
+ Reset user password using a token.
+
+ This endpoint allows users to set a new password using a token received via email.
+
+ Parameters:
+ - **token**: Required. The password reset token received via email
+ - **new_password**: Required. The new password to set
+
+ Returns a success message if the password was updated successfully.
+
+ Raises:
+ - 400: If the token is invalid
+ - 404: If the user associated with the token doesn't exist
+ - 400: If the user account is inactive
"""
email = verify_password_reset_token(token=body.token)
if not email:
@@ -105,7 +155,17 @@ def reset_password(session: SessionDep, body: NewPassword) -> Message:
)
def recover_password_html_content(email: str, session: SessionDep) -> Any:
"""
- HTML Content for Password Recovery
+ Get HTML content for password recovery email.
+
+ This endpoint generates and returns the HTML content that would be sent in a
+ password recovery email. This is primarily for testing and debugging purposes.
+
+ Parameters:
+ - **email**: Required. The email address of the user
+
+ Returns the HTML content of the password recovery email.
+
+ Note: This endpoint is restricted to superusers only.
"""
user = crud.get_user_by_email(session=session, email=email)
diff --git a/backend/app/api/routes/notifications.py b/backend/app/api/routes/notifications.py
new file mode 100644
index 0000000000..c32cba7b53
--- /dev/null
+++ b/backend/app/api/routes/notifications.py
@@ -0,0 +1,108 @@
+from datetime import date, datetime, timedelta
+from typing import List
+import uuid
+
+from fastapi import APIRouter, Depends, HTTPException
+from pydantic import BaseModel
+from sqlmodel import Session, select
+
+from app.crudFuncs import (
+ create_or_update_push_token,
+ schedule_custom_reminder
+)
+from app.core.db import get_session # adjust if your DBβsession dependency lives elsewhere
+from app.models import User
+
+router = APIRouter(tags=["notifications"])
+
+
+# Pydantic schema that the client will POST when registering a token
+class PushTokenPayload(BaseModel):
+ user_id: uuid.UUID
+ expo_token: str
+
+
+@router.post("/register_push_token", status_code=201)
+def register_push_token(
+ payload: PushTokenPayload,
+ session: Session = Depends(get_session),
+):
+ """
+ Client calls this with { user_id, expo_token } to store/update the Expo token.
+ """
+ try:
+ token_obj = create_or_update_push_token(
+ session=session,
+ user_id=payload.user_id,
+ expo_token=payload.expo_token,
+ )
+ return {"status": "ok", "push_token_id": token_obj.id}
+ except ValueError as e:
+ # If user_id doesnβt exist in Users table
+ raise HTTPException(status_code=404, detail=str(e))
+
+
+# Pydantic schema that the client will POST when scheduling a reminder
+class CustomReminderPayload(BaseModel):
+ user_id: uuid.UUID
+ expo_token: str
+ remind_time: datetime # e.g. "2025-06-05T12:00:00Z"
+ message: str
+
+
+@router.post("/schedule_reminder", status_code=201)
+def schedule_reminder_endpoint(
+ payload: CustomReminderPayload,
+ session: Session = Depends(get_session),
+):
+ """
+ Client calls this with { user_id, expo_token, remind_time, message }
+ to schedule a oneβoff reminder.
+ """
+ try:
+ rem_obj = schedule_custom_reminder(
+ session=session,
+ user_id=payload.user_id,
+ expo_token=payload.expo_token,
+ remind_time=payload.remind_time,
+ message=payload.message,
+ )
+ return {"status": "scheduled", "reminder_id": rem_obj.id}
+ except ValueError as e:
+ raise HTTPException(status_code=404, detail=str(e))
+
+class QuoteOut(BaseModel):
+ date: date
+ text: str
+
+PRESET_QUOTES = [
+ "Believe you can and youβre halfway there.",
+ "Fall seven times, stand up eight.",
+ "Your limitationβitβs only your imagination.",
+ "Push yourself, because no one else is going to do it for you.",
+ "Great things never come from comfort zones.",
+ "Dream it. Wish it. Do it.",
+ "Success doesnβt just find youβyou have to go out and get it.",
+]
+
+@router.get("/quotes", response_model=List[QuoteOut])
+def get_daily_quotes():
+ """
+ Return a list of the past 7 days (including today) with
+ one motivational quote per day, in descending date order.
+ """
+ today = date.today()
+ results: List[QuoteOut] = []
+
+ for i in range(7):
+ d = today - timedelta(days=i)
+ # determine which quote to use for date d
+ # calculate day-of-year for d
+ start_of_year = date(d.year, 1, 1)
+ day_of_year = (d - start_of_year).days
+ idx = day_of_year % len(PRESET_QUOTES)
+ quote_text = PRESET_QUOTES[idx]
+
+ results.append(QuoteOut(date=d, text=quote_text))
+
+ return results
\ No newline at end of file
diff --git a/backend/app/api/routes/p_bests.py b/backend/app/api/routes/p_bests.py
new file mode 100644
index 0000000000..04b90c759f
--- /dev/null
+++ b/backend/app/api/routes/p_bests.py
@@ -0,0 +1,47 @@
+# TODO test PB dedicated router, 3 REST routes, 1 post & 2 gets
+
+from typing import List
+from fastapi import APIRouter, Depends, HTTPException, status
+from sqlmodel import Session
+
+from app import crud
+from app.core.db import get_session
+from app.core.security import get_current_active_user
+from app.models import PersonalBestCreate, PersonalBestRead, PersonalBestsList
+
+router = APIRouter(tags=["personal-bests"], prefix="/personal-bests")
+
+@router.post("/", response_model=PersonalBestRead, status_code=status.HTTP_201_CREATED)
+def record_personal_best(
+ pb_in: PersonalBestCreate,
+ session: Session = Depends(get_session),
+ current_user=Depends(get_current_active_user),
+):
+ """
+ Record or update a personal best for the current user.
+ """
+ pb = crud.create_or_update_personal_best(
+ session=session, user_id=current_user.id, pb_in=pb_in
+ )
+ return pb
+
+@router.get("/", response_model=PersonalBestsList)
+def list_personal_bests(
+ session: Session = Depends(get_session),
+ current_user=Depends(get_current_active_user),
+):
+ all_pbs = crud.get_personal_bests(session=session, user_id=current_user.id)
+ return {"data": all_pbs, "count": len(all_pbs)}
+
+@router.get("/{metric}", response_model=PersonalBestRead)
+def get_personal_best(
+ metric: str,
+ session: Session = Depends(get_session),
+ current_user=Depends(get_current_active_user),
+):
+ pb = crud.get_personal_best(
+ session=session, user_id=current_user.id, metric=metric
+ )
+ if not pb:
+ raise HTTPException(status_code=404, detail="No personal best for that metric")
+ return pb
diff --git a/backend/app/api/routes/social.py b/backend/app/api/routes/social.py
new file mode 100644
index 0000000000..bf21331ec7
--- /dev/null
+++ b/backend/app/api/routes/social.py
@@ -0,0 +1,712 @@
+import uuid
+from typing import Any
+
+from fastapi import APIRouter, HTTPException, status
+from sqlmodel import Session, select
+
+from app import crud
+from app.api.deps import CurrentUser, SessionDep
+from app.models.social import (
+ UserFollow,
+ UserSearchResult,
+ UserSearchResultsPublic,
+ WorkoutPost,
+ WorkoutPostCreate,
+ WorkoutPostPublic,
+ WorkoutPostsPublic,
+ WorkoutPostUpdate,
+)
+from app.models.token import Message
+from app.models.user import User, UserPublic, UserPublicExtended, UsersPublic
+
+router = APIRouter(prefix="/social", tags=["social"])
+
+
+# User Follow Endpoints
+
+@router.post("/follow/{user_id}", response_model=Message)
+def follow_user(
+ user_id: uuid.UUID,
+ session: SessionDep,
+ current_user: CurrentUser
+) -> Any:
+ """
+ Follow a user.
+
+ This endpoint allows the current user to follow another user.
+
+ Parameters:
+ - **user_id**: Required. The UUID of the user to follow
+
+ Returns a success message confirming the follow action.
+
+ Raises:
+ - 400: If the user tries to follow themselves
+ - 404: If the user to follow doesn't exist
+ """
+ if user_id == current_user.id:
+ raise HTTPException(
+ status_code=status.HTTP_400_BAD_REQUEST,
+ detail="You cannot follow yourself",
+ )
+
+ # Check if the user to follow exists
+ user_to_follow = crud.get_user_by_id(session=session, user_id=user_id)
+ if not user_to_follow:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail="User not found",
+ )
+
+ # Create follow relationship
+ crud.follow_user(
+ session=session, follower_id=current_user.id, followed_id=user_id
+ )
+
+ return Message(message=f"You are now following {user_to_follow.full_name or user_to_follow.email}")
+
+
+@router.delete("/unfollow/{user_id}", response_model=Message)
+def unfollow_user(
+ user_id: uuid.UUID,
+ session: SessionDep,
+ current_user: CurrentUser
+) -> Any:
+ """
+ Unfollow a user.
+
+ This endpoint allows the current user to unfollow a user they are currently following.
+
+ Parameters:
+ - **user_id**: Required. The UUID of the user to unfollow
+
+ Returns a success message confirming the unfollow action.
+
+ Raises:
+ - 400: If the user tries to unfollow themselves
+ - 404: If the user to unfollow doesn't exist
+ """
+ if user_id == current_user.id:
+ raise HTTPException(
+ status_code=status.HTTP_400_BAD_REQUEST,
+ detail="You cannot unfollow yourself",
+ )
+
+ # Check if the user to unfollow exists
+ user_to_unfollow = crud.get_user_by_id(session=session, user_id=user_id)
+ if not user_to_unfollow:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail="User not found",
+ )
+
+ # Remove follow relationship
+ success = crud.unfollow_user(
+ session=session, follower_id=current_user.id, followed_id=user_id
+ )
+
+ if not success:
+ raise HTTPException(
+ status_code=status.HTTP_400_BAD_REQUEST,
+ detail=f"You are not following {user_to_unfollow.full_name or user_to_unfollow.email}",
+ )
+
+ return Message(message=f"You have unfollowed {user_to_unfollow.full_name or user_to_unfollow.email}")
+
+
+@router.get("/followers", response_model=UserSearchResultsPublic)
+def get_followers(
+ session: SessionDep, current_user: CurrentUser, skip: int = 0, limit: int = 100
+) -> Any:
+ """
+ Get users who follow the current user.
+ """
+ followers, count = crud.get_followers(
+ session=session, user_id=current_user.id, skip=skip, limit=limit
+ )
+
+ # Convert to UserSearchResult with proper is_following status and social stats
+ followers_with_status = []
+ for user in followers:
+ # Get follower and following counts
+ follower_count = crud.get_follower_count(session=session, user_id=user.id)
+ following_count = crud.get_following_count(session=session, user_id=user.id)
+
+ # Check if current user is following this follower back
+ is_following_back = crud.is_following(
+ session=session, follower_id=current_user.id, followed_id=user.id
+ )
+
+ # Create UserSearchResult with all required fields
+ user_result = UserSearchResult(
+ id=user.id,
+ email=user.email,
+ full_name=user.full_name,
+ is_active=user.is_active,
+ is_superuser=user.is_superuser,
+ gender=user.gender,
+ date_of_birth=user.date_of_birth,
+ weight=user.weight,
+ height=user.height,
+ follower_count=follower_count,
+ following_count=following_count,
+ is_following=is_following_back # True if current user follows this follower back
+ )
+ followers_with_status.append(user_result)
+
+ return UserSearchResultsPublic(data=followers_with_status, count=count)
+
+
+@router.get("/following", response_model=UserSearchResultsPublic)
+def get_following(
+ session: SessionDep, current_user: CurrentUser, skip: int = 0, limit: int = 100
+) -> Any:
+ """
+ Get users that the current user follows.
+ """
+ following, count = crud.get_following(
+ session=session, user_id=current_user.id, skip=skip, limit=limit
+ )
+
+ # Convert to UserSearchResult with is_following always true and social stats
+ following_with_status = []
+ for user in following:
+ # Get follower and following counts
+ follower_count = crud.get_follower_count(session=session, user_id=user.id)
+ following_count = crud.get_following_count(session=session, user_id=user.id)
+
+ # Create UserSearchResult with all required fields
+ user_result = UserSearchResult(
+ id=user.id,
+ email=user.email,
+ full_name=user.full_name,
+ is_active=user.is_active,
+ is_superuser=user.is_superuser,
+ gender=user.gender,
+ date_of_birth=user.date_of_birth,
+ weight=user.weight,
+ height=user.height,
+ follower_count=follower_count,
+ following_count=following_count,
+ is_following=True # Always true for users in following list
+ )
+ following_with_status.append(user_result)
+
+ return UserSearchResultsPublic(data=following_with_status, count=count)
+
+
+@router.get("/user/{user_id}/followers", response_model=UsersPublic)
+def get_user_followers(
+ user_id: uuid.UUID, session: SessionDep, current_user: CurrentUser, skip: int = 0, limit: int = 100
+) -> Any:
+ """
+ Get followers of a specific user.
+ """
+ # Check if the user exists
+ user = crud.get_user_by_id(session=session, user_id=user_id)
+ if not user:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail="User not found",
+ )
+
+ followers, count = crud.get_followers(
+ session=session, user_id=user_id, skip=skip, limit=limit
+ )
+
+ return UsersPublic(data=followers, count=count)
+
+
+@router.get("/user/{user_id}/following", response_model=UsersPublic)
+def get_user_following(
+ user_id: uuid.UUID, session: SessionDep, current_user: CurrentUser, skip: int = 0, limit: int = 100
+) -> Any:
+ """
+ Get users that a specific user follows.
+ """
+ # Check if the user exists
+ user = crud.get_user_by_id(session=session, user_id=user_id)
+ if not user:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail="User not found",
+ )
+
+ following, count = crud.get_following(
+ session=session, user_id=user_id, skip=skip, limit=limit
+ )
+
+ return UsersPublic(data=following, count=count)
+
+
+@router.get("/user/{user_id}/profile", response_model=UserPublicExtended)
+def get_user_profile(
+ user_id: uuid.UUID, session: SessionDep, current_user: CurrentUser
+) -> Any:
+ """
+ Get a user's profile with follower and following counts.
+ """
+ # Check if the user exists
+ user = crud.get_user_by_id(session=session, user_id=user_id)
+ if not user:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail="User not found",
+ )
+
+ # Get follower and following counts
+ follower_count = crud.get_follower_count(session=session, user_id=user_id)
+ following_count = crud.get_following_count(session=session, user_id=user_id)
+
+ # Create extended user profile
+ user_data = UserPublic.model_validate(user).model_dump()
+ user_extended = UserPublicExtended(
+ **user_data,
+ follower_count=follower_count,
+ following_count=following_count
+ )
+
+ return user_extended
+
+
+@router.get("/is-following/{user_id}", response_model=dict)
+def check_if_following(
+ user_id: uuid.UUID,
+ session: SessionDep,
+ current_user: CurrentUser
+) -> Any:
+ """
+ Check if the current user is following a specific user.
+
+ This endpoint checks whether the current user is following another user.
+
+ Parameters:
+ - **user_id**: Required. The UUID of the user to check
+
+ Returns a JSON object with a boolean "is_following" field.
+
+ Example response:
+ ```json
+ {
+ "is_following": true
+ }
+ ```
+
+ Raises:
+ - 404: If the user to check doesn't exist
+ """
+ # Check if the user exists
+ user = crud.get_user_by_id(session=session, user_id=user_id)
+ if not user:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail="User not found",
+ )
+
+ is_following = crud.is_following(
+ session=session, follower_id=current_user.id, followed_id=user_id
+ )
+
+ return {"is_following": is_following}
+
+
+@router.get("/users/search", response_model=UserSearchResultsPublic)
+def search_users(
+ q: str,
+ session: SessionDep,
+ current_user: CurrentUser,
+ skip: int = 0,
+ limit: int = 20
+) -> Any:
+ """
+ Search for users by name or email to enable user discovery functionality.
+
+ This endpoint allows users to search for other users by full_name or email
+ with case-insensitive partial matching. Results include social stats and
+ following status for each user.
+
+ Parameters:
+ - **q**: Required. The search query string to match against user names or emails
+ - **skip**: Optional. Number of records to skip for pagination (default: 0)
+ - **limit**: Optional. Maximum number of records to return (default: 20, max: 100)
+
+ Returns:
+ - A paginated list of users matching the search criteria
+ - Each user includes follower/following counts and is_following status
+ - The current user is excluded from results
+
+ Example usage:
+ - `GET /social/users/search?q=john` - Search for users with "john" in name or email
+ - `GET /social/users/search?q=john&skip=0&limit=10` - Search with pagination
+
+ Raises:
+ - 400: If the search query is empty or too short
+ """
+ # Validate search query
+ if not q or len(q.strip()) < 1:
+ raise HTTPException(
+ status_code=status.HTTP_400_BAD_REQUEST,
+ detail="Search query must be at least 1 character long",
+ )
+
+ # Limit the maximum number of results
+ if limit > 100:
+ limit = 100
+
+ # Perform the search
+ users_data, count = crud.search_users(
+ session=session,
+ query=q.strip(),
+ current_user_id=current_user.id,
+ skip=skip,
+ limit=limit
+ )
+
+ # Convert to UserSearchResult objects
+ search_results = [UserSearchResult(**user_data) for user_data in users_data]
+
+ return UserSearchResultsPublic(data=search_results, count=count)
+
+
+# Workout Post Endpoints
+
+@router.post("/workout-posts", response_model=WorkoutPostPublic)
+def create_workout_post(
+ *,
+ session: SessionDep,
+ current_user: CurrentUser,
+ post_in: WorkoutPostCreate
+) -> Any:
+ """
+ Create a new workout post.
+
+ This endpoint allows users to create a new workout post to share with followers.
+
+ Parameters:
+ - **title**: Required. The title of the workout post
+ - **description**: Optional. A description of the workout
+ - **workout_type**: Required. The type of workout (e.g., Running, Strength, Yoga)
+ - **duration_minutes**: Required. The duration of the workout in minutes
+ - **calories_burned**: Optional. The number of calories burned during the workout
+ - **is_public**: Optional. Whether the post is public (default: true)
+
+ Returns the created workout post with user information.
+
+ Privacy Settings:
+ - **Public posts**: Visible to everyone regardless of following status
+ - **Private posts**: Only visible to mutual followers
+
+ Example request body:
+ ```json
+ {
+ "title": "Morning Run",
+ "description": "Great 5k run this morning",
+ "workout_type": "Running",
+ "duration_minutes": 30,
+ "calories_burned": 300,
+ "is_public": true
+ }
+ ```
+ """
+ post = crud.create_workout_post(
+ session=session, post_in=post_in, user_id=current_user.id
+ )
+
+ # Add user's full name and mutual follow status to the response
+ post_data = WorkoutPostPublic.model_validate(post).model_dump()
+ post_data["user_full_name"] = current_user.full_name
+ post_data["is_mutual_follow"] = True # User's own posts
+
+ return WorkoutPostPublic(**post_data)
+
+
+@router.get("/workout-posts", response_model=WorkoutPostsPublic)
+def get_my_workout_posts(
+ session: SessionDep,
+ current_user: CurrentUser,
+ skip: int = 0,
+ limit: int = 100
+) -> Any:
+ """
+ Get the current user's workout posts.
+
+ This endpoint retrieves all workout posts created by the current user.
+
+ Parameters:
+ - **skip**: Number of records to skip for pagination
+ - **limit**: Maximum number of records to return
+
+ Returns a list of workout posts and the total count.
+ """
+ posts, count = crud.get_user_workout_posts(
+ session=session, user_id=current_user.id, skip=skip, limit=limit
+ )
+
+ # Add user's full name and mutual follow status to each post
+ post_data = []
+ for post in posts:
+ post_dict = WorkoutPostPublic.model_validate(post).model_dump()
+ post_dict["user_full_name"] = current_user.full_name
+ post_dict["is_mutual_follow"] = True # User's own posts
+ post_data.append(WorkoutPostPublic(**post_dict))
+
+ return WorkoutPostsPublic(data=post_data, count=count)
+
+
+@router.get("/user/{user_id}/workout-posts", response_model=WorkoutPostsPublic)
+def get_user_workout_posts(
+ user_id: uuid.UUID, session: SessionDep, current_user: CurrentUser, skip: int = 0, limit: int = 100
+) -> Any:
+ """
+ Get a specific user's workout posts with privacy filtering.
+ Only returns posts that the current user is allowed to see.
+ """
+ # Check if the user exists
+ user = crud.get_user_by_id(session=session, user_id=user_id)
+ if not user:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail="User not found",
+ )
+
+ posts, count = crud.get_user_workout_posts(
+ session=session, user_id=user_id, skip=skip, limit=limit
+ )
+
+ # Filter posts based on privacy settings
+ is_own_profile = user_id == current_user.id
+ is_mutual = crud.is_mutual_follow(session=session, user1_id=current_user.id, user2_id=user_id)
+
+ filtered_posts = []
+ for post in posts:
+ # Apply privacy filtering
+ if is_own_profile or post.is_public or is_mutual:
+ post_dict = WorkoutPostPublic.model_validate(post).model_dump()
+ post_dict["user_full_name"] = user.full_name
+ post_dict["is_mutual_follow"] = is_own_profile or is_mutual
+ filtered_posts.append(WorkoutPostPublic(**post_dict))
+
+ return WorkoutPostsPublic(data=filtered_posts, count=len(filtered_posts))
+
+
+@router.get("/feed", response_model=WorkoutPostsPublic)
+def get_feed(
+ session: SessionDep,
+ current_user: CurrentUser,
+ skip: int = 0,
+ limit: int = 100,
+ feed_type: str = "personal"
+) -> Any:
+ """
+ Get workout posts based on feed type with privacy filtering.
+
+ Parameters:
+ - **feed_type**: "personal" (default), "public", or "combined"
+ - **skip**: Number of records to skip for pagination
+ - **limit**: Maximum number of records to return
+
+ Feed Types:
+ - **personal**: Posts from users you follow (respecting privacy)
+ - **public**: All public posts from all users (discovery)
+ - **combined**: Mix of personal and additional public posts
+ """
+ # Validate feed_type
+ valid_feed_types = ["personal", "public", "combined"]
+ if feed_type not in valid_feed_types:
+ raise HTTPException(
+ status_code=status.HTTP_400_BAD_REQUEST,
+ detail=f"Invalid feed_type. Must be one of: {', '.join(valid_feed_types)}",
+ )
+
+ posts, count = crud.get_feed_posts(
+ session=session, user_id=current_user.id, skip=skip, limit=limit, feed_type=feed_type
+ )
+
+ # Add user's full name and mutual follow status to each post
+ post_data = []
+ for post in posts:
+ post_dict = WorkoutPostPublic.model_validate(post).model_dump()
+
+ # Get the user who created the post
+ user = crud.get_user_by_id(session=session, user_id=post.user_id)
+ if user:
+ post_dict["user_full_name"] = user.full_name
+
+ # Add mutual follow status for privacy logic
+ if post.user_id != current_user.id:
+ post_dict["is_mutual_follow"] = crud.is_mutual_follow(
+ session=session, user1_id=current_user.id, user2_id=post.user_id
+ )
+ else:
+ post_dict["is_mutual_follow"] = True # User's own posts
+
+ post_data.append(WorkoutPostPublic(**post_dict))
+
+ return WorkoutPostsPublic(data=post_data, count=count)
+
+
+@router.get("/feed/public", response_model=WorkoutPostsPublic)
+def get_public_feed(
+ session: SessionDep, current_user: CurrentUser, skip: int = 0, limit: int = 100
+) -> Any:
+ """
+ Get all public workout posts from all users (discovery feed).
+ """
+ posts, count = crud.get_public_feed_posts(
+ session=session, user_id=current_user.id, skip=skip, limit=limit
+ )
+
+ # Add user's full name to each post
+ post_data = []
+ for post in posts:
+ post_dict = WorkoutPostPublic.model_validate(post).model_dump()
+
+ # Get the user who created the post
+ user = crud.get_user_by_id(session=session, user_id=post.user_id)
+ if user:
+ post_dict["user_full_name"] = user.full_name
+
+ # Add mutual follow status
+ if post.user_id != current_user.id:
+ post_dict["is_mutual_follow"] = crud.is_mutual_follow(
+ session=session, user1_id=current_user.id, user2_id=post.user_id
+ )
+ else:
+ post_dict["is_mutual_follow"] = True
+
+ post_data.append(WorkoutPostPublic(**post_dict))
+
+ return WorkoutPostsPublic(data=post_data, count=count)
+
+
+@router.get("/feed/personal", response_model=WorkoutPostsPublic)
+def get_personal_feed(
+ session: SessionDep, current_user: CurrentUser, skip: int = 0, limit: int = 100
+) -> Any:
+ """
+ Get workout posts from users you follow (respecting privacy settings).
+ """
+ posts, count = crud.get_personal_feed_posts(
+ session=session, user_id=current_user.id, skip=skip, limit=limit
+ )
+
+ # Add user's full name and mutual follow status to each post
+ post_data = []
+ for post in posts:
+ post_dict = WorkoutPostPublic.model_validate(post).model_dump()
+
+ # Get the user who created the post
+ user = crud.get_user_by_id(session=session, user_id=post.user_id)
+ if user:
+ post_dict["user_full_name"] = user.full_name
+
+ # Add mutual follow status
+ if post.user_id != current_user.id:
+ post_dict["is_mutual_follow"] = crud.is_mutual_follow(
+ session=session, user1_id=current_user.id, user2_id=post.user_id
+ )
+ else:
+ post_dict["is_mutual_follow"] = True
+
+ post_data.append(WorkoutPostPublic(**post_dict))
+
+ return WorkoutPostsPublic(data=post_data, count=count)
+
+
+@router.get("/workout-posts/{post_id}", response_model=WorkoutPostPublic)
+def get_workout_post(
+ post_id: uuid.UUID, session: SessionDep, current_user: CurrentUser
+) -> Any:
+ """
+ Get a specific workout post with privacy filtering.
+ """
+ post = crud.get_workout_post(session=session, post_id=post_id)
+ if not post:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail="Workout post not found",
+ )
+
+ # Check privacy permissions
+ is_own_post = post.user_id == current_user.id
+ is_mutual = crud.is_mutual_follow(session=session, user1_id=current_user.id, user2_id=post.user_id)
+
+ # Apply privacy filtering
+ if not is_own_post and not post.is_public and not is_mutual:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail="Workout post not found",
+ )
+
+ # Add user's full name and mutual follow status to the response
+ post_data = WorkoutPostPublic.model_validate(post).model_dump()
+ user = crud.get_user_by_id(session=session, user_id=post.user_id)
+ if user:
+ post_data["user_full_name"] = user.full_name
+
+ post_data["is_mutual_follow"] = is_own_post or is_mutual
+
+ return WorkoutPostPublic(**post_data)
+
+
+@router.put("/workout-posts/{post_id}", response_model=WorkoutPostPublic)
+def update_workout_post(
+ *,
+ post_id: uuid.UUID,
+ session: SessionDep,
+ current_user: CurrentUser,
+ post_in: WorkoutPostUpdate,
+) -> Any:
+ """
+ Update a workout post.
+ """
+ post = crud.get_workout_post(session=session, post_id=post_id)
+ if not post:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail="Workout post not found",
+ )
+
+ # Check if the user is the owner of the post
+ if post.user_id != current_user.id:
+ raise HTTPException(
+ status_code=status.HTTP_403_FORBIDDEN,
+ detail="Not enough permissions",
+ )
+
+ post = crud.update_workout_post(
+ session=session, db_post=post, post_in=post_in
+ )
+
+ # Add user's full name and mutual follow status to the response
+ post_data = WorkoutPostPublic.model_validate(post).model_dump()
+ post_data["user_full_name"] = current_user.full_name
+ post_data["is_mutual_follow"] = True # User's own posts
+
+ return WorkoutPostPublic(**post_data)
+
+
+@router.delete("/workout-posts/{post_id}", response_model=Message)
+def delete_workout_post(
+ post_id: uuid.UUID, session: SessionDep, current_user: CurrentUser
+) -> Any:
+ """
+ Delete a workout post.
+ """
+ post = crud.get_workout_post(session=session, post_id=post_id)
+ if not post:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail="Workout post not found",
+ )
+
+ # Check if the user is the owner of the post or a superuser
+ if not current_user.is_superuser and post.user_id != current_user.id:
+ raise HTTPException(
+ status_code=status.HTTP_403_FORBIDDEN,
+ detail="Not enough permissions",
+ )
+
+ crud.delete_workout_post(session=session, post_id=post_id)
+
+ return Message(message="Workout post deleted successfully")
\ No newline at end of file
diff --git a/backend/app/api/routes/users.py b/backend/app/api/routes/users.py
index 6429818458..b0332b44ac 100644
--- a/backend/app/api/routes/users.py
+++ b/backend/app/api/routes/users.py
@@ -34,9 +34,23 @@
dependencies=[Depends(get_current_active_superuser)],
response_model=UsersPublic,
)
-def read_users(session: SessionDep, skip: int = 0, limit: int = 100) -> Any:
+def read_users(
+ session: SessionDep,
+ skip: int = 0,
+ limit: int = 100
+) -> Any:
"""
- Retrieve users.
+ Retrieve all users.
+
+ This endpoint allows superusers to retrieve a list of all users in the system.
+
+ Parameters:
+ - **skip**: Number of records to skip for pagination
+ - **limit**: Maximum number of records to return
+
+ Returns a list of users and the total count.
+
+ Note: This endpoint is restricted to superusers only.
"""
count_statement = select(func.count()).select_from(User)
@@ -51,9 +65,25 @@ def read_users(session: SessionDep, skip: int = 0, limit: int = 100) -> Any:
@router.post(
"/", dependencies=[Depends(get_current_active_superuser)], response_model=UserPublic
)
-def create_user(*, session: SessionDep, user_in: UserCreate) -> Any:
+def create_user(
+ *,
+ session: SessionDep,
+ user_in: UserCreate
+) -> Any:
"""
- Create new user.
+ Create a new user.
+
+ This endpoint allows superusers to create new user accounts.
+
+ Parameters:
+ - **email**: Required. The email address of the new user
+ - **password**: Required. The password for the new user
+ - **full_name**: Optional. The full name of the user
+ - **is_superuser**: Optional. Whether the user should have superuser privileges
+
+ Returns the created user.
+
+ Note: This endpoint is restricted to superusers only.
"""
user = crud.get_user_by_email(session=session, email=user_in.email)
if user:
@@ -77,10 +107,23 @@ def create_user(*, session: SessionDep, user_in: UserCreate) -> Any:
@router.patch("/me", response_model=UserPublic)
def update_user_me(
- *, session: SessionDep, user_in: UserUpdateMe, current_user: CurrentUser
+ *,
+ session: SessionDep,
+ user_in: UserUpdateMe,
+ current_user: CurrentUser
) -> Any:
"""
- Update own user.
+ Update current user's profile.
+
+ This endpoint allows users to update their own profile information.
+
+ Parameters:
+ - **email**: Optional. The new email address
+ - **full_name**: Optional. The new full name
+
+ Returns the updated user profile.
+
+ Note: If the email is changed, the system will check if the new email is already in use.
"""
if user_in.email:
@@ -99,10 +142,23 @@ def update_user_me(
@router.patch("/me/password", response_model=Message)
def update_password_me(
- *, session: SessionDep, body: UpdatePassword, current_user: CurrentUser
+ *,
+ session: SessionDep,
+ body: UpdatePassword,
+ current_user: CurrentUser
) -> Any:
"""
- Update own password.
+ Update current user's password.
+
+ This endpoint allows users to change their password.
+
+ Parameters:
+ - **current_password**: Required. The user's current password for verification
+ - **new_password**: Required. The new password to set
+
+ Returns a success message.
+
+ Note: The current password must be correct for the change to be accepted.
"""
if not verify_password(body.current_password, current_user.hashed_password):
raise HTTPException(status_code=400, detail="Incorrect password")
@@ -120,7 +176,11 @@ def update_password_me(
@router.get("/me", response_model=UserPublic)
def read_user_me(current_user: CurrentUser) -> Any:
"""
- Get current user.
+ Get current user profile.
+
+ This endpoint returns the profile information of the currently authenticated user.
+
+ Returns the user's profile information including email, full name, and other details.
"""
return current_user
@@ -128,7 +188,13 @@ def read_user_me(current_user: CurrentUser) -> Any:
@router.delete("/me", response_model=Message)
def delete_user_me(session: SessionDep, current_user: CurrentUser) -> Any:
"""
- Delete own user.
+ Delete current user account.
+
+ This endpoint allows users to delete their own account.
+
+ Returns a success message upon successful deletion.
+
+ Note: Superusers are not allowed to delete their own accounts.
"""
if current_user.is_superuser:
raise HTTPException(
@@ -142,6 +208,18 @@ def delete_user_me(session: SessionDep, current_user: CurrentUser) -> Any:
@router.post("/signup", response_model=UserPublic)
def register_user(session: SessionDep, user_in: UserRegister) -> Any:
"""
+ Register a new user account.
+
+ This endpoint allows new users to create an account.
+
+ Parameters:
+ - **email**: Required. The email address for the new account
+ - **password**: Required. The password for the new account
+ - **full_name**: Optional. The user's full name
+
+ Returns the created user profile.
+
+ Note: If an account with the provided email already exists, an error will be returned.
Create new user without the need to be logged in.
"""
user = crud.get_user_by_email(session=session, email=user_in.email)
diff --git a/backend/app/api/routes/workouts.py b/backend/app/api/routes/workouts.py
new file mode 100644
index 0000000000..2e392d0b3c
--- /dev/null
+++ b/backend/app/api/routes/workouts.py
@@ -0,0 +1,796 @@
+import uuid
+from datetime import datetime
+from typing import Any, List, Optional
+
+from fastapi import APIRouter, HTTPException, Query, Path, Body, status
+from sqlmodel import Session, select, func
+
+from app.api.deps import CurrentUser, SessionDep
+from app.models.token import Message
+from app.models.workout import (
+ Workout,
+ WorkoutCreate,
+ WorkoutUpdate,
+ WorkoutPublic,
+ WorkoutsPublic,
+ WorkoutWithExercisesPublic,
+ Exercise,
+ ExerciseCreate,
+ ExerciseUpdate,
+ ExercisePublic,
+)
+from app.crudFuncs import update_personal_bests_after_workout
+
+
+router = APIRouter(prefix="/workouts", tags=["workouts"])
+
+
+@router.post("/", response_model=WorkoutPublic)
+def create_workout(
+ *,
+ session: SessionDep,
+ current_user: CurrentUser,
+ workout_in: WorkoutCreate = Body(
+ ...,
+ examples=[
+ {
+ "name": "Monday Strength Training",
+ "description": "Focus on upper body strength",
+ "scheduled_date": "2025-05-20T08:00:00Z",
+ "duration_minutes": 60
+ }
+ ],
+ )
+) -> Any:
+ """
+ Create a new workout.
+
+ This endpoint allows users to create a new workout plan with details such as name,
+ description, scheduled date, and expected duration.
+
+ - **name**: Required. The name of the workout (1-255 characters)
+ - **description**: Optional. A detailed description of the workout (up to 1000 characters)
+ - **scheduled_date**: Optional. When the workout is scheduled to take place
+ - **duration_minutes**: Optional. The expected duration of the workout in minutes
+
+ Returns the created workout with its ID and other metadata.
+ """
+ workout = Workout(
+ user_id=current_user.id,
+ name=workout_in.name,
+ description=workout_in.description,
+ scheduled_date=workout_in.scheduled_date,
+ duration_minutes=workout_in.duration_minutes,
+ is_completed=False,
+ created_at=datetime.utcnow(),
+ )
+
+ session.add(workout)
+ session.commit()
+ session.refresh(workout)
+
+ return workout
+
+
+@router.get("/", response_model=WorkoutsPublic)
+def get_workouts(
+ session: SessionDep,
+ current_user: CurrentUser,
+ skip: int = Query(0, description="Number of records to skip for pagination"),
+ limit: int = Query(100, description="Maximum number of records to return")
+) -> Any:
+ """
+ Get all workouts for the current user.
+
+ This endpoint retrieves all workouts created by the current user, with pagination support.
+
+ - **skip**: Number of records to skip (for pagination)
+ - **limit**: Maximum number of records to return (for pagination)
+
+ Returns a list of workouts and the total count.
+ """
+ # Get count of user's workouts
+ count_statement = (
+ select(func.count())
+ .select_from(Workout)
+ .where(Workout.user_id == current_user.id)
+ )
+ count = session.exec(count_statement).one()
+
+ # Get user's workouts with pagination
+ statement = (
+ select(Workout)
+ .where(Workout.user_id == current_user.id)
+ .offset(skip)
+ .limit(limit)
+ )
+ workouts = session.exec(statement).all()
+
+ # Add exercise count to each workout
+ workout_data = []
+ for workout in workouts:
+ # Count exercises for this workout
+ exercise_count_stmt = (
+ select(func.count())
+ .select_from(Exercise)
+ .where(Exercise.workout_id == workout.id)
+ )
+ exercise_count = session.exec(exercise_count_stmt).one()
+
+ # Convert to dict and add exercise_count
+ workout_dict = workout.model_dump()
+ workout_dict["exercise_count"] = exercise_count
+ workout_data.append(workout_dict)
+
+ return WorkoutsPublic(data=workout_data, count=count)
+
+
+@router.get("/{workout_id}", response_model=WorkoutWithExercisesPublic)
+def get_workout(
+ workout_id: uuid.UUID = Path(..., description="The ID of the workout to retrieve"),
+ session: SessionDep = None,
+ current_user: CurrentUser = None,
+) -> Any:
+ """
+ Get a specific workout by ID.
+
+ This endpoint retrieves a specific workout by its ID, including all exercises
+ associated with the workout.
+
+ - **workout_id**: The UUID of the workout to retrieve
+
+ Returns the workout with its exercises.
+
+ Raises:
+ - 404: If the workout is not found
+ - 403: If the workout doesn't belong to the current user
+ """
+ workout = session.get(Workout, workout_id)
+
+ if not workout:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail="Workout not found"
+ )
+
+ if workout.user_id != current_user.id:
+ raise HTTPException(
+ status_code=status.HTTP_403_FORBIDDEN,
+ detail="Not enough permissions"
+ )
+
+ return workout
+
+
+@router.put("/{workout_id}", response_model=WorkoutPublic)
+def update_workout(
+ *,
+ session: SessionDep,
+ current_user: CurrentUser,
+ workout_id: uuid.UUID = Path(..., description="The ID of the workout to update"),
+ workout_in: WorkoutUpdate = Body(
+ ...,
+ examples=[
+ {
+ "name": "Updated Strength Training",
+ "description": "Updated focus on full body",
+ "scheduled_date": "2025-05-21T09:00:00Z",
+ "duration_minutes": 75,
+ "is_completed": False
+ }
+ ],
+ )
+) -> Any:
+ """
+ Update a workout.
+
+ This endpoint allows users to update an existing workout's details.
+
+ - **workout_id**: The UUID of the workout to update
+ - **name**: Optional. The updated name of the workout
+ - **description**: Optional. The updated description
+ - **scheduled_date**: Optional. The updated scheduled date
+ - **completed_date**: Optional. When the workout was completed
+ - **duration_minutes**: Optional. The updated expected duration
+ - **is_completed**: Optional. Whether the workout is marked as completed
+
+ Returns the updated workout.
+
+ Raises:
+ - 404: If the workout is not found
+ - 403: If the workout doesn't belong to the current user
+ """
+ workout = session.get(Workout, workout_id)
+
+ if not workout:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail="Workout not found"
+ )
+
+ if workout.user_id != current_user.id:
+ raise HTTPException(
+ status_code=status.HTTP_403_FORBIDDEN,
+ detail="Not enough permissions"
+ )
+
+ update_dict = workout_in.model_dump(exclude_unset=True)
+
+ # Set updated_at timestamp
+ update_dict["updated_at"] = datetime.utcnow()
+
+ workout.sqlmodel_update(update_dict)
+ session.add(workout)
+ session.commit()
+ session.refresh(workout)
+
+ return workout
+
+
+@router.delete("/{workout_id}", response_model=Message)
+def delete_workout(
+ workout_id: uuid.UUID = Path(..., description="The ID of the workout to delete"),
+ session: SessionDep = None,
+ current_user: CurrentUser = None,
+) -> Any:
+ """
+ Delete a workout.
+
+ This endpoint allows users to delete a workout. All associated exercises will also be deleted.
+
+ - **workout_id**: The UUID of the workout to delete
+
+ Returns a success message.
+
+ Raises:
+ - 404: If the workout is not found
+ - 403: If the workout doesn't belong to the current user
+ """
+ workout = session.get(Workout, workout_id)
+
+ if not workout:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail="Workout not found"
+ )
+
+ if workout.user_id != current_user.id:
+ raise HTTPException(
+ status_code=status.HTTP_403_FORBIDDEN,
+ detail="Not enough permissions"
+ )
+
+ session.delete(workout)
+ session.commit()
+
+ return Message(message="Workout deleted successfully")
+
+# TODO - Kush, added PB updating, test
+
+@router.post("/{workout_id}/complete", response_model=WorkoutPublic)
+def complete_workout(
+ workout_id: uuid.UUID = Path(..., description="The ID of the workout to mark as completed"),
+ session: SessionDep = None,
+ current_user: CurrentUser = None,
+) -> Any:
+ """
+ Mark a workout as completed.
+
+ This endpoint allows users to mark a workout as completed, setting the completed_date
+ to the current time and is_completed to True.
+
+ - **workout_id**: The UUID of the workout to mark as completed
+
+ Returns the updated workout.
+
+ Raises:
+ - 404: If the workout is not found
+ - 403: If the workout doesn't belong to the current user
+ """
+ workout = session.get(Workout, workout_id)
+
+ if not workout:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail="Workout not found"
+ )
+
+ if workout.user_id != current_user.id:
+ raise HTTPException(
+ status_code=status.HTTP_403_FORBIDDEN,
+ detail="Not enough permissions"
+ )
+
+ workout.is_completed = True
+ workout.completed_date = datetime.utcnow()
+ workout.updated_at = datetime.utcnow()
+
+ session.add(workout)
+ session.commit()
+ session.refresh(workout)
+
+ #added for updating personalbests
+ update_personal_bests_after_workout(session=session, workout=workout)
+
+
+ return workout
+
+
+@router.get("/scheduled", response_model=WorkoutsPublic)
+def get_scheduled_workouts(
+ session: SessionDep,
+ current_user: CurrentUser,
+ skip: int = Query(0, description="Number of records to skip for pagination"),
+ limit: int = Query(100, description="Maximum number of records to return")
+) -> Any:
+ """
+ Get upcoming scheduled workouts.
+
+ This endpoint retrieves all scheduled workouts for the current user that have not been
+ completed yet, ordered by scheduled date.
+
+ - **skip**: Number of records to skip (for pagination)
+ - **limit**: Maximum number of records to return (for pagination)
+
+ Returns a list of upcoming workouts and the total count.
+ """
+ # Get count of user's scheduled workouts
+ count_statement = (
+ select(func.count())
+ .select_from(Workout)
+ .where(Workout.user_id == current_user.id)
+ .where(Workout.is_completed == False)
+ .where(Workout.scheduled_date != None)
+ .where(Workout.scheduled_date >= datetime.utcnow())
+ )
+ count = session.exec(count_statement).one()
+
+ # Get user's scheduled workouts with pagination
+ statement = (
+ select(Workout)
+ .where(Workout.user_id == current_user.id)
+ .where(Workout.is_completed == False)
+ .where(Workout.scheduled_date != None)
+ .where(Workout.scheduled_date >= datetime.utcnow())
+ .order_by(Workout.scheduled_date)
+ .offset(skip)
+ .limit(limit)
+ )
+ workouts = session.exec(statement).all()
+
+ return WorkoutsPublic(data=workouts, count=count)
+
+
+@router.get("/completed", response_model=WorkoutsPublic)
+def get_completed_workouts(
+ session: SessionDep,
+ current_user: CurrentUser,
+ skip: int = Query(0, description="Number of records to skip for pagination"),
+ limit: int = Query(100, description="Maximum number of records to return")
+) -> Any:
+ """
+ Get completed workouts.
+
+ This endpoint retrieves all completed workouts for the current user,
+ ordered by completed date (most recent first).
+
+ - **skip**: Number of records to skip (for pagination)
+ - **limit**: Maximum number of records to return (for pagination)
+
+ Returns a list of completed workouts and the total count.
+ """
+ # Get count of user's completed workouts
+ count_statement = (
+ select(func.count())
+ .select_from(Workout)
+ .where(Workout.user_id == current_user.id)
+ .where(Workout.is_completed == True)
+ )
+ count = session.exec(count_statement).one()
+
+ # Get user's completed workouts with pagination
+ statement = (
+ select(Workout)
+ .where(Workout.user_id == current_user.id)
+ .where(Workout.is_completed == True)
+ .order_by(Workout.completed_date.desc())
+ .offset(skip)
+ .limit(limit)
+ )
+ workouts = session.exec(statement).all()
+
+ return WorkoutsPublic(data=workouts, count=count)
+
+
+@router.get("/stats", response_model=dict)
+def get_workout_stats(
+ session: SessionDep,
+ current_user: CurrentUser,
+) -> Any:
+ """
+ Get workout statistics.
+
+ This endpoint provides statistics about the user's workouts, including:
+ - Total number of workouts
+ - Number of completed workouts
+ - Number of scheduled workouts
+ - Total workout duration (in minutes)
+ - Average workout duration (in minutes)
+
+ Returns a dictionary with the statistics.
+ """
+ # Total workouts
+ total_workouts_stmt = (
+ select(func.count())
+ .select_from(Workout)
+ .where(Workout.user_id == current_user.id)
+ )
+ total_workouts = session.exec(total_workouts_stmt).one()
+
+ # Completed workouts
+ completed_workouts_stmt = (
+ select(func.count())
+ .select_from(Workout)
+ .where(Workout.user_id == current_user.id)
+ .where(Workout.is_completed == True)
+ )
+ completed_workouts = session.exec(completed_workouts_stmt).one()
+
+ # Scheduled workouts
+ scheduled_workouts_stmt = (
+ select(func.count())
+ .select_from(Workout)
+ .where(Workout.user_id == current_user.id)
+ .where(Workout.is_completed == False)
+ .where(Workout.scheduled_date != None)
+ .where(Workout.scheduled_date >= datetime.utcnow())
+ )
+ scheduled_workouts = session.exec(scheduled_workouts_stmt).one()
+
+ # Total duration
+ total_duration_stmt = (
+ select(func.sum(Workout.duration_minutes))
+ .where(Workout.user_id == current_user.id)
+ .where(Workout.duration_minutes != None)
+ )
+ total_duration = session.exec(total_duration_stmt).one() or 0
+
+ # Average duration
+ avg_duration_stmt = (
+ select(func.avg(Workout.duration_minutes))
+ .where(Workout.user_id == current_user.id)
+ .where(Workout.duration_minutes != None)
+ )
+ avg_duration = session.exec(avg_duration_stmt).one() or 0
+
+ return {
+ "total_workouts": total_workouts,
+ "completed_workouts": completed_workouts,
+ "scheduled_workouts": scheduled_workouts,
+ "total_duration_minutes": total_duration,
+ "average_duration_minutes": round(avg_duration, 1),
+ }
+
+
+@router.post("/{workout_id}/exercises", response_model=ExercisePublic)
+def add_exercise(
+ *,
+ session: SessionDep,
+ current_user: CurrentUser,
+ workout_id: uuid.UUID = Path(..., description="The ID of the workout to add the exercise to"),
+ exercise_in: ExerciseCreate = Body(
+ ...,
+ examples=[
+ {
+ "name": "Bench Press",
+ "description": "Flat bench press with barbell",
+ "category": "Strength",
+ "sets": 4,
+ "reps": 8,
+ "weight": 135.5
+ }
+ ],
+ )
+) -> Any:
+ """
+ Add an exercise to a workout.
+
+ This endpoint allows users to add an exercise to an existing workout.
+
+ - **workout_id**: The UUID of the workout to add the exercise to
+ - **name**: Required. The name of the exercise (1-255 characters)
+ - **description**: Optional. A detailed description of the exercise (up to 1000 characters)
+ - **category**: Required. The category of the exercise (e.g., Strength, Cardio)
+ - **sets**: Optional. The number of sets
+ - **reps**: Optional. The number of repetitions per set
+ - **weight**: Optional. The weight used (in pounds/kg)
+
+ Returns the created exercise.
+
+ Raises:
+ - 404: If the workout is not found
+ - 403: If the workout doesn't belong to the current user
+ """
+ # Check if workout exists and belongs to user
+ workout = session.get(Workout, workout_id)
+
+ if not workout:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail="Workout not found"
+ )
+
+ if workout.user_id != current_user.id:
+ raise HTTPException(
+ status_code=status.HTTP_403_FORBIDDEN,
+ detail="Not enough permissions"
+ )
+
+ # Create exercise
+ exercise = Exercise(
+ workout_id=workout_id,
+ name=exercise_in.name,
+ description=exercise_in.description,
+ category=exercise_in.category,
+ sets=exercise_in.sets,
+ reps=exercise_in.reps,
+ weight=exercise_in.weight,
+ created_at=datetime.utcnow(),
+ )
+
+ session.add(exercise)
+ session.commit()
+ session.refresh(exercise)
+
+ return exercise
+
+
+@router.get("/{workout_id}/exercises", response_model=List[ExercisePublic])
+def get_exercises(
+ workout_id: uuid.UUID = Path(..., description="The ID of the workout to get exercises for"),
+ session: SessionDep = None,
+ current_user: CurrentUser = None,
+) -> Any:
+ """
+ Get exercises for a workout.
+
+ This endpoint retrieves all exercises associated with a specific workout.
+
+ - **workout_id**: The UUID of the workout to get exercises for
+
+ Returns a list of exercises.
+
+ Raises:
+ - 404: If the workout is not found
+ - 403: If the workout doesn't belong to the current user
+ """
+ # Check if workout exists and belongs to user
+ workout = session.get(Workout, workout_id)
+
+ if not workout:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail="Workout not found"
+ )
+
+ if workout.user_id != current_user.id:
+ raise HTTPException(
+ status_code=status.HTTP_403_FORBIDDEN,
+ detail="Not enough permissions"
+ )
+
+ # Get exercises
+ statement = select(Exercise).where(Exercise.workout_id == workout_id)
+ exercises = session.exec(statement).all()
+
+ return exercises
+
+
+@router.put("/exercises/{exercise_id}", response_model=ExercisePublic)
+def update_exercise(
+ *,
+ session: SessionDep,
+ current_user: CurrentUser,
+ exercise_id: uuid.UUID = Path(..., description="The ID of the exercise to update"),
+ exercise_in: ExerciseUpdate = Body(
+ ...,
+ examples=[
+ {
+ "name": "Incline Bench Press",
+ "description": "Incline bench press with dumbbells",
+ "category": "Strength",
+ "sets": 3,
+ "reps": 10,
+ "weight": 50.0
+ }
+ ],
+ )
+) -> Any:
+ """
+ Update an exercise.
+
+ This endpoint allows users to update an existing exercise's details.
+
+ - **exercise_id**: The UUID of the exercise to update
+ - **name**: Optional. The updated name of the exercise
+ - **description**: Optional. The updated description
+ - **category**: Optional. The updated category
+ - **sets**: Optional. The updated number of sets
+ - **reps**: Optional. The updated number of repetitions
+ - **weight**: Optional. The updated weight
+
+ Returns the updated exercise.
+
+ Raises:
+ - 404: If the exercise is not found
+ - 403: If the exercise's workout doesn't belong to the current user
+ """
+ # Get exercise
+ exercise = session.get(Exercise, exercise_id)
+
+ if not exercise:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail="Exercise not found"
+ )
+
+ # Check if workout belongs to user
+ workout = session.get(Workout, exercise.workout_id)
+
+ if not workout or workout.user_id != current_user.id:
+ raise HTTPException(
+ status_code=status.HTTP_403_FORBIDDEN,
+ detail="Not enough permissions"
+ )
+
+ # Update exercise
+ update_dict = exercise_in.model_dump(exclude_unset=True)
+
+ # Set updated_at timestamp
+ update_dict["updated_at"] = datetime.utcnow()
+
+ exercise.sqlmodel_update(update_dict)
+ session.add(exercise)
+ session.commit()
+ session.refresh(exercise)
+
+ return exercise
+
+
+@router.delete("/exercises/{exercise_id}", response_model=Message)
+def delete_exercise(
+ exercise_id: uuid.UUID = Path(..., description="The ID of the exercise to delete"),
+ session: SessionDep = None,
+ current_user: CurrentUser = None,
+) -> Any:
+ """
+ Delete an exercise.
+
+ This endpoint allows users to delete an exercise from a workout.
+
+ - **exercise_id**: The UUID of the exercise to delete
+
+ Returns a success message.
+
+ Raises:
+ - 404: If the exercise is not found
+ - 403: If the exercise's workout doesn't belong to the current user
+ """
+ # Get exercise
+ exercise = session.get(Exercise, exercise_id)
+
+ if not exercise:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail="Exercise not found"
+ )
+
+ # Check if workout belongs to user
+ workout = session.get(Workout, exercise.workout_id)
+
+ if not workout or workout.user_id != current_user.id:
+ raise HTTPException(
+ status_code=status.HTTP_403_FORBIDDEN,
+ detail="Not enough permissions"
+ )
+
+ # Delete exercise
+ session.delete(exercise)
+ session.commit()
+
+ return Message(message="Exercise deleted successfully")
+
+
+@router.get("/exercises/library", response_model=List[dict])
+def get_exercise_library() -> Any:
+ """
+ Get exercise library.
+
+ This endpoint provides a library of common exercises that users can add to their workouts.
+ The library is organized by category and includes exercise names and descriptions.
+
+ Returns a list of exercise categories, each containing a list of exercises.
+ """
+ # This is a static library of common exercises
+ return [
+ {
+ "category": "Strength",
+ "exercises": [
+ {
+ "name": "Bench Press",
+ "description": "Lie on a flat bench and press a barbell upwards",
+ "muscle_groups": ["Chest", "Shoulders", "Triceps"]
+ },
+ {
+ "name": "Squat",
+ "description": "Lower your body by bending your knees and hips, then return to standing",
+ "muscle_groups": ["Quadriceps", "Hamstrings", "Glutes"]
+ },
+ {
+ "name": "Deadlift",
+ "description": "Lift a barbell from the ground to hip level",
+ "muscle_groups": ["Back", "Hamstrings", "Glutes"]
+ },
+ {
+ "name": "Overhead Press",
+ "description": "Press a barbell or dumbbells overhead from shoulder height",
+ "muscle_groups": ["Shoulders", "Triceps"]
+ },
+ {
+ "name": "Pull-up",
+ "description": "Pull your body up to a bar from a hanging position",
+ "muscle_groups": ["Back", "Biceps"]
+ }
+ ]
+ },
+ {
+ "category": "Cardio",
+ "exercises": [
+ {
+ "name": "Running",
+ "description": "Running at a steady pace",
+ "muscle_groups": ["Legs", "Core"]
+ },
+ {
+ "name": "Cycling",
+ "description": "Cycling on a stationary bike or outdoors",
+ "muscle_groups": ["Legs"]
+ },
+ {
+ "name": "Rowing",
+ "description": "Using a rowing machine for full-body cardio",
+ "muscle_groups": ["Back", "Arms", "Legs"]
+ },
+ {
+ "name": "Jump Rope",
+ "description": "Skipping with a jump rope",
+ "muscle_groups": ["Legs", "Shoulders"]
+ },
+ {
+ "name": "HIIT",
+ "description": "High-intensity interval training with alternating work and rest periods",
+ "muscle_groups": ["Full Body"]
+ }
+ ]
+ },
+ {
+ "category": "Flexibility",
+ "exercises": [
+ {
+ "name": "Yoga",
+ "description": "Various yoga poses and flows",
+ "muscle_groups": ["Full Body"]
+ },
+ {
+ "name": "Static Stretching",
+ "description": "Holding stretches for extended periods",
+ "muscle_groups": ["Full Body"]
+ },
+ {
+ "name": "Dynamic Stretching",
+ "description": "Moving stretches that prepare the body for exercise",
+ "muscle_groups": ["Full Body"]
+ }
+ ]
+ }
+ ]
\ No newline at end of file
diff --git a/backend/app/core/db.py b/backend/app/core/db.py
index ba991fb36d..e4f55db883 100644
--- a/backend/app/core/db.py
+++ b/backend/app/core/db.py
@@ -31,3 +31,10 @@ def init_db(session: Session) -> None:
is_superuser=True,
)
user = crud.create_user(session=session, user_create=user_in)
+
+def get_session():
+ """
+ Provide a transactional SQLModel Session for dependency injection.
+ """
+ with Session(engine) as session:
+ yield session
diff --git a/backend/app/crud/__init__.py b/backend/app/crud/__init__.py
new file mode 100644
index 0000000000..f3664801c3
--- /dev/null
+++ b/backend/app/crud/__init__.py
@@ -0,0 +1,74 @@
+from app.crud.item import (
+ create_item,
+ delete_item,
+ get_item,
+ get_items,
+ update_item,
+)
+from app.crud.social import (
+ create_workout_post,
+ delete_workout_post,
+ follow_user,
+ get_feed_posts,
+ get_personal_feed_posts,
+ get_public_feed_posts,
+ get_combined_feed_posts,
+ get_follower_count,
+ get_followers,
+ get_following,
+ get_following_count,
+ get_user_workout_posts,
+ get_workout_post,
+ is_following,
+ is_mutual_follow,
+ search_users,
+ unfollow_user,
+ update_workout_post,
+)
+from app.crud.user import (
+ authenticate,
+ create_user,
+ get_user_by_email,
+ get_user_by_id,
+ get_users,
+ update_user,
+)
+
+__all__ = [
+ # User operations
+ "authenticate",
+ "create_user",
+ "get_user_by_email",
+ "get_user_by_id",
+ "get_users",
+ "update_user",
+
+ # Item operations
+ "create_item",
+ "delete_item",
+ "get_item",
+ "get_items",
+ "update_item",
+
+ # Social operations - User Follow
+ "follow_user",
+ "unfollow_user",
+ "get_followers",
+ "get_following",
+ "is_following",
+ "get_follower_count",
+ "get_following_count",
+ "search_users",
+
+ # Social operations - Workout Posts
+ "create_workout_post",
+ "get_workout_post",
+ "get_user_workout_posts",
+ "get_feed_posts",
+ "get_personal_feed_posts",
+ "get_public_feed_posts",
+ "get_combined_feed_posts",
+ "is_mutual_follow",
+ "update_workout_post",
+ "delete_workout_post",
+]
\ No newline at end of file
diff --git a/backend/app/crud/item.py b/backend/app/crud/item.py
new file mode 100644
index 0000000000..8d86c2a558
--- /dev/null
+++ b/backend/app/crud/item.py
@@ -0,0 +1,76 @@
+import uuid
+from typing import List, Optional, Tuple
+
+from sqlmodel import Session, select, func
+
+from app.models.item import Item, ItemCreate, ItemUpdate
+
+
+def create_item(*, session: Session, item_in: ItemCreate, owner_id: uuid.UUID) -> Item:
+ """
+ Create a new item.
+ """
+ db_item = Item.model_validate(item_in, update={"owner_id": owner_id})
+ session.add(db_item)
+ session.commit()
+ session.refresh(db_item)
+ return db_item
+
+
+def get_item(*, session: Session, item_id: uuid.UUID) -> Optional[Item]:
+ """
+ Get an item by ID.
+ """
+ return session.get(Item, item_id)
+
+
+def get_items(
+ *, session: Session, owner_id: Optional[uuid.UUID] = None, skip: int = 0, limit: int = 100
+) -> Tuple[List[Item], int]:
+ """
+ Get multiple items with pagination.
+ """
+ if owner_id:
+ count_statement = (
+ select(func.count())
+ .select_from(Item)
+ .where(Item.owner_id == owner_id)
+ )
+ count = session.exec(count_statement).one()
+ statement = (
+ select(Item)
+ .where(Item.owner_id == owner_id)
+ .offset(skip)
+ .limit(limit)
+ )
+ else:
+ count_statement = select(func.count()).select_from(Item)
+ count = session.exec(count_statement).one()
+ statement = select(Item).offset(skip).limit(limit)
+
+ items = session.exec(statement).all()
+ return items, count
+
+
+def update_item(
+ *, session: Session, db_item: Item, item_in: ItemUpdate
+) -> Item:
+ """
+ Update an item.
+ """
+ update_dict = item_in.model_dump(exclude_unset=True)
+ db_item.sqlmodel_update(update_dict)
+ session.add(db_item)
+ session.commit()
+ session.refresh(db_item)
+ return db_item
+
+
+def delete_item(*, session: Session, item_id: uuid.UUID) -> None:
+ """
+ Delete an item.
+ """
+ item = session.get(Item, item_id)
+ if item:
+ session.delete(item)
+ session.commit()
\ No newline at end of file
diff --git a/backend/app/crud/social.py b/backend/app/crud/social.py
new file mode 100644
index 0000000000..47c16db7c2
--- /dev/null
+++ b/backend/app/crud/social.py
@@ -0,0 +1,470 @@
+import uuid
+from datetime import datetime
+from typing import List, Optional, Tuple
+
+from sqlmodel import Session, select, func, or_, and_
+
+from app.models.social import UserFollow, WorkoutPost, WorkoutPostCreate, WorkoutPostUpdate
+from app.models.user import User
+
+
+# User Follow Operations
+
+def follow_user(*, session: Session, follower_id: uuid.UUID, followed_id: uuid.UUID) -> UserFollow:
+ """
+ Create a follow relationship between users.
+ """
+ # Check if the follow relationship already exists
+ statement = select(UserFollow).where(
+ and_(
+ UserFollow.follower_id == follower_id,
+ UserFollow.followed_id == followed_id
+ )
+ )
+ existing_follow = session.exec(statement).first()
+
+ if existing_follow:
+ return existing_follow
+
+ # Create new follow relationship
+ follow = UserFollow(follower_id=follower_id, followed_id=followed_id)
+ session.add(follow)
+ session.commit()
+ session.refresh(follow)
+ return follow
+
+
+def unfollow_user(*, session: Session, follower_id: uuid.UUID, followed_id: uuid.UUID) -> bool:
+ """
+ Remove a follow relationship between users.
+ """
+ statement = select(UserFollow).where(
+ and_(
+ UserFollow.follower_id == follower_id,
+ UserFollow.followed_id == followed_id
+ )
+ )
+ follow = session.exec(statement).first()
+
+ if follow:
+ session.delete(follow)
+ session.commit()
+ return True
+ return False
+
+
+def get_followers(*, session: Session, user_id: uuid.UUID, skip: int = 0, limit: int = 100) -> Tuple[List[User], int]:
+ """
+ Get users who follow the specified user.
+ """
+ # Count followers
+ count_statement = (
+ select(func.count())
+ .select_from(UserFollow)
+ .where(UserFollow.followed_id == user_id)
+ )
+ count = session.exec(count_statement).one()
+
+ # Get followers with pagination
+ statement = (
+ select(User)
+ .join(UserFollow, User.id == UserFollow.follower_id)
+ .where(UserFollow.followed_id == user_id)
+ .offset(skip)
+ .limit(limit)
+ )
+ followers = session.exec(statement).all()
+
+ return followers, count
+
+
+def get_following(*, session: Session, user_id: uuid.UUID, skip: int = 0, limit: int = 100) -> Tuple[List[User], int]:
+ """
+ Get users that the specified user follows.
+ """
+ # Count following
+ count_statement = (
+ select(func.count())
+ .select_from(UserFollow)
+ .where(UserFollow.follower_id == user_id)
+ )
+ count = session.exec(count_statement).one()
+
+ # Get following with pagination
+ statement = (
+ select(User)
+ .join(UserFollow, User.id == UserFollow.followed_id)
+ .where(UserFollow.follower_id == user_id)
+ .offset(skip)
+ .limit(limit)
+ )
+ following = session.exec(statement).all()
+
+ return following, count
+
+
+def is_following(*, session: Session, follower_id: uuid.UUID, followed_id: uuid.UUID) -> bool:
+ """
+ Check if a user is following another user.
+ """
+ statement = select(UserFollow).where(
+ and_(
+ UserFollow.follower_id == follower_id,
+ UserFollow.followed_id == followed_id
+ )
+ )
+ follow = session.exec(statement).first()
+ return follow is not None
+
+
+def get_follower_count(*, session: Session, user_id: uuid.UUID) -> int:
+ """
+ Get the number of followers for a user.
+ """
+ statement = (
+ select(func.count())
+ .select_from(UserFollow)
+ .where(UserFollow.followed_id == user_id)
+ )
+ return session.exec(statement).one()
+
+
+def get_following_count(*, session: Session, user_id: uuid.UUID) -> int:
+ """
+ Get the number of users a user is following.
+ """
+ statement = (
+ select(func.count())
+ .select_from(UserFollow)
+ .where(UserFollow.follower_id == user_id)
+ )
+ return session.exec(statement).one()
+
+
+def is_mutual_follow(*, session: Session, user1_id: uuid.UUID, user2_id: uuid.UUID) -> bool:
+ """
+ Check if two users follow each other (mutual follow).
+ """
+ # Check if user1 follows user2 AND user2 follows user1
+ user1_follows_user2 = is_following(session=session, follower_id=user1_id, followed_id=user2_id)
+ user2_follows_user1 = is_following(session=session, follower_id=user2_id, followed_id=user1_id)
+
+ return user1_follows_user2 and user2_follows_user1
+
+
+# Workout Post Operations
+
+def create_workout_post(*, session: Session, post_in: WorkoutPostCreate, user_id: uuid.UUID) -> WorkoutPost:
+ """
+ Create a new workout post.
+ """
+ db_post = WorkoutPost.model_validate(post_in, update={"user_id": user_id})
+ session.add(db_post)
+ session.commit()
+ session.refresh(db_post)
+ return db_post
+
+
+def get_workout_post(*, session: Session, post_id: uuid.UUID) -> Optional[WorkoutPost]:
+ """
+ Get a workout post by ID.
+ """
+ return session.get(WorkoutPost, post_id)
+
+
+def get_user_workout_posts(
+ *, session: Session, user_id: uuid.UUID, skip: int = 0, limit: int = 100
+) -> Tuple[List[WorkoutPost], int]:
+ """
+ Get workout posts for a specific user.
+ """
+ count_statement = (
+ select(func.count())
+ .select_from(WorkoutPost)
+ .where(WorkoutPost.user_id == user_id)
+ )
+ count = session.exec(count_statement).one()
+
+ statement = (
+ select(WorkoutPost)
+ .where(WorkoutPost.user_id == user_id)
+ .order_by(WorkoutPost.created_at.desc())
+ .offset(skip)
+ .limit(limit)
+ )
+ posts = session.exec(statement).all()
+
+ return posts, count
+
+
+def get_feed_posts(
+ *, session: Session, user_id: uuid.UUID, skip: int = 0, limit: int = 100, feed_type: str = "personal"
+) -> Tuple[List[WorkoutPost], int]:
+ """
+ Get workout posts based on feed type with privacy filtering.
+
+ Args:
+ session: Database session
+ user_id: Current user's ID
+ skip: Number of posts to skip for pagination
+ limit: Maximum number of posts to return
+ feed_type: "personal", "public", or "combined"
+ """
+ if feed_type == "public":
+ return get_public_feed_posts(session=session, user_id=user_id, skip=skip, limit=limit)
+ elif feed_type == "combined":
+ return get_combined_feed_posts(session=session, user_id=user_id, skip=skip, limit=limit)
+ else: # personal feed
+ return get_personal_feed_posts(session=session, user_id=user_id, skip=skip, limit=limit)
+
+
+def get_personal_feed_posts(
+ *, session: Session, user_id: uuid.UUID, skip: int = 0, limit: int = 100
+) -> Tuple[List[WorkoutPost], int]:
+ """
+ Get workout posts from users that the specified user follows (personal feed).
+ Includes privacy filtering: private posts only visible if mutual follow.
+ """
+ # Early check: if no WorkoutPost records exist, return empty result immediately
+ total_posts_check = session.exec(select(func.count()).select_from(WorkoutPost)).one()
+ if total_posts_check == 0:
+ return [], 0
+
+ # Get IDs of users that the current user follows
+ following_statement = (
+ select(UserFollow.followed_id)
+ .where(UserFollow.follower_id == user_id)
+ )
+ following_ids = session.exec(following_statement).all()
+
+ # Include the user's own posts in the feed
+ following_ids.append(user_id)
+
+ # If no one to follow (including self), return empty
+ if not following_ids:
+ return [], 0
+
+ # Build privacy filter conditions
+ privacy_conditions = []
+
+ for followed_id in following_ids:
+ if followed_id == user_id:
+ # User's own posts - always visible
+ privacy_conditions.append(
+ WorkoutPost.user_id == user_id
+ )
+ else:
+ # Posts from followed users
+ is_mutual = is_mutual_follow(session=session, user1_id=user_id, user2_id=followed_id)
+
+ if is_mutual:
+ # Mutual follow - can see both public and private posts
+ privacy_conditions.append(
+ WorkoutPost.user_id == followed_id
+ )
+ else:
+ # Not mutual - only public posts
+ privacy_conditions.append(
+ and_(
+ WorkoutPost.user_id == followed_id,
+ WorkoutPost.is_public == True
+ )
+ )
+
+ if not privacy_conditions:
+ return [], 0
+
+ # Combine all conditions with OR
+ combined_condition = or_(*privacy_conditions)
+
+ # Count posts
+ count_statement = (
+ select(func.count())
+ .select_from(WorkoutPost)
+ .where(combined_condition)
+ )
+ count = session.exec(count_statement).one()
+
+ # Get posts with pagination
+ statement = (
+ select(WorkoutPost)
+ .where(combined_condition)
+ .order_by(WorkoutPost.created_at.desc())
+ .offset(skip)
+ .limit(limit)
+ )
+ posts = session.exec(statement).all()
+
+ return posts, count
+
+
+def get_public_feed_posts(
+ *, session: Session, user_id: uuid.UUID, skip: int = 0, limit: int = 100
+) -> Tuple[List[WorkoutPost], int]:
+ """
+ Get all public workout posts from all users (discovery feed).
+ """
+ # Early check: if no WorkoutPost records exist, return empty result immediately
+ total_posts_check = session.exec(select(func.count()).select_from(WorkoutPost)).one()
+ if total_posts_check == 0:
+ return [], 0
+
+ # Count all public posts
+ count_statement = (
+ select(func.count())
+ .select_from(WorkoutPost)
+ .where(WorkoutPost.is_public == True)
+ )
+ count = session.exec(count_statement).one()
+
+ # Get public posts with pagination
+ statement = (
+ select(WorkoutPost)
+ .where(WorkoutPost.is_public == True)
+ .order_by(WorkoutPost.created_at.desc())
+ .offset(skip)
+ .limit(limit)
+ )
+ posts = session.exec(statement).all()
+
+ return posts, count
+
+
+def get_combined_feed_posts(
+ *, session: Session, user_id: uuid.UUID, skip: int = 0, limit: int = 100
+) -> Tuple[List[WorkoutPost], int]:
+ """
+ Get combined feed: personal feed + additional public posts.
+ """
+ # Get personal feed posts first
+ personal_posts, personal_count = get_personal_feed_posts(
+ session=session, user_id=user_id, skip=0, limit=limit * 2 # Get more to mix
+ )
+
+ # Get personal post IDs to exclude from public feed
+ personal_post_ids = [post.id for post in personal_posts]
+
+ # Get additional public posts not in personal feed
+ public_condition = and_(
+ WorkoutPost.is_public == True,
+ ~WorkoutPost.id.in_(personal_post_ids) if personal_post_ids else True
+ )
+
+ additional_public_statement = (
+ select(WorkoutPost)
+ .where(public_condition)
+ .order_by(WorkoutPost.created_at.desc())
+ .limit(limit)
+ )
+ additional_public_posts = session.exec(additional_public_statement).all()
+
+ # Combine and sort by created_at
+ all_posts = personal_posts + additional_public_posts
+ all_posts.sort(key=lambda x: x.created_at, reverse=True)
+
+ # Apply pagination to combined results
+ paginated_posts = all_posts[skip:skip + limit]
+
+ # Count would be total of personal + additional public (approximation)
+ total_count = personal_count + len(additional_public_posts)
+
+ return paginated_posts, total_count
+
+
+def update_workout_post(
+ *, session: Session, db_post: WorkoutPost, post_in: WorkoutPostUpdate
+) -> WorkoutPost:
+ """
+ Update a workout post.
+ """
+ update_dict = post_in.model_dump(exclude_unset=True)
+ update_dict["updated_at"] = datetime.utcnow()
+ db_post.sqlmodel_update(update_dict)
+ session.add(db_post)
+ session.commit()
+ session.refresh(db_post)
+ return db_post
+
+
+def delete_workout_post(*, session: Session, post_id: uuid.UUID) -> bool:
+ """
+ Delete a workout post.
+ """
+ post = session.get(WorkoutPost, post_id)
+ if post:
+ session.delete(post)
+ session.commit()
+ return True
+ return False
+
+
+def search_users(
+ *,
+ session: Session,
+ query: str,
+ current_user_id: uuid.UUID,
+ skip: int = 0,
+ limit: int = 20
+) -> Tuple[List[dict], int]:
+ """
+ Search for users by full_name or email (case-insensitive, partial matches).
+ Returns users with their social stats and is_following status.
+ Excludes the current user from results.
+ """
+ # Create the search pattern for case-insensitive partial matching
+ search_pattern = f"%{query.lower()}%"
+
+ # Base query to find users matching the search criteria
+ base_query = (
+ select(User)
+ .where(
+ and_(
+ User.id != current_user_id, # Exclude current user
+ or_(
+ func.lower(User.full_name).like(search_pattern),
+ func.lower(User.email).like(search_pattern)
+ )
+ )
+ )
+ )
+
+ # Count total matching users
+ count_statement = select(func.count()).select_from(base_query.subquery())
+ count = session.exec(count_statement).one()
+
+ # Get paginated results
+ users_statement = base_query.offset(skip).limit(limit)
+ users = session.exec(users_statement).all()
+
+ # Build result with social stats and is_following status
+ result = []
+ for user in users:
+ # Get follower and following counts
+ follower_count = get_follower_count(session=session, user_id=user.id)
+ following_count = get_following_count(session=session, user_id=user.id)
+
+ # Check if current user is following this user
+ is_following_user = is_following(
+ session=session,
+ follower_id=current_user_id,
+ followed_id=user.id
+ )
+
+ # Create user dict with extended info
+ user_dict = {
+ "id": user.id,
+ "email": user.email,
+ "full_name": user.full_name,
+ "is_active": user.is_active,
+ "is_superuser": user.is_superuser,
+ "gender": user.gender,
+ "date_of_birth": user.date_of_birth,
+ "weight": user.weight,
+ "height": user.height,
+ "follower_count": follower_count,
+ "following_count": following_count,
+ "is_following": is_following_user
+ }
+ result.append(user_dict)
+
+ return result, count
\ No newline at end of file
diff --git a/backend/app/crud.py b/backend/app/crud/user.py
similarity index 57%
rename from backend/app/crud.py
rename to backend/app/crud/user.py
index 905bf48724..0217bd3f2e 100644
--- a/backend/app/crud.py
+++ b/backend/app/crud/user.py
@@ -1,13 +1,15 @@
import uuid
-from typing import Any
-
-from sqlmodel import Session, select
+from typing import Any, List, Optional
+from sqlmodel import Session, select, func
from app.core.security import get_password_hash, verify_password
-from app.models import Item, ItemCreate, User, UserCreate, UserUpdate
+from app.models.user import User, UserCreate, UserUpdate
def create_user(*, session: Session, user_create: UserCreate) -> User:
+ """
+ Create a new user.
+ """
db_obj = User.model_validate(
user_create, update={"hashed_password": get_password_hash(user_create.password)}
)
@@ -18,6 +20,9 @@ def create_user(*, session: Session, user_create: UserCreate) -> User:
def update_user(*, session: Session, db_user: User, user_in: UserUpdate) -> Any:
+ """
+ Update a user.
+ """
user_data = user_in.model_dump(exclude_unset=True)
extra_data = {}
if "password" in user_data:
@@ -31,13 +36,26 @@ def update_user(*, session: Session, db_user: User, user_in: UserUpdate) -> Any:
return db_user
-def get_user_by_email(*, session: Session, email: str) -> User | None:
+def get_user_by_email(*, session: Session, email: str) -> Optional[User]:
+ """
+ Get a user by email.
+ """
statement = select(User).where(User.email == email)
session_user = session.exec(statement).first()
return session_user
-def authenticate(*, session: Session, email: str, password: str) -> User | None:
+def get_user_by_id(*, session: Session, user_id: uuid.UUID) -> Optional[User]:
+ """
+ Get a user by ID.
+ """
+ return session.get(User, user_id)
+
+
+def authenticate(*, session: Session, email: str, password: str) -> Optional[User]:
+ """
+ Authenticate a user.
+ """
db_user = get_user_by_email(session=session, email=email)
if not db_user:
return None
@@ -46,9 +64,16 @@ def authenticate(*, session: Session, email: str, password: str) -> User | None:
return db_user
-def create_item(*, session: Session, item_in: ItemCreate, owner_id: uuid.UUID) -> Item:
- db_item = Item.model_validate(item_in, update={"owner_id": owner_id})
- session.add(db_item)
- session.commit()
- session.refresh(db_item)
- return db_item
+def get_users(
+ *, session: Session, skip: int = 0, limit: int = 100
+) -> tuple[List[User], int]:
+ """
+ Get multiple users with pagination.
+ """
+ count_statement = select(func.count()).select_from(User)
+ count = session.exec(count_statement).one()
+
+ statement = select(User).offset(skip).limit(limit)
+ users = session.exec(statement).all()
+
+ return users, count
\ No newline at end of file
diff --git a/backend/app/crudFuncs.py b/backend/app/crudFuncs.py
new file mode 100644
index 0000000000..c3cbb72057
--- /dev/null
+++ b/backend/app/crudFuncs.py
@@ -0,0 +1,233 @@
+import uuid
+from typing import Any, List
+
+from sqlmodel import Session, select
+from app.core.security import get_password_hash, verify_password
+from app.models import Item, ItemCreate, User, UserCreate, UserUpdate
+from app.models import Workout, Exercise, PersonalBest, PersonalBestCreate
+from app.models import User, PushToken, CustomReminder
+from datetime import date, datetime, timezone
+
+# Define metric keys for exercises we want to track personal bests for
+TRACKED_EXERCISES = {
+ "bench press": "bench-press",
+ "squat": "squat",
+ "deadlift": "deadlift",
+ "push-ups": "pushups",
+ "pull-ups": "pullups",
+ # extend as needed
+}
+
+def create_user(*, session: Session, user_create: UserCreate) -> User:
+ db_obj = User.model_validate(
+ user_create, update={"hashed_password": get_password_hash(user_create.password)}
+ )
+ session.add(db_obj)
+ session.commit()
+ session.refresh(db_obj)
+ return db_obj
+
+
+def update_user(*, session: Session, db_user: User, user_in: UserUpdate) -> Any:
+ user_data = user_in.model_dump(exclude_unset=True)
+ extra_data = {}
+ if "password" in user_data:
+ password = user_data["password"]
+ hashed_password = get_password_hash(password)
+ extra_data["hashed_password"] = hashed_password
+ db_user.sqlmodel_update(user_data, update=extra_data)
+ session.add(db_user)
+ session.commit()
+ session.refresh(db_user)
+ return db_user
+
+
+def get_user_by_email(*, session: Session, email: str) -> User | None:
+ statement = select(User).where(User.email == email)
+ session_user = session.exec(statement).first()
+ return session_user
+
+
+def authenticate(*, session: Session, email: str, password: str) -> User | None:
+ db_user = get_user_by_email(session=session, email=email)
+ if not db_user:
+ return None
+ if not verify_password(password, db_user.hashed_password):
+ return None
+ return db_user
+
+
+def create_item(*, session: Session, item_in: ItemCreate, owner_id: uuid.UUID) -> Item:
+ db_item = Item.model_validate(item_in, update={"owner_id": owner_id})
+ session.add(db_item)
+ session.commit()
+ session.refresh(db_item)
+ return db_item
+
+
+
+#TODO test, create_or_update_personal_best will upsert only if the new value is strictly better.
+# Two getters: one for all metrics, one for a single metric.
+
+def create_or_update_personal_best(
+ *, session: Session, user_id: uuid.UUID, pb_in: PersonalBestCreate
+) -> PersonalBest:
+ # see if user already has a PB on this metric
+ stmt = select(PersonalBest).where(
+ PersonalBest.user_id == user_id,
+ PersonalBest.metric == pb_in.metric
+ )
+ existing = session.exec(stmt).one_or_none()
+
+ # update if new value is "better" (you define the logic per metric)
+ if existing:
+ if pb_in.value > existing.value:
+ existing.value = pb_in.value
+ existing.date = pb_in.date
+ session.add(existing)
+ else:
+ existing = PersonalBest.model_validate(pb_in, update={"user_id": user_id})
+ session.add(existing)
+
+ session.commit()
+ session.refresh(existing)
+ return existing
+
+def get_personal_bests(
+ *, session: Session, user_id: uuid.UUID
+) -> list[PersonalBest]:
+ stmt = select(PersonalBest).where(PersonalBest.user_id == user_id)
+ return session.exec(stmt).all()
+
+def get_personal_best(
+ *, session: Session, user_id: uuid.UUID, metric: str
+) -> PersonalBest | None:
+ stmt = select(PersonalBest).where(
+ PersonalBest.user_id == user_id,
+ PersonalBest.metric == metric
+ )
+ return session.exec(stmt).one_or_none()
+
+def update_personal_bests_after_workout(*, session: Session, workout: Workout):
+ user_id = workout.user_id
+
+ # Fetch all exercises associated with this workout
+ exercises = workout.exercises
+
+ for exercise in exercises:
+ name = exercise.name.lower()
+ metric_key = TRACKED_EXERCISES.get(name)
+ if not metric_key:
+ #
+ continue # skip exercises we don't track
+
+ # Use weight * reps as performance metric for now
+ value = (exercise.weight or 0) * (exercise.reps or 0)
+ if value <= 0:
+ #continue
+ value = 0
+
+ pb_in = PersonalBestCreate(metric=metric_key, value=value, date=date.today())
+
+ from app.crud import create_or_update_personal_best # to avoid circular imports
+ create_or_update_personal_best(session=session, user_id=user_id, pb_in=pb_in)
+
+# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+# PushToken & CustomReminder CRUD helpers
+# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+
+
+def create_or_update_push_token(
+ *,
+ session: Session,
+ user_id: uuid.UUID,
+ expo_token: str
+) -> PushToken:
+ """
+ If a PushToken already exists for this user_id, update its expo_token.
+ Otherwise, create a new row in push_tokens.
+ """
+ user_exists = session.exec(select(User).where(User.id == user_id)).first()
+ if not user_exists:
+ raise ValueError(f"User {user_id} not found when registering push token.")
+
+ # See if there's already a PushToken
+ stmt = select(PushToken).where(PushToken.user_id == user_id)
+ existing = session.exec(stmt).one_or_none()
+
+ if existing:
+ existing.expo_token = expo_token
+ session.add(existing)
+ session.commit()
+ session.refresh(existing)
+ return existing
+ else:
+ new_token = PushToken(user_id=user_id, expo_token=expo_token)
+ session.add(new_token)
+ session.commit()
+ session.refresh(new_token)
+ return new_token
+
+
+def schedule_custom_reminder(
+ *,
+ session: Session,
+ user_id: uuid.UUID,
+ expo_token: str,
+ remind_time: datetime,
+ message: str
+) -> CustomReminder:
+ """
+ Insert a new CustomReminder row. The background scheduler
+ (running every minute) will pick it up when remind_time <= now().
+ """
+ # (Optional) Confirm the user exists
+ user_exists = session.exec(select(User).where(User.id == user_id)).first()
+ if not user_exists:
+ raise ValueError(f"User {user_id} not found when scheduling reminder.")
+
+ new_reminder = CustomReminder(
+ user_id=user_id,
+ expo_token=expo_token,
+ remind_time=remind_time,
+ message=message,
+ sent_at=None,
+ )
+ session.add(new_reminder)
+ session.commit()
+ session.refresh(new_reminder)
+ return new_reminder
+
+
+def get_due_custom_reminders(*, session: Session) -> List[CustomReminder]:
+ """
+ Return all CustomReminder rows where:
+ - remind_time <= current UTC time
+ - sent_at is still None (i.e. not yet delivered).
+ """
+ now_utc = datetime.now(timezone.utc)
+ stmt = select(CustomReminder).where(
+ CustomReminder.remind_time <= now_utc,
+ CustomReminder.sent_at.is_(None),
+ )
+ return session.exec(stmt).all()
+
+
+def mark_reminder_as_sent(
+ *,
+ session: Session,
+ reminder_id: uuid.UUID
+) -> CustomReminder:
+ """
+ After sending a reminder, set its sent_at timestamp to UTC now,
+ so it will no longer appear in get_due_custom_reminders().
+ """
+ stmt = select(CustomReminder).where(CustomReminder.id == reminder_id)
+ reminder = session.exec(stmt).one_or_none()
+ if not reminder:
+ raise ValueError(f"CustomReminder {reminder_id} not found.")
+ reminder.sent_at = datetime.now(timezone.utc)
+ session.add(reminder)
+ session.commit()
+ session.refresh(reminder)
+ return reminder
diff --git a/backend/app/main.py b/backend/app/main.py
index 9a95801e74..f18e8ee808 100644
--- a/backend/app/main.py
+++ b/backend/app/main.py
@@ -5,7 +5,9 @@
from app.api.main import api_router
from app.core.config import settings
-
+from app.scheduler import start_scheduler
+from app.core.db import engine
+from sqlmodel import SQLModel
def custom_generate_unique_id(route: APIRoute) -> str:
return f"{route.tags[0]}-{route.name}"
@@ -21,7 +23,16 @@ def custom_generate_unique_id(route: APIRoute) -> str:
)
# Set all CORS enabled origins
-if settings.all_cors_origins:
+if settings.ENVIRONMENT == "local":
+ # For local development, allow all origins
+ app.add_middleware(
+ CORSMiddleware,
+ allow_origins=["*"],
+ allow_credentials=True,
+ allow_methods=["*"],
+ allow_headers=["*"],
+ )
+elif settings.all_cors_origins:
app.add_middleware(
CORSMiddleware,
allow_origins=settings.all_cors_origins,
@@ -31,3 +42,11 @@ def custom_generate_unique_id(route: APIRoute) -> str:
)
app.include_router(api_router, prefix=settings.API_V1_STR)
+
+@app.on_event("startup")
+def on_startup():
+ # 1) Create any tables that donβt yet exist (including push_tokens & custom_reminders)
+ SQLModel.metadata.create_all(engine)
+
+ # 2) Start APSchedulerβs background jobs
+ start_scheduler()
\ No newline at end of file
diff --git a/backend/app/models.py b/backend/app/models.py
index 2389b4a532..59fc4f0d6f 100644
--- a/backend/app/models.py
+++ b/backend/app/models.py
@@ -1,7 +1,12 @@
import uuid
from pydantic import EmailStr
-from sqlmodel import Field, Relationship, SQLModel
+from sqlmodel import Field, Relationship, SQLModel, Column, DateTime
+from dates import date
+from datetime import datetime
+from typing import Optional
+
+
# Shared properties
@@ -45,6 +50,9 @@ class User(UserBase, table=True):
hashed_password: str
items: list["Item"] = Relationship(back_populates="owner", cascade_delete=True)
+ # TODO test
+ personal_bests: list["PB"] = Relationship(back_populates="user", cascade_delete=True)
+
# Properties to return via API, id is always required
class UserPublic(UserBase):
@@ -81,6 +89,25 @@ class Item(ItemBase, table=True):
owner: User | None = Relationship(back_populates="items")
+
+
+# TODO test, one-to-many User.personal_bests relationship.
+# expose PersonalBestBase for writes, PersonalBest for reads, and wrapper list.
+
+class PersonalBestBase(SQLModel):
+ metric: str = Field(max_length=100, description = "e.g. deadlift, 5k-run, pushups")
+ value: float = Field(..., description="numeric value of the best (e.g. kg, seconds, reps)")
+ date: date = Field(default_factory=date.today)
+
+class PersonalBest(PersonalBestBase, table=True):
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
+ user_id: uuid.UUID = Field(foreign_key="user.id", nullable=False, ondelete="CASCADE")
+ user: User = Relationship(back_populates="personal_bests")
+
+class PersonalBestsList(SQLModel):
+ data: list[PersonalBest]
+ count: int
+
# Properties to return via API, id is always required
class ItemPublic(ItemBase):
id: uuid.UUID
@@ -111,3 +138,64 @@ class TokenPayload(SQLModel):
class NewPassword(SQLModel):
token: str
new_password: str = Field(min_length=8, max_length=40)
+
+# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+# Push notifications & custom reminders
+# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+
+class PushToken(SQLModel, table=True):
+ """
+ Stores the Expo push token for each User.
+ Weβll assume one token per user (so user_id is unique).
+ """
+ __tablename__ = "push_tokens"
+
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
+ user_id: uuid.UUID = Field(
+ foreign_key="user.id",
+ nullable=False,
+ unique=True,
+ index=True,
+ )
+ expo_token: str = Field(
+ sa_column=Column("expo_token", str, unique=True, index=True),
+ description="The Expo Push Token (e.g. ExponentPushToken[xxxxx])",
+ )
+
+
+class CustomReminderBase(SQLModel):
+ """
+ Shared properties for custom reminders.
+ """
+ user_id: uuid.UUID = Field(
+ foreign_key="user.id",
+ nullable=False,
+ index=True,
+ description="Which user this reminder belongs to",
+ )
+ expo_token: str = Field(
+ description="Copy the Expo push token here for convenience",
+ )
+ remind_time: datetime = Field(
+ sa_column=Column(DateTime(timezone=True)),
+ description="UTCβtimestamp when the push should fire",
+ )
+ message: str = Field(
+ max_length=255,
+ description="The custom reminder text to send",
+ )
+
+
+class CustomReminder(CustomReminderBase, table=True):
+ """
+ A table of singleβfire reminders. Once sent, you can delete them in your scheduler.
+ """
+ __tablename__ = "custom_reminders"
+
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
+ sent_at: Optional[datetime] = Field(
+ default=None,
+ sa_column=Column(DateTime(timezone=True)),
+ description="Set to now() after you send the notification (optional)",
+ )
+
diff --git a/backend/app/models/__init__.py b/backend/app/models/__init__.py
new file mode 100644
index 0000000000..2981ae95c8
--- /dev/null
+++ b/backend/app/models/__init__.py
@@ -0,0 +1,99 @@
+from sqlmodel import SQLModel # Re-export SQLModel for Alembic
+
+from app.models.token import Message, NewPassword, Token, TokenPayload
+from app.models.notifications import PushToken, CustomReminder
+from app.models.user import (
+ UpdatePassword,
+ User,
+ UserBase,
+ UserCreate,
+ UserPublic,
+ UserPublicExtended,
+ UserRegister,
+ UsersPublic,
+ UserUpdate,
+ UserUpdateMe,
+)
+from app.models.item import (
+ Item,
+ ItemBase,
+ ItemCreate,
+ ItemPublic,
+ ItemsPublic,
+ ItemUpdate,
+)
+from app.models.social import (
+ UserFollow,
+ WorkoutPost,
+ WorkoutPostCreate,
+ WorkoutPostPublic,
+ WorkoutPostsPublic,
+ WorkoutPostUpdate,
+)
+from app.models.workout import (
+ Workout,
+ Exercise,
+ WorkoutCreate,
+ WorkoutUpdate,
+ ExerciseCreate,
+ ExerciseUpdate,
+ WorkoutPublic,
+ ExercisePublic,
+ WorkoutWithExercisesPublic,
+ WorkoutsPublic,
+ PersonalBest,
+ PersonalBestCreate
+)
+# not needed - from app.models.personal_best import PersonalBest, PersonalBestCreate
+
+__all__ = [
+ # SQLModel
+ "SQLModel",
+ # User models
+ "User",
+ "UserBase",
+ "UserCreate",
+ "UserPublic",
+ "UserPublicExtended",
+ "UserRegister",
+ "UsersPublic",
+ "UserUpdate",
+ "UserUpdateMe",
+ "UpdatePassword",
+
+ # Item models
+ "Item",
+ "ItemBase",
+ "ItemCreate",
+ "ItemPublic",
+ "ItemsPublic",
+ "ItemUpdate",
+
+ # Token models
+ "Token",
+ "TokenPayload",
+ "NewPassword",
+ "Message",
+
+ # Social models
+ "UserFollow",
+ "WorkoutPost",
+ "WorkoutPostCreate",
+ "WorkoutPostPublic",
+ "WorkoutPostsPublic",
+ "WorkoutPostUpdate",
+
+ # Workout models
+ "Workout",
+ "Exercise",
+ "WorkoutCreate",
+ "WorkoutUpdate",
+ "ExerciseCreate",
+ "ExerciseUpdate",
+ "WorkoutPublic",
+ "ExercisePublic",
+ "WorkoutWithExercisesPublic",
+ "WorkoutsPublic",
+ "PersonalBest",
+ "PersonalBestCreate",
+]
\ No newline at end of file
diff --git a/backend/app/models/item.py b/backend/app/models/item.py
new file mode 100644
index 0000000000..a59bab7c81
--- /dev/null
+++ b/backend/app/models/item.py
@@ -0,0 +1,42 @@
+import uuid
+from typing import List, Optional
+
+from sqlmodel import Field, Relationship, SQLModel
+
+from app.models.user import User
+
+
+# Shared properties
+class ItemBase(SQLModel):
+ title: str = Field(min_length=1, max_length=255)
+ description: Optional[str] = Field(default=None, max_length=255)
+
+
+# Properties to receive on item creation
+class ItemCreate(ItemBase):
+ pass
+
+
+# Properties to receive on item update
+class ItemUpdate(ItemBase):
+ title: Optional[str] = Field(default=None, min_length=1, max_length=255)
+
+
+# Database model, database table inferred from class name
+class Item(ItemBase, table=True):
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
+ owner_id: uuid.UUID = Field(
+ foreign_key="user.id", nullable=False, ondelete="CASCADE"
+ )
+ owner: Optional[User] = Relationship(back_populates="items")
+
+
+# Properties to return via API, id is always required
+class ItemPublic(ItemBase):
+ id: uuid.UUID
+ owner_id: uuid.UUID
+
+
+class ItemsPublic(SQLModel):
+ data: List[ItemPublic]
+ count: int
\ No newline at end of file
diff --git a/backend/app/models/notifications.py b/backend/app/models/notifications.py
new file mode 100644
index 0000000000..5310a30fed
--- /dev/null
+++ b/backend/app/models/notifications.py
@@ -0,0 +1,53 @@
+# app/models/notifications.py
+
+import uuid
+from datetime import datetime
+from typing import Optional
+
+from sqlmodel import Field, SQLModel
+from sqlalchemy import Column, DateTime, String
+
+
+class PushToken(SQLModel, table=True):
+ """
+ Stores each userβs Expo push token.
+ """
+ __tablename__ = "push_tokens"
+
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
+ user_id: uuid.UUID = Field(
+ foreign_key="user.id", nullable=False, unique=True, index=True
+ )
+ expo_token: str = Field(
+ sa_column=Column("expo_token", String, unique=True, index=True),
+ description="The Expo Push Token (e.g. ExponentPushToken[β¦])",
+ )
+
+
+class CustomReminderBase(SQLModel):
+ """
+ Shared properties for a scheduled reminder.
+ """
+ user_id: uuid.UUID = Field(
+ foreign_key="user.id", nullable=False, index=True, description="Which user"
+ )
+ expo_token: str = Field(description="Store the Expo push token here")
+ remind_time: datetime = Field(
+ sa_column=Column(DateTime(timezone=True)),
+ description="UTC timestamp when this reminder should fire",
+ )
+ message: str = Field(max_length=255, description="Your reminder text")
+
+
+class CustomReminder(CustomReminderBase, table=True):
+ """
+ A one-off reminder. Once sent, weβll set sent_at so it isnβt sent again.
+ """
+ __tablename__ = "custom_reminders"
+
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
+ sent_at: Optional[datetime] = Field(
+ default=None,
+ sa_column=Column(DateTime(timezone=True)),
+ description="Set to now() after sending",
+ )
diff --git a/backend/app/models/social.py b/backend/app/models/social.py
new file mode 100644
index 0000000000..e34278b235
--- /dev/null
+++ b/backend/app/models/social.py
@@ -0,0 +1,135 @@
+import uuid
+from datetime import datetime
+from typing import List, Optional
+from sqlmodel import Field, Relationship, SQLModel
+
+# Import directly from user module to avoid circular imports
+from app.models.user import User
+
+
+
+# User Follow Relationship Model
+class UserFollow(SQLModel, table=True):
+ """
+ Model representing a follow relationship between users.
+ """
+ follower_id: uuid.UUID = Field(
+ foreign_key="user.id", primary_key=True, nullable=False
+ )
+ followed_id: uuid.UUID = Field(
+ foreign_key="user.id", primary_key=True, nullable=False
+ )
+ created_at: datetime = Field(default_factory=datetime.utcnow)
+
+ # Relationships
+ follower: User = Relationship(
+ back_populates="following",
+ sa_relationship_kwargs={"foreign_keys": "[UserFollow.follower_id]"},
+ )
+ followed: User = Relationship(
+ back_populates="followers",
+ sa_relationship_kwargs={"foreign_keys": "[UserFollow.followed_id]"},
+ )
+
+
+# Workout/Activity Post Model
+class WorkoutPost(SQLModel, table=True):
+ """
+ Model representing a workout or activity post.
+ """
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
+ user_id: uuid.UUID = Field(foreign_key="user.id", nullable=False)
+ title: str = Field(min_length=1, max_length=255)
+ description: Optional[str] = Field(default=None, max_length=1000)
+ workout_type: str = Field(max_length=50)
+ duration_minutes: int = Field(ge=1)
+ calories_burned: Optional[int] = Field(default=None, ge=0)
+ is_public: bool = Field(default=True) # True for public posts, False for private posts
+ created_at: datetime = Field(default_factory=datetime.utcnow)
+ updated_at: Optional[datetime] = Field(default=None)
+
+ # Relationships
+ user: User = Relationship(back_populates="workout_posts")
+
+
+# Workout Post Create Schema
+class WorkoutPostCreate(SQLModel):
+ """
+ Schema for creating a workout post.
+ """
+ title: str = Field(min_length=1, max_length=255)
+ description: Optional[str] = Field(default=None, max_length=1000)
+ workout_type: str = Field(max_length=50)
+ duration_minutes: int = Field(ge=1)
+ calories_burned: Optional[int] = Field(default=None, ge=0)
+ is_public: bool = Field(default=True) # Default to public posts
+
+
+# Workout Post Update Schema
+class WorkoutPostUpdate(SQLModel):
+ """
+ Schema for updating a workout post.
+ """
+ title: Optional[str] = Field(default=None, min_length=1, max_length=255)
+ description: Optional[str] = Field(default=None, max_length=1000)
+ workout_type: Optional[str] = Field(default=None, max_length=50)
+ duration_minutes: Optional[int] = Field(default=None, ge=1)
+ calories_burned: Optional[int] = Field(default=None, ge=0)
+ is_public: Optional[bool] = Field(default=None)
+
+
+# Workout Post Public Schema
+class WorkoutPostPublic(SQLModel):
+ """
+ Schema for returning workout post data via API.
+ """
+ id: uuid.UUID
+ user_id: uuid.UUID
+ title: str
+ description: Optional[str] = None
+ workout_type: str
+ duration_minutes: int
+ calories_burned: Optional[int] = None
+ is_public: bool
+ created_at: datetime
+ updated_at: Optional[datetime] = None
+ user_full_name: Optional[str] = None # Included for convenience in the feed
+ is_mutual_follow: Optional[bool] = None # For privacy logic in responses
+
+
+# Workout Posts Public Schema
+class WorkoutPostsPublic(SQLModel):
+ """
+ Schema for returning multiple workout posts via API.
+ """
+ data: List[WorkoutPostPublic]
+ count: int
+
+
+# User Search Result Schema
+class UserSearchResult(SQLModel):
+ """
+ Schema for returning user search results with extended information.
+ Extends UserPublicExtended with is_following status.
+ """
+ id: uuid.UUID
+ email: str
+ full_name: Optional[str] = None
+ is_active: bool = True
+ is_superuser: bool = False
+ gender: Optional[str] = None
+ date_of_birth: Optional[str] = None
+ weight: Optional[float] = None
+ height: Optional[float] = None
+ follower_count: int = 0
+ following_count: int = 0
+ is_following: bool = False
+
+
+# User Search Results Public Schema
+class UserSearchResultsPublic(SQLModel):
+ """
+ Schema for returning multiple user search results via API.
+ """
+ data: List[UserSearchResult]
+ count: int
\ No newline at end of file
diff --git a/backend/app/models/token.py b/backend/app/models/token.py
new file mode 100644
index 0000000000..3914971b03
--- /dev/null
+++ b/backend/app/models/token.py
@@ -0,0 +1,24 @@
+from typing import Optional
+
+from sqlmodel import SQLModel
+
+
+# JSON payload containing access token
+class Token(SQLModel):
+ access_token: str
+ token_type: str = "bearer"
+
+
+# Contents of JWT token
+class TokenPayload(SQLModel):
+ sub: Optional[str] = None
+
+
+class NewPassword(SQLModel):
+ token: str
+ new_password: str
+
+
+# Generic message
+class Message(SQLModel):
+ message: str
\ No newline at end of file
diff --git a/backend/app/models/user.py b/backend/app/models/user.py
new file mode 100644
index 0000000000..d20da4b701
--- /dev/null
+++ b/backend/app/models/user.py
@@ -0,0 +1,99 @@
+import uuid
+from typing import List, Optional
+
+from pydantic import EmailStr
+from sqlmodel import Field, Relationship, SQLModel
+
+# Shared properties
+class UserBase(SQLModel):
+ email: EmailStr = Field(unique=True, index=True, max_length=255)
+ is_active: bool = True
+ is_superuser: bool = False
+ full_name: Optional[str] = Field(default=None, max_length=255)
+ # Profile fields
+ gender: Optional[str] = Field(default=None, max_length=50)
+ date_of_birth: Optional[str] = Field(default=None, max_length=10) # YYYY-MM-DD format
+ weight: Optional[float] = Field(default=None) # in kg
+ height: Optional[float] = Field(default=None) # in cm
+
+
+# Properties to receive via API on creation
+class UserCreate(UserBase):
+ password: str = Field(min_length=8, max_length=50)
+
+
+class UserRegister(SQLModel):
+ email: EmailStr = Field(max_length=255)
+ password: str = Field(min_length=8, max_length=40)
+ full_name: Optional[str] = Field(default=None, max_length=255)
+
+
+# Properties to receive via API on update, all are optional
+class UserUpdate(UserBase):
+ email: Optional[EmailStr] = Field(default=None, max_length=255)
+ password: Optional[str] = Field(default=None, min_length=8, max_length=40)
+
+
+class UserUpdateMe(SQLModel):
+ full_name: Optional[str] = Field(default=None, max_length=255)
+ email: Optional[EmailStr] = Field(default=None, max_length=255)
+ # Profile fields
+ gender: Optional[str] = Field(default=None, max_length=50)
+ date_of_birth: Optional[str] = Field(default=None, max_length=10) # YYYY-MM-DD format
+ weight: Optional[float] = Field(default=None) # in kg
+ height: Optional[float] = Field(default=None) # in cm
+
+
+class UpdatePassword(SQLModel):
+ current_password: str = Field(min_length=8, max_length=40)
+ new_password: str = Field(min_length=8, max_length=40)
+
+
+# Database model, database table inferred from class name
+class User(UserBase, table=True):
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
+ hashed_password: str
+
+ # Relationships
+ items: List["Item"] = Relationship(back_populates="owner", cascade_delete=True)
+
+ # Social relationships
+ following: List["UserFollow"] = Relationship(
+ back_populates="follower",
+ sa_relationship_kwargs={"foreign_keys": "[UserFollow.follower_id]", "cascade": "all, delete-orphan"},
+ )
+ followers: List["UserFollow"] = Relationship(
+ back_populates="followed",
+ sa_relationship_kwargs={"foreign_keys": "[UserFollow.followed_id]", "cascade": "all, delete-orphan"},
+ )
+
+ # Workout posts
+ workout_posts: List["WorkoutPost"] = Relationship(
+ back_populates="user", cascade_delete=True
+ )
+
+ # Workouts
+ workouts: List["Workout"] = Relationship(
+ back_populates="user", cascade_delete=True
+ )
+
+
+# Properties to return via API, id is always required
+class UserPublic(UserBase):
+ id: uuid.UUID
+
+
+# Extended user public model with follower counts
+class UserPublicExtended(UserPublic):
+ follower_count: int = 0
+ following_count: int = 0
+
+
+class UsersPublic(SQLModel):
+ data: List[UserPublic]
+ count: int
+
+
+# Forward references for type hints
+from app.models.social import UserFollow, WorkoutPost
+from app.models.workout import Workout
\ No newline at end of file
diff --git a/backend/app/models/workout.py b/backend/app/models/workout.py
new file mode 100644
index 0000000000..b7615e907a
--- /dev/null
+++ b/backend/app/models/workout.py
@@ -0,0 +1,165 @@
+import uuid
+from uuid import UUID, uuid4
+from datetime import datetime
+from typing import List, Optional
+from sqlmodel import Field, Relationship, SQLModel
+
+# Import directly from user module to avoid circular imports
+from app.models.user import User
+
+# Workout Model
+class Workout(SQLModel, table=True):
+ """
+ Model representing a workout plan.
+ """
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
+ user_id: uuid.UUID = Field(foreign_key="user.id", nullable=False)
+ name: str = Field(min_length=1, max_length=255)
+ description: Optional[str] = Field(default=None, max_length=1000)
+ scheduled_date: Optional[datetime] = Field(default=None)
+ completed_date: Optional[datetime] = Field(default=None)
+ duration_minutes: Optional[int] = Field(default=None, ge=0)
+ is_completed: bool = Field(default=False)
+ created_at: datetime = Field(default_factory=datetime.utcnow)
+ updated_at: Optional[datetime] = Field(default=None)
+
+ # Relationships
+ user: User = Relationship(back_populates="workouts")
+ exercises: List["Exercise"] = Relationship(back_populates="workout", cascade_delete=True)
+
+
+# Exercise Model
+class Exercise(SQLModel, table=True):
+ """
+ Model representing an exercise within a workout.
+ """
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
+ workout_id: uuid.UUID = Field(foreign_key="workout.id", nullable=False)
+ name: str = Field(min_length=1, max_length=255)
+ description: Optional[str] = Field(default=None, max_length=1000)
+ category: str = Field(max_length=50)
+ sets: Optional[int] = Field(default=None, ge=0)
+ reps: Optional[int] = Field(default=None, ge=0)
+ weight: Optional[float] = Field(default=None, ge=0)
+ created_at: datetime = Field(default_factory=datetime.utcnow)
+ updated_at: Optional[datetime] = Field(default=None)
+
+ # Relationships
+ workout: Workout = Relationship(back_populates="exercises")
+
+
+# Workout Create Schema
+class WorkoutCreate(SQLModel):
+ """
+ Schema for creating a workout.
+ """
+ name: str = Field(min_length=1, max_length=255)
+ description: Optional[str] = Field(default=None, max_length=1000)
+ scheduled_date: Optional[datetime] = Field(default=None)
+ duration_minutes: Optional[int] = Field(default=None, ge=0)
+
+
+# Workout Update Schema
+class WorkoutUpdate(SQLModel):
+ """
+ Schema for updating a workout.
+ """
+ name: Optional[str] = Field(default=None, min_length=1, max_length=255)
+ description: Optional[str] = Field(default=None, max_length=1000)
+ scheduled_date: Optional[datetime] = Field(default=None)
+ completed_date: Optional[datetime] = Field(default=None)
+ duration_minutes: Optional[int] = Field(default=None, ge=0)
+ is_completed: Optional[bool] = Field(default=None)
+
+
+# Exercise Create Schema
+class ExerciseCreate(SQLModel):
+ """
+ Schema for creating an exercise.
+ """
+ name: str = Field(min_length=1, max_length=255)
+ description: Optional[str] = Field(default=None, max_length=1000)
+ category: str = Field(max_length=50)
+ sets: Optional[int] = Field(default=None, ge=0)
+ reps: Optional[int] = Field(default=None, ge=0)
+ weight: Optional[float] = Field(default=None, ge=0)
+
+
+# Exercise Update Schema
+class ExerciseUpdate(SQLModel):
+ """
+ Schema for updating an exercise.
+ """
+ name: Optional[str] = Field(default=None, min_length=1, max_length=255)
+ description: Optional[str] = Field(default=None, max_length=1000)
+ category: Optional[str] = Field(default=None, max_length=50)
+ sets: Optional[int] = Field(default=None, ge=0)
+ reps: Optional[int] = Field(default=None, ge=0)
+ weight: Optional[float] = Field(default=None, ge=0)
+
+
+# Workout Public Schema
+class WorkoutPublic(SQLModel):
+ """
+ Schema for returning workout data via API.
+ """
+ id: uuid.UUID
+ user_id: uuid.UUID
+ name: str
+ description: Optional[str] = None
+ scheduled_date: Optional[datetime] = None
+ completed_date: Optional[datetime] = None
+ duration_minutes: Optional[int] = None
+ is_completed: bool
+ created_at: datetime
+ updated_at: Optional[datetime] = None
+ exercise_count: Optional[int] = None
+
+
+# Exercise Public Schema
+class ExercisePublic(SQLModel):
+ """
+ Schema for returning exercise data via API.
+ """
+ id: uuid.UUID
+ workout_id: uuid.UUID
+ name: str
+ description: Optional[str] = None
+ category: str
+ sets: Optional[int] = None
+ reps: Optional[int] = None
+ weight: Optional[float] = None
+ created_at: datetime
+ updated_at: Optional[datetime] = None
+
+
+# Workout With Exercises Public Schema
+class WorkoutWithExercisesPublic(WorkoutPublic):
+ """
+ Schema for returning workout data with exercises via API.
+ """
+ exercises: List[ExercisePublic] = []
+
+
+# Workouts Public Schema
+class WorkoutsPublic(SQLModel):
+ """
+ Schema for returning multiple workouts via API.
+ """
+ data: List[WorkoutPublic]
+ count: int
+
+#Personal Bests Model - Related to Workouts
+class PersonalBest(SQLModel, table=True):
+ id: UUID = Field(default_factory=uuid4, primary_key=True)
+ user_id: UUID = Field(foreign_key="user.id")
+ exercise_name: str
+ metric_type: str # e.g. "max_weight", "max_reps"
+ metric_value: float
+ date_achieved: Optional[datetime]
+
+class PersonalBestCreate(SQLModel):
+ exercise_name: str
+ metric_type: str
+ metric_value: float
+ date_achieved: Optional[datetime]
\ No newline at end of file
diff --git a/backend/app/scheduler.py b/backend/app/scheduler.py
new file mode 100644
index 0000000000..79dcf4f37d
--- /dev/null
+++ b/backend/app/scheduler.py
@@ -0,0 +1,112 @@
+# βββ app/scheduler.py ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+
+import random
+import requests
+from datetime import datetime, timezone
+from apscheduler.schedulers.background import BackgroundScheduler
+from apscheduler.triggers.cron import CronTrigger
+
+from sqlmodel import Session, select
+from app.core.db import engine # your SQLModel engine from db.py
+from app.crudFuncs import (
+ get_due_custom_reminders,
+ mark_reminder_as_sent
+)
+from app.models import PushToken # to fetch all tokens for daily quotes
+
+EXPO_PUSH_URL = "https://exp.host/--/api/v2/push/send"
+
+# A small list of motivational quotes
+QUOTES = [
+ "Believe you can and youβre halfway there.",
+ "Fall seven times, stand up eight.",
+ "Your limitationβitβs only your imagination.",
+ "Push yourself, because no one else is going to do it for you.",
+ "Great things never come from comfort zones.",
+ "Dream it. Wish it. Do it.",
+ "Success doesnβt just find youβyou have to go out and get it.",
+]
+
+
+def send_expo_push(expo_token: str, title: str, body: str, data: dict = None):
+ """
+ Send a single push via Expoβs REST API.
+ """
+ message = {
+ "to": expo_token,
+ "sound": "default",
+ "title": title,
+ "body": body,
+ "data": data or {},
+ }
+ resp = requests.post(
+ EXPO_PUSH_URL,
+ json=message,
+ headers={"Accept": "application/json", "Content-Type": "application/json"},
+ )
+ if resp.status_code != 200:
+ print(
+ f"[Scheduler] Failed to send push to {expo_token}: "
+ f"{resp.status_code} β {resp.text}"
+ )
+
+
+def job_send_custom_reminders():
+ """
+ Runs every minute: finds all due reminders, sends them, and marks them as sent.
+ """
+ with Session(engine) as session:
+ due_list = get_due_custom_reminders(session=session)
+ for reminder in due_list:
+ send_expo_push(
+ reminder.expo_token,
+ "β° Reminder",
+ reminder.message,
+ {"type": "custom_reminder", "reminder_id": str(reminder.id)},
+ )
+ mark_reminder_as_sent(session=session, reminder_id=reminder.id)
+ print(f"[{datetime.now(timezone.utc)}] Sent reminder {reminder.id}")
+
+
+def job_send_quote_of_the_day():
+ """
+ Runs once per day at 15:00 UTC: picks a random quote and sends it
+ to every Expo token in push_tokens.
+ """
+ with Session(engine) as session:
+ quote = random.choice(QUOTES)
+ title = "π Motivation of the Day π"
+ body = quote
+ tokens = session.exec(select(PushToken)).all()
+ for tok in tokens:
+ send_expo_push(tok.expo_token, title, body, {"type": "daily_quote"})
+ print(f"[{datetime.now(timezone.utc)}] Sent daily quote to {len(tokens)} users.")
+
+
+def start_scheduler():
+ """
+ Initialize APScheduler (non-blocking).
+ - job_send_custom_reminders runs every minute
+ - job_send_quote_of_the_day runs daily at 15:00 UTC
+ """
+ scheduler = BackgroundScheduler(timezone=timezone.utc)
+
+ scheduler.add_job(
+ job_send_custom_reminders,
+ CronTrigger(minute="*"),
+ id="custom_reminders",
+ replace_existing=True,
+ )
+
+ scheduler.add_job(
+ job_send_quote_of_the_day,
+ CronTrigger(hour=15, minute=0),
+ id="daily_quote",
+ replace_existing=True,
+ )
+
+ scheduler.start()
+ print(
+ "Scheduler started: custom reminders (every minute), "
+ "daily quote (15:00 UTC)"
+ )
diff --git a/backend/app/tests/README.md b/backend/app/tests/README.md
new file mode 100644
index 0000000000..35d9e855d5
--- /dev/null
+++ b/backend/app/tests/README.md
@@ -0,0 +1,105 @@
+# KonditionFastAPI Backend Testing Framework
+
+This directory contains the testing framework for the KonditionFastAPI backend. The framework is built using pytest and follows test-driven development principles.
+
+## Test Structure
+
+The tests are organized as follows:
+
+```
+tests/
+βββ __init__.py
+βββ conftest.py # Pytest fixtures and configuration
+βββ api/ # API endpoint tests
+β βββ __init__.py
+β βββ routes/ # Tests for API routes
+β β βββ __init__.py
+β β βββ test_auth.py # Authentication tests
+β β βββ test_items.py # Item API tests
+β β βββ test_login.py # Login API tests
+β β βββ test_private.py # Private API tests
+β β βββ test_social.py # Social features tests
+β β βββ test_users.py # User API tests
+βββ crud/ # CRUD operation tests
+β βββ __init__.py
+β βββ test_user.py # User CRUD tests
+βββ utils/ # Test utilities
+ βββ __init__.py
+ βββ test_client.py # Test client utilities
+ βββ test_db.py # Database testing utilities
+ βββ user.py # User testing utilities
+ βββ utils.py # General testing utilities
+```
+
+## Test Database
+
+The tests use an in-memory SQLite database for fast execution. The database is created fresh for each test function, ensuring test isolation.
+
+## Test Client
+
+The test client is a wrapper around FastAPI's TestClient that provides helper methods for making API requests and handling authentication.
+
+## Running Tests
+
+To run the tests, use the following command from the backend directory:
+
+```bash
+# Run all tests
+bash scripts/test.sh
+
+# Run specific tests
+pytest app/tests/api/routes/test_auth.py -v
+
+# Run tests with coverage report
+coverage run --source=app -m pytest
+coverage report --show-missing
+coverage html
+```
+
+## Writing Tests
+
+When writing tests, follow these guidelines:
+
+1. **Test Isolation**: Each test should be independent and not rely on the state from other tests.
+2. **Use Fixtures**: Use pytest fixtures for setup and teardown.
+3. **Mock External Services**: Use monkeypatch to mock external services like email sending.
+4. **Test Both Success and Failure Cases**: Test both valid and invalid inputs.
+5. **Use Helper Functions**: Use the provided helper functions for common operations.
+
+## Test Utilities
+
+The framework provides several utilities to make testing easier:
+
+- `test_client.py`: Provides a wrapper around FastAPI's TestClient with helper methods.
+- `test_db.py`: Provides utilities for database operations during tests.
+- `user.py`: Provides utilities for creating and authenticating users.
+- `utils.py`: Provides general utilities like generating random strings and emails.
+
+## Authentication in Tests
+
+To test authenticated endpoints, use the provided fixtures:
+
+```python
+def test_authenticated_endpoint(test_client, normal_user_token_headers):
+ response = test_client.get("/protected-endpoint", headers=normal_user_token_headers)
+ assert response.status_code == 200
+```
+
+Or use the TestClientWrapper to login:
+
+```python
+def test_authenticated_endpoint(test_client, db):
+ # Create a user
+ user = create_test_user(db)
+
+ # Login
+ auth_headers = test_client.login(user.email, "testpassword")
+
+ # Make authenticated request
+ response = test_client.get("/protected-endpoint", headers=auth_headers)
+ assert response.status_code == 200
+```
+
+## Continuous Integration
+
+The tests are run automatically in the CI pipeline. Make sure all tests pass before submitting a pull request.
\ No newline at end of file
diff --git a/backend/app/tests/api/routes/test_auth.py b/backend/app/tests/api/routes/test_auth.py
new file mode 100644
index 0000000000..4220684074
--- /dev/null
+++ b/backend/app/tests/api/routes/test_auth.py
@@ -0,0 +1,157 @@
+"""
+Tests for authentication endpoints.
+"""
+import pytest
+from fastapi.testclient import TestClient
+from sqlmodel import Session
+
+from app.core.config import settings
+from app.models import User, UserCreate
+from app.tests.utils.test_client import TestClientWrapper, assert_successful_response, assert_error_response
+from app.tests.utils.test_db import create_test_user
+from app.tests.utils.utils import random_email, random_lower_string
+
+
+def test_login_access_token(client: TestClient, db: Session):
+ """Test login with access token."""
+ email = random_email()
+ password = random_lower_string()
+ user = create_test_user(db, email=email, password=password)
+
+ login_data = {
+ "username": email,
+ "password": password,
+ }
+
+ response = client.post(f"{settings.API_V1_STR}/login/access-token", data=login_data)
+ tokens = assert_successful_response(response)
+
+ assert "access_token" in tokens
+ assert tokens["token_type"] == "bearer"
+
+
+def test_login_access_token_incorrect_password(client: TestClient, db: Session):
+ """Test login with incorrect password."""
+ email = random_email()
+ password = random_lower_string()
+ user = create_test_user(db, email=email, password=password)
+
+ login_data = {
+ "username": email,
+ "password": "wrong-password",
+ }
+
+ response = client.post(f"{settings.API_V1_STR}/login/access-token", data=login_data)
+ assert_error_response(response, status_code=400)
+
+
+def test_login_access_token_user_not_exists(client: TestClient):
+ """Test login with non-existent user."""
+ login_data = {
+ "username": "nonexistent@example.com",
+ "password": random_lower_string(),
+ }
+
+ response = client.post(f"{settings.API_V1_STR}/login/access-token", data=login_data)
+ assert_error_response(response, status_code=400)
+
+
+def test_use_access_token(test_client: TestClientWrapper, db: Session):
+ """Test using the access token to access a protected endpoint."""
+ email = random_email()
+ password = random_lower_string()
+ user = create_test_user(db, email=email, password=password)
+
+ # Login and get token
+ auth_headers = test_client.login(email, password)
+
+ # Use token to access protected endpoint
+ response = test_client.get("/users/me", headers=auth_headers)
+ user_data = assert_successful_response(response)
+
+ assert user_data["email"] == email
+ assert user_data["id"] == str(user.id)
+
+
+def test_signup_user(test_client: TestClientWrapper, db: Session):
+ """Test user signup."""
+ email = random_email()
+ password = random_lower_string()
+ full_name = "Test User"
+
+ data = {
+ "email": email,
+ "password": password,
+ "full_name": full_name
+ }
+
+ response = test_client.post("/users/signup", json=data)
+ user_data = assert_successful_response(response)
+
+ assert user_data["email"] == email
+ assert user_data["full_name"] == full_name
+ assert "id" in user_data
+
+ # Verify user was created in the database
+ from app.crud import get_user_by_email
+ db_user = get_user_by_email(session=db, email=email)
+ assert db_user is not None
+ assert db_user.email == email
+ assert db_user.full_name == full_name
+
+
+def test_signup_user_existing_email(test_client: TestClientWrapper, db: Session):
+ """Test signup with an existing email."""
+ email = random_email()
+ password = random_lower_string()
+ user = create_test_user(db, email=email, password=password)
+
+ data = {
+ "email": email,
+ "password": random_lower_string(),
+ "full_name": "Another User"
+ }
+
+ response = test_client.post("/users/signup", json=data)
+ assert_error_response(response, status_code=400)
+
+
+def test_reset_password_request(test_client: TestClientWrapper, db: Session, monkeypatch):
+ """Test password reset request."""
+ # Mock the send_reset_password_email function
+ email_sent = False
+
+ def mock_send_email(*args, **kwargs):
+ nonlocal email_sent
+ email_sent = True
+ return None
+
+ monkeypatch.setattr("app.utils.send_email", mock_send_email)
+
+ # Create a user
+ email = random_email()
+ password = random_lower_string()
+ user = create_test_user(db, email=email, password=password)
+
+ # Request password reset
+ data = {"email": email}
+ response = test_client.post("/password-recovery/", json=data)
+ assert_successful_response(response)
+
+ # Check that the email was "sent"
+ assert email_sent
+
+
+def test_reset_password_request_nonexistent_email(test_client: TestClientWrapper):
+ """Test password reset request with non-existent email."""
+ data = {"email": "nonexistent@example.com"}
+ response = test_client.post("/password-recovery/", json=data)
+ # Should still return 200 for security reasons (don't reveal if email exists)
+ assert_successful_response(response)
+
+
+def test_logout(test_client: TestClientWrapper, db: Session):
+ """Test user logout functionality if implemented."""
+ # This test is a placeholder for logout functionality
+ # Implement if the API has a logout endpoint
+ pass
\ No newline at end of file
diff --git a/backend/app/tests/api/routes/test_social.py b/backend/app/tests/api/routes/test_social.py
new file mode 100644
index 0000000000..7c7ffa164b
--- /dev/null
+++ b/backend/app/tests/api/routes/test_social.py
@@ -0,0 +1,433 @@
+"""
+Tests for social features endpoints.
+"""
+import pytest
+from fastapi.testclient import TestClient
+from sqlmodel import Session
+
+from app.core.config import settings
+from app.models import User
+from app.tests.utils.test_client import TestClientWrapper, assert_successful_response, assert_error_response
+from app.tests.utils.test_db import (
+ create_test_user,
+ create_test_workout_post,
+ create_test_follow_relationship
+)
+from app.tests.utils.utils import random_email, random_lower_string
+
+
+def test_create_workout_post(test_client: TestClientWrapper, db: Session):
+ """Test creating a workout post."""
+ # Create a user
+ email = random_email()
+ password = random_lower_string()
+ user = create_test_user(db, email=email, password=password)
+
+ # Login
+ auth_headers = test_client.login(email, password)
+
+ # Create workout post
+ post_data = {
+ "title": "Morning Run",
+ "description": "Great 5k run this morning",
+ "workout_type": "Running",
+ "duration_minutes": 30,
+ "calories_burned": 350
+ }
+
+ response = test_client.post("/social/workout-posts/", headers=auth_headers, json=post_data)
+ post = assert_successful_response(response)
+
+ assert post["title"] == post_data["title"]
+ assert post["description"] == post_data["description"]
+ assert post["workout_type"] == post_data["workout_type"]
+ assert post["duration_minutes"] == post_data["duration_minutes"]
+ assert post["calories_burned"] == post_data["calories_burned"]
+ assert post["user_id"] == str(user.id)
+
+
+def test_get_workout_post(test_client: TestClientWrapper, db: Session):
+ """Test getting a workout post by ID."""
+ # Create a user
+ email = random_email()
+ password = random_lower_string()
+ user = create_test_user(db, email=email, password=password)
+
+ # Create a workout post
+ post = create_test_workout_post(
+ db,
+ user_id=user.id,
+ title="Evening Yoga",
+ workout_type="Yoga"
+ )
+
+ # Login
+ auth_headers = test_client.login(email, password)
+
+ # Get the post
+ response = test_client.get(f"/social/workout-posts/{post.id}", headers=auth_headers)
+ post_data = assert_successful_response(response)
+
+ assert post_data["id"] == str(post.id)
+ assert post_data["title"] == post.title
+ assert post_data["workout_type"] == post.workout_type
+
+
+def test_update_workout_post(test_client: TestClientWrapper, db: Session):
+ """Test updating a workout post."""
+ # Create a user
+ email = random_email()
+ password = random_lower_string()
+ user = create_test_user(db, email=email, password=password)
+
+ # Create a workout post
+ post = create_test_workout_post(
+ db,
+ user_id=user.id,
+ title="Evening Yoga",
+ workout_type="Yoga"
+ )
+
+ # Login
+ auth_headers = test_client.login(email, password)
+
+ # Update the post
+ update_data = {
+ "title": "Updated Yoga Session",
+ "duration_minutes": 45
+ }
+
+ response = test_client.patch(
+ f"/social/workout-posts/{post.id}",
+ headers=auth_headers,
+ json=update_data
+ )
+ updated_post = assert_successful_response(response)
+
+ assert updated_post["id"] == str(post.id)
+ assert updated_post["title"] == update_data["title"]
+ assert updated_post["duration_minutes"] == update_data["duration_minutes"]
+ assert updated_post["workout_type"] == post.workout_type # Unchanged
+
+
+def test_delete_workout_post(test_client: TestClientWrapper, db: Session):
+ """Test deleting a workout post."""
+ # Create a user
+ email = random_email()
+ password = random_lower_string()
+ user = create_test_user(db, email=email, password=password)
+
+ # Create a workout post
+ post = create_test_workout_post(
+ db,
+ user_id=user.id,
+ title="Evening Yoga",
+ workout_type="Yoga"
+ )
+
+ # Login
+ auth_headers = test_client.login(email, password)
+
+ # Delete the post
+ response = test_client.delete(f"/social/workout-posts/{post.id}", headers=auth_headers)
+ assert_successful_response(response)
+
+ # Verify post is deleted
+ response = test_client.get(f"/social/workout-posts/{post.id}", headers=auth_headers)
+ assert_error_response(response, status_code=404)
+
+
+def test_follow_user(test_client: TestClientWrapper, db: Session):
+ """Test following another user."""
+ # Create two users
+ follower_email = random_email()
+ follower_password = random_lower_string()
+ follower = create_test_user(db, email=follower_email, password=follower_password)
+
+ followed_email = random_email()
+ followed_password = random_lower_string()
+ followed = create_test_user(db, email=followed_email, password=followed_password)
+
+ # Login as follower
+ auth_headers = test_client.login(follower_email, follower_password)
+
+ # Follow the other user
+ response = test_client.post(
+ f"/social/users/{followed.id}/follow",
+ headers=auth_headers
+ )
+ follow_data = assert_successful_response(response)
+
+ assert follow_data["success"] is True
+
+ # Verify follow relationship in DB
+ from app.models.social import UserFollow
+ from sqlmodel import select
+
+ statement = select(UserFollow).where(
+ UserFollow.follower_id == follower.id,
+ UserFollow.followed_id == followed.id
+ )
+ follow = db.exec(statement).first()
+ assert follow is not None
+
+
+def test_unfollow_user(test_client: TestClientWrapper, db: Session):
+ """Test unfollowing a user."""
+ # Create two users
+ follower_email = random_email()
+ follower_password = random_lower_string()
+ follower = create_test_user(db, email=follower_email, password=follower_password)
+
+ followed_email = random_email()
+ followed_password = random_lower_string()
+ followed = create_test_user(db, email=followed_email, password=followed_password)
+
+ # Create follow relationship
+ create_test_follow_relationship(db, follower_id=follower.id, followed_id=followed.id)
+
+ # Login as follower
+ auth_headers = test_client.login(follower_email, follower_password)
+
+ # Unfollow the user
+ response = test_client.delete(
+ f"/social/users/{followed.id}/follow",
+ headers=auth_headers
+ )
+ unfollow_data = assert_successful_response(response)
+
+ assert unfollow_data["success"] is True
+
+ # Verify follow relationship is removed from DB
+ from app.models.social import UserFollow
+ from sqlmodel import select
+
+ statement = select(UserFollow).where(
+ UserFollow.follower_id == follower.id,
+ UserFollow.followed_id == followed.id
+ )
+ follow = db.exec(statement).first()
+ assert follow is None
+
+
+def test_get_user_followers(test_client: TestClientWrapper, db: Session):
+ """Test getting a user's followers."""
+ # Create users
+ user_email = random_email()
+ user_password = random_lower_string()
+ user = create_test_user(db, email=user_email, password=user_password)
+
+ follower1_email = random_email()
+ follower1 = create_test_user(db, email=follower1_email, password="password")
+
+ follower2_email = random_email()
+ follower2 = create_test_user(db, email=follower2_email, password="password")
+
+ # Create follow relationships
+ create_test_follow_relationship(db, follower_id=follower1.id, followed_id=user.id)
+ create_test_follow_relationship(db, follower_id=follower2.id, followed_id=user.id)
+
+ # Login
+ auth_headers = test_client.login(user_email, user_password)
+
+ # Get followers
+ response = test_client.get(f"/social/users/{user.id}/followers", headers=auth_headers)
+ followers_data = assert_successful_response(response)
+
+ assert "data" in followers_data
+ assert "count" in followers_data
+ assert followers_data["count"] == 2
+
+ follower_emails = [follower["email"] for follower in followers_data["data"]]
+ assert follower1_email in follower_emails
+ assert follower2_email in follower_emails
+
+
+def test_get_user_following(test_client: TestClientWrapper, db: Session):
+ """Test getting users that a user is following."""
+ # Create users
+ user_email = random_email()
+ user_password = random_lower_string()
+ user = create_test_user(db, email=user_email, password=user_password)
+
+ followed1_email = random_email()
+ followed1 = create_test_user(db, email=followed1_email, password="password")
+
+ followed2_email = random_email()
+ followed2 = create_test_user(db, email=followed2_email, password="password")
+
+ # Create follow relationships
+ create_test_follow_relationship(db, follower_id=user.id, followed_id=followed1.id)
+ create_test_follow_relationship(db, follower_id=user.id, followed_id=followed2.id)
+
+ # Login
+ auth_headers = test_client.login(user_email, user_password)
+
+ # Get following
+ response = test_client.get(f"/social/users/{user.id}/following", headers=auth_headers)
+ following_data = assert_successful_response(response)
+
+ assert "data" in following_data
+ assert "count" in following_data
+ assert following_data["count"] == 2
+
+ following_emails = [followed["email"] for followed in following_data["data"]]
+ assert followed1_email in following_emails
+ assert followed2_email in following_emails
+
+
+def test_get_feed(test_client: TestClientWrapper, db: Session):
+ """Test getting a user's feed of workout posts."""
+ # Create users
+ user_email = random_email()
+ user_password = random_lower_string()
+ user = create_test_user(db, email=user_email, password=user_password)
+
+ followed_email = random_email()
+ followed = create_test_user(db, email=followed_email, password="password")
+
+ # Create follow relationship
+ create_test_follow_relationship(db, follower_id=user.id, followed_id=followed.id)
+
+ # Create workout posts for followed user
+ post1 = create_test_workout_post(
+ db,
+ user_id=followed.id,
+ title="Morning Run",
+ workout_type="Running"
+ )
+
+ post2 = create_test_workout_post(
+ db,
+ user_id=followed.id,
+ title="Evening Yoga",
+ workout_type="Yoga"
+ )
+
+ # Login
+ auth_headers = test_client.login(user_email, user_password)
+
+ # Get feed
+ response = test_client.get("/social/feed", headers=auth_headers)
+ feed_data = assert_successful_response(response)
+
+ assert "data" in feed_data
+ assert "count" in feed_data
+ assert feed_data["count"] == 2
+
+ post_titles = [post["title"] for post in feed_data["data"]]
+ assert "Morning Run" in post_titles
+ assert "Evening Yoga" in post_titles
+
+
+def test_search_users(test_client: TestClientWrapper, db: Session):
+ """Test searching for users by name or email."""
+ # Create a searcher user
+ searcher_email = random_email()
+ searcher_password = random_lower_string()
+ searcher = create_test_user(db, email=searcher_email, password=searcher_password)
+
+ # Create users to search for
+ user1_email = "john.doe@example.com"
+ user1 = create_test_user(
+ db,
+ email=user1_email,
+ password="password",
+ full_name="John Doe"
+ )
+
+ user2_email = "jane.smith@example.com"
+ user2 = create_test_user(
+ db,
+ email=user2_email,
+ password="password",
+ full_name="Jane Smith"
+ )
+
+ user3_email = "bob.johnson@test.com"
+ user3 = create_test_user(
+ db,
+ email=user3_email,
+ password="password",
+ full_name="Bob Johnson"
+ )
+
+ # Create some follow relationships to test is_following
+ create_test_follow_relationship(db, follower_id=searcher.id, followed_id=user1.id)
+
+ # Login as searcher
+ auth_headers = test_client.login(searcher_email, searcher_password)
+
+ # Test search by name
+ response = test_client.get("/social/users/search?q=john", headers=auth_headers)
+ search_data = assert_successful_response(response)
+
+ assert "data" in search_data
+ assert "count" in search_data
+ assert search_data["count"] == 2 # John Doe and Bob Johnson
+
+ # Check that results include expected users
+ found_names = [user["full_name"] for user in search_data["data"]]
+ assert "John Doe" in found_names
+ assert "Bob Johnson" in found_names
+ assert "Jane Smith" not in found_names
+
+ # Check that is_following is correctly set
+ john_doe_result = next(user for user in search_data["data"] if user["full_name"] == "John Doe")
+ bob_johnson_result = next(user for user in search_data["data"] if user["full_name"] == "Bob Johnson")
+
+ assert john_doe_result["is_following"] is True
+ assert bob_johnson_result["is_following"] is False
+
+ # Check that follower/following counts are included
+ assert "follower_count" in john_doe_result
+ assert "following_count" in john_doe_result
+
+ # Test search by email
+ response = test_client.get("/social/users/search?q=example.com", headers=auth_headers)
+ search_data = assert_successful_response(response)
+
+ assert search_data["count"] == 2 # john.doe@example.com and jane.smith@example.com
+
+ # Test pagination
+ response = test_client.get("/social/users/search?q=john&skip=0&limit=1", headers=auth_headers)
+ search_data = assert_successful_response(response)
+
+ assert len(search_data["data"]) == 1
+ assert search_data["count"] == 2 # Total count should still be 2
+
+ # Test case insensitive search
+ response = test_client.get("/social/users/search?q=JOHN", headers=auth_headers)
+ search_data = assert_successful_response(response)
+
+ assert search_data["count"] == 2
+
+ # Test that current user is excluded from results
+ response = test_client.get(f"/social/users/search?q={searcher_email}", headers=auth_headers)
+ search_data = assert_successful_response(response)
+
+ assert search_data["count"] == 0 # Searcher should not appear in their own search results
+
+
+def test_search_users_validation(test_client: TestClientWrapper, db: Session):
+ """Test user search input validation."""
+ # Create a user
+ email = random_email()
+ password = random_lower_string()
+ user = create_test_user(db, email=email, password=password)
+
+ # Login
+ auth_headers = test_client.login(email, password)
+
+ # Test empty query
+ response = test_client.get("/social/users/search?q=", headers=auth_headers)
+ assert_error_response(response, status_code=400)
+
+ # Test missing query parameter
+ response = test_client.get("/social/users/search", headers=auth_headers)
+ assert_error_response(response, status_code=422) # FastAPI validation error
+
+ # Test limit validation (should cap at 100)
+ response = test_client.get("/social/users/search?q=test&limit=200", headers=auth_headers)
+ search_data = assert_successful_response(response)
+ # The endpoint should work but limit should be capped at 100
\ No newline at end of file
diff --git a/backend/app/tests/conftest.py b/backend/app/tests/conftest.py
index 90ab39a357..7a824bb788 100644
--- a/backend/app/tests/conftest.py
+++ b/backend/app/tests/conftest.py
@@ -1,42 +1,110 @@
from collections.abc import Generator
+import os
+import logging
+from typing import Dict, Generator, Any
import pytest
from fastapi.testclient import TestClient
-from sqlmodel import Session, delete
+from sqlmodel import Session, SQLModel, create_engine, delete
+from sqlalchemy.pool import StaticPool
from app.core.config import settings
-from app.core.db import engine, init_db
+from app.core.db import init_db
from app.main import app
-from app.models import Item, User
+from app.models import Item, User, WorkoutPost, UserFollow, Workout, Exercise
from app.tests.utils.user import authentication_token_from_email
from app.tests.utils.utils import get_superuser_token_headers
+from app.tests.utils.test_client import TestClientWrapper, get_test_client_wrapper
+# Configure logging for tests
+logging.basicConfig(level=logging.INFO)
+logger = logging.getLogger(__name__)
-@pytest.fixture(scope="session", autouse=True)
-def db() -> Generator[Session, None, None]:
- with Session(engine) as session:
+
+# Create a test database engine with in-memory SQLite for fast tests
+@pytest.fixture(scope="session")
+def test_engine():
+ """Create a test SQLite engine for fast tests."""
+ # Use in-memory SQLite for tests
+ TEST_SQLALCHEMY_DATABASE_URL = "sqlite:///:memory:"
+
+ engine = create_engine(
+ TEST_SQLALCHEMY_DATABASE_URL,
+ connect_args={"check_same_thread": False},
+ poolclass=StaticPool,
+ )
+
+ # Create all tables in the test database
+ SQLModel.metadata.create_all(engine)
+
+ return engine
+
+
+@pytest.fixture(scope="function")
+def db(test_engine) -> Generator[Session, None, None]:
+ """
+ Create a fresh database session for each test.
+ This fixture is function-scoped to isolate test data.
+ """
+ with Session(test_engine) as session:
init_db(session)
yield session
- statement = delete(Item)
- session.execute(statement)
- statement = delete(User)
- session.execute(statement)
+
+ # Clean up all test data after each test
+ for model in [Exercise, Workout, WorkoutPost, UserFollow, Item, User]:
+ statement = delete(model)
+ session.execute(statement)
+
session.commit()
-@pytest.fixture(scope="module")
-def client() -> Generator[TestClient, None, None]:
- with TestClient(app) as c:
- yield c
+@pytest.fixture(scope="function")
+def client(db: Session) -> Generator[TestClient, None, None]:
+ """
+ Create a new FastAPI TestClient that uses the `db` fixture.
+ """
+ # Override the get_db dependency to use the test database
+ def get_test_db():
+ try:
+ yield db
+ finally:
+ pass
+
+ # Override the dependency
+ app.dependency_overrides = {} # Clear any existing overrides
+
+ # Add test database override
+ from app.api.deps import get_db
+ app.dependency_overrides[get_db] = get_test_db
+
+ with TestClient(app) as test_client:
+ yield test_client
+
+ # Clear the dependency override after the test
+ app.dependency_overrides = {}
-@pytest.fixture(scope="module")
-def superuser_token_headers(client: TestClient) -> dict[str, str]:
+@pytest.fixture(scope="function")
+def superuser_token_headers(client: TestClient) -> Dict[str, str]:
+ """Get superuser token headers for authenticated requests."""
return get_superuser_token_headers(client)
-@pytest.fixture(scope="module")
-def normal_user_token_headers(client: TestClient, db: Session) -> dict[str, str]:
+@pytest.fixture(scope="function")
+def normal_user_token_headers(client: TestClient, db: Session) -> Dict[str, str]:
+ """Get normal user token headers for authenticated requests."""
return authentication_token_from_email(
client=client, email=settings.EMAIL_TEST_USER, db=db
)
+
+
+@pytest.fixture(scope="function")
+def test_app():
+ """Return the FastAPI app for testing."""
+ return app
+
+
+@pytest.fixture(scope="function")
+def test_client(client: TestClient) -> TestClientWrapper:
+ """Return a TestClientWrapper for the test client."""
+ return get_test_client_wrapper(client)
diff --git a/backend/app/tests/models/test_workout.py b/backend/app/tests/models/test_workout.py
new file mode 100644
index 0000000000..c221457ea1
--- /dev/null
+++ b/backend/app/tests/models/test_workout.py
@@ -0,0 +1,149 @@
+"""
+Tests for workout models.
+"""
+import uuid
+from datetime import datetime, timedelta
+
+import pytest
+from sqlmodel import Session
+
+from app.models import User, Workout, Exercise
+from app.tests.utils.test_db import (
+ create_test_user,
+ create_test_workout,
+ create_test_exercise,
+)
+
+
+def test_create_workout(db: Session):
+ """Test creating a workout."""
+ # Create a test user
+ user = create_test_user(db)
+
+ # Create a workout
+ workout_name = "Monday Strength Training"
+ workout_description = "Focus on upper body"
+ scheduled_date = datetime.utcnow() + timedelta(days=1)
+
+ workout = create_test_workout(
+ db=db,
+ user_id=user.id,
+ name=workout_name,
+ description=workout_description,
+ scheduled_date=scheduled_date,
+ duration_minutes=45,
+ )
+
+ # Check that the workout was created correctly
+ assert workout.id is not None
+ assert workout.user_id == user.id
+ assert workout.name == workout_name
+ assert workout.description == workout_description
+ assert workout.scheduled_date == scheduled_date
+ assert workout.duration_minutes == 45
+ assert workout.is_completed is False
+ assert workout.created_at is not None
+ assert workout.updated_at is None
+
+
+def test_create_exercise(db: Session):
+ """Test creating an exercise for a workout."""
+ # Create a test user and workout
+ user = create_test_user(db)
+ workout = create_test_workout(db=db, user_id=user.id)
+
+ # Create an exercise
+ exercise_name = "Bench Press"
+ exercise_category = "Strength"
+ exercise_description = "Flat bench press with barbell"
+
+ exercise = create_test_exercise(
+ db=db,
+ workout_id=workout.id,
+ name=exercise_name,
+ category=exercise_category,
+ description=exercise_description,
+ sets=4,
+ reps=8,
+ weight=135.5,
+ )
+
+ # Check that the exercise was created correctly
+ assert exercise.id is not None
+ assert exercise.workout_id == workout.id
+ assert exercise.name == exercise_name
+ assert exercise.category == exercise_category
+ assert exercise.description == exercise_description
+ assert exercise.sets == 4
+ assert exercise.reps == 8
+ assert exercise.weight == 135.5
+ assert exercise.created_at is not None
+ assert exercise.updated_at is None
+
+
+def test_workout_user_relationship(db: Session):
+ """Test the relationship between workout and user."""
+ # Create a test user
+ user = create_test_user(db)
+
+ # Create a workout
+ workout = create_test_workout(db=db, user_id=user.id)
+
+ # Check that the relationship works
+ assert workout.user.id == user.id
+ assert workout in user.workouts
+
+
+def test_workout_exercise_relationship(db: Session):
+ """Test the relationship between workout and exercises."""
+ # Create a test user and workout
+ user = create_test_user(db)
+ workout = create_test_workout(db=db, user_id=user.id)
+
+ # Create exercises
+ exercise1 = create_test_exercise(
+ db=db, workout_id=workout.id, name="Squats", category="Legs"
+ )
+ exercise2 = create_test_exercise(
+ db=db, workout_id=workout.id, name="Deadlifts", category="Back"
+ )
+
+ # Check that the relationship works
+ assert exercise1 in workout.exercises
+ assert exercise2 in workout.exercises
+ assert len(workout.exercises) == 2
+ assert exercise1.workout.id == workout.id
+ assert exercise2.workout.id == workout.id
+
+
+def test_cascade_delete(db: Session):
+ """Test that deleting a workout cascades to exercises."""
+ # Create a test user and workout
+ user = create_test_user(db)
+ workout = create_test_workout(db=db, user_id=user.id)
+
+ # Create exercises
+ exercise1 = create_test_exercise(db=db, workout_id=workout.id)
+ exercise2 = create_test_exercise(db=db, workout_id=workout.id)
+
+ # Delete the workout
+ db.delete(workout)
+ db.commit()
+
+ # Check that the exercises were also deleted
+ exercises = db.query(Exercise).all()
+ assert len(exercises) == 0
+
+ # Create a new workout for the user
+ workout = create_test_workout(db=db, user_id=user.id)
+ exercise = create_test_exercise(db=db, workout_id=workout.id)
+
+ # Delete the user
+ db.delete(user)
+ db.commit()
+
+ # Check that the workout and exercise were also deleted
+ workouts = db.query(Workout).all()
+ exercises = db.query(Exercise).all()
+ assert len(workouts) == 0
+ assert len(exercises) == 0
\ No newline at end of file
diff --git a/backend/app/tests/models/test_workout_migrations.py b/backend/app/tests/models/test_workout_migrations.py
new file mode 100644
index 0000000000..7dbdd5a0fa
--- /dev/null
+++ b/backend/app/tests/models/test_workout_migrations.py
@@ -0,0 +1,81 @@
+"""
+Tests for workout model migrations.
+"""
+import pytest
+from sqlalchemy import inspect
+from sqlmodel import Session
+
+from app.models import Workout, Exercise
+
+
+def test_workout_table_exists(db: Session):
+ """Test that the workout table exists with the correct columns."""
+ # Get the inspector
+ inspector = inspect(db.get_bind())
+
+ # Check if the workout table exists
+ assert "workout" in inspector.get_table_names()
+
+ # Check if the workout table has the expected columns
+ columns = {col["name"] for col in inspector.get_columns("workout")}
+ expected_columns = {
+ "id", "user_id", "name", "description", "scheduled_date",
+ "completed_date", "duration_minutes", "is_completed",
+ "created_at", "updated_at"
+ }
+ assert expected_columns.issubset(columns)
+
+ # Check foreign keys
+ foreign_keys = inspector.get_foreign_keys("workout")
+ assert any(fk["referred_table"] == "user" for fk in foreign_keys)
+
+
+def test_exercise_table_exists(db: Session):
+ """Test that the exercise table exists with the correct columns."""
+ # Get the inspector
+ inspector = inspect(db.get_bind())
+
+ # Check if the exercise table exists
+ assert "exercise" in inspector.get_table_names()
+
+ # Check if the exercise table has the expected columns
+ columns = {col["name"] for col in inspector.get_columns("exercise")}
+ expected_columns = {
+ "id", "workout_id", "name", "description", "category",
+ "sets", "reps", "weight", "created_at", "updated_at"
+ }
+ assert expected_columns.issubset(columns)
+
+ # Check foreign keys
+ foreign_keys = inspector.get_foreign_keys("exercise")
+ assert any(fk["referred_table"] == "workout" for fk in foreign_keys)
+
+
+def test_workout_model_can_be_instantiated():
+ """Test that the Workout model can be instantiated."""
+ workout = Workout(
+ name="Test Workout",
+ description="Test description",
+ duration_minutes=60,
+ is_completed=False
+ )
+ assert workout.name == "Test Workout"
+ assert workout.description == "Test description"
+ assert workout.duration_minutes == 60
+ assert workout.is_completed is False
+
+
+def test_exercise_model_can_be_instantiated():
+ """Test that the Exercise model can be instantiated."""
+ exercise = Exercise(
+ name="Test Exercise",
+ category="Strength",
+ sets=3,
+ reps=10,
+ weight=50.0
+ )
+ assert exercise.name == "Test Exercise"
+ assert exercise.category == "Strength"
+ assert exercise.sets == 3
+ assert exercise.reps == 10
+ assert exercise.weight == 50.0
\ No newline at end of file
diff --git a/backend/app/tests/utils/test_client.py b/backend/app/tests/utils/test_client.py
new file mode 100644
index 0000000000..7534bdbabe
--- /dev/null
+++ b/backend/app/tests/utils/test_client.py
@@ -0,0 +1,83 @@
+"""
+Test client utilities for API testing.
+"""
+from typing import Dict, Any, Optional
+
+from fastapi.testclient import TestClient
+from sqlmodel import Session
+
+from app.core.config import settings
+from app.models import User
+
+
+class TestClientWrapper:
+ """
+ A wrapper around FastAPI TestClient that provides helper methods for testing.
+ """
+
+ def __init__(self, client: TestClient):
+ self.client = client
+ self.base_url = settings.API_V1_STR
+
+ def get_url(self, path: str) -> str:
+ """Get the full URL for a given API path."""
+ return f"{self.base_url}{path}"
+
+ def get(self, path: str, headers: Optional[Dict[str, str]] = None, **kwargs) -> Any:
+ """Make a GET request to the API."""
+ return self.client.get(self.get_url(path), headers=headers, **kwargs)
+
+ def post(self, path: str, headers: Optional[Dict[str, str]] = None, **kwargs) -> Any:
+ """Make a POST request to the API."""
+ return self.client.post(self.get_url(path), headers=headers, **kwargs)
+
+ def put(self, path: str, headers: Optional[Dict[str, str]] = None, **kwargs) -> Any:
+ """Make a PUT request to the API."""
+ return self.client.put(self.get_url(path), headers=headers, **kwargs)
+
+ def patch(self, path: str, headers: Optional[Dict[str, str]] = None, **kwargs) -> Any:
+ """Make a PATCH request to the API."""
+ return self.client.patch(self.get_url(path), headers=headers, **kwargs)
+
+ def delete(self, path: str, headers: Optional[Dict[str, str]] = None, **kwargs) -> Any:
+ """Make a DELETE request to the API."""
+ return self.client.delete(self.get_url(path), headers=headers, **kwargs)
+
+ def login(self, email: str, password: str) -> Dict[str, str]:
+ """
+ Login with the given credentials and return the authentication headers.
+ """
+ login_data = {
+ "username": email,
+ "password": password,
+ }
+ r = self.client.post(
+ self.get_url("/login/access-token"),
+ data=login_data
+ )
+ tokens = r.json()
+ access_token = tokens["access_token"]
+ return {"Authorization": f"Bearer {access_token}"}
+
+ def login_user(self, user: User, password: str) -> Dict[str, str]:
+ """
+ Login with a user object and return the authentication headers.
+ """
+ return self.login(user.email, password)
+
+
+def get_test_client_wrapper(client: TestClient) -> TestClientWrapper:
+ """Get a TestClientWrapper instance for the given TestClient."""
+ return TestClientWrapper(client)
+
+
+def assert_successful_response(response, status_code=200):
+ """Assert that a response was successful."""
+ assert response.status_code == status_code, f"Response: {response.json()}"
+ return response.json()
+
+
+def assert_error_response(response, status_code=400):
+ """Assert that a response was an error."""
+ assert response.status_code == status_code, f"Response: {response.json()}"
+ return response.json()
\ No newline at end of file
diff --git a/backend/app/tests/utils/test_db.py b/backend/app/tests/utils/test_db.py
new file mode 100644
index 0000000000..9e7277afc5
--- /dev/null
+++ b/backend/app/tests/utils/test_db.py
@@ -0,0 +1,138 @@
+"""
+Database utilities for testing.
+"""
+import uuid
+from datetime import datetime
+from typing import Dict, List, Type, Any, Optional
+
+from sqlmodel import Session, SQLModel, select
+
+from app.models import User, Item, WorkoutPost, UserFollow, Workout, Exercise
+from app.models.user import UserCreate
+from app.crud import create_user
+
+
+def create_test_db_and_tables(engine):
+ """Create all tables in the test database."""
+ SQLModel.metadata.create_all(engine)
+
+
+def clear_test_db(db: Session):
+ """Clear all data from the test database."""
+ for model in [Exercise, Workout, WorkoutPost, UserFollow, Item, User]:
+ db.exec(f"DELETE FROM {model.__tablename__}")
+ db.commit()
+
+
+def create_test_user(
+ db: Session,
+ email: str = "test@example.com",
+ password: str = "testpassword",
+ is_superuser: bool = False,
+ full_name: Optional[str] = "Test User"
+) -> User:
+ """Create a test user in the database."""
+ user_in = UserCreate(
+ email=email,
+ password=password,
+ is_superuser=is_superuser,
+ full_name=full_name,
+ )
+ return create_user(session=db, user_create=user_in)
+
+
+def create_test_workout_post(
+ db: Session,
+ user_id: str,
+ title: str = "Test Workout",
+ workout_type: str = "Running",
+ duration_minutes: int = 30,
+ calories_burned: Optional[int] = 300,
+ description: Optional[str] = "Test workout description"
+) -> WorkoutPost:
+ """Create a test workout post in the database."""
+ from app.models.social import WorkoutPost
+
+ workout_post = WorkoutPost(
+ user_id=user_id,
+ title=title,
+ workout_type=workout_type,
+ duration_minutes=duration_minutes,
+ calories_burned=calories_burned,
+ description=description
+ )
+ db.add(workout_post)
+ db.commit()
+ db.refresh(workout_post)
+ return workout_post
+
+
+def create_test_follow_relationship(
+ db: Session,
+ follower_id: str,
+ followed_id: str
+) -> UserFollow:
+ """Create a test follow relationship in the database."""
+ follow = UserFollow(
+ follower_id=follower_id,
+ followed_id=followed_id
+ )
+ db.add(follow)
+ db.commit()
+ db.refresh(follow)
+ return follow
+
+
+def get_test_object_count(db: Session, model: Type[SQLModel]) -> int:
+ """Get the count of objects of a specific model in the test database."""
+ return db.exec(select(model)).count()
+
+
+def create_test_workout(
+ db: Session,
+ user_id: uuid.UUID,
+ name: str = "Test Workout",
+ description: Optional[str] = "Test workout description",
+ scheduled_date: Optional[datetime] = None,
+ duration_minutes: Optional[int] = 60,
+ is_completed: bool = False
+) -> Workout:
+ """Create a test workout in the database."""
+ workout = Workout(
+ user_id=user_id,
+ name=name,
+ description=description,
+ scheduled_date=scheduled_date,
+ duration_minutes=duration_minutes,
+ is_completed=is_completed
+ )
+ db.add(workout)
+ db.commit()
+ db.refresh(workout)
+ return workout
+
+
+def create_test_exercise(
+ db: Session,
+ workout_id: uuid.UUID,
+ name: str = "Test Exercise",
+ category: str = "Strength",
+ description: Optional[str] = "Test exercise description",
+ sets: Optional[int] = 3,
+ reps: Optional[int] = 10,
+ weight: Optional[float] = 50.0
+) -> Exercise:
+ """Create a test exercise in the database."""
+ exercise = Exercise(
+ workout_id=workout_id,
+ name=name,
+ description=description,
+ category=category,
+ sets=sets,
+ reps=reps,
+ weight=weight
+ )
+ db.add(exercise)
+ db.commit()
+ db.refresh(exercise)
+ return exercise
\ No newline at end of file
diff --git a/backend/pyproject.toml b/backend/pyproject.toml
index 1c77b83ded..cda093c244 100644
--- a/backend/pyproject.toml
+++ b/backend/pyproject.toml
@@ -2,7 +2,7 @@
name = "app"
version = "0.1.0"
description = ""
-requires-python = ">=3.10,<4.0"
+requires-python = ">=3.9,<4.0"
dependencies = [
"fastapi[standard]<1.0.0,>=0.114.2",
"python-multipart<1.0.0,>=0.0.7",
@@ -21,6 +21,8 @@ dependencies = [
"pydantic-settings<3.0.0,>=2.2.1",
"sentry-sdk[fastapi]<2.0.0,>=1.40.6",
"pyjwt<3.0.0,>=2.8.0",
+ "apscheduler<4.0.0,>=3.10",
+ "requests<3.0.0,>=2.31",
]
[tool.uv]
diff --git a/backend/pytest.ini b/backend/pytest.ini
new file mode 100644
index 0000000000..57d33b6f7a
--- /dev/null
+++ b/backend/pytest.ini
@@ -0,0 +1,19 @@
+[pytest]
+testpaths = app/tests
+python_files = test_*.py
+python_classes = Test*
+python_functions = test_*
+
+# Configure test verbosity
+addopts = -v --cov=app --cov-report=term-missing
+
+# Environment variables for testing
+env =
+ ENVIRONMENT=test
+ POSTGRES_SERVER=localhost
+ POSTGRES_USER=postgres
+ POSTGRES_PASSWORD=postgres
+ POSTGRES_DB=kondition_test
+ FIRST_SUPERUSER=admin@example.com
+ FIRST_SUPERUSER_PASSWORD=admin
+ SECRET_KEY=testing_secret_key_for_kondition_app_tests
\ No newline at end of file
diff --git a/backend/scripts/prestart.sh b/backend/scripts/prestart.sh
index 1b395d513f..eb4306c01d 100644
--- a/backend/scripts/prestart.sh
+++ b/backend/scripts/prestart.sh
@@ -7,7 +7,7 @@ set -x
python app/backend_pre_start.py
# Run migrations
-alembic upgrade head
+alembic upgrade heads
# Create initial data in DB
python app/initial_data.py
diff --git a/backend/scripts/test_migrations.py b/backend/scripts/test_migrations.py
new file mode 100755
index 0000000000..0df36078bf
--- /dev/null
+++ b/backend/scripts/test_migrations.py
@@ -0,0 +1,173 @@
+#!/usr/bin/env python3
+"""
+Script to test Alembic migrations in an isolated environment.
+This script:
+1. Creates a temporary database
+2. Applies the migrations
+3. Verifies that the tables were created correctly
+4. Rolls back the migrations
+5. Verifies that the tables were dropped correctly
+"""
+import os
+import sys
+import tempfile
+import subprocess
+from pathlib import Path
+
+# Add the parent directory to the path so we can import app modules
+sys.path.append(str(Path(__file__).parent.parent))
+
+from sqlalchemy import create_engine, inspect, text
+from sqlalchemy.pool import NullPool
+from alembic.config import Config
+from alembic import command
+
+# Configuration
+ALEMBIC_INI = Path(__file__).parent.parent / "alembic.ini"
+MIGRATIONS_DIR = Path(__file__).parent.parent / "app" / "alembic"
+
+
+def create_temp_db():
+ """Create a temporary SQLite database for testing migrations."""
+ temp_db_file = tempfile.NamedTemporaryFile(suffix=".db", delete=False)
+ temp_db_file.close()
+ db_url = f"sqlite:///{temp_db_file.name}"
+
+ # Create a custom alembic.ini file for the test
+ temp_alembic_ini = tempfile.NamedTemporaryFile(suffix=".ini", delete=False)
+ with open(ALEMBIC_INI, "r") as original:
+ content = original.read()
+ # Replace the SQLAlchemy URL with our temporary database
+ content = content.replace(
+ "sqlalchemy.url = driver://user:pass@localhost/dbname",
+ f"sqlalchemy.url = {db_url}"
+ )
+
+ with open(temp_alembic_ini.name, "w") as temp:
+ temp.write(content)
+
+ return db_url, temp_db_file.name, temp_alembic_ini.name
+
+
+def get_alembic_config(alembic_ini):
+ """Get the Alembic configuration."""
+ config = Config(alembic_ini)
+ config.set_main_option("script_location", str(MIGRATIONS_DIR))
+ return config
+
+
+def check_tables(engine, expected_tables):
+ """Check if the expected tables exist in the database."""
+ inspector = inspect(engine)
+ actual_tables = inspector.get_table_names()
+
+ print(f"Expected tables: {sorted(expected_tables)}")
+ print(f"Actual tables: {sorted(actual_tables)}")
+
+ missing_tables = set(expected_tables) - set(actual_tables)
+ extra_tables = set(actual_tables) - set(expected_tables)
+
+ if missing_tables:
+ print(f"ERROR: Missing tables: {missing_tables}")
+ return False
+
+ if extra_tables:
+ print(f"WARNING: Extra tables found: {extra_tables}")
+
+ return True
+
+
+def test_migrations():
+ """Test the migrations by applying them and then rolling them back."""
+ print("Testing migrations in an isolated environment...")
+
+ # Create a temporary database
+ db_url, db_file, alembic_ini = create_temp_db()
+ print(f"Created temporary database at: {db_file}")
+
+ try:
+ # Create engine
+ engine = create_engine(db_url, poolclass=NullPool)
+
+ # Get Alembic config
+ config = get_alembic_config(alembic_ini)
+
+ # Apply all migrations
+ print("\nApplying migrations...")
+ command.upgrade(config, "head")
+ print("Migrations applied successfully.")
+
+ # Check if all expected tables exist
+ expected_tables = [
+ "user", "item", "userfollow", "workoutpost",
+ "workout", "exercise"
+ ]
+
+ print("\nChecking if all tables were created...")
+ if not check_tables(engine, expected_tables):
+ print("ERROR: Not all expected tables were created.")
+ return False
+
+ print("All expected tables were created successfully.")
+
+ # Test some basic operations
+ print("\nTesting basic database operations...")
+ with engine.connect() as conn:
+ # Check user table structure
+ user_columns = inspect(engine).get_columns("user")
+ user_column_names = [col["name"] for col in user_columns]
+ print(f"User table columns: {user_column_names}")
+
+ # Check workout table structure
+ workout_columns = inspect(engine).get_columns("workout")
+ workout_column_names = [col["name"] for col in workout_columns]
+ print(f"Workout table columns: {workout_column_names}")
+
+ # Check exercise table structure
+ exercise_columns = inspect(engine).get_columns("exercise")
+ exercise_column_names = [col["name"] for col in exercise_columns]
+ print(f"Exercise table columns: {exercise_column_names}")
+
+ # Check foreign keys
+ workout_fks = inspect(engine).get_foreign_keys("workout")
+ exercise_fks = inspect(engine).get_foreign_keys("exercise")
+
+ print(f"Workout foreign keys: {workout_fks}")
+ print(f"Exercise foreign keys: {exercise_fks}")
+
+ # Roll back all migrations
+ print("\nRolling back migrations...")
+ command.downgrade(config, "base")
+ print("Migrations rolled back successfully.")
+
+ # Check if all tables were dropped
+ print("\nChecking if all tables were dropped...")
+ inspector = inspect(engine)
+ remaining_tables = inspector.get_table_names()
+
+ if remaining_tables:
+ print(f"ERROR: Some tables remain after downgrade: {remaining_tables}")
+ return False
+
+ print("All tables were dropped successfully.")
+
+ print("\nMigration test completed successfully!")
+ return True
+
+ except Exception as e:
+ print(f"ERROR: Migration test failed: {e}")
+ return False
+
+ finally:
+ # Clean up temporary files
+ try:
+ os.unlink(db_file)
+ os.unlink(alembic_ini)
+ print(f"\nCleaned up temporary files.")
+ except Exception as e:
+ print(f"WARNING: Failed to clean up temporary files: {e}")
+
+
+if __name__ == "__main__":
+ success = test_migrations()
+ sys.exit(0 if success else 1)
\ No newline at end of file
diff --git a/backend/uv.lock b/backend/uv.lock
index feb6598ee2..64b7efb323 100644
--- a/backend/uv.lock
+++ b/backend/uv.lock
@@ -1,9 +1,9 @@
version = 1
revision = 1
-requires-python = ">=3.10, <4.0"
+requires-python = ">=3.9, <4.0"
resolution-markers = [
- "python_full_version < '3.13'",
"python_full_version >= '3.13'",
+ "python_full_version < '3.13'",
]
[[package]]
@@ -124,6 +124,9 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/87/69/edacb37481d360d06fc947dab5734aaf511acb7d1a1f9e2849454376c0f8/bcrypt-4.0.1-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:e9a51bbfe7e9802b5f3508687758b564069ba937748ad7b9e890086290d2f79e", size = 624290 },
{ url = "https://files.pythonhosted.org/packages/aa/ca/6a534669890725cbb8c1fb4622019be31813c8edaa7b6d5b62fc9360a17e/bcrypt-4.0.1-cp36-abi3-win32.whl", hash = "sha256:2caffdae059e06ac23fce178d31b4a702f2a3264c20bfb5ff541b338194d8fab", size = 159428 },
{ url = "https://files.pythonhosted.org/packages/46/81/d8c22cd7e5e1c6a7d48e41a1d1d46c92f17dae70a54d9814f746e6027dec/bcrypt-4.0.1-cp36-abi3-win_amd64.whl", hash = "sha256:8a68f4341daf7522fe8d73874de8906f3a339048ba406be6ddc1b3ccb16fc0d9", size = 152930 },
+ { url = "https://files.pythonhosted.org/packages/13/68/f3184c1f15581ebd936125b4da04cba0995f97ecd5ee8f4262c8ebba2646/bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:705b2cea8a9ed3d55b4491887ceadb0106acf7c6387699fca771af56b1cdeeda", size = 592456 },
+ { url = "https://files.pythonhosted.org/packages/5e/01/098b798dc6c6984f2d5026269e80d7cad22d6ecacd5989bdf35a9c99a03d/bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:2b3ac11cf45161628f1f3733263e63194f22664bf4d0c0f3ab34099c02134665", size = 592248 },
+ { url = "https://files.pythonhosted.org/packages/fb/4b/e255df2000c2de4df524740b5f1d0a31157a1f7715b3eaf2e8f9c5c0acbb/bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3100851841186c25f127731b9fa11909ab7b1df6fc4b9f8353f4f1fd952fbf71", size = 592714 },
]
[[package]]
@@ -213,6 +216,21 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/5b/ae/ce2c12fcac59cb3860b2e2d76dc405253a4475436b1861d95fe75bdea520/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", size = 142167 },
{ url = "https://files.pythonhosted.org/packages/ed/3a/a448bf035dce5da359daf9ae8a16b8a39623cc395a2ffb1620aa1bce62b0/charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", size = 93041 },
{ url = "https://files.pythonhosted.org/packages/b6/7c/8debebb4f90174074b827c63242c23851bdf00a532489fba57fef3416e40/charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", size = 100397 },
+ { url = "https://files.pythonhosted.org/packages/f7/9d/bcf4a449a438ed6f19790eee543a86a740c77508fbc5ddab210ab3ba3a9a/charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4", size = 194198 },
+ { url = "https://files.pythonhosted.org/packages/66/fe/c7d3da40a66a6bf2920cce0f436fa1f62ee28aaf92f412f0bf3b84c8ad6c/charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d", size = 122494 },
+ { url = "https://files.pythonhosted.org/packages/2a/9d/a6d15bd1e3e2914af5955c8eb15f4071997e7078419328fee93dfd497eb7/charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0", size = 120393 },
+ { url = "https://files.pythonhosted.org/packages/3d/85/5b7416b349609d20611a64718bed383b9251b5a601044550f0c8983b8900/charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269", size = 138331 },
+ { url = "https://files.pythonhosted.org/packages/79/66/8946baa705c588521afe10b2d7967300e49380ded089a62d38537264aece/charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c", size = 148097 },
+ { url = "https://files.pythonhosted.org/packages/44/80/b339237b4ce635b4af1c73742459eee5f97201bd92b2371c53e11958392e/charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519", size = 140711 },
+ { url = "https://files.pythonhosted.org/packages/98/69/5d8751b4b670d623aa7a47bef061d69c279e9f922f6705147983aa76c3ce/charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796", size = 142251 },
+ { url = "https://files.pythonhosted.org/packages/1f/8d/33c860a7032da5b93382cbe2873261f81467e7b37f4ed91e25fed62fd49b/charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185", size = 144636 },
+ { url = "https://files.pythonhosted.org/packages/c2/65/52aaf47b3dd616c11a19b1052ce7fa6321250a7a0b975f48d8c366733b9f/charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c", size = 139514 },
+ { url = "https://files.pythonhosted.org/packages/51/fd/0ee5b1c2860bb3c60236d05b6e4ac240cf702b67471138571dad91bcfed8/charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458", size = 145528 },
+ { url = "https://files.pythonhosted.org/packages/e1/9c/60729bf15dc82e3aaf5f71e81686e42e50715a1399770bcde1a9e43d09db/charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2", size = 149804 },
+ { url = "https://files.pythonhosted.org/packages/53/cd/aa4b8a4d82eeceb872f83237b2d27e43e637cac9ffaef19a1321c3bafb67/charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8", size = 141708 },
+ { url = "https://files.pythonhosted.org/packages/54/7f/cad0b328759630814fcf9d804bfabaf47776816ad4ef2e9938b7e1123d04/charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561", size = 142708 },
+ { url = "https://files.pythonhosted.org/packages/c1/9d/254a2f1bcb0ce9acad838e94ed05ba71a7cb1e27affaa4d9e1ca3958cdb6/charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f", size = 92830 },
+ { url = "https://files.pythonhosted.org/packages/2f/0e/d7303ccae9735ff8ff01e36705ad6233ad2002962e8668a970fc000c5e1b/charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d", size = 100376 },
{ url = "https://files.pythonhosted.org/packages/28/76/e6222113b83e3622caa4bb41032d0b1bf785250607392e1b778aca0b8a7d/charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", size = 48543 },
]
@@ -293,6 +311,16 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/52/76/1766bb8b803a88f93c3a2d07e30ffa359467810e5cbc68e375ebe6906efb/coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3", size = 247598 },
{ url = "https://files.pythonhosted.org/packages/66/8b/f54f8db2ae17188be9566e8166ac6df105c1c611e25da755738025708d54/coverage-7.6.1-cp313-cp313t-win32.whl", hash = "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f", size = 210307 },
{ url = "https://files.pythonhosted.org/packages/9f/b0/e0dca6da9170aefc07515cce067b97178cefafb512d00a87a1c717d2efd5/coverage-7.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657", size = 211453 },
+ { url = "https://files.pythonhosted.org/packages/19/d3/d54c5aa83268779d54c86deb39c1c4566e5d45c155369ca152765f8db413/coverage-7.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abd5fd0db5f4dc9289408aaf34908072f805ff7792632250dcb36dc591d24255", size = 206688 },
+ { url = "https://files.pythonhosted.org/packages/a5/fe/137d5dca72e4a258b1bc17bb04f2e0196898fe495843402ce826a7419fe3/coverage-7.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:547f45fa1a93154bd82050a7f3cddbc1a7a4dd2a9bf5cb7d06f4ae29fe94eaf8", size = 207120 },
+ { url = "https://files.pythonhosted.org/packages/78/5b/a0a796983f3201ff5485323b225d7c8b74ce30c11f456017e23d8e8d1945/coverage-7.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:645786266c8f18a931b65bfcefdbf6952dd0dea98feee39bd188607a9d307ed2", size = 235249 },
+ { url = "https://files.pythonhosted.org/packages/4e/e1/76089d6a5ef9d68f018f65411fcdaaeb0141b504587b901d74e8587606ad/coverage-7.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e0b2df163b8ed01d515807af24f63de04bebcecbd6c3bfeff88385789fdf75a", size = 233237 },
+ { url = "https://files.pythonhosted.org/packages/9a/6f/eef79b779a540326fee9520e5542a8b428cc3bfa8b7c8f1022c1ee4fc66c/coverage-7.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:609b06f178fe8e9f89ef676532760ec0b4deea15e9969bf754b37f7c40326dbc", size = 234311 },
+ { url = "https://files.pythonhosted.org/packages/75/e1/656d65fb126c29a494ef964005702b012f3498db1a30dd562958e85a4049/coverage-7.6.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:702855feff378050ae4f741045e19a32d57d19f3e0676d589df0575008ea5004", size = 233453 },
+ { url = "https://files.pythonhosted.org/packages/68/6a/45f108f137941a4a1238c85f28fd9d048cc46b5466d6b8dda3aba1bb9d4f/coverage-7.6.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2bdb062ea438f22d99cba0d7829c2ef0af1d768d1e4a4f528087224c90b132cb", size = 231958 },
+ { url = "https://files.pythonhosted.org/packages/9b/e7/47b809099168b8b8c72ae311efc3e88c8d8a1162b3ba4b8da3cfcdb85743/coverage-7.6.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9c56863d44bd1c4fe2abb8a4d6f5371d197f1ac0ebdee542f07f35895fc07f36", size = 232938 },
+ { url = "https://files.pythonhosted.org/packages/52/80/052222ba7058071f905435bad0ba392cc12006380731c37afaf3fe749b88/coverage-7.6.1-cp39-cp39-win32.whl", hash = "sha256:6e2cd258d7d927d09493c8df1ce9174ad01b381d4729a9d8d4e38670ca24774c", size = 209352 },
+ { url = "https://files.pythonhosted.org/packages/b8/d8/1b92e0b3adcf384e98770a00ca095da1b5f7b483e6563ae4eb5e935d24a1/coverage-7.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:06a737c882bd26d0d6ee7269b20b12f14a8704807a01056c80bb881a4b2ce6ca", size = 210153 },
{ url = "https://files.pythonhosted.org/packages/a5/2b/0354ed096bca64dc8e32a7cbcae28b34cb5ad0b1fe2125d6d99583313ac0/coverage-7.6.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:e9a6e0eb86070e8ccaedfbd9d38fec54864f3125ab95419970575b42af7541df", size = 198926 },
]
@@ -474,6 +502,16 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/18/87/470e01a940307796f1d25f8167b551a968540fbe0551c0ebb853cb527dd6/greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822", size = 602753 },
{ url = "https://files.pythonhosted.org/packages/e2/72/576815ba674eddc3c25028238f74d7b8068902b3968cbe456771b166455e/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01", size = 1122731 },
{ url = "https://files.pythonhosted.org/packages/ac/38/08cc303ddddc4b3d7c628c3039a61a3aae36c241ed01393d00c2fd663473/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6", size = 1142112 },
+ { url = "https://files.pythonhosted.org/packages/8c/82/8051e82af6d6b5150aacb6789a657a8afd48f0a44d8e91cb72aaaf28553a/greenlet-3.1.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:396979749bd95f018296af156201d6211240e7a23090f50a8d5d18c370084dc3", size = 270027 },
+ { url = "https://files.pythonhosted.org/packages/f9/74/f66de2785880293780eebd18a2958aeea7cbe7814af1ccef634f4701f846/greenlet-3.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca9d0ff5ad43e785350894d97e13633a66e2b50000e8a183a50a88d834752d42", size = 634822 },
+ { url = "https://files.pythonhosted.org/packages/68/23/acd9ca6bc412b02b8aa755e47b16aafbe642dde0ad2f929f836e57a7949c/greenlet-3.1.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f6ff3b14f2df4c41660a7dec01045a045653998784bf8cfcb5a525bdffffbc8f", size = 646866 },
+ { url = "https://files.pythonhosted.org/packages/a9/ab/562beaf8a53dc9f6b2459f200e7bc226bb07e51862a66351d8b7817e3efd/greenlet-3.1.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94ebba31df2aa506d7b14866fed00ac141a867e63143fe5bca82a8e503b36437", size = 641985 },
+ { url = "https://files.pythonhosted.org/packages/03/d3/1006543621f16689f6dc75f6bcf06e3c23e044c26fe391c16c253623313e/greenlet-3.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73aaad12ac0ff500f62cebed98d8789198ea0e6f233421059fa68a5aa7220145", size = 641268 },
+ { url = "https://files.pythonhosted.org/packages/2f/c1/ad71ce1b5f61f900593377b3f77b39408bce5dc96754790311b49869e146/greenlet-3.1.1-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63e4844797b975b9af3a3fb8f7866ff08775f5426925e1e0bbcfe7932059a12c", size = 597376 },
+ { url = "https://files.pythonhosted.org/packages/f7/ff/183226685b478544d61d74804445589e069d00deb8ddef042699733950c7/greenlet-3.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7939aa3ca7d2a1593596e7ac6d59391ff30281ef280d8632fa03d81f7c5f955e", size = 1123359 },
+ { url = "https://files.pythonhosted.org/packages/c0/8b/9b3b85a89c22f55f315908b94cd75ab5fed5973f7393bbef000ca8b2c5c1/greenlet-3.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d0028e725ee18175c6e422797c407874da24381ce0690d6b9396c204c7f7276e", size = 1147458 },
+ { url = "https://files.pythonhosted.org/packages/b8/1c/248fadcecd1790b0ba793ff81fa2375c9ad6442f4c748bf2cc2e6563346a/greenlet-3.1.1-cp39-cp39-win32.whl", hash = "sha256:5e06afd14cbaf9e00899fae69b24a32f2196c19de08fcb9f4779dd4f004e5e7c", size = 281131 },
+ { url = "https://files.pythonhosted.org/packages/ae/02/e7d0aef2354a38709b764df50b2b83608f0621493e47f47694eb80922822/greenlet-3.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:3319aa75e0e0639bc15ff54ca327e8dc7a6fe404003496e3c6925cd3142e0e22", size = 298306 },
]
[[package]]
@@ -525,6 +563,13 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/cf/3a/3fd8dfb987c4247651baf2ac6f28e8e9f889d484ca1a41a9ad0f04dfe300/httptools-0.6.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9bb68d3a085c2174c2477eb3ffe84ae9fb4fde8792edb7bcd09a1d8467e30a84", size = 345096 },
{ url = "https://files.pythonhosted.org/packages/80/01/379f6466d8e2edb861c1f44ccac255ed1f8a0d4c5c666a1ceb34caad7555/httptools-0.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b512aa728bc02354e5ac086ce76c3ce635b62f5fbc32ab7082b5e582d27867bb", size = 343535 },
{ url = "https://files.pythonhosted.org/packages/d3/97/60860e9ee87a7d4712b98f7e1411730520053b9d69e9e42b0b9751809c17/httptools-0.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:97662ce7fb196c785344d00d638fc9ad69e18ee4bfb4000b35a52efe5adcc949", size = 55660 },
+ { url = "https://files.pythonhosted.org/packages/02/ba/e7c6040bd3b5e46c17dcf84c8c667e3e2fb4a1ac7bec92d925fc0a35fb96/httptools-0.6.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:95fb92dd3649f9cb139e9c56604cc2d7c7bf0fc2e7c8d7fbd58f96e35eddd2a3", size = 152871 },
+ { url = "https://files.pythonhosted.org/packages/53/d3/968ab0568634f226ed20d82131c0304550fa2d60088a3699281ea8f4b34d/httptools-0.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dcbab042cc3ef272adc11220517278519adf8f53fd3056d0e68f0a6f891ba94e", size = 79137 },
+ { url = "https://files.pythonhosted.org/packages/69/45/0f5014fa50f923599fead11e001e23fb210a1f82dddc1afbf00db20ff4ff/httptools-0.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cf2372e98406efb42e93bfe10f2948e467edfd792b015f1b4ecd897903d3e8d", size = 351164 },
+ { url = "https://files.pythonhosted.org/packages/7c/58/d3728a369eaacd125918469c767e4af00326255db29e5e070433d9f40165/httptools-0.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:678fcbae74477a17d103b7cae78b74800d795d702083867ce160fc202104d0da", size = 345180 },
+ { url = "https://files.pythonhosted.org/packages/59/13/9c253d23e62539922032a967ae06ce16e53c3bba592d4ff63920058f0bbb/httptools-0.6.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e0b281cf5a125c35f7f6722b65d8542d2e57331be573e9e88bc8b0115c4a7a81", size = 369162 },
+ { url = "https://files.pythonhosted.org/packages/8c/0f/ac82bdc14f5e4bff59a3c3c35fa7a9b7a2f8d983c4d5a33b20e4848b3f14/httptools-0.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:95658c342529bba4e1d3d2b1a874db16c7cca435e8827422154c9da76ac4e13a", size = 360831 },
+ { url = "https://files.pythonhosted.org/packages/0a/0d/ca545a8a2831fc3e326fffecab268a2e7775e5ec4d57afc8f5ddc578cbd7/httptools-0.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:7ebaec1bf683e4bf5e9fbb49b8cc36da482033596a415b3e4ebab5a4c0d7ec5e", size = 60238 },
]
[[package]]
@@ -655,12 +700,35 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/b6/17/71e9984cf0570cd202ac0a1c9ed5c1b8889b0fc8dc736f5ef0ffb181c284/lxml-5.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:df5c7333167b9674aa8ae1d4008fa4bc17a313cc490b2cca27838bbdcc6bb15b", size = 5011053 },
{ url = "https://files.pythonhosted.org/packages/69/68/9f7e6d3312a91e30829368c2b3217e750adef12a6f8eb10498249f4e8d72/lxml-5.3.0-cp313-cp313-win32.whl", hash = "sha256:c802e1c2ed9f0c06a65bc4ed0189d000ada8049312cfeab6ca635e39c9608957", size = 3485634 },
{ url = "https://files.pythonhosted.org/packages/7d/db/214290d58ad68c587bd5d6af3d34e56830438733d0d0856c0275fde43652/lxml-5.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:406246b96d552e0503e17a1006fd27edac678b3fcc9f1be71a2f94b4ff61528d", size = 3814417 },
+ { url = "https://files.pythonhosted.org/packages/89/a9/63af38c7f42baff8251d937be91c6decfe9e4725fe16283dcee428e08d5c/lxml-5.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1ffc23010330c2ab67fac02781df60998ca8fe759e8efde6f8b756a20599c5de", size = 8129239 },
+ { url = "https://files.pythonhosted.org/packages/23/b2/45e12a5b8508ee9de0af432d0dc5fcc786cd78037d692a3de7571c2db04c/lxml-5.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2b3778cb38212f52fac9fe913017deea2fdf4eb1a4f8e4cfc6b009a13a6d3fcc", size = 4415821 },
+ { url = "https://files.pythonhosted.org/packages/88/88/a01dc8055d431c39859ec3806dbe4df6cf7a80b0431227a52de8428d2cf6/lxml-5.3.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b0c7a688944891086ba192e21c5229dea54382f4836a209ff8d0a660fac06be", size = 5139927 },
+ { url = "https://files.pythonhosted.org/packages/13/d9/c0f3fd5582a26ea887122feb9cfe84215642ecf10886dcb50a603a6ef448/lxml-5.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:747a3d3e98e24597981ca0be0fd922aebd471fa99d0043a3842d00cdcad7ad6a", size = 4839659 },
+ { url = "https://files.pythonhosted.org/packages/64/06/290728f6fde1761c323db28ece9601018db72ecafa21b182cfea99e7cb2e/lxml-5.3.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86a6b24b19eaebc448dc56b87c4865527855145d851f9fc3891673ff97950540", size = 5427269 },
+ { url = "https://files.pythonhosted.org/packages/52/43/af104743bb733e85efc0be0e32c140e3e7be6050aca52b1e8a0b2867c382/lxml-5.3.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b11a5d918a6216e521c715b02749240fb07ae5a1fefd4b7bf12f833bc8b4fe70", size = 4876667 },
+ { url = "https://files.pythonhosted.org/packages/d8/5f/9dea130ae3ba77848f4b93d11dfd365085620fb34c5c9d22746227b86952/lxml-5.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68b87753c784d6acb8a25b05cb526c3406913c9d988d51f80adecc2b0775d6aa", size = 5013541 },
+ { url = "https://files.pythonhosted.org/packages/e8/87/a089806f0327ad7f7268c3f4d22f1d76215a923bf33ea808bb665bdeacfa/lxml-5.3.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:109fa6fede314cc50eed29e6e56c540075e63d922455346f11e4d7a036d2b8cf", size = 4818394 },
+ { url = "https://files.pythonhosted.org/packages/87/63/b36ddd4a829a5de681bde7e9be4008a8b53c392dea4c8b1492c35727e150/lxml-5.3.0-cp39-cp39-manylinux_2_28_ppc64le.whl", hash = "sha256:02ced472497b8362c8e902ade23e3300479f4f43e45f4105c85ef43b8db85229", size = 5472977 },
+ { url = "https://files.pythonhosted.org/packages/99/1f/677226f48e2d1ea590c24f3ead1799584517a62a394a338b96f62d3c732e/lxml-5.3.0-cp39-cp39-manylinux_2_28_s390x.whl", hash = "sha256:6b038cc86b285e4f9fea2ba5ee76e89f21ed1ea898e287dc277a25884f3a7dfe", size = 4978803 },
+ { url = "https://files.pythonhosted.org/packages/9d/f8/1b96af1396f237de488b14f70b2c6ced5079b792770e6a0f7153f912124d/lxml-5.3.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:7437237c6a66b7ca341e868cda48be24b8701862757426852c9b3186de1da8a2", size = 5026166 },
+ { url = "https://files.pythonhosted.org/packages/a9/42/86a09a2cabb7bed04d904e38cc09ac65e4916fc1b7eadf94bb924893988b/lxml-5.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7f41026c1d64043a36fda21d64c5026762d53a77043e73e94b71f0521939cc71", size = 4890234 },
+ { url = "https://files.pythonhosted.org/packages/c9/0a/bf0edfe5635ed05ed69a8ae9c1e06dc28cf8becc4ea72f39d3624f20b3d9/lxml-5.3.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:482c2f67761868f0108b1743098640fbb2a28a8e15bf3f47ada9fa59d9fe08c3", size = 5533730 },
+ { url = "https://files.pythonhosted.org/packages/00/cd/dfd8fd56415508751caac07c7ddb3b0a40aff346c11fabdd9d8aa2bfb329/lxml-5.3.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:1483fd3358963cc5c1c9b122c80606a3a79ee0875bcac0204149fa09d6ff2727", size = 5406452 },
+ { url = "https://files.pythonhosted.org/packages/3f/35/fcc233c86f4e59f9498cde8ad6131e1ca41dc7aa084ec982d2cccca91cd7/lxml-5.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2dec2d1130a9cda5b904696cec33b2cfb451304ba9081eeda7f90f724097300a", size = 5078114 },
+ { url = "https://files.pythonhosted.org/packages/9b/55/94c9bc55ec20744a21c949138649442298cff4189067b7e0844dd0a111d0/lxml-5.3.0-cp39-cp39-win32.whl", hash = "sha256:a0eabd0a81625049c5df745209dc7fcef6e2aea7793e5f003ba363610aa0a3ff", size = 3478072 },
+ { url = "https://files.pythonhosted.org/packages/bb/ab/68821837e454c4c34f40cbea8806637ec4d814b76d3d017a24a39c651a79/lxml-5.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:89e043f1d9d341c52bf2af6d02e6adde62e0a46e6755d5eb60dc6e4f0b8aeca2", size = 3806100 },
{ url = "https://files.pythonhosted.org/packages/99/f7/b73a431c8500565aa500e99e60b448d305eaf7c0b4c893c7c5a8a69cc595/lxml-5.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7b1cd427cb0d5f7393c31b7496419da594fe600e6fdc4b105a54f82405e6626c", size = 3925431 },
{ url = "https://files.pythonhosted.org/packages/db/48/4a206623c0d093d0e3b15f415ffb4345b0bdf661a3d0b15a112948c033c7/lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51806cfe0279e06ed8500ce19479d757db42a30fd509940b1701be9c86a5ff9a", size = 4216683 },
{ url = "https://files.pythonhosted.org/packages/54/47/577820c45dd954523ae8453b632d91e76da94ca6d9ee40d8c98dd86f916b/lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee70d08fd60c9565ba8190f41a46a54096afa0eeb8f76bd66f2c25d3b1b83005", size = 4326732 },
{ url = "https://files.pythonhosted.org/packages/68/de/96cb6d3269bc994b4f5ede8ca7bf0840f5de0a278bc6e50cb317ff71cafa/lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:8dc2c0395bea8254d8daebc76dcf8eb3a95ec2a46fa6fae5eaccee366bfe02ce", size = 4218377 },
{ url = "https://files.pythonhosted.org/packages/a5/43/19b1ef6cbffa4244a217f95cc5f41a6cb4720fed33510a49670b03c5f1a0/lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6ba0d3dcac281aad8a0e5b14c7ed6f9fa89c8612b47939fc94f80b16e2e9bc83", size = 4351237 },
{ url = "https://files.pythonhosted.org/packages/ba/b2/6a22fb5c0885da3b00e116aee81f0b829ec9ac8f736cd414b4a09413fc7d/lxml-5.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:6e91cf736959057f7aac7adfc83481e03615a8e8dd5758aa1d95ea69e8931dba", size = 3487557 },
+ { url = "https://files.pythonhosted.org/packages/c9/ac/e8ec7b6f7d76f8b88dfe78dd547b0d8915350160a5a01cca7aceba91e87f/lxml-5.3.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:68934b242c51eb02907c5b81d138cb977b2129a0a75a8f8b60b01cb8586c7b21", size = 3923032 },
+ { url = "https://files.pythonhosted.org/packages/f7/b6/d94041c11aa294a09ffac7caa633114941935938eaaba159a93985283c07/lxml-5.3.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b710bc2b8292966b23a6a0121f7a6c51d45d2347edcc75f016ac123b8054d3f2", size = 4214557 },
+ { url = "https://files.pythonhosted.org/packages/dd/0d/ccb5e4e7a4188a9c881a3c07ee7eaf21772ae847ca5e9a3b140341f2668a/lxml-5.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18feb4b93302091b1541221196a2155aa296c363fd233814fa11e181adebc52f", size = 4325217 },
+ { url = "https://files.pythonhosted.org/packages/7a/17/9d3b43b63b0ddd77f1a680edf00de3c8c2441e8d379be17d2b712b67688b/lxml-5.3.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:3eb44520c4724c2e1a57c0af33a379eee41792595023f367ba3952a2d96c2aab", size = 4216018 },
+ { url = "https://files.pythonhosted.org/packages/19/4f/f71029b3f37f43e846b6ec0d6baaa1791c65f8c3356cc78d18076f4c5422/lxml-5.3.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:609251a0ca4770e5a8768ff902aa02bf636339c5a93f9349b48eb1f606f7f3e9", size = 4347893 },
+ { url = "https://files.pythonhosted.org/packages/17/45/0fe53cb16a704b35b5ec93af305f77a14ec65830fc399e6634a81f17a1ea/lxml-5.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:516f491c834eb320d6c843156440fe7fc0d50b33e44387fcec5b02f0bc118a4c", size = 3486287 },
]
[[package]]
@@ -723,6 +791,16 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/88/07/2dc76aa51b481eb96a4c3198894f38b480490e834479611a4053fbf08623/MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169", size = 33038 },
{ url = "https://files.pythonhosted.org/packages/96/0c/620c1fb3661858c0e37eb3cbffd8c6f732a67cd97296f725789679801b31/MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad", size = 16572 },
{ url = "https://files.pythonhosted.org/packages/3f/14/c3554d512d5f9100a95e737502f4a2323a1959f6d0d01e0d0997b35f7b10/MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb", size = 17127 },
+ { url = "https://files.pythonhosted.org/packages/0f/31/780bb297db036ba7b7bbede5e1d7f1e14d704ad4beb3ce53fb495d22bc62/MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf", size = 18193 },
+ { url = "https://files.pythonhosted.org/packages/6c/77/d77701bbef72892affe060cdacb7a2ed7fd68dae3b477a8642f15ad3b132/MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2", size = 14073 },
+ { url = "https://files.pythonhosted.org/packages/d9/a7/1e558b4f78454c8a3a0199292d96159eb4d091f983bc35ef258314fe7269/MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8", size = 26486 },
+ { url = "https://files.pythonhosted.org/packages/5f/5a/360da85076688755ea0cceb92472923086993e86b5613bbae9fbc14136b0/MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3", size = 25685 },
+ { url = "https://files.pythonhosted.org/packages/6a/18/ae5a258e3401f9b8312f92b028c54d7026a97ec3ab20bfaddbdfa7d8cce8/MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465", size = 25338 },
+ { url = "https://files.pythonhosted.org/packages/0b/cc/48206bd61c5b9d0129f4d75243b156929b04c94c09041321456fd06a876d/MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e", size = 30439 },
+ { url = "https://files.pythonhosted.org/packages/d1/06/a41c112ab9ffdeeb5f77bc3e331fdadf97fa65e52e44ba31880f4e7f983c/MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea", size = 29531 },
+ { url = "https://files.pythonhosted.org/packages/02/8c/ab9a463301a50dab04d5472e998acbd4080597abc048166ded5c7aa768c8/MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6", size = 29823 },
+ { url = "https://files.pythonhosted.org/packages/bc/29/9bc18da763496b055d8e98ce476c8e718dcfd78157e17f555ce6dd7d0895/MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf", size = 16658 },
+ { url = "https://files.pythonhosted.org/packages/f6/f8/4da07de16f10551ca1f640c92b5f316f9394088b183c6a57183df6de5ae4/MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5", size = 17211 },
]
[[package]]
@@ -769,6 +847,11 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/f3/3c/350a9da895f8a7e87ade0028b962be0252d152e0c2fbaafa6f0658b4d0d4/mypy-1.11.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e7184632d89d677973a14d00ae4d03214c8bc301ceefcdaf5c474866814c987", size = 12506856 },
{ url = "https://files.pythonhosted.org/packages/b6/49/ee5adf6a49ff13f4202d949544d3d08abb0ea1f3e7f2a6d5b4c10ba0360a/mypy-1.11.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3a66169b92452f72117e2da3a576087025449018afc2d8e9bfe5ffab865709ca", size = 12952066 },
{ url = "https://files.pythonhosted.org/packages/27/c0/b19d709a42b24004d720db37446a42abadf844d5c46a2c442e2a074d70d9/mypy-1.11.2-cp312-cp312-win_amd64.whl", hash = "sha256:969ea3ef09617aff826885a22ece0ddef69d95852cdad2f60c8bb06bf1f71f70", size = 9664000 },
+ { url = "https://files.pythonhosted.org/packages/16/64/bb5ed751487e2bea0dfaa6f640a7e3bb88083648f522e766d5ef4a76f578/mypy-1.11.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:801ca29f43d5acce85f8e999b1e431fb479cb02d0e11deb7d2abb56bdaf24fd6", size = 10937294 },
+ { url = "https://files.pythonhosted.org/packages/a9/a3/67a0069abed93c3bf3b0bebb8857e2979a02828a4a3fd82f107f8f1143e8/mypy-1.11.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af8d155170fcf87a2afb55b35dc1a0ac21df4431e7d96717621962e4b9192e70", size = 10107707 },
+ { url = "https://files.pythonhosted.org/packages/2f/4d/0379daf4258b454b1f9ed589a9dabd072c17f97496daea7b72fdacf7c248/mypy-1.11.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7821776e5c4286b6a13138cc935e2e9b6fde05e081bdebf5cdb2bb97c9df81d", size = 12498367 },
+ { url = "https://files.pythonhosted.org/packages/3b/dc/3976a988c280b3571b8eb6928882dc4b723a403b21735a6d8ae6ed20e82b/mypy-1.11.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:539c570477a96a4e6fb718b8d5c3e0c0eba1f485df13f86d2970c91f0673148d", size = 13018014 },
+ { url = "https://files.pythonhosted.org/packages/83/84/adffc7138fb970e7e2a167bd20b33bb78958370179853a4ebe9008139342/mypy-1.11.2-cp39-cp39-win_amd64.whl", hash = "sha256:3f14cd3d386ac4d05c5a39a51b84387403dadbd936e17cb35882134d4f8f0d24", size = 9568056 },
{ url = "https://files.pythonhosted.org/packages/42/3a/bdf730640ac523229dd6578e8a581795720a9321399de494374afc437ec5/mypy-1.11.2-py3-none-any.whl", hash = "sha256:b499bc07dbdcd3de92b0a8b29fdf592c111276f6a12fe29c30f6c417dd546d12", size = 2619625 },
]
@@ -930,6 +1013,16 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/8b/76/c77643d97292673d8a5e3eea643812d585993155658f840c86bfa855e077/psycopg_binary-3.2.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:705da5bc4364bd7529473225fca02b795653bc5bd824dbe43e1df0b1a40fe691", size = 3189435 },
{ url = "https://files.pythonhosted.org/packages/30/31/b4ea793bdf44acca51e3fa6f68cc80d03725e8ef87fc2ee2b332c49fa521/psycopg_binary-3.2.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:05406b96139912574571b1c56bb023839a9146cf4b57c4548f36251dd5909fa1", size = 3233951 },
{ url = "https://files.pythonhosted.org/packages/49/e3/633d6d05e40651acb30458e296c90e878fa4caf3b3c21bb9e6adc912b811/psycopg_binary-3.2.2-cp313-cp313-win_amd64.whl", hash = "sha256:7c357cf87e8d7612cfe781225be7669f35038a765d1b53ec9605f6c5aef9ee85", size = 2913412 },
+ { url = "https://files.pythonhosted.org/packages/a7/00/35efbb781585c1332f01bb8e91e72955ef275282a90a8bcf53c3f6aea954/psycopg_binary-3.2.2-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:6c7b6a8d4e1b77cdb50192b61235b33fc2f1d28c67627fc93a1d43e9130dd479", size = 3383069 },
+ { url = "https://files.pythonhosted.org/packages/e3/9c/22d609f4e21805ccf4916961da87af91afebfbb38e0280a944504118ca97/psycopg_binary-3.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e234edc4bb746d8ac3daae8753ee38eaa7af2ee333a1d35ce6b02a02874aed18", size = 4467053 },
+ { url = "https://files.pythonhosted.org/packages/ce/03/feffa600bd12f2a98be2e521196b6de18005efaa9c68e61482995e81bd34/psycopg_binary-3.2.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f12640ba92c538b3b64a199a918d3bb0cc0d7f7123c6ba93cb065e1a2d049f0", size = 4268798 },
+ { url = "https://files.pythonhosted.org/packages/b9/a2/fc1ed94ef433650606d5bcd0b97df4f6220a14a301305263f7eac5b589d2/psycopg_binary-3.2.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8937dc548621b336b0d8383a3470fb7192b42a108c760a152282909867bf5b26", size = 4516687 },
+ { url = "https://files.pythonhosted.org/packages/f6/58/6871162d923b1abecc8cf606c780caf1ff49e310dd8a0b0548c2e0762d41/psycopg_binary-3.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4afbb97d64cd8078edec859b07859a18ef3de7261a3a873ba52f32548373ae92", size = 4213227 },
+ { url = "https://files.pythonhosted.org/packages/40/73/74bde60684a3bed3543c06b8fc1b487489b135b1ca719984c93bcb067160/psycopg_binary-3.2.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c432710bdf8ccfdd75b0bc9cdf1fd21ff394363e4daec099c667f3c5f1721e2b", size = 3138101 },
+ { url = "https://files.pythonhosted.org/packages/c2/93/01a271ec5f3cd6c9e4ab474f603ed10e3b34a7d653b9959689f72ddd2c12/psycopg_binary-3.2.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:366cc4e194f7feb4e3038d6775fd4b69835e7d923972aee5baec986de972abd6", size = 3113100 },
+ { url = "https://files.pythonhosted.org/packages/ec/4b/0031b7b57f2bedf32ddbc84044054b94eebec2cc600335a066ce12956fe5/psycopg_binary-3.2.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b286ed65a891928bd457ffa0cd5fec09b9b5208bfd096d087e45369f07c5cb85", size = 3222688 },
+ { url = "https://files.pythonhosted.org/packages/9c/6e/edbd5aa5a5f6ddf389dd35c5a2c37d5e4886f5ad5502a9a020f03f1960cb/psycopg_binary-3.2.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9fee41c99312002e5d1f7462b1954aefed44c6efe5f021c3eac311640c16f6b7", size = 3256988 },
+ { url = "https://files.pythonhosted.org/packages/c4/02/08992555508d1e0d48914cb7cd069088cfafb51b0e7a2a06aaba543c974d/psycopg_binary-3.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:87cceaf07760a04023596f9ca1d4e929d38ae8d778161cb3e8d27a0f990dd264", size = 2923336 },
]
[[package]]
@@ -1003,6 +1096,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/16/16/b805c74b35607d24d37103007f899abc4880923b04929547ae68d478b7f4/pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f", size = 2116814 },
{ url = "https://files.pythonhosted.org/packages/d1/58/5305e723d9fcdf1c5a655e6a4cc2a07128bf644ff4b1d98daf7a9dbf57da/pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769", size = 1738360 },
{ url = "https://files.pythonhosted.org/packages/a5/ae/e14b0ff8b3f48e02394d8acd911376b7b66e164535687ef7dc24ea03072f/pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5", size = 1919411 },
+ { url = "https://files.pythonhosted.org/packages/7a/04/2580b2deaae37b3e30fc30c54298be938b973990b23612d6b61c7bdd01c7/pydantic_core-2.23.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a4fa4fc04dff799089689f4fd502ce7d59de529fc2f40a2c8836886c03e0175a", size = 1868200 },
+ { url = "https://files.pythonhosted.org/packages/39/6e/e311bd0751505350f0cdcee3077841eb1f9253c5a1ddbad048cd9fbf7c6e/pydantic_core-2.23.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a7df63886be5e270da67e0966cf4afbae86069501d35c8c1b3b6c168f42cb36", size = 1749316 },
+ { url = "https://files.pythonhosted.org/packages/d0/b4/95b5eb47c6dc8692508c3ca04a1f8d6f0884c9dacb34cf3357595cbe73be/pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcedcd19a557e182628afa1d553c3895a9f825b936415d0dbd3cd0bbcfd29b4b", size = 1800880 },
+ { url = "https://files.pythonhosted.org/packages/da/79/41c4f817acd7f42d94cd1e16526c062a7b089f66faed4bd30852314d9a66/pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f54b118ce5de9ac21c363d9b3caa6c800341e8c47a508787e5868c6b79c9323", size = 1807077 },
+ { url = "https://files.pythonhosted.org/packages/fb/53/d13d1eb0a97d5c06cf7a225935d471e9c241afd389a333f40c703f214973/pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86d2f57d3e1379a9525c5ab067b27dbb8a0642fb5d454e17a9ac434f9ce523e3", size = 2002859 },
+ { url = "https://files.pythonhosted.org/packages/53/7d/6b8a1eff453774b46cac8c849e99455b27167971a003212f668e94bc4c9c/pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de6d1d1b9e5101508cb37ab0d972357cac5235f5c6533d1071964c47139257df", size = 2661437 },
+ { url = "https://files.pythonhosted.org/packages/6c/ea/8820f57f0b46e6148ee42d8216b15e8fe3b360944284bbc705bf34fac888/pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1278e0d324f6908e872730c9102b0112477a7f7cf88b308e4fc36ce1bdb6d58c", size = 2054404 },
+ { url = "https://files.pythonhosted.org/packages/0f/36/d4ae869e473c3c7868e1cd1e2a1b9e13bce5cd1a7d287f6ac755a0b1575e/pydantic_core-2.23.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a6b5099eeec78827553827f4c6b8615978bb4b6a88e5d9b93eddf8bb6790f55", size = 1921680 },
+ { url = "https://files.pythonhosted.org/packages/0d/f8/eed5c65b80c4ac4494117e2101973b45fc655774ef647d17dde40a70f7d2/pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e55541f756f9b3ee346b840103f32779c695a19826a4c442b7954550a0972040", size = 1966093 },
+ { url = "https://files.pythonhosted.org/packages/e8/c8/1d42ce51d65e571ab53d466cae83434325a126811df7ce4861d9d97bee4b/pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5c7ba8ffb6d6f8f2ab08743be203654bb1aaa8c9dcb09f82ddd34eadb695605", size = 2111437 },
+ { url = "https://files.pythonhosted.org/packages/aa/c9/7fea9d13383c2ec6865919e09cffe44ab77e911eb281b53a4deaafd4c8e8/pydantic_core-2.23.4-cp39-none-win32.whl", hash = "sha256:37b0fe330e4a58d3c58b24d91d1eb102aeec675a3db4c292ec3928ecd892a9a6", size = 1735049 },
+ { url = "https://files.pythonhosted.org/packages/98/95/dd7045c4caa2b73d0bf3b989d66b23cfbb7a0ef14ce99db15677a000a953/pydantic_core-2.23.4-cp39-none-win_amd64.whl", hash = "sha256:1498bec4c05c9c787bde9125cfdcc63a41004ff167f495063191b863399b1a29", size = 1920180 },
{ url = "https://files.pythonhosted.org/packages/13/a9/5d582eb3204464284611f636b55c0a7410d748ff338756323cb1ce721b96/pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5", size = 1857135 },
{ url = "https://files.pythonhosted.org/packages/2c/57/faf36290933fe16717f97829eabfb1868182ac495f99cf0eda9f59687c9d/pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec", size = 1740583 },
{ url = "https://files.pythonhosted.org/packages/91/7c/d99e3513dc191c4fec363aef1bf4c8af9125d8fa53af7cb97e8babef4e40/pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480", size = 1793637 },
@@ -1011,6 +1116,14 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/f7/ca/9c0854829311fb446020ebb540ee22509731abad886d2859c855dd29b904/pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728", size = 1957926 },
{ url = "https://files.pythonhosted.org/packages/c0/1c/7836b67c42d0cd4441fcd9fafbf6a027ad4b79b6559f80cf11f89fd83648/pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433", size = 2100342 },
{ url = "https://files.pythonhosted.org/packages/a9/f9/b6bcaf874f410564a78908739c80861a171788ef4d4f76f5009656672dfe/pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753", size = 1920344 },
+ { url = "https://files.pythonhosted.org/packages/32/fd/ac9cdfaaa7cf2d32590b807d900612b39acb25e5527c3c7e482f0553025b/pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:78ddaaa81421a29574a682b3179d4cf9e6d405a09b99d93ddcf7e5239c742e21", size = 1857850 },
+ { url = "https://files.pythonhosted.org/packages/08/fe/038f4b2bcae325ea643c8ad353191187a4c92a9c3b913b139289a6f2ef04/pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:883a91b5dd7d26492ff2f04f40fbb652de40fcc0afe07e8129e8ae779c2110eb", size = 1740265 },
+ { url = "https://files.pythonhosted.org/packages/51/14/b215c9c3cbd1edaaea23014d4b3304260823f712d3fdee52549b19b25d62/pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88ad334a15b32a791ea935af224b9de1bf99bcd62fabf745d5f3442199d86d59", size = 1793912 },
+ { url = "https://files.pythonhosted.org/packages/62/de/2c3ad79b63ba564878cbce325be725929ba50089cd5156f89ea5155cb9b3/pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:233710f069d251feb12a56da21e14cca67994eab08362207785cf8c598e74577", size = 1942870 },
+ { url = "https://files.pythonhosted.org/packages/cb/55/c222af19e4644c741b3f3fe4fd8bbb6b4cdca87d8a49258b61cf7826b19e/pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19442362866a753485ba5e4be408964644dd6a09123d9416c54cd49171f50744", size = 1915610 },
+ { url = "https://files.pythonhosted.org/packages/c4/7a/9a8760692a6f76bb54bcd43f245ff3d8b603db695899bbc624099c00af80/pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:624e278a7d29b6445e4e813af92af37820fafb6dcc55c012c834f9e26f9aaaef", size = 1958403 },
+ { url = "https://files.pythonhosted.org/packages/4c/91/9b03166feb914bb5698e2f6499e07c2617e2eebf69f9374d0358d7eb2009/pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5ef8f42bec47f21d07668a043f077d507e5bf4e668d5c6dfe6aaba89de1a5b8", size = 2101154 },
+ { url = "https://files.pythonhosted.org/packages/1d/d9/1d7ecb98318da4cb96986daaf0e20d66f1651d0aeb9e2d4435b916ce031d/pydantic_core-2.23.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:aea443fffa9fbe3af1a9ba721a87f926fe548d32cab71d188a6ede77d0ff244e", size = 1920855 },
]
[[package]]
@@ -1134,6 +1247,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 },
{ url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 },
{ url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 },
+ { url = "https://files.pythonhosted.org/packages/65/d8/b7a1db13636d7fb7d4ff431593c510c8b8fca920ade06ca8ef20015493c5/PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", size = 184777 },
+ { url = "https://files.pythonhosted.org/packages/0a/02/6ec546cd45143fdf9840b2c6be8d875116a64076218b61d68e12548e5839/PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", size = 172318 },
+ { url = "https://files.pythonhosted.org/packages/0e/9a/8cc68be846c972bda34f6c2a93abb644fb2476f4dcc924d52175786932c9/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", size = 720891 },
+ { url = "https://files.pythonhosted.org/packages/e9/6c/6e1b7f40181bc4805e2e07f4abc10a88ce4648e7e95ff1abe4ae4014a9b2/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", size = 722614 },
+ { url = "https://files.pythonhosted.org/packages/3d/32/e7bd8535d22ea2874cef6a81021ba019474ace0d13a4819c2a4bce79bd6a/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", size = 737360 },
+ { url = "https://files.pythonhosted.org/packages/d7/12/7322c1e30b9be969670b672573d45479edef72c9a0deac3bb2868f5d7469/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", size = 699006 },
+ { url = "https://files.pythonhosted.org/packages/82/72/04fcad41ca56491995076630c3ec1e834be241664c0c09a64c9a2589b507/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", size = 723577 },
+ { url = "https://files.pythonhosted.org/packages/ed/5e/46168b1f2757f1fcd442bc3029cd8767d88a98c9c05770d8b420948743bb/PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", size = 144593 },
+ { url = "https://files.pythonhosted.org/packages/19/87/5124b1c1f2412bb95c59ec481eaf936cd32f0fe2a7b16b97b81c4c017a6a/PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", size = 162312 },
]
[[package]]
@@ -1268,6 +1390,14 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/df/d2/336b18cac68eecb67de474fc15c85f13be4e615c6f5bae87ea38c6734ce0/SQLAlchemy-2.0.35-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1b56961e2d31389aaadf4906d453859f35302b4eb818d34a26fab72596076bb8", size = 3202753 },
{ url = "https://files.pythonhosted.org/packages/f0/f3/ee1e62fabdc10910b5ef720ae08e59bc785f26652876af3a50b89b97b412/SQLAlchemy-2.0.35-cp312-cp312-win32.whl", hash = "sha256:0f9f3f9a3763b9c4deb8c5d09c4cc52ffe49f9876af41cc1b2ad0138878453cf", size = 2060113 },
{ url = "https://files.pythonhosted.org/packages/60/63/a3cef44a52979169d884f3583d0640e64b3c28122c096474a1d7cfcaf1f3/SQLAlchemy-2.0.35-cp312-cp312-win_amd64.whl", hash = "sha256:25b0f63e7fcc2a6290cb5f7f5b4fc4047843504983a28856ce9b35d8f7de03cc", size = 2085839 },
+ { url = "https://files.pythonhosted.org/packages/d1/4c/3a538c077057a449441b3e10a62ff1608eaa6e0910e681b2664ce0bcf961/SQLAlchemy-2.0.35-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ccae5de2a0140d8be6838c331604f91d6fafd0735dbdcee1ac78fc8fbaba76b4", size = 2092966 },
+ { url = "https://files.pythonhosted.org/packages/4e/f0/ac290c05f9118cff70e48abd598bcb0dfb725a7ad0aeebaaa0d781d75367/SQLAlchemy-2.0.35-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2a275a806f73e849e1c309ac11108ea1a14cd7058577aba962cd7190e27c9e3c", size = 2084203 },
+ { url = "https://files.pythonhosted.org/packages/8f/a7/6ddbcefb0ada3dbc9bc9260de32f9cd85b1eb5ea35c12344472a028a606e/SQLAlchemy-2.0.35-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:732e026240cdd1c1b2e3ac515c7a23820430ed94292ce33806a95869c46bd139", size = 3078233 },
+ { url = "https://files.pythonhosted.org/packages/08/7b/6fae14cf33ebc54423f0e9c7de793dd2debe3ffd44b399a4905adb4b1225/SQLAlchemy-2.0.35-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:890da8cd1941fa3dab28c5bac3b9da8502e7e366f895b3b8e500896f12f94d11", size = 3086017 },
+ { url = "https://files.pythonhosted.org/packages/d7/0c/fa68271e608bf4a86c131044966a9f63bc7c6f44e93535be70c7562ff19e/SQLAlchemy-2.0.35-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c0d8326269dbf944b9201911b0d9f3dc524d64779a07518199a58384c3d37a44", size = 3045236 },
+ { url = "https://files.pythonhosted.org/packages/f1/29/65d6441a2875c9e960703fb730c651f0a62505a16e968e6f99f5dd5bb925/SQLAlchemy-2.0.35-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b76d63495b0508ab9fc23f8152bac63205d2a704cd009a2b0722f4c8e0cba8e0", size = 3071708 },
+ { url = "https://files.pythonhosted.org/packages/13/cb/c354d16eb4b9af27a8ef16ef476118497f61f99bf21cf177ec85d9a23d3f/SQLAlchemy-2.0.35-cp39-cp39-win32.whl", hash = "sha256:69683e02e8a9de37f17985905a5eca18ad651bf592314b4d3d799029797d0eb3", size = 2064708 },
+ { url = "https://files.pythonhosted.org/packages/56/0a/3025867277836c325a0f69d3e43cfd35e72f877f472d4c4791d32c264d13/SQLAlchemy-2.0.35-cp39-cp39-win_amd64.whl", hash = "sha256:aee110e4ef3c528f3abbc3c2018c121e708938adeeff9006428dd7c8555e9b3f", size = 2088933 },
{ url = "https://files.pythonhosted.org/packages/0e/c6/33c706449cdd92b1b6d756b247761e27d32230fd6b2de5f44c4c3e5632b2/SQLAlchemy-2.0.35-py3-none-any.whl", hash = "sha256:2ab3f0336c0387662ce6221ad30ab3a5e6499aab01b9790879b6578fd9b8faa1", size = 1881276 },
]
@@ -1290,6 +1420,7 @@ version = "0.38.6"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
+ { name = "typing-extensions", marker = "python_full_version < '3.10'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/42/b4/e25c3b688ef703d85e55017c6edd0cbf38e5770ab748234363d54ff0251a/starlette-0.38.6.tar.gz", hash = "sha256:863a1588f5574e70a821dadefb41e4881ea451a47a3cd1b4df359d4ffefe5ead", size = 2569491 }
wheels = [
@@ -1426,6 +1557,12 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/0a/f8/5ceea6876154d926604f10c1dd896adf9bce6d55a55911364337b8a5ed8d/uvloop-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:649c33034979273fa71aa25d0fe120ad1777c551d8c4cd2c0c9851d88fcb13ab", size = 4173357 },
{ url = "https://files.pythonhosted.org/packages/18/b2/117ab6bfb18274753fbc319607bf06e216bd7eea8be81d5bac22c912d6a7/uvloop-0.20.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3a609780e942d43a275a617c0839d85f95c334bad29c4c0918252085113285b5", size = 4029868 },
{ url = "https://files.pythonhosted.org/packages/6f/52/deb4be09060637ef4752adaa0b75bf770c20c823e8108705792f99cd4a6f/uvloop-0.20.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aea15c78e0d9ad6555ed201344ae36db5c63d428818b4b2a42842b3870127c00", size = 4115980 },
+ { url = "https://files.pythonhosted.org/packages/7b/71/a0ada4783ad4950512214fc2bd8bbd0977e33aff9c37b2e1e2f2de4bb830/uvloop-0.20.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d8c36fdf3e02cec92aed2d44f63565ad1522a499c654f07935c8f9d04db69e95", size = 1324825 },
+ { url = "https://files.pythonhosted.org/packages/9e/a6/9061ac2f0a576d116b973f31f1a7ef18271df5feecb6841be767e09c1442/uvloop-0.20.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a0fac7be202596c7126146660725157d4813aa29a4cc990fe51346f75ff8fde7", size = 740140 },
+ { url = "https://files.pythonhosted.org/packages/01/92/80db889ab59dd2e0602264c825f08d7b53ca26df0adf7abf474d16ee8565/uvloop-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d0fba61846f294bce41eb44d60d58136090ea2b5b99efd21cbdf4e21927c56a", size = 3535856 },
+ { url = "https://files.pythonhosted.org/packages/c9/de/16412389b48edc1ad93300be2ccbf86880341850663e7a462d0acbd3577b/uvloop-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95720bae002ac357202e0d866128eb1ac82545bcf0b549b9abe91b5178d9b541", size = 3543573 },
+ { url = "https://files.pythonhosted.org/packages/83/84/88bad98f47f54564498555d927c64a34366d5ca3b96af09235a38d4db783/uvloop-0.20.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:36c530d8fa03bfa7085af54a48f2ca16ab74df3ec7108a46ba82fd8b411a2315", size = 4212335 },
+ { url = "https://files.pythonhosted.org/packages/b3/39/c9a9413c87b6913cbe261e6078f7e86e3bc3c9f1f9f68f72e18cb2805c0f/uvloop-0.20.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e97152983442b499d7a71e44f29baa75b3b02e65d9c44ba53b10338e98dedb66", size = 4206443 },
]
[[package]]
@@ -1501,10 +1638,26 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/6d/d5/b96eeb9fe3fda137200dd2f31553670cbc731b1e13164fd69b49870b76ec/watchfiles-0.24.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:edf71b01dec9f766fb285b73930f95f730bb0943500ba0566ae234b5c1618c18", size = 593625 },
{ url = "https://files.pythonhosted.org/packages/c1/e5/c326fe52ee0054107267608d8cea275e80be4455b6079491dfd9da29f46f/watchfiles-0.24.0-cp313-none-win32.whl", hash = "sha256:f4c96283fca3ee09fb044f02156d9570d156698bc3734252175a38f0e8975f07", size = 263899 },
{ url = "https://files.pythonhosted.org/packages/a6/8b/8a7755c5e7221bb35fe4af2dc44db9174f90ebf0344fd5e9b1e8b42d381e/watchfiles-0.24.0-cp313-none-win_amd64.whl", hash = "sha256:a974231b4fdd1bb7f62064a0565a6b107d27d21d9acb50c484d2cdba515b9366", size = 276622 },
+ { url = "https://files.pythonhosted.org/packages/93/90/15b3b1cc19799c217e4369ca15dbf9ba1d0f821d61b27173a54800002478/watchfiles-0.24.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:b665caeeda58625c3946ad7308fbd88a086ee51ccb706307e5b1fa91556ac886", size = 375920 },
+ { url = "https://files.pythonhosted.org/packages/74/e2/ec7766a5b20ba5e37397ef8d32ff39a90daf7e4677410efe077c386338b6/watchfiles-0.24.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5c51749f3e4e269231510da426ce4a44beb98db2dce9097225c338f815b05d4f", size = 369382 },
+ { url = "https://files.pythonhosted.org/packages/a2/82/915b3a3295292f860181dc9c7d922834ac7265c8915052d396d40ccf4617/watchfiles-0.24.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82b2509f08761f29a0fdad35f7e1638b8ab1adfa2666d41b794090361fb8b855", size = 438556 },
+ { url = "https://files.pythonhosted.org/packages/9b/76/750eab8e7baecedca05e712b9571ac5eb240e494e8d4c78b359da2939619/watchfiles-0.24.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a60e2bf9dc6afe7f743e7c9b149d1fdd6dbf35153c78fe3a14ae1a9aee3d98b", size = 433677 },
+ { url = "https://files.pythonhosted.org/packages/1a/69/7c55142bafcdad9fec58433b7c1f162b59c48f0a3732a9441252147b13c7/watchfiles-0.24.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f7d9b87c4c55e3ea8881dfcbf6d61ea6775fffed1fedffaa60bd047d3c08c430", size = 451845 },
+ { url = "https://files.pythonhosted.org/packages/d7/a6/124b0043a8936adf96fffa1d73217b205cc254b4a5a313b0a6ea33a44b7b/watchfiles-0.24.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:78470906a6be5199524641f538bd2c56bb809cd4bf29a566a75051610bc982c3", size = 469931 },
+ { url = "https://files.pythonhosted.org/packages/7a/14/29ffa6c7a695fb46a7ff835a5524976dbc07577215647fb35a61ea099c75/watchfiles-0.24.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:07cdef0c84c03375f4e24642ef8d8178e533596b229d32d2bbd69e5128ede02a", size = 476577 },
+ { url = "https://files.pythonhosted.org/packages/9d/8b/fea47dd852c644bd933108877293d0a1c56953c584e8b971530a9042c153/watchfiles-0.24.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d337193bbf3e45171c8025e291530fb7548a93c45253897cd764a6a71c937ed9", size = 426432 },
+ { url = "https://files.pythonhosted.org/packages/0e/11/cfa073f1d9fa18867c2b4220ba445044fd48101ac481f8cbfea1c208ea88/watchfiles-0.24.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ec39698c45b11d9694a1b635a70946a5bad066b593af863460a8e600f0dff1ca", size = 613173 },
+ { url = "https://files.pythonhosted.org/packages/77/44/05c8959304f96fbcd68b6c131c59df7bd3d7f0c2a7410324b7f63b1f9fe6/watchfiles-0.24.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e28d91ef48eab0afb939fa446d8ebe77e2f7593f5f463fd2bb2b14132f95b6e", size = 596184 },
+ { url = "https://files.pythonhosted.org/packages/9b/85/033ecdb5eccb77770d6f24f9fa055067ffa962313a1383645afc711a3cd8/watchfiles-0.24.0-cp39-none-win32.whl", hash = "sha256:7138eff8baa883aeaa074359daabb8b6c1e73ffe69d5accdc907d62e50b1c0da", size = 264345 },
+ { url = "https://files.pythonhosted.org/packages/16/6e/5ded97365346eceaf7fa32d4e2d16f4f97b11d648026b2903c2528c544f8/watchfiles-0.24.0-cp39-none-win_amd64.whl", hash = "sha256:b3ef2c69c655db63deb96b3c3e587084612f9b1fa983df5e0c3379d41307467f", size = 277760 },
{ url = "https://files.pythonhosted.org/packages/df/94/1ad200e937ec91b2a9d6b39ae1cf9c2b1a9cc88d5ceb43aa5c6962eb3c11/watchfiles-0.24.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:632676574429bee8c26be8af52af20e0c718cc7f5f67f3fb658c71928ccd4f7f", size = 376986 },
{ url = "https://files.pythonhosted.org/packages/ee/fd/d9e020d687ccf90fe95efc513fbb39a8049cf5a3ff51f53c59fcf4c47a5d/watchfiles-0.24.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:a2a9891723a735d3e2540651184be6fd5b96880c08ffe1a98bae5017e65b544b", size = 369445 },
{ url = "https://files.pythonhosted.org/packages/43/cb/c0279b35053555d10ef03559c5aebfcb0c703d9c70a7b4e532df74b9b0e8/watchfiles-0.24.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7fa2bc0efef3e209a8199fd111b8969fe9db9c711acc46636686331eda7dd4", size = 439383 },
{ url = "https://files.pythonhosted.org/packages/8b/c4/08b3c2cda45db5169148a981c2100c744a4a222fa7ae7644937c0c002069/watchfiles-0.24.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01550ccf1d0aed6ea375ef259706af76ad009ef5b0203a3a4cce0f6024f9b68a", size = 426804 },
+ { url = "https://files.pythonhosted.org/packages/02/81/9c9a1e6a83d3c320d2f89eca2b71854ae727eca8ab298ad5da00e36c69e2/watchfiles-0.24.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:96619302d4374de5e2345b2b622dc481257a99431277662c30f606f3e22f42be", size = 377076 },
+ { url = "https://files.pythonhosted.org/packages/f1/05/9ef4158f15c417a420d907a6eb887c81953565d0268262195766a844a6d9/watchfiles-0.24.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:85d5f0c7771dcc7a26c7a27145059b6bb0ce06e4e751ed76cdf123d7039b60b5", size = 371717 },
+ { url = "https://files.pythonhosted.org/packages/81/1b/8d036da7a9e4d490385b6979804229fb7ac9b591e4d84f159edf2b3f7cc2/watchfiles-0.24.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:951088d12d339690a92cef2ec5d3cfd957692834c72ffd570ea76a6790222777", size = 440973 },
+ { url = "https://files.pythonhosted.org/packages/fb/fc/885015d4a17ada85508e406c10d638808e7bfbb5622a2e342c868ede18c0/watchfiles-0.24.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49fb58bcaa343fedc6a9e91f90195b20ccb3135447dc9e4e2570c3a39565853e", size = 428343 },
]
[[package]]
@@ -1557,11 +1710,28 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/a1/6e/66b6b756aebbd680b934c8bdbb6dcb9ce45aad72cde5f8a7208dbb00dd36/websockets-13.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:70c5be9f416aa72aab7a2a76c90ae0a4fe2755c1816c153c1a2bcc3333ce4ce6", size = 164732 },
{ url = "https://files.pythonhosted.org/packages/35/c6/12e3aab52c11aeb289e3dbbc05929e7a9d90d7a9173958477d3ef4f8ce2d/websockets-13.1-cp313-cp313-win32.whl", hash = "sha256:624459daabeb310d3815b276c1adef475b3e6804abaf2d9d2c061c319f7f187d", size = 158709 },
{ url = "https://files.pythonhosted.org/packages/41/d8/63d6194aae711d7263df4498200c690a9c39fb437ede10f3e157a6343e0d/websockets-13.1-cp313-cp313-win_amd64.whl", hash = "sha256:c518e84bb59c2baae725accd355c8dc517b4a3ed8db88b4bc93c78dae2974bf2", size = 159144 },
+ { url = "https://files.pythonhosted.org/packages/61/26/5f7a7fb03efedb4f90ed61968338bfe7c389863b0ceda239b94ae61c5ae4/websockets-13.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9b37c184f8b976f0c0a231a5f3d6efe10807d41ccbe4488df8c74174805eea7d", size = 157810 },
+ { url = "https://files.pythonhosted.org/packages/0e/d4/9b4814a07dffaa7a79d71b4944d10836f9adbd527a113f6675734ef3abed/websockets-13.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:163e7277e1a0bd9fb3c8842a71661ad19c6aa7bb3d6678dc7f89b17fbcc4aeb7", size = 155467 },
+ { url = "https://files.pythonhosted.org/packages/1a/1a/2abdc7ce3b56429ae39d6bfb48d8c791f5a26bbcb6f44aabcf71ffc3fda2/websockets-13.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4b889dbd1342820cc210ba44307cf75ae5f2f96226c0038094455a96e64fb07a", size = 155714 },
+ { url = "https://files.pythonhosted.org/packages/2a/98/189d7cf232753a719b2726ec55e7922522632248d5d830adf078e3f612be/websockets-13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:586a356928692c1fed0eca68b4d1c2cbbd1ca2acf2ac7e7ebd3b9052582deefa", size = 164587 },
+ { url = "https://files.pythonhosted.org/packages/a5/2b/fb77cedf3f9f55ef8605238c801eef6b9a5269b01a396875a86896aea3a6/websockets-13.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7bd6abf1e070a6b72bfeb71049d6ad286852e285f146682bf30d0296f5fbadfa", size = 163588 },
+ { url = "https://files.pythonhosted.org/packages/a3/b7/070481b83d2d5ac0f19233d9f364294e224e6478b0762f07fa7f060e0619/websockets-13.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2aad13a200e5934f5a6767492fb07151e1de1d6079c003ab31e1823733ae79", size = 163894 },
+ { url = "https://files.pythonhosted.org/packages/eb/be/d6e1cff7d441cfe5eafaacc5935463e5f14c8b1c0d39cb8afde82709b55a/websockets-13.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:df01aea34b6e9e33572c35cd16bae5a47785e7d5c8cb2b54b2acdb9678315a17", size = 164315 },
+ { url = "https://files.pythonhosted.org/packages/8b/5e/ffa234473e46ab2d3f9fd9858163d5db3ecea1439e4cb52966d78906424b/websockets-13.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e54affdeb21026329fb0744ad187cf812f7d3c2aa702a5edb562b325191fcab6", size = 163714 },
+ { url = "https://files.pythonhosted.org/packages/cc/92/cea9eb9d381ca57065a5eb4ec2ce7a291bd96c85ce742915c3c9ffc1069f/websockets-13.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9ef8aa8bdbac47f4968a5d66462a2a0935d044bf35c0e5a8af152d58516dbeb5", size = 163673 },
+ { url = "https://files.pythonhosted.org/packages/a4/f1/279104fff239bfd04c12b1e58afea227d72fd1acf431e3eed3f6ac2c96d2/websockets-13.1-cp39-cp39-win32.whl", hash = "sha256:deeb929efe52bed518f6eb2ddc00cc496366a14c726005726ad62c2dd9017a3c", size = 158702 },
+ { url = "https://files.pythonhosted.org/packages/25/0b/b87370ff141375c41f7dd67941728e4b3682ebb45882591516c792a2ebee/websockets-13.1-cp39-cp39-win_amd64.whl", hash = "sha256:7c65ffa900e7cc958cd088b9a9157a8141c991f8c53d11087e6fb7277a03f81d", size = 159146 },
{ url = "https://files.pythonhosted.org/packages/2d/75/6da22cb3ad5b8c606963f9a5f9f88656256fecc29d420b4b2bf9e0c7d56f/websockets-13.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5dd6da9bec02735931fccec99d97c29f47cc61f644264eb995ad6c0c27667238", size = 155499 },
{ url = "https://files.pythonhosted.org/packages/c0/ba/22833d58629088fcb2ccccedfae725ac0bbcd713319629e97125b52ac681/websockets-13.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:2510c09d8e8df777177ee3d40cd35450dc169a81e747455cc4197e63f7e7bfe5", size = 155737 },
{ url = "https://files.pythonhosted.org/packages/95/54/61684fe22bdb831e9e1843d972adadf359cf04ab8613285282baea6a24bb/websockets-13.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1c3cf67185543730888b20682fb186fc8d0fa6f07ccc3ef4390831ab4b388d9", size = 157095 },
{ url = "https://files.pythonhosted.org/packages/fc/f5/6652fb82440813822022a9301a30afde85e5ff3fb2aebb77f34aabe2b4e8/websockets-13.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcc03c8b72267e97b49149e4863d57c2d77f13fae12066622dc78fe322490fe6", size = 156701 },
{ url = "https://files.pythonhosted.org/packages/67/33/ae82a7b860fa8a08aba68818bdf7ff61f04598aa5ab96df4cd5a3e418ca4/websockets-13.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:004280a140f220c812e65f36944a9ca92d766b6cc4560be652a0a3883a79ed8a", size = 156654 },
{ url = "https://files.pythonhosted.org/packages/63/0b/a1b528d36934f833e20f6da1032b995bf093d55cb416b9f2266f229fb237/websockets-13.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e2620453c075abeb0daa949a292e19f56de518988e079c36478bacf9546ced23", size = 159192 },
+ { url = "https://files.pythonhosted.org/packages/59/fd/e4bf9a7159dba6a16c59ae9e670e3e8ad9dcb6791bc0599eb86de32d50a9/websockets-13.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:25c35bf84bf7c7369d247f0b8cfa157f989862c49104c5cf85cb5436a641d93e", size = 155499 },
+ { url = "https://files.pythonhosted.org/packages/74/42/d48ede93cfe0c343f3b552af08efc60778d234989227b16882eed1b8b189/websockets-13.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:83f91d8a9bb404b8c2c41a707ac7f7f75b9442a0a876df295de27251a856ad09", size = 155731 },
+ { url = "https://files.pythonhosted.org/packages/f6/f2/2ef6bff1c90a43b80622a17c0852b48c09d3954ab169266ad7b15e17cdcb/websockets-13.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a43cfdcddd07f4ca2b1afb459824dd3c6d53a51410636a2c7fc97b9a8cf4842", size = 157093 },
+ { url = "https://files.pythonhosted.org/packages/d1/14/6f20bbaeeb350f155edf599aad949c554216f90e5d4ae7373d1f2e5931fb/websockets-13.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48a2ef1381632a2f0cb4efeff34efa97901c9fbc118e01951ad7cfc10601a9bb", size = 156701 },
+ { url = "https://files.pythonhosted.org/packages/c7/86/38279dfefecd035e22b79c38722d4f87c4b6196f1556b7a631d0a3095ca7/websockets-13.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:459bf774c754c35dbb487360b12c5727adab887f1622b8aed5755880a21c4a20", size = 156649 },
+ { url = "https://files.pythonhosted.org/packages/f6/c5/12c6859a2eaa8c53f59a647617a27f1835a226cd7106c601067c53251d98/websockets-13.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:95858ca14a9f6fa8413d29e0a585b31b278388aa775b8a81fa24830123874678", size = 159187 },
{ url = "https://files.pythonhosted.org/packages/56/27/96a5cd2626d11c8280656c6c71d8ab50fe006490ef9971ccd154e0c42cd2/websockets-13.1-py3-none-any.whl", hash = "sha256:a9a396a6ad26130cdae92ae10c36af09d9bfe6cafe69670fd3b6da9b07b4044f", size = 152134 },
]
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index ef1ad57113..222bdbd7d7 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -56,63 +56,68 @@
}
},
"node_modules/@ark-ui/react": {
- "version": "4.9.1",
- "resolved": "https://registry.npmjs.org/@ark-ui/react/-/react-4.9.1.tgz",
- "integrity": "sha512-grnfoSUrGxN0VMgtf4yvpMgin2T4ERINqYm3x/XKny+q2iIO76PD7yjNP7IW+CDmNxy3QPOidcvRiCyy6x0LGA==",
- "license": "MIT",
- "dependencies": {
- "@internationalized/date": "3.7.0",
- "@zag-js/accordion": "0.82.1",
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/auto-resize": "0.82.1",
- "@zag-js/avatar": "0.82.1",
- "@zag-js/carousel": "0.82.1",
- "@zag-js/checkbox": "0.82.1",
- "@zag-js/clipboard": "0.82.1",
- "@zag-js/collapsible": "0.82.1",
- "@zag-js/collection": "0.82.1",
- "@zag-js/color-picker": "0.82.1",
- "@zag-js/color-utils": "0.82.1",
- "@zag-js/combobox": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/date-picker": "0.82.1",
- "@zag-js/date-utils": "0.82.1",
- "@zag-js/dialog": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/editable": "0.82.1",
- "@zag-js/file-upload": "0.82.1",
- "@zag-js/file-utils": "0.82.1",
- "@zag-js/focus-trap": "0.82.1",
- "@zag-js/highlight-word": "0.82.1",
- "@zag-js/hover-card": "0.82.1",
- "@zag-js/i18n-utils": "0.82.1",
- "@zag-js/menu": "0.82.1",
- "@zag-js/number-input": "0.82.1",
- "@zag-js/pagination": "0.82.1",
- "@zag-js/pin-input": "0.82.1",
- "@zag-js/popover": "0.82.1",
- "@zag-js/presence": "0.82.1",
- "@zag-js/progress": "0.82.1",
- "@zag-js/qr-code": "0.82.1",
- "@zag-js/radio-group": "0.82.1",
- "@zag-js/rating-group": "0.82.1",
- "@zag-js/react": "0.82.1",
- "@zag-js/select": "0.82.1",
- "@zag-js/signature-pad": "0.82.1",
- "@zag-js/slider": "0.82.1",
- "@zag-js/splitter": "0.82.1",
- "@zag-js/steps": "0.82.1",
- "@zag-js/switch": "0.82.1",
- "@zag-js/tabs": "0.82.1",
- "@zag-js/tags-input": "0.82.1",
- "@zag-js/time-picker": "0.82.1",
- "@zag-js/timer": "0.82.1",
- "@zag-js/toast": "0.82.1",
- "@zag-js/toggle-group": "0.82.1",
- "@zag-js/tooltip": "0.82.1",
- "@zag-js/tour": "0.82.1",
- "@zag-js/tree-view": "0.82.1",
- "@zag-js/types": "0.82.1"
+ "version": "5.8.0",
+ "resolved": "https://registry.npmjs.org/@ark-ui/react/-/react-5.8.0.tgz",
+ "integrity": "sha512-5KrB7YqJ1YXz5DNmu5LWoWp1JeqC/SPdXMRekvOopDdKqWARsfdSV+mZuTHq2UY5bCSv8GYZJEBhbQyk265Yxw==",
+ "license": "MIT",
+ "dependencies": {
+ "@internationalized/date": "3.8.0",
+ "@zag-js/accordion": "1.12.0",
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/angle-slider": "1.12.0",
+ "@zag-js/auto-resize": "1.12.0",
+ "@zag-js/avatar": "1.12.0",
+ "@zag-js/carousel": "1.12.0",
+ "@zag-js/checkbox": "1.12.0",
+ "@zag-js/clipboard": "1.12.0",
+ "@zag-js/collapsible": "1.12.0",
+ "@zag-js/collection": "1.12.0",
+ "@zag-js/color-picker": "1.12.0",
+ "@zag-js/color-utils": "1.12.0",
+ "@zag-js/combobox": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/date-picker": "1.12.0",
+ "@zag-js/date-utils": "1.12.0",
+ "@zag-js/dialog": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/editable": "1.12.0",
+ "@zag-js/file-upload": "1.12.0",
+ "@zag-js/file-utils": "1.12.0",
+ "@zag-js/floating-panel": "1.12.0",
+ "@zag-js/focus-trap": "1.12.0",
+ "@zag-js/highlight-word": "1.12.0",
+ "@zag-js/hover-card": "1.12.0",
+ "@zag-js/i18n-utils": "1.12.0",
+ "@zag-js/listbox": "1.12.0",
+ "@zag-js/menu": "1.12.0",
+ "@zag-js/number-input": "1.12.0",
+ "@zag-js/pagination": "1.12.0",
+ "@zag-js/pin-input": "1.12.0",
+ "@zag-js/popover": "1.12.0",
+ "@zag-js/presence": "1.12.0",
+ "@zag-js/progress": "1.12.0",
+ "@zag-js/qr-code": "1.12.0",
+ "@zag-js/radio-group": "1.12.0",
+ "@zag-js/rating-group": "1.12.0",
+ "@zag-js/react": "1.12.0",
+ "@zag-js/select": "1.12.0",
+ "@zag-js/signature-pad": "1.12.0",
+ "@zag-js/slider": "1.12.0",
+ "@zag-js/splitter": "1.12.0",
+ "@zag-js/steps": "1.12.0",
+ "@zag-js/switch": "1.12.0",
+ "@zag-js/tabs": "1.12.0",
+ "@zag-js/tags-input": "1.12.0",
+ "@zag-js/time-picker": "1.12.0",
+ "@zag-js/timer": "1.12.0",
+ "@zag-js/toast": "1.12.0",
+ "@zag-js/toggle": "1.12.0",
+ "@zag-js/toggle-group": "1.12.0",
+ "@zag-js/tooltip": "1.12.0",
+ "@zag-js/tour": "1.12.0",
+ "@zag-js/tree-view": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
},
"peerDependencies": {
"react": ">=18.0.0",
@@ -489,18 +494,19 @@
}
},
"node_modules/@chakra-ui/react": {
- "version": "3.8.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react/-/react-3.8.0.tgz",
- "integrity": "sha512-UOkDxxMYHqQ6z/ExMcLYnjIIj2Ulu6syAkrpSueYmzLlG93cljkMCze5y9GXh/M6fyQEbLBuDVesULTqMmHuiA==",
+ "version": "3.17.0",
+ "resolved": "https://registry.npmjs.org/@chakra-ui/react/-/react-3.17.0.tgz",
+ "integrity": "sha512-b6syn8PTCAEqXQa52KtVFs2lAavFTldb2SkBbAqmrlWQyE58jTxpgxaEsYsqQxq/bljwC0xHsh5/ACU7Xwr6sA==",
"license": "MIT",
"dependencies": {
- "@ark-ui/react": "4.9.1",
+ "@ark-ui/react": "5.8.0",
"@emotion/is-prop-valid": "1.3.1",
"@emotion/serialize": "1.3.3",
"@emotion/use-insertion-effect-with-fallbacks": "1.2.0",
"@emotion/utils": "1.4.2",
- "@pandacss/is-valid-prop": "0.41.0",
- "csstype": "3.1.3"
+ "@pandacss/is-valid-prop": "0.53.6",
+ "csstype": "3.1.3",
+ "fast-safe-stringify": "2.1.1"
},
"peerDependencies": {
"@emotion/react": ">=11",
@@ -1065,22 +1071,22 @@
}
},
"node_modules/@floating-ui/core": {
- "version": "1.6.9",
- "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.9.tgz",
- "integrity": "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==",
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.0.tgz",
+ "integrity": "sha512-FRdBLykrPPA6P76GGGqlex/e7fbe0F1ykgxHYNXQsH/iTEtjMj/f9bpY5oQqbjt5VgZvgz/uKXbGuROijh3VLA==",
"license": "MIT",
"dependencies": {
"@floating-ui/utils": "^0.2.9"
}
},
"node_modules/@floating-ui/dom": {
- "version": "1.6.12",
- "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.12.tgz",
- "integrity": "sha512-NP83c0HjokcGVEMeoStg317VD9W7eDlGK7457dMBANbKA6GJZdc7rjujdgqzTaz93jkGgc5P/jeWbaCHnMNc+w==",
+ "version": "1.6.13",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.13.tgz",
+ "integrity": "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==",
"license": "MIT",
"dependencies": {
"@floating-ui/core": "^1.6.0",
- "@floating-ui/utils": "^0.2.8"
+ "@floating-ui/utils": "^0.2.9"
}
},
"node_modules/@floating-ui/utils": {
@@ -1115,18 +1121,18 @@
}
},
"node_modules/@internationalized/date": {
- "version": "3.7.0",
- "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.7.0.tgz",
- "integrity": "sha512-VJ5WS3fcVx0bejE/YHfbDKR/yawZgKqn/if+oEeLqNwBtPzVB06olkfcnojTmEMX+gTpH+FlQ69SHNitJ8/erQ==",
+ "version": "3.8.0",
+ "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.8.0.tgz",
+ "integrity": "sha512-J51AJ0fEL68hE4CwGPa6E0PO6JDaVLd8aln48xFCSy7CZkZc96dGEGmLs2OEEbBxcsVZtfrqkXJwI2/MSG8yKw==",
"license": "Apache-2.0",
"dependencies": {
"@swc/helpers": "^0.5.0"
}
},
"node_modules/@internationalized/number": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/@internationalized/number/-/number-3.6.0.tgz",
- "integrity": "sha512-PtrRcJVy7nw++wn4W2OuePQQfTqDzfusSuY1QTtui4wa7r+rGVtR75pO8CyKvHvzyQYi3Q1uO5sY0AsB4e65Bw==",
+ "version": "3.6.1",
+ "resolved": "https://registry.npmjs.org/@internationalized/number/-/number-3.6.1.tgz",
+ "integrity": "sha512-UVsb4bCwbL944E0SX50CHFtWEeZ2uB5VozZ5yDXJdq6iPZsZO5p+bjVMZh2GxHf4Bs/7xtDCcPwEa2NU9DaG/g==",
"license": "Apache-2.0",
"dependencies": {
"@swc/helpers": "^0.5.0"
@@ -1140,9 +1146,9 @@
"license": "MIT"
},
"node_modules/@pandacss/is-valid-prop": {
- "version": "0.41.0",
- "resolved": "https://registry.npmjs.org/@pandacss/is-valid-prop/-/is-valid-prop-0.41.0.tgz",
- "integrity": "sha512-BE6h6CsJk14ugIRrsazJtN3fcg+KDFRat1Bs93YFKH6jd4DOb1yUyVvC70jKqPVvg70zEcV8acZ7VdcU5TLu+w=="
+ "version": "0.53.6",
+ "resolved": "https://registry.npmjs.org/@pandacss/is-valid-prop/-/is-valid-prop-0.53.6.tgz",
+ "integrity": "sha512-TgWBQmz/5j/oAMjavqJAjQh1o+yxhYspKvepXPn4lFhAN3yBhilrw9HliAkvpUr0sB2CkJ2BYMpFXbAJYEocsA=="
},
"node_modules/@playwright/test": {
"version": "1.52.0",
@@ -1919,508 +1925,542 @@
}
},
"node_modules/@zag-js/accordion": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/accordion/-/accordion-0.82.1.tgz",
- "integrity": "sha512-DWaElpm6RhntW8zVPMfd+s461FuXi6rv4pDPpXb4xCAJ0KTkBzS6PFxoBLL+11Mjv9XioaBoJatIGOCF8GAtTA==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/accordion/-/accordion-1.12.0.tgz",
+ "integrity": "sha512-9mZgGiyPPKOcNgCjHO67P0pN8tP8wORWc1IhQ9oWCWCtYvGNal8+3+ddooH/N8qW0ZSFURm9gZWbCX1any4Trg==",
"license": "MIT",
"dependencies": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/anatomy": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/anatomy/-/anatomy-0.82.1.tgz",
- "integrity": "sha512-wpgU7LyU9St3o/ft8Nkundi7MkW37vN1hYc2E7VA/R6mun0qiANsEf83ymIlAYnovLC6WUlBso9xwqejr6wRCg==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/anatomy/-/anatomy-1.12.0.tgz",
+ "integrity": "sha512-BpnD2qh+shANl127hin9gfOmiHnUaL0whgbBGL6KMuurFg+WPtOBMTxAtbhSRa3524cu61GBkgETH4VAg1xS7w==",
"license": "MIT"
},
+ "node_modules/@zag-js/angle-slider": {
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/angle-slider/-/angle-slider-1.12.0.tgz",
+ "integrity": "sha512-PrxmA9EBr0aG0tq3gEYQvX0VlPxGH4tDTWK8ipO05z09wBdfFJIIYQbGCYp4VpZQqJT3QBpDsf43gJFJ5cq4Sw==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/rect-utils": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
+ }
+ },
"node_modules/@zag-js/aria-hidden": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/aria-hidden/-/aria-hidden-0.82.1.tgz",
- "integrity": "sha512-KSz9oMY9rn1N3k3tFTKHlU66eQf8XZ/gy/ex27J0ykZoaYJplWQerSZvVakbILeh+rtpvdiTNaSgrCAwYwvAPA==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/aria-hidden/-/aria-hidden-1.12.0.tgz",
+ "integrity": "sha512-YQkGo49bPzjMWTPbnuqGMDDemmppVk8w6QlF+Gygurp2gfeCQngj+2Oz6ZCzvSNIFXjRAL6i31O8BolfV/aLPg==",
"license": "MIT"
},
"node_modules/@zag-js/auto-resize": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/auto-resize/-/auto-resize-0.82.1.tgz",
- "integrity": "sha512-adOB7Y4p4i6b8GJv4V6qhlK1YRj4Ejs5I+eWFd8Rx535uQIcxEEVtpEAD5SRYg5PNk1ikaT+GCoHnTadGj6PuA==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/auto-resize/-/auto-resize-1.12.0.tgz",
+ "integrity": "sha512-9y7HCXp4cUcaHxVB5fgC94q6+/ZwsF8X/F3ZY34dSF06xZ4L8mjnp1lF75gzPJMbUGY+sKd0Excpxty8ecWrjg==",
"license": "MIT",
"dependencies": {
- "@zag-js/dom-query": "0.82.1"
+ "@zag-js/dom-query": "1.12.0"
}
},
"node_modules/@zag-js/avatar": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/avatar/-/avatar-0.82.1.tgz",
- "integrity": "sha512-XjRvDRmBxwy5OtIzlQOpf7zNk4g0b/uA7qZve5Hz0R7yWOu+NFlbFv0GsvRfgyYMCT5J0xBu271EG9FJq3QKyw==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/avatar/-/avatar-1.12.0.tgz",
+ "integrity": "sha512-0e4ti7Be0p8KbZInkbrz45v/dd5w/OBN0rxeP2yOkHJRCUfPRqx/aE2wXL/66iAuh0RadgOYhgPVf1DjFSqWLw==",
"license": "MIT",
"dependencies": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/carousel": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/carousel/-/carousel-0.82.1.tgz",
- "integrity": "sha512-MO9+9oedxdKynxgvLLzXs+VQSOhu+GvsCLV4fBt7nMBMGIRHtRSzXHRNRkO0aqbsO/nKQ8TFH7GYzI1NqT/y4A==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/carousel/-/carousel-1.12.0.tgz",
+ "integrity": "sha512-E77u1wq472Mh+1dRC+iId5zq35LA10njL7jImO9Mi2Ia1LDrMwxfwsEUs+jh94W0MV1d+jpcJ7MCquQeZrurIg==",
"license": "MIT",
"dependencies": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/scroll-snap": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/scroll-snap": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/checkbox": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/checkbox/-/checkbox-0.82.1.tgz",
- "integrity": "sha512-yD/h8ao/JTljEo+zthpKzTy/f9fqOlJ7Nd6psPoSKZy2MRGD0TDUbOjravb3icVgjTLCiaPVWMWdonny08Me6A==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/checkbox/-/checkbox-1.12.0.tgz",
+ "integrity": "sha512-tt7oWTR2YSk3DT7F49fhNL3AqUyxbl5Qk3qiiQbiRxTVKmbq3ATNW5cm2QifXEwIPbBaIiXdI/W69DJjcszqoQ==",
"license": "MIT",
"dependencies": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/focus-visible": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/focus-visible": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/clipboard": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/clipboard/-/clipboard-0.82.1.tgz",
- "integrity": "sha512-r1r3vwozs+lyNgccR3OfmYAydP0cJbIHGsgDKGuempinqv6xIoptHOkFgWNd6Kxz/3MnxP+BMEy6fZzECXkhdQ==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/clipboard/-/clipboard-1.12.0.tgz",
+ "integrity": "sha512-6x14LF51633/vzL6q7SqQ9FSUFDpkB4JXaqFeP6BVgo4sMMgYimQIRaufJ6G1lXV3sof2bcJhNXPJWZkYn4wnQ==",
"license": "MIT",
"dependencies": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/collapsible": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/collapsible/-/collapsible-0.82.1.tgz",
- "integrity": "sha512-TuggUoXRVBOwACksi63TsN2rOukzUpe6oVMUvp9MaQaDbg9gpw0JzLTrdAaHfE+bhgXAb3EjN6wcZjq8zBctZQ==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/collapsible/-/collapsible-1.12.0.tgz",
+ "integrity": "sha512-2jzKy2Dbbit66oRmhbacHKqPuE7xt37+YBE71sJWmj2To8NKWrwibF61vFbHVjaoAgZ0mhgXiAJYPMyPHXaWWg==",
"license": "MIT",
"dependencies": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/collection": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/collection/-/collection-0.82.1.tgz",
- "integrity": "sha512-uteM+xWZlWhRQe5biA5QWyva9PdzXONs+bpycUtZt8MakQgPmhW2whY9r1aW5NFVb/ScTwGAIGB3Eyc6Npz7Wg==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/collection/-/collection-1.12.0.tgz",
+ "integrity": "sha512-gk/qRc5rUyjD6P/fxT2UHvpXWtjXflaM++J4DjAu4bPiBAzhz8zjBZRo6Yyiffm1+YNBIZKxFemb813P16iQNg==",
"license": "MIT",
"dependencies": {
- "@zag-js/utils": "0.82.1"
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/color-picker": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/color-picker/-/color-picker-0.82.1.tgz",
- "integrity": "sha512-/MShDVBFNnXResLzeyWyKApeHuB9rmUeJo3WD/Bl6rTwjmvVCKRYguIe1SQviOokMLjuAyh0YWXdKMQw0HvMqQ==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/color-picker/-/color-picker-1.12.0.tgz",
+ "integrity": "sha512-nbmY0ljFLZFhfjAMJkYsfiJ3tk8ux0lzdF6v7kif/ZtPfFIchVkCXX5ESx5w71j7MpfzJN3PrQ0F/2wDCHHPtg==",
"license": "MIT",
"dependencies": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/color-utils": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dismissable": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/popper": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/color-utils": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dismissable": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/popper": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/color-utils": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/color-utils/-/color-utils-0.82.1.tgz",
- "integrity": "sha512-BMSYcBeypGX0wCLszU2jxWBRUmd5/wPDJ59Y3Zwl9yNld0gtMnuBLSUeokMcG0UVQ/BxkyrWu3VDkKTUYKprqQ==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/color-utils/-/color-utils-1.12.0.tgz",
+ "integrity": "sha512-KZsR8gEStPC5X6A+eMS5ZLRWWcsHi5byGfF8UaNybFJ8VsB9MqrPw5xtcQm9zOkJQlWaeEFOMQuojl/hGGUIXQ==",
"license": "MIT",
"dependencies": {
- "@zag-js/utils": "0.82.1"
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/combobox": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/combobox/-/combobox-0.82.1.tgz",
- "integrity": "sha512-Me3a0Sw4dTtmBRmbLGO/C1LJ4btZwbd5RLYnf8RPhEnqGJ5Z05i+ffWEe+SNBvpQO14njqBcF6P8VypVD/Ro1A==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/combobox/-/combobox-1.12.0.tgz",
+ "integrity": "sha512-a4eLrgdmsJ4Dc8Yob7SXm1PYVfqT2IAvwqX/3DpWCVisff57e9GlfLh9n/AgoH1SWNs2709nm56P2lQrVtK7uQ==",
"license": "MIT",
"dependencies": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/aria-hidden": "0.82.1",
- "@zag-js/collection": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dismissable": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/popper": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/aria-hidden": "1.12.0",
+ "@zag-js/collection": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dismissable": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/popper": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/core": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/core/-/core-0.82.1.tgz",
- "integrity": "sha512-Ux0fkt1PumcqLwExcEozCMEfKBxtd2JlnitXo4hR3lJW5q9G52FkgWDyPSrhblyTkX+7RgxViZTMnHxaXs99jg==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/core/-/core-1.12.0.tgz",
+ "integrity": "sha512-GIDXIgTDHQZCnzKEhi+VMEvcJBsloHQNSmBRxZFllPh7htYGijwK8CrDQSzQPrGW074aR2pzu/RhdyqCUGQJyQ==",
"license": "MIT",
"dependencies": {
- "@zag-js/store": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/date-picker": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/date-picker/-/date-picker-0.82.1.tgz",
- "integrity": "sha512-f+4CV29+hcQ3Yw9hh0yyVRANONIUEWIrPS1fpnrrUNtIC0Y7f1Ajx+x089X9VxgQhwreK1sEwpnrL2vIqy+9+A==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/date-picker/-/date-picker-1.12.0.tgz",
+ "integrity": "sha512-qJoPGRH8yOs7OWLdsfINMbsMQW8Qh1FR0ixlyFkDJi3K5KUer2p2O91DmkJll4VdXIM1FJ8oBtbcDK1M55PtyQ==",
"license": "MIT",
"dependencies": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/date-utils": "0.82.1",
- "@zag-js/dismissable": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/live-region": "0.82.1",
- "@zag-js/popper": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/date-utils": "1.12.0",
+ "@zag-js/dismissable": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/live-region": "1.12.0",
+ "@zag-js/popper": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
},
"peerDependencies": {
"@internationalized/date": ">=3.0.0"
}
},
"node_modules/@zag-js/date-utils": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/date-utils/-/date-utils-0.82.1.tgz",
- "integrity": "sha512-z9sHtgV4fvtXsqLaTD4/o+D+H5wumLYhIw/Bj3yC41gR5oa4Wo9QifRT9DBfvuokmXsrnRZ8k32hUtWoYb6M/A==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/date-utils/-/date-utils-1.12.0.tgz",
+ "integrity": "sha512-nalAtiVqabj9Ozlftj/Veq58dm6fcqUx2Ok3IofIdqxZJ6Q7Xdrk8vuba+5+lhipfemyVbjZQoQHwX6TBM5H0g==",
"license": "MIT",
"peerDependencies": {
"@internationalized/date": ">=3.0.0"
}
},
"node_modules/@zag-js/dialog": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/dialog/-/dialog-0.82.1.tgz",
- "integrity": "sha512-oqi+6Y/rx6ZKxg3s9r6bIuo33x+5+UDhvrlk31kE3LWgU1KJjVV0VEkFMK9B1SJTY7IizhlWMyDx+JXJ+jOy5Q==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/dialog/-/dialog-1.12.0.tgz",
+ "integrity": "sha512-l1HYpIR0Puz+L7i+Di4DWIJbeJamse5lFb3SNyYFMkZHT6zPn/ylQhcsmB13eFWnveVGoOp8+WT4XVRw6Ff1Tg==",
"license": "MIT",
"dependencies": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/aria-hidden": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dismissable": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/focus-trap": "0.82.1",
- "@zag-js/remove-scroll": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/aria-hidden": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dismissable": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/focus-trap": "1.12.0",
+ "@zag-js/remove-scroll": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/dismissable": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/dismissable/-/dismissable-0.82.1.tgz",
- "integrity": "sha512-vs+zkORzaeNzX4Wsy4OkW1AVce7l4Tc6DHZq8gqNB5SvhK+5wEPl6EmacQRvZyoCxi2m6xpaI98UkLCmVJKU+Q==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/dismissable/-/dismissable-1.12.0.tgz",
+ "integrity": "sha512-YXXzaCUPykfzDQUK5TTOShTioZbUh/tkZIA+uRUZK18qvEI8fmGrLaVHyD3Z0smtA0KWW2O5mC2v8lwq9DB3Lw==",
"license": "MIT",
"dependencies": {
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/interact-outside": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/interact-outside": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/dom-query": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-0.82.1.tgz",
- "integrity": "sha512-KFtbqDUykQur587hyrGi8LL8GfTS2mqBpIT0kL3E+S63Mq7U84i+hGf3VyNuInMB5ONpkNEk5JN4G9/HWQ6pAQ==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.12.0.tgz",
+ "integrity": "sha512-3fw4+kyFn+K+ESBSG7uhvxgOAJcB+WLYsMrktgK4SUC1ukq3wDKF4oAP+uhXA6OYdVw2T5ZwLU0aY37q94hC2A==",
"license": "MIT",
"dependencies": {
- "@zag-js/types": "0.82.1"
+ "@zag-js/types": "1.12.0"
}
},
"node_modules/@zag-js/editable": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/editable/-/editable-0.82.1.tgz",
- "integrity": "sha512-V5i3kYSHFJYj8914nBf4VKKtm6m59gG482vm20As4EnLcwGFrOBbm4HXUgsKq0wYSLy/lTtvMrUT8Iqudye2gw==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/editable/-/editable-1.12.0.tgz",
+ "integrity": "sha512-6QJSDDCSWjCZuPyJ28ctqRvpFzRDt39a/s17SyywWdW+qLeFkXtdod21ZMXx2RGsrSnqYPVEQOD6sYJ4e+iEMQ==",
"license": "MIT",
"dependencies": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/interact-outside": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/interact-outside": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
- "node_modules/@zag-js/element-rect": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/element-rect/-/element-rect-0.82.1.tgz",
- "integrity": "sha512-xXUjmeIUdxkxic5bepp6fVqN9Qs+54PXCAUl6g/DtJecQVmVooIfa3SLSULhany4aR4mlGojp5TJxvSpUBA58Q==",
- "license": "MIT"
- },
- "node_modules/@zag-js/element-size": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/element-size/-/element-size-0.82.1.tgz",
- "integrity": "sha512-k1rOE6NhoULI9d5pt2qVUxWCQVEf3OTPH8UDnbsdf11xn+hMCzRYd9lekUdVGrcHHGvEK+W6iAfWZnlwsJsmow==",
- "license": "MIT"
- },
"node_modules/@zag-js/file-upload": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/file-upload/-/file-upload-0.82.1.tgz",
- "integrity": "sha512-6cgJsy9bf2DB0v+CVq1L4g4aCePTpfWsV4C0HC+82K+OSPomiIPsQS87wo4+eAcy3z+80Qh+uglZCFAwkW8W+g==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/file-upload/-/file-upload-1.12.0.tgz",
+ "integrity": "sha512-5VOL/5sEfmZ2PpvZpB+j7dXgTWPgvJk4zo/JjX/UbQdL/NTOMWRfEZVaOZKgAb0Ngo9AvqI8GYWxoUAYLyupDg==",
"license": "MIT",
"dependencies": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/file-utils": "0.82.1",
- "@zag-js/i18n-utils": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/file-utils": "1.12.0",
+ "@zag-js/i18n-utils": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/file-utils": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/file-utils/-/file-utils-0.82.1.tgz",
- "integrity": "sha512-/u86hMd+E5UCrrY9akDAExkO7sgPA1lXzWC9gSX4LSxHATk7Vo4o5+4LiE1MX4WZRytOhtxAycJzNDVpqzmppQ==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/file-utils/-/file-utils-1.12.0.tgz",
+ "integrity": "sha512-1UGt+juPdyZT2Dzzj6qMvtzdX1tAp/weazlgV4+Vw2kvOn8jn1+l3z0FTmFyA1e2r0n7dvVyhCRq/qw6vGIe/Q==",
"license": "MIT",
"dependencies": {
- "@zag-js/i18n-utils": "0.82.1"
+ "@zag-js/i18n-utils": "1.12.0"
+ }
+ },
+ "node_modules/@zag-js/floating-panel": {
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/floating-panel/-/floating-panel-1.12.0.tgz",
+ "integrity": "sha512-9SGV5/OfEU6t4pEVoCbyHr95pH47fYfA4ZWecDJ9I/bZ4Fft2bozAqyjIyoTnj+RPzvHKkqvC6tGmt0j/4DDSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dismissable": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/popper": "1.12.0",
+ "@zag-js/rect-utils": "1.12.0",
+ "@zag-js/store": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/focus-trap": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/focus-trap/-/focus-trap-0.82.1.tgz",
- "integrity": "sha512-z5OzmR8O3n2043Lwhp1qcizNHXvzc/Xteb3hWmxbX9hR3k0wHJeMXMj3GTDO0FBixRt+d8iHEmt3/8CkI72mqw==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/focus-trap/-/focus-trap-1.12.0.tgz",
+ "integrity": "sha512-T8eNgWDFcSf/oyut0kG3zYji3CqH2GtnivpNSeDsiNdwJ2bHXgeC65HqUExESClBTJz0W74AatruPvFzEhxDxg==",
"license": "MIT",
"dependencies": {
- "@zag-js/dom-query": "0.82.1"
+ "@zag-js/dom-query": "1.12.0"
}
},
"node_modules/@zag-js/focus-visible": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/focus-visible/-/focus-visible-0.82.1.tgz",
- "integrity": "sha512-b87FqZO6e9RmTY4msEzwZ3hZ8pRuPd2vbR2b6SlXr6ohtmGKlGgBGO4kmarZN/ClE+7VOnOEqIicatRBEgX9bw==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/focus-visible/-/focus-visible-1.12.0.tgz",
+ "integrity": "sha512-u+6k8fyQ12a0sZvSk02V7JY1OAsxWI6MhfW11OvSixMSHfd82YaWOG2HA/GIAcyaUEKKPqZUtAJZl0ZIZvJG7g==",
"license": "MIT",
"dependencies": {
- "@zag-js/dom-query": "0.82.1"
+ "@zag-js/dom-query": "1.12.0"
}
},
"node_modules/@zag-js/highlight-word": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/highlight-word/-/highlight-word-0.82.1.tgz",
- "integrity": "sha512-lS5r3V0l7Z53QyNwkxulYp5QYA9mFkU+3XsZqfM6cBjh+wmGE1xeIwknAmFtYvuYNK37AwT7pp5z0Rm1Ep6WVQ==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/highlight-word/-/highlight-word-1.12.0.tgz",
+ "integrity": "sha512-9VLG98hH34NDfubR1SkC5fMUzAYZqkw/4KeJbEzduC9qIrDe58fQkCPITsGKzTz7XncONxwqf+wgfIr0qlwESw==",
"license": "MIT"
},
"node_modules/@zag-js/hover-card": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/hover-card/-/hover-card-0.82.1.tgz",
- "integrity": "sha512-fp9t/PNXODwxXR1X+VzgYeSpgoJ+M3W/qvuA2stgPI4kEinwKEssSlP2sH6gTmQVZKL8SV1jiNQinVh00NE85g==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/hover-card/-/hover-card-1.12.0.tgz",
+ "integrity": "sha512-hkciHD3rgokvl0MNzUcEOyGcJ1SZtpe2G6rtH9k04pFFUm4nL7TljcFuFzBWESn1fBHcQrzXUl8AWhBcuXl/IA==",
"license": "MIT",
"dependencies": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dismissable": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/popper": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dismissable": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/popper": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/i18n-utils": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/i18n-utils/-/i18n-utils-0.82.1.tgz",
- "integrity": "sha512-YcTIqka6+/YoH2VRBMnv3CvTjHdUo/NG2nMenAB9Wq0MLTn+TAtcsujenz7ckJcgayVhFAchWNhwK9+/cs1dAw==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/i18n-utils/-/i18n-utils-1.12.0.tgz",
+ "integrity": "sha512-5g7fCeAB8x7mUhsiyfI+w6994HSziX39Wwx4wN+tR424quBNs/EaFALScOMQ0hZjq8VCWFk9DM6+TBNstNLMhw==",
"license": "MIT",
"dependencies": {
- "@zag-js/dom-query": "0.82.1"
+ "@zag-js/dom-query": "1.12.0"
}
},
"node_modules/@zag-js/interact-outside": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/interact-outside/-/interact-outside-0.82.1.tgz",
- "integrity": "sha512-WcWJB5kM41fDM6YMGC3ZEPVn1q3Nrm+cAFkllRJrRY4+bUKXmtN8bqDaRKghP+dG5CXz66SiM6xBvDE4nqtK5Q==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/interact-outside/-/interact-outside-1.12.0.tgz",
+ "integrity": "sha512-lcYo84syK3eF0MYESpMBQsfxT4+uzbaA8izZ0HvIC3kSCXLAaDZNagxPcRoB2yaTvWrn3FUw3YmJHKBOW7Vs9w==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/utils": "1.12.0"
+ }
+ },
+ "node_modules/@zag-js/listbox": {
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/listbox/-/listbox-1.12.0.tgz",
+ "integrity": "sha512-6z/FGF9zshiIOXeov4NpU4foaeKAKR8hfBo4M8RNwvPUBeqDHCfu+IJlab4mHPB2Rz+4N5rhap2krpqBk9OT5A==",
"license": "MIT",
"dependencies": {
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/collection": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/focus-visible": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/live-region": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/live-region/-/live-region-0.82.1.tgz",
- "integrity": "sha512-BmSXc41y1uOra/UV1lt8BurWkuwne/+c371IJCK6l+MWsO0ufq1lrjfx4cyFf5yhVcPRkhv/b/0i+7RxfDSK1A==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/live-region/-/live-region-1.12.0.tgz",
+ "integrity": "sha512-m9lJ3zDLYIV/MY72ZYc6x1Dm5bnUzhKwObKhuSlpDG+7OzN/Mub5se9SXHe5e4bJ879VcsZQ543/hguo0G1hng==",
"license": "MIT"
},
"node_modules/@zag-js/menu": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/menu/-/menu-0.82.1.tgz",
- "integrity": "sha512-faAlQZYeWHcGH8nIxBYh7HHfVjSKsHV8yUsbhMD0XkePWM6eB+dPRd/Fc3DeT8ieM8+sUODnTHEuxar0i48v4w==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/menu/-/menu-1.12.0.tgz",
+ "integrity": "sha512-ciKCeQ1heLGt4RcyhB5f9GQB7CE1rP9Zfwp5oHX9FC5rq4nyneI0qhXEYYklE0P9z7+Z6EAH8FyvLORj5j/SpA==",
"license": "MIT",
"dependencies": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dismissable": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/popper": "0.82.1",
- "@zag-js/rect-utils": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dismissable": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/popper": "1.12.0",
+ "@zag-js/rect-utils": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/number-input": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/number-input/-/number-input-0.82.1.tgz",
- "integrity": "sha512-QIQlxlxM78+TkEhPEGlTbkBR3G2ngm5vhc3BFw4sG6ABMyre8TiIH37EqQB7EGKyAcuz6QwPk3AervHMFKe4YQ==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/number-input/-/number-input-1.12.0.tgz",
+ "integrity": "sha512-8CEdDRPU3v0z2mAO5kVlp4B3IwgLzdBv+1JwrA3nM1NFp5cSzcsPK1gULkwUh0rbMrA3s/dFolPBZHSfldbKEA==",
"license": "MIT",
"dependencies": {
- "@internationalized/number": "3.6.0",
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@internationalized/number": "3.6.1",
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/pagination": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/pagination/-/pagination-0.82.1.tgz",
- "integrity": "sha512-1Rsd3cSnlewefNB1RBI0ymK5wlgiBcK42H1IrJIhly6+SXDAhp0Oc45ofsCzpfhkQ4be+A9Cb30ayc6J4ZU2kA==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/pagination/-/pagination-1.12.0.tgz",
+ "integrity": "sha512-AoX/Ks2hOAXubZEqnNrgLgalb1+qgTMv9wnahXef7amhEn/L+4wq+p9Nzmk7SXzeqR/p1RweH2lJEnLdAbbP5Q==",
"license": "MIT",
"dependencies": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/pin-input": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/pin-input/-/pin-input-0.82.1.tgz",
- "integrity": "sha512-P7UN7rIt03YHt05SuK+kZ9mhl4AfvCvaSGB/9KzEq5r6p1D3lc4+0LVkkOvL2EEB8vbGY/y5BNcvaF2jPQPH5Q==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/pin-input/-/pin-input-1.12.0.tgz",
+ "integrity": "sha512-8291cewBFGtUwX1iAObmNUvnp5/31iAYuWIn87vejqG+fVNxPnV9bUYN0nKAXhQTEZo03N/90FxbX8chZSgWtA==",
"license": "MIT",
"dependencies": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/popover": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/popover/-/popover-0.82.1.tgz",
- "integrity": "sha512-zZ8H/jcjaXcLRX4dBcmandexeKV/5cBOt4AUVEnd3/X5NFFkA2Njz8rpQFcNRZl814NxG4RCchIu8kmonmUKCA==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/popover/-/popover-1.12.0.tgz",
+ "integrity": "sha512-Syp6E/op1g2g7y2ErqGaoMfM6Fln3N027UzxjIFdtNX0i83h04LMO7oyP3+0AcaqRtVuh1cCK0CCjEa7HuSGBA==",
"license": "MIT",
"dependencies": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/aria-hidden": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dismissable": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/focus-trap": "0.82.1",
- "@zag-js/popper": "0.82.1",
- "@zag-js/remove-scroll": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/aria-hidden": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dismissable": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/focus-trap": "1.12.0",
+ "@zag-js/popper": "1.12.0",
+ "@zag-js/remove-scroll": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/popper": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/popper/-/popper-0.82.1.tgz",
- "integrity": "sha512-vQTmVUs6aLGqKmWb+FnLDntsulvd/sCvgndeTmwOHRW8PBwPb86aDnvNrNosBSS+Kk9p6CMJwWZ6CuPWR5Kf7Q==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/popper/-/popper-1.12.0.tgz",
+ "integrity": "sha512-YVdQHdYT32OtIeYmOBpdhhDoZa1aFQA5PrhSMAX9gtEnDIWg3m4bwdwC0Fs7G++zguHjgJ6ve1/QxTs6lHnC7Q==",
"license": "MIT",
"dependencies": {
- "@floating-ui/dom": "1.6.12",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@floating-ui/dom": "1.6.13",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/presence": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/presence/-/presence-0.82.1.tgz",
- "integrity": "sha512-eZeAkq2s7NYCiNVMvkWL2Or458hZj71u7ygCt6skA18sO1ZksY+qIFqj99leCov+fesz06Hf8bxZz5029t/Wjg==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/presence/-/presence-1.12.0.tgz",
+ "integrity": "sha512-QZm7bf8Xy9D2Y5HqtaODzyL44+A62GuuUked6y1Z0RfJXwVkzjNFqCvqZb8Zf/UvKmReaofczkslL4xzyy/IFA==",
"license": "MIT",
"dependencies": {
- "@zag-js/core": "0.82.1",
- "@zag-js/types": "0.82.1"
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0"
}
},
"node_modules/@zag-js/progress": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/progress/-/progress-0.82.1.tgz",
- "integrity": "sha512-Fy1EjUda7o7e/yTKbZgKKayGOsHxkjLG+x0AakHmbR/k2VKbM4QuFHB9RJLlqNd9a+m/BzS1kEKWzCJ7/mXL9Q==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/progress/-/progress-1.12.0.tgz",
+ "integrity": "sha512-jHe11FZwt1cJj/4u4oTAFzR5AEqxJSXt9kO+Jg5yb48UdpZ7L9SL6Layju9fsT8l4yp8XpWZD/cI0kELbP7kvQ==",
"license": "MIT",
"dependencies": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/qr-code": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/qr-code/-/qr-code-0.82.1.tgz",
- "integrity": "sha512-E1N1o1dPVuhWkcrg6urut2aaCqrc16OeE9VJh1mAGIUknF3p0QScH+ql7J/n9r8WOa21xyF6HLKhnWVPRQmHGg==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/qr-code/-/qr-code-1.12.0.tgz",
+ "integrity": "sha512-hk5HV5oPVDRyN1yUUshmcE8NPUC0hwT5Rmm1X4bDfYxu8qCAhTsxkpcdOgQoFE720OLFsadHbpWd4PzfUY3LvA==",
"license": "MIT",
"dependencies": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1",
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0",
"proxy-memoize": "3.0.1",
"uqr": "0.1.2"
}
},
"node_modules/@zag-js/radio-group": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/radio-group/-/radio-group-0.82.1.tgz",
- "integrity": "sha512-YTqP4Ok2YEmEXCEiNW2tufZ6svt4sh7KHqrHZq81vPAJMKKhVosP6LnZvmt4dVn6tKJ0OU8idwFVtPM5jSAWoA==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/radio-group/-/radio-group-1.12.0.tgz",
+ "integrity": "sha512-ByJq8PMAPmg7w4rWCZwqKJ06CDjY8mMzWbJb8YgOy1Aos/JUUUybVdgFpg1VLntN12plrnff5xzq/67LJawVrA==",
"license": "MIT",
"dependencies": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/element-rect": "0.82.1",
- "@zag-js/focus-visible": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/focus-visible": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/rating-group": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/rating-group/-/rating-group-0.82.1.tgz",
- "integrity": "sha512-ULl0OA207b6Ilsr2QWt4dbx58hA/NnyCmHpvv1pAYSlH3K0Es5b25B80Cc5jM/3NK3yqoY81OkS9U8lxmpWo+A==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/rating-group/-/rating-group-1.12.0.tgz",
+ "integrity": "sha512-vH2Hu3/rKfP6/EN80V7nMZe06/q+w1jLplojfyiajOaRHn0H+/4oN72Y/6Rd6jk9uAM7Reo/dTLk92BrJ5hyfA==",
"license": "MIT",
"dependencies": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/react": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/react/-/react-0.82.1.tgz",
- "integrity": "sha512-CZivUTFQ4TdRKTN+9wpWAo0lEZlMnbjJPVn2VJVpcz+eRNUeoVzevkNY/OzAqdV3mp+VtdNabQn1fAz8ngViPQ==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/react/-/react-1.12.0.tgz",
+ "integrity": "sha512-cipenUT8mpF+fNv7gGpnLEMuu6cHHGhKSlrBhPAHv8JABIlsog8TGifz/xTSn8WIbcg0e+LEhfEZG5LRff6dZw==",
"license": "MIT",
"dependencies": {
- "@zag-js/core": "0.82.1",
- "@zag-js/store": "0.82.1",
- "@zag-js/types": "0.82.1",
- "proxy-compare": "3.0.1"
+ "@zag-js/core": "1.12.0",
+ "@zag-js/store": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
},
"peerDependencies": {
"react": ">=18.0.0",
@@ -2428,269 +2468,281 @@
}
},
"node_modules/@zag-js/rect-utils": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/rect-utils/-/rect-utils-0.82.1.tgz",
- "integrity": "sha512-gXmvj1wK9FeToOCzvoZ5gycqUNRzfeqd84uwJeG9zA8SVdoyEnoAji8IAynneq8t3LbiNUcu37wjTw0dcWM6ig==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/rect-utils/-/rect-utils-1.12.0.tgz",
+ "integrity": "sha512-rREWLVbXV2Sy7Kh+TBuLST3QLUNKi+lYZcyuulIV5X3aEEMfNKvB4svdZonfkPYA5uz1MGRYZoM/K3LY77EEVw==",
"license": "MIT"
},
"node_modules/@zag-js/remove-scroll": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/remove-scroll/-/remove-scroll-0.82.1.tgz",
- "integrity": "sha512-68cvXvqgNOlucbnGKRyephk8Qg8wb4xpjgUdmF9xQwICdY/uhW2p4ZGJ4471TDCDIlpoBrJPYsWqV2oWH3QNfA==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/remove-scroll/-/remove-scroll-1.12.0.tgz",
+ "integrity": "sha512-a/6NKblhUfNk8GzLPC2sB2nFXnMhTDysDj6T7wkWsYlPYi2IIX8NwbRl0tSoFoe5w1n0o0g4oe4SDViYOFoBUg==",
"license": "MIT",
"dependencies": {
- "@zag-js/dom-query": "0.82.1"
+ "@zag-js/dom-query": "1.12.0"
}
},
"node_modules/@zag-js/scroll-snap": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/scroll-snap/-/scroll-snap-0.82.1.tgz",
- "integrity": "sha512-HL3MkBpWx4Cw0+h1UP/PnvLP3Z1T+F5mkeS8HWmiP+KPzhtFiEBRrve+xk7h7BMXifteg2UZy53ZiZfJeGsd3w==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/scroll-snap/-/scroll-snap-1.12.0.tgz",
+ "integrity": "sha512-78j6RNi+enlKjDUY9PfDUyHzfK7vQDpq7Rsw2Glkf2obu5Ye6O9ElwiLyolBL+YUm/+y7KDIJe9zAndG+Hq6kQ==",
"license": "MIT",
"dependencies": {
- "@zag-js/dom-query": "0.82.1"
+ "@zag-js/dom-query": "1.12.0"
}
},
"node_modules/@zag-js/select": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/select/-/select-0.82.1.tgz",
- "integrity": "sha512-cc6D8Iz+Ewnx9L0J63QGqC2bbiwzCEcJVE/j4OZDcy4Qk3lqr3qA09uuJbQxAi7yvIeB44DIEt9ryTZPkZbgiw==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/select/-/select-1.12.0.tgz",
+ "integrity": "sha512-GRfQnlG3YdwkD1fAUH/DvfhXIxymB90ZRYcHtuSxF36v2sJIcRhUVZIQvZzGDKX4CvlGVxhobq0w6nHihJh5rA==",
"license": "MIT",
"dependencies": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/collection": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dismissable": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/popper": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/collection": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dismissable": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/popper": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/signature-pad": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/signature-pad/-/signature-pad-0.82.1.tgz",
- "integrity": "sha512-s8ae88OpAafkpuqimO9beUiVTn3FG+bnWeWnYQOLtNYMCNHzQbVZp9QBNbOoUpNcDT14mx9rfZe98BqfiMohFw==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/signature-pad/-/signature-pad-1.12.0.tgz",
+ "integrity": "sha512-+01KZSSp2iWZSi8J9j6qTI1iWX1oLXayXJA2RMZhrgjG6i6Aab9SALZA/3ViEl9WLEgedJ/rWZrQsShz+QB66A==",
"license": "MIT",
"dependencies": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1",
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0",
"perfect-freehand": "^1.2.2"
}
},
"node_modules/@zag-js/slider": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/slider/-/slider-0.82.1.tgz",
- "integrity": "sha512-qXVvXbDRq6Cla036M9OH6plO7ubefM7k65NJQKjtITDua+VliKQLXj9BrdPLT9K96wWntW+D/TiZXE+JNbR4ow==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/slider/-/slider-1.12.0.tgz",
+ "integrity": "sha512-nyA+67/Wm2/znX1vJMzk10bdm14wJfAU8kYffc8RruqUse4WwSz7Tr0W4jLXTcc+IRZqBc1mrHynfwwOrnvXHA==",
"license": "MIT",
"dependencies": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/element-size": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/splitter": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/splitter/-/splitter-0.82.1.tgz",
- "integrity": "sha512-eMNncj+pcepYTf+51s4ysDS/tjtKXswpwsSQR0AeNqCE3SW3TGzHOM0+uheyjgv9EmDGDrr3Imdo0PCkq3bqug==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/splitter/-/splitter-1.12.0.tgz",
+ "integrity": "sha512-9twlVGYrjtpQ9yn7MgBGR5H64vd0OKVW1rt5LBsVDEPPkUdhb1gaxHJQSAIYlTfFyiYs72oXXq33uO2Sn7enPA==",
"license": "MIT",
"dependencies": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/steps": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/steps/-/steps-0.82.1.tgz",
- "integrity": "sha512-N/LVOPbpQGtqpnNsdgZsQytpvXVoJ9Uldo8G38Q7892wwhVx63L0qLaiOK+SkU7kUTueOh109HezZ67nq3sadw==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/steps/-/steps-1.12.0.tgz",
+ "integrity": "sha512-KpIFwISjdLWjDuPKSKK3o68Qm2B1IAsJxYv1BL+6LwAAm31yJN1PEdGFXzvmylLBkAkeUbXQlHMCV7vLgae77w==",
"license": "MIT",
"dependencies": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/store": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/store/-/store-0.82.1.tgz",
- "integrity": "sha512-uWlVivLZBCuAEXrXOITM1srwfBtAnT8kBYVPElrT5aSO9gkV1YC/g+YdFRol7KKOg12qO561CPKReVfilmtAKg==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/store/-/store-1.12.0.tgz",
+ "integrity": "sha512-AW+EJI1reNzWDEaH/LHnjBFZyLOGJNkf+GYEJ8treRJZXEVIaIjHZtSKQZrFJKAM8JIkrb08vawS/lj1izlimg==",
"license": "MIT",
"dependencies": {
"proxy-compare": "3.0.1"
}
},
"node_modules/@zag-js/switch": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/switch/-/switch-0.82.1.tgz",
- "integrity": "sha512-lIZsOs5nG9TkPs75+OK5THprEO0u3NAiLnEJ489KEFautVX/GMwAWvGHNFS7CcCpLZv+EpVKAPAdmGfEphrzhA==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/switch/-/switch-1.12.0.tgz",
+ "integrity": "sha512-ihn+6f4pMJjL1BDheDP5MpWVyIUpyZNo3lDIKrCSunqJflBomHuB3uVR//tkcgG0QsWIQ0b2IoHL1mtbyYn74w==",
"license": "MIT",
"dependencies": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/focus-visible": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/focus-visible": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/tabs": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/tabs/-/tabs-0.82.1.tgz",
- "integrity": "sha512-1uwNRvy8LyUTCAWjL1kD7BexOZ0sHrZ4OnUwDNuaWbqxUjtzoe+ftvcLXvmwFMmrns7o1SVnjqkgSVKuE4mcDA==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/tabs/-/tabs-1.12.0.tgz",
+ "integrity": "sha512-VRVE2nOTs8jHdAAV2nIskjbeu9n/QEr6k6Sq1srhOztiEPmLMdMF9F6DlMRK2UR+KuNsOqTBV3gsjZj5Av//fw==",
"license": "MIT",
"dependencies": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/element-rect": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/tags-input": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/tags-input/-/tags-input-0.82.1.tgz",
- "integrity": "sha512-1mY8nCNMQgMwWBV5zX0bUcIgstqKjvFOAuYhGLIxbQPbgX7lP8Kr3nuhABh0oC0KnWaKfOMlItir2k795G4KMQ==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/tags-input/-/tags-input-1.12.0.tgz",
+ "integrity": "sha512-uRUTPPQSm/zV9iZ+/F3Hu8XJMLqZRMnTZZbYS7kEemkpDc3nwUen5XRPwSxIcej77mehvhBPxkPOEdt4nrkZyw==",
"license": "MIT",
"dependencies": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/auto-resize": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/interact-outside": "0.82.1",
- "@zag-js/live-region": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/auto-resize": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/interact-outside": "1.12.0",
+ "@zag-js/live-region": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/time-picker": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/time-picker/-/time-picker-0.82.1.tgz",
- "integrity": "sha512-nWKx3yyHFBUBPOTDFhi3du4wWlQe8wY0EoeWLQN6bpJSF4qo/BosTZJkUHm//FgUdwdhQBFOAsrlrJ0vL4qvNA==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/time-picker/-/time-picker-1.12.0.tgz",
+ "integrity": "sha512-rEE5Sr+xdij7ZLNeyi/I0DcuFPn0ghSXUPLvp7XJ1rF/qrn8xIR7hR84KqVA1wjrSzuhkr0QyJKqR/k88AxKCg==",
"license": "MIT",
"dependencies": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dismissable": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/popper": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dismissable": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/popper": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
},
"peerDependencies": {
"@internationalized/date": ">=3.0.0"
}
},
"node_modules/@zag-js/timer": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/timer/-/timer-0.82.1.tgz",
- "integrity": "sha512-uG4xCrYHgDZJgvW+71ROQX0xIkqMup37ZpNSLS2f5eD5DO1n/9NYLztA1YyeCJyv1aEDsZreeJLJvNDElgXA2A==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/timer/-/timer-1.12.0.tgz",
+ "integrity": "sha512-37iJz2Qcih9+AIevwQkDm/BB4n3wi2xg3vPswjavStlClPHkmBnERey+KcHULmvfRyKNgXfR0oiet5eCZK5bvg==",
"license": "MIT",
"dependencies": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/toast": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/toast/-/toast-0.82.1.tgz",
- "integrity": "sha512-4dL99zHXQg8j7ReJAR9zLAp5lNKMS4Nm+THnJaKsA0TF5QkELGnsZz47oKhFY0aQn46paxMLVagLqQ0+2i6D1w==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/toast/-/toast-1.12.0.tgz",
+ "integrity": "sha512-G9Emqd5AaUQ9Hl18HQNoOVkGaL7qyUffoAI8+xv+QxJOIobMeRwaoGplOs8f9CDxK+wgl00ZnIa1tJThpq34ZQ==",
"license": "MIT",
"dependencies": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dismissable": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dismissable": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
+ }
+ },
+ "node_modules/@zag-js/toggle": {
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/toggle/-/toggle-1.12.0.tgz",
+ "integrity": "sha512-krpJdt3P8/u0AmM6LDrSuCrQWqbaRIGswxg9LoFBCs9AAa+nmhP3LIu0FsNeSZzGWSOGC779ELf1ZnN4z6V+Rw==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/toggle-group": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/toggle-group/-/toggle-group-0.82.1.tgz",
- "integrity": "sha512-8YaYKFz3ciiQhlTFScrvqH3Ke6UMDQLSgMEsCcERBYatd6TxkJwlFiBzpksIDsZpmloBrylyItJvqmzj9jt6Ig==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/toggle-group/-/toggle-group-1.12.0.tgz",
+ "integrity": "sha512-vw458MNJNcfiMVf54usbWJ6W9079/TtjhyGJdR1p8U73ps0RT/9/rEItAWXyBpoDvHP486zdffyH6HFMIW9c8A==",
"license": "MIT",
"dependencies": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/tooltip": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/tooltip/-/tooltip-0.82.1.tgz",
- "integrity": "sha512-ewF/1h2INDJlzYnoIigcWFWim56ezhfl7YGKgqLBdxBoRvZHyhRIfR8bbddVZk4k144gXsMVMeXwS6VEt6D0eQ==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/tooltip/-/tooltip-1.12.0.tgz",
+ "integrity": "sha512-sdYSUQp+ziSyKlLPJVN5pythQacMpNYfRximG0ddpZ43iTZp71P9hN0ZvO6RccND+vP7uGNzncEqAIk30emlzw==",
"license": "MIT",
"dependencies": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/focus-visible": "0.82.1",
- "@zag-js/popper": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/focus-visible": "1.12.0",
+ "@zag-js/popper": "1.12.0",
+ "@zag-js/store": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/tour": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/tour/-/tour-0.82.1.tgz",
- "integrity": "sha512-Oo4ZA3vG2sYEotfrWVXfIV1KW0Z+s91U+2YPtM2sOLnhetEVXxj/AwAruZfvS6WOcTI7D9UBrrQolY94fdZeOA==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/tour/-/tour-1.12.0.tgz",
+ "integrity": "sha512-yTJQPbYE3J5nIR1EbxjBIbGdFgwfFqwEZ22qFOC4vw81tkdi2h4qJcDr/ucML3oeEVmJBmFU4EWzNKcoBATsdw==",
"license": "MIT",
"dependencies": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dismissable": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/focus-trap": "0.82.1",
- "@zag-js/interact-outside": "0.82.1",
- "@zag-js/popper": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dismissable": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/focus-trap": "1.12.0",
+ "@zag-js/interact-outside": "1.12.0",
+ "@zag-js/popper": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/tree-view": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/tree-view/-/tree-view-0.82.1.tgz",
- "integrity": "sha512-xvYwaL49ffC8nnb+ENgNtkSZE1jMh8tm1E777AqBqnrhJZ28+FA9Sk8YDuWIWhNOV/r4n97jTXqj4SAGCrlAMQ==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/tree-view/-/tree-view-1.12.0.tgz",
+ "integrity": "sha512-p/tKMPqHxx9Xal7zmaNFC/35VZBPtNMXqbpIlT5GVPPge+9NwtWd7a2Nt5RLdvztqfcyZUkfANWkMUnWgXgucA==",
"license": "MIT",
"dependencies": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/collection": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/collection": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"node_modules/@zag-js/types": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/types/-/types-0.82.1.tgz",
- "integrity": "sha512-Nr/CU/z/SZWDL92P2u9VDZL9JUxY8L1P7dGI0CmDKHlEHk1+vzqg3UnVkUKkZ5eVMNLtloHbrux5X9Gmkl39WQ==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/types/-/types-1.12.0.tgz",
+ "integrity": "sha512-qewKPnURQ9v4BKL0zFrqPIkC77areamYBkO2+DRet2U2WYofrOf4q7T6qIOcF4lLPYvCOv9LnYuw4pAlHyqcgA==",
"license": "MIT",
"dependencies": {
"csstype": "3.1.3"
}
},
"node_modules/@zag-js/utils": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/utils/-/utils-0.82.1.tgz",
- "integrity": "sha512-JUGdEjstrzB0G2AJqzQiURIl6UZ1ONYgby/pqBKX57LO5LxasQXk9oNZh8+ZAvePNC/lKqqTtyyI02YQB4XwkA==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/utils/-/utils-1.12.0.tgz",
+ "integrity": "sha512-PapS6VDeQtlWyZg3gC4pIY0++2OCAzqrnlRiB7IhEyOYsqi5MiAs+TKVDFXU+eUM77aR8pgtXo10yvdnNw6b+A==",
"license": "MIT"
},
"node_modules/acorn": {
@@ -3119,6 +3171,12 @@
"url": "https://github.com/sindresorhus/execa?sponsor=1"
}
},
+ "node_modules/fast-safe-stringify": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
+ "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==",
+ "license": "MIT"
+ },
"node_modules/fdir": {
"version": "6.4.4",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
@@ -4446,62 +4504,67 @@
}
},
"@ark-ui/react": {
- "version": "4.9.1",
- "resolved": "https://registry.npmjs.org/@ark-ui/react/-/react-4.9.1.tgz",
- "integrity": "sha512-grnfoSUrGxN0VMgtf4yvpMgin2T4ERINqYm3x/XKny+q2iIO76PD7yjNP7IW+CDmNxy3QPOidcvRiCyy6x0LGA==",
- "requires": {
- "@internationalized/date": "3.7.0",
- "@zag-js/accordion": "0.82.1",
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/auto-resize": "0.82.1",
- "@zag-js/avatar": "0.82.1",
- "@zag-js/carousel": "0.82.1",
- "@zag-js/checkbox": "0.82.1",
- "@zag-js/clipboard": "0.82.1",
- "@zag-js/collapsible": "0.82.1",
- "@zag-js/collection": "0.82.1",
- "@zag-js/color-picker": "0.82.1",
- "@zag-js/color-utils": "0.82.1",
- "@zag-js/combobox": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/date-picker": "0.82.1",
- "@zag-js/date-utils": "0.82.1",
- "@zag-js/dialog": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/editable": "0.82.1",
- "@zag-js/file-upload": "0.82.1",
- "@zag-js/file-utils": "0.82.1",
- "@zag-js/focus-trap": "0.82.1",
- "@zag-js/highlight-word": "0.82.1",
- "@zag-js/hover-card": "0.82.1",
- "@zag-js/i18n-utils": "0.82.1",
- "@zag-js/menu": "0.82.1",
- "@zag-js/number-input": "0.82.1",
- "@zag-js/pagination": "0.82.1",
- "@zag-js/pin-input": "0.82.1",
- "@zag-js/popover": "0.82.1",
- "@zag-js/presence": "0.82.1",
- "@zag-js/progress": "0.82.1",
- "@zag-js/qr-code": "0.82.1",
- "@zag-js/radio-group": "0.82.1",
- "@zag-js/rating-group": "0.82.1",
- "@zag-js/react": "0.82.1",
- "@zag-js/select": "0.82.1",
- "@zag-js/signature-pad": "0.82.1",
- "@zag-js/slider": "0.82.1",
- "@zag-js/splitter": "0.82.1",
- "@zag-js/steps": "0.82.1",
- "@zag-js/switch": "0.82.1",
- "@zag-js/tabs": "0.82.1",
- "@zag-js/tags-input": "0.82.1",
- "@zag-js/time-picker": "0.82.1",
- "@zag-js/timer": "0.82.1",
- "@zag-js/toast": "0.82.1",
- "@zag-js/toggle-group": "0.82.1",
- "@zag-js/tooltip": "0.82.1",
- "@zag-js/tour": "0.82.1",
- "@zag-js/tree-view": "0.82.1",
- "@zag-js/types": "0.82.1"
+ "version": "5.8.0",
+ "resolved": "https://registry.npmjs.org/@ark-ui/react/-/react-5.8.0.tgz",
+ "integrity": "sha512-5KrB7YqJ1YXz5DNmu5LWoWp1JeqC/SPdXMRekvOopDdKqWARsfdSV+mZuTHq2UY5bCSv8GYZJEBhbQyk265Yxw==",
+ "requires": {
+ "@internationalized/date": "3.8.0",
+ "@zag-js/accordion": "1.12.0",
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/angle-slider": "1.12.0",
+ "@zag-js/auto-resize": "1.12.0",
+ "@zag-js/avatar": "1.12.0",
+ "@zag-js/carousel": "1.12.0",
+ "@zag-js/checkbox": "1.12.0",
+ "@zag-js/clipboard": "1.12.0",
+ "@zag-js/collapsible": "1.12.0",
+ "@zag-js/collection": "1.12.0",
+ "@zag-js/color-picker": "1.12.0",
+ "@zag-js/color-utils": "1.12.0",
+ "@zag-js/combobox": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/date-picker": "1.12.0",
+ "@zag-js/date-utils": "1.12.0",
+ "@zag-js/dialog": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/editable": "1.12.0",
+ "@zag-js/file-upload": "1.12.0",
+ "@zag-js/file-utils": "1.12.0",
+ "@zag-js/floating-panel": "1.12.0",
+ "@zag-js/focus-trap": "1.12.0",
+ "@zag-js/highlight-word": "1.12.0",
+ "@zag-js/hover-card": "1.12.0",
+ "@zag-js/i18n-utils": "1.12.0",
+ "@zag-js/listbox": "1.12.0",
+ "@zag-js/menu": "1.12.0",
+ "@zag-js/number-input": "1.12.0",
+ "@zag-js/pagination": "1.12.0",
+ "@zag-js/pin-input": "1.12.0",
+ "@zag-js/popover": "1.12.0",
+ "@zag-js/presence": "1.12.0",
+ "@zag-js/progress": "1.12.0",
+ "@zag-js/qr-code": "1.12.0",
+ "@zag-js/radio-group": "1.12.0",
+ "@zag-js/rating-group": "1.12.0",
+ "@zag-js/react": "1.12.0",
+ "@zag-js/select": "1.12.0",
+ "@zag-js/signature-pad": "1.12.0",
+ "@zag-js/slider": "1.12.0",
+ "@zag-js/splitter": "1.12.0",
+ "@zag-js/steps": "1.12.0",
+ "@zag-js/switch": "1.12.0",
+ "@zag-js/tabs": "1.12.0",
+ "@zag-js/tags-input": "1.12.0",
+ "@zag-js/time-picker": "1.12.0",
+ "@zag-js/timer": "1.12.0",
+ "@zag-js/toast": "1.12.0",
+ "@zag-js/toggle": "1.12.0",
+ "@zag-js/toggle-group": "1.12.0",
+ "@zag-js/tooltip": "1.12.0",
+ "@zag-js/tour": "1.12.0",
+ "@zag-js/tree-view": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@babel/code-frame": {
@@ -4734,17 +4797,18 @@
"optional": true
},
"@chakra-ui/react": {
- "version": "3.8.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react/-/react-3.8.0.tgz",
- "integrity": "sha512-UOkDxxMYHqQ6z/ExMcLYnjIIj2Ulu6syAkrpSueYmzLlG93cljkMCze5y9GXh/M6fyQEbLBuDVesULTqMmHuiA==",
+ "version": "3.17.0",
+ "resolved": "https://registry.npmjs.org/@chakra-ui/react/-/react-3.17.0.tgz",
+ "integrity": "sha512-b6syn8PTCAEqXQa52KtVFs2lAavFTldb2SkBbAqmrlWQyE58jTxpgxaEsYsqQxq/bljwC0xHsh5/ACU7Xwr6sA==",
"requires": {
- "@ark-ui/react": "4.9.1",
+ "@ark-ui/react": "5.8.0",
"@emotion/is-prop-valid": "1.3.1",
"@emotion/serialize": "1.3.3",
"@emotion/use-insertion-effect-with-fallbacks": "1.2.0",
"@emotion/utils": "1.4.2",
- "@pandacss/is-valid-prop": "0.41.0",
- "csstype": "3.1.3"
+ "@pandacss/is-valid-prop": "0.53.6",
+ "csstype": "3.1.3",
+ "fast-safe-stringify": "2.1.1"
}
},
"@emotion/babel-plugin": {
@@ -5031,20 +5095,20 @@
"optional": true
},
"@floating-ui/core": {
- "version": "1.6.9",
- "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.9.tgz",
- "integrity": "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==",
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.0.tgz",
+ "integrity": "sha512-FRdBLykrPPA6P76GGGqlex/e7fbe0F1ykgxHYNXQsH/iTEtjMj/f9bpY5oQqbjt5VgZvgz/uKXbGuROijh3VLA==",
"requires": {
"@floating-ui/utils": "^0.2.9"
}
},
"@floating-ui/dom": {
- "version": "1.6.12",
- "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.12.tgz",
- "integrity": "sha512-NP83c0HjokcGVEMeoStg317VD9W7eDlGK7457dMBANbKA6GJZdc7rjujdgqzTaz93jkGgc5P/jeWbaCHnMNc+w==",
+ "version": "1.6.13",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.13.tgz",
+ "integrity": "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==",
"requires": {
"@floating-ui/core": "^1.6.0",
- "@floating-ui/utils": "^0.2.8"
+ "@floating-ui/utils": "^0.2.9"
}
},
"@floating-ui/utils": {
@@ -5065,17 +5129,17 @@
}
},
"@internationalized/date": {
- "version": "3.7.0",
- "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.7.0.tgz",
- "integrity": "sha512-VJ5WS3fcVx0bejE/YHfbDKR/yawZgKqn/if+oEeLqNwBtPzVB06olkfcnojTmEMX+gTpH+FlQ69SHNitJ8/erQ==",
+ "version": "3.8.0",
+ "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.8.0.tgz",
+ "integrity": "sha512-J51AJ0fEL68hE4CwGPa6E0PO6JDaVLd8aln48xFCSy7CZkZc96dGEGmLs2OEEbBxcsVZtfrqkXJwI2/MSG8yKw==",
"requires": {
"@swc/helpers": "^0.5.0"
}
},
"@internationalized/number": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/@internationalized/number/-/number-3.6.0.tgz",
- "integrity": "sha512-PtrRcJVy7nw++wn4W2OuePQQfTqDzfusSuY1QTtui4wa7r+rGVtR75pO8CyKvHvzyQYi3Q1uO5sY0AsB4e65Bw==",
+ "version": "3.6.1",
+ "resolved": "https://registry.npmjs.org/@internationalized/number/-/number-3.6.1.tgz",
+ "integrity": "sha512-UVsb4bCwbL944E0SX50CHFtWEeZ2uB5VozZ5yDXJdq6iPZsZO5p+bjVMZh2GxHf4Bs/7xtDCcPwEa2NU9DaG/g==",
"requires": {
"@swc/helpers": "^0.5.0"
}
@@ -5087,9 +5151,9 @@
"dev": true
},
"@pandacss/is-valid-prop": {
- "version": "0.41.0",
- "resolved": "https://registry.npmjs.org/@pandacss/is-valid-prop/-/is-valid-prop-0.41.0.tgz",
- "integrity": "sha512-BE6h6CsJk14ugIRrsazJtN3fcg+KDFRat1Bs93YFKH6jd4DOb1yUyVvC70jKqPVvg70zEcV8acZ7VdcU5TLu+w=="
+ "version": "0.53.6",
+ "resolved": "https://registry.npmjs.org/@pandacss/is-valid-prop/-/is-valid-prop-0.53.6.tgz",
+ "integrity": "sha512-TgWBQmz/5j/oAMjavqJAjQh1o+yxhYspKvepXPn4lFhAN3yBhilrw9HliAkvpUr0sB2CkJ2BYMpFXbAJYEocsA=="
},
"@playwright/test": {
"version": "1.52.0",
@@ -5508,703 +5572,747 @@
}
},
"@zag-js/accordion": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/accordion/-/accordion-0.82.1.tgz",
- "integrity": "sha512-DWaElpm6RhntW8zVPMfd+s461FuXi6rv4pDPpXb4xCAJ0KTkBzS6PFxoBLL+11Mjv9XioaBoJatIGOCF8GAtTA==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/accordion/-/accordion-1.12.0.tgz",
+ "integrity": "sha512-9mZgGiyPPKOcNgCjHO67P0pN8tP8wORWc1IhQ9oWCWCtYvGNal8+3+ddooH/N8qW0ZSFURm9gZWbCX1any4Trg==",
"requires": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/anatomy": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/anatomy/-/anatomy-0.82.1.tgz",
- "integrity": "sha512-wpgU7LyU9St3o/ft8Nkundi7MkW37vN1hYc2E7VA/R6mun0qiANsEf83ymIlAYnovLC6WUlBso9xwqejr6wRCg=="
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/anatomy/-/anatomy-1.12.0.tgz",
+ "integrity": "sha512-BpnD2qh+shANl127hin9gfOmiHnUaL0whgbBGL6KMuurFg+WPtOBMTxAtbhSRa3524cu61GBkgETH4VAg1xS7w=="
+ },
+ "@zag-js/angle-slider": {
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/angle-slider/-/angle-slider-1.12.0.tgz",
+ "integrity": "sha512-PrxmA9EBr0aG0tq3gEYQvX0VlPxGH4tDTWK8ipO05z09wBdfFJIIYQbGCYp4VpZQqJT3QBpDsf43gJFJ5cq4Sw==",
+ "requires": {
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/rect-utils": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
+ }
},
"@zag-js/aria-hidden": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/aria-hidden/-/aria-hidden-0.82.1.tgz",
- "integrity": "sha512-KSz9oMY9rn1N3k3tFTKHlU66eQf8XZ/gy/ex27J0ykZoaYJplWQerSZvVakbILeh+rtpvdiTNaSgrCAwYwvAPA=="
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/aria-hidden/-/aria-hidden-1.12.0.tgz",
+ "integrity": "sha512-YQkGo49bPzjMWTPbnuqGMDDemmppVk8w6QlF+Gygurp2gfeCQngj+2Oz6ZCzvSNIFXjRAL6i31O8BolfV/aLPg=="
},
"@zag-js/auto-resize": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/auto-resize/-/auto-resize-0.82.1.tgz",
- "integrity": "sha512-adOB7Y4p4i6b8GJv4V6qhlK1YRj4Ejs5I+eWFd8Rx535uQIcxEEVtpEAD5SRYg5PNk1ikaT+GCoHnTadGj6PuA==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/auto-resize/-/auto-resize-1.12.0.tgz",
+ "integrity": "sha512-9y7HCXp4cUcaHxVB5fgC94q6+/ZwsF8X/F3ZY34dSF06xZ4L8mjnp1lF75gzPJMbUGY+sKd0Excpxty8ecWrjg==",
"requires": {
- "@zag-js/dom-query": "0.82.1"
+ "@zag-js/dom-query": "1.12.0"
}
},
"@zag-js/avatar": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/avatar/-/avatar-0.82.1.tgz",
- "integrity": "sha512-XjRvDRmBxwy5OtIzlQOpf7zNk4g0b/uA7qZve5Hz0R7yWOu+NFlbFv0GsvRfgyYMCT5J0xBu271EG9FJq3QKyw==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/avatar/-/avatar-1.12.0.tgz",
+ "integrity": "sha512-0e4ti7Be0p8KbZInkbrz45v/dd5w/OBN0rxeP2yOkHJRCUfPRqx/aE2wXL/66iAuh0RadgOYhgPVf1DjFSqWLw==",
"requires": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/carousel": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/carousel/-/carousel-0.82.1.tgz",
- "integrity": "sha512-MO9+9oedxdKynxgvLLzXs+VQSOhu+GvsCLV4fBt7nMBMGIRHtRSzXHRNRkO0aqbsO/nKQ8TFH7GYzI1NqT/y4A==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/carousel/-/carousel-1.12.0.tgz",
+ "integrity": "sha512-E77u1wq472Mh+1dRC+iId5zq35LA10njL7jImO9Mi2Ia1LDrMwxfwsEUs+jh94W0MV1d+jpcJ7MCquQeZrurIg==",
"requires": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/scroll-snap": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/scroll-snap": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/checkbox": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/checkbox/-/checkbox-0.82.1.tgz",
- "integrity": "sha512-yD/h8ao/JTljEo+zthpKzTy/f9fqOlJ7Nd6psPoSKZy2MRGD0TDUbOjravb3icVgjTLCiaPVWMWdonny08Me6A==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/checkbox/-/checkbox-1.12.0.tgz",
+ "integrity": "sha512-tt7oWTR2YSk3DT7F49fhNL3AqUyxbl5Qk3qiiQbiRxTVKmbq3ATNW5cm2QifXEwIPbBaIiXdI/W69DJjcszqoQ==",
"requires": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/focus-visible": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/focus-visible": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/clipboard": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/clipboard/-/clipboard-0.82.1.tgz",
- "integrity": "sha512-r1r3vwozs+lyNgccR3OfmYAydP0cJbIHGsgDKGuempinqv6xIoptHOkFgWNd6Kxz/3MnxP+BMEy6fZzECXkhdQ==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/clipboard/-/clipboard-1.12.0.tgz",
+ "integrity": "sha512-6x14LF51633/vzL6q7SqQ9FSUFDpkB4JXaqFeP6BVgo4sMMgYimQIRaufJ6G1lXV3sof2bcJhNXPJWZkYn4wnQ==",
"requires": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/collapsible": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/collapsible/-/collapsible-0.82.1.tgz",
- "integrity": "sha512-TuggUoXRVBOwACksi63TsN2rOukzUpe6oVMUvp9MaQaDbg9gpw0JzLTrdAaHfE+bhgXAb3EjN6wcZjq8zBctZQ==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/collapsible/-/collapsible-1.12.0.tgz",
+ "integrity": "sha512-2jzKy2Dbbit66oRmhbacHKqPuE7xt37+YBE71sJWmj2To8NKWrwibF61vFbHVjaoAgZ0mhgXiAJYPMyPHXaWWg==",
"requires": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/collection": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/collection/-/collection-0.82.1.tgz",
- "integrity": "sha512-uteM+xWZlWhRQe5biA5QWyva9PdzXONs+bpycUtZt8MakQgPmhW2whY9r1aW5NFVb/ScTwGAIGB3Eyc6Npz7Wg==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/collection/-/collection-1.12.0.tgz",
+ "integrity": "sha512-gk/qRc5rUyjD6P/fxT2UHvpXWtjXflaM++J4DjAu4bPiBAzhz8zjBZRo6Yyiffm1+YNBIZKxFemb813P16iQNg==",
"requires": {
- "@zag-js/utils": "0.82.1"
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/color-picker": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/color-picker/-/color-picker-0.82.1.tgz",
- "integrity": "sha512-/MShDVBFNnXResLzeyWyKApeHuB9rmUeJo3WD/Bl6rTwjmvVCKRYguIe1SQviOokMLjuAyh0YWXdKMQw0HvMqQ==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/color-picker/-/color-picker-1.12.0.tgz",
+ "integrity": "sha512-nbmY0ljFLZFhfjAMJkYsfiJ3tk8ux0lzdF6v7kif/ZtPfFIchVkCXX5ESx5w71j7MpfzJN3PrQ0F/2wDCHHPtg==",
"requires": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/color-utils": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dismissable": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/popper": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/color-utils": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dismissable": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/popper": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/color-utils": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/color-utils/-/color-utils-0.82.1.tgz",
- "integrity": "sha512-BMSYcBeypGX0wCLszU2jxWBRUmd5/wPDJ59Y3Zwl9yNld0gtMnuBLSUeokMcG0UVQ/BxkyrWu3VDkKTUYKprqQ==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/color-utils/-/color-utils-1.12.0.tgz",
+ "integrity": "sha512-KZsR8gEStPC5X6A+eMS5ZLRWWcsHi5byGfF8UaNybFJ8VsB9MqrPw5xtcQm9zOkJQlWaeEFOMQuojl/hGGUIXQ==",
"requires": {
- "@zag-js/utils": "0.82.1"
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/combobox": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/combobox/-/combobox-0.82.1.tgz",
- "integrity": "sha512-Me3a0Sw4dTtmBRmbLGO/C1LJ4btZwbd5RLYnf8RPhEnqGJ5Z05i+ffWEe+SNBvpQO14njqBcF6P8VypVD/Ro1A==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/combobox/-/combobox-1.12.0.tgz",
+ "integrity": "sha512-a4eLrgdmsJ4Dc8Yob7SXm1PYVfqT2IAvwqX/3DpWCVisff57e9GlfLh9n/AgoH1SWNs2709nm56P2lQrVtK7uQ==",
"requires": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/aria-hidden": "0.82.1",
- "@zag-js/collection": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dismissable": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/popper": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/aria-hidden": "1.12.0",
+ "@zag-js/collection": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dismissable": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/popper": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/core": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/core/-/core-0.82.1.tgz",
- "integrity": "sha512-Ux0fkt1PumcqLwExcEozCMEfKBxtd2JlnitXo4hR3lJW5q9G52FkgWDyPSrhblyTkX+7RgxViZTMnHxaXs99jg==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/core/-/core-1.12.0.tgz",
+ "integrity": "sha512-GIDXIgTDHQZCnzKEhi+VMEvcJBsloHQNSmBRxZFllPh7htYGijwK8CrDQSzQPrGW074aR2pzu/RhdyqCUGQJyQ==",
"requires": {
- "@zag-js/store": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/date-picker": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/date-picker/-/date-picker-0.82.1.tgz",
- "integrity": "sha512-f+4CV29+hcQ3Yw9hh0yyVRANONIUEWIrPS1fpnrrUNtIC0Y7f1Ajx+x089X9VxgQhwreK1sEwpnrL2vIqy+9+A==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/date-picker/-/date-picker-1.12.0.tgz",
+ "integrity": "sha512-qJoPGRH8yOs7OWLdsfINMbsMQW8Qh1FR0ixlyFkDJi3K5KUer2p2O91DmkJll4VdXIM1FJ8oBtbcDK1M55PtyQ==",
"requires": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/date-utils": "0.82.1",
- "@zag-js/dismissable": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/live-region": "0.82.1",
- "@zag-js/popper": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/date-utils": "1.12.0",
+ "@zag-js/dismissable": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/live-region": "1.12.0",
+ "@zag-js/popper": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/date-utils": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/date-utils/-/date-utils-0.82.1.tgz",
- "integrity": "sha512-z9sHtgV4fvtXsqLaTD4/o+D+H5wumLYhIw/Bj3yC41gR5oa4Wo9QifRT9DBfvuokmXsrnRZ8k32hUtWoYb6M/A==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/date-utils/-/date-utils-1.12.0.tgz",
+ "integrity": "sha512-nalAtiVqabj9Ozlftj/Veq58dm6fcqUx2Ok3IofIdqxZJ6Q7Xdrk8vuba+5+lhipfemyVbjZQoQHwX6TBM5H0g==",
"requires": {}
},
"@zag-js/dialog": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/dialog/-/dialog-0.82.1.tgz",
- "integrity": "sha512-oqi+6Y/rx6ZKxg3s9r6bIuo33x+5+UDhvrlk31kE3LWgU1KJjVV0VEkFMK9B1SJTY7IizhlWMyDx+JXJ+jOy5Q==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/dialog/-/dialog-1.12.0.tgz",
+ "integrity": "sha512-l1HYpIR0Puz+L7i+Di4DWIJbeJamse5lFb3SNyYFMkZHT6zPn/ylQhcsmB13eFWnveVGoOp8+WT4XVRw6Ff1Tg==",
"requires": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/aria-hidden": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dismissable": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/focus-trap": "0.82.1",
- "@zag-js/remove-scroll": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/aria-hidden": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dismissable": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/focus-trap": "1.12.0",
+ "@zag-js/remove-scroll": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/dismissable": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/dismissable/-/dismissable-0.82.1.tgz",
- "integrity": "sha512-vs+zkORzaeNzX4Wsy4OkW1AVce7l4Tc6DHZq8gqNB5SvhK+5wEPl6EmacQRvZyoCxi2m6xpaI98UkLCmVJKU+Q==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/dismissable/-/dismissable-1.12.0.tgz",
+ "integrity": "sha512-YXXzaCUPykfzDQUK5TTOShTioZbUh/tkZIA+uRUZK18qvEI8fmGrLaVHyD3Z0smtA0KWW2O5mC2v8lwq9DB3Lw==",
"requires": {
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/interact-outside": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/interact-outside": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/dom-query": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-0.82.1.tgz",
- "integrity": "sha512-KFtbqDUykQur587hyrGi8LL8GfTS2mqBpIT0kL3E+S63Mq7U84i+hGf3VyNuInMB5ONpkNEk5JN4G9/HWQ6pAQ==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.12.0.tgz",
+ "integrity": "sha512-3fw4+kyFn+K+ESBSG7uhvxgOAJcB+WLYsMrktgK4SUC1ukq3wDKF4oAP+uhXA6OYdVw2T5ZwLU0aY37q94hC2A==",
"requires": {
- "@zag-js/types": "0.82.1"
+ "@zag-js/types": "1.12.0"
}
},
"@zag-js/editable": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/editable/-/editable-0.82.1.tgz",
- "integrity": "sha512-V5i3kYSHFJYj8914nBf4VKKtm6m59gG482vm20As4EnLcwGFrOBbm4HXUgsKq0wYSLy/lTtvMrUT8Iqudye2gw==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/editable/-/editable-1.12.0.tgz",
+ "integrity": "sha512-6QJSDDCSWjCZuPyJ28ctqRvpFzRDt39a/s17SyywWdW+qLeFkXtdod21ZMXx2RGsrSnqYPVEQOD6sYJ4e+iEMQ==",
"requires": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/interact-outside": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/interact-outside": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
- "@zag-js/element-rect": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/element-rect/-/element-rect-0.82.1.tgz",
- "integrity": "sha512-xXUjmeIUdxkxic5bepp6fVqN9Qs+54PXCAUl6g/DtJecQVmVooIfa3SLSULhany4aR4mlGojp5TJxvSpUBA58Q=="
- },
- "@zag-js/element-size": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/element-size/-/element-size-0.82.1.tgz",
- "integrity": "sha512-k1rOE6NhoULI9d5pt2qVUxWCQVEf3OTPH8UDnbsdf11xn+hMCzRYd9lekUdVGrcHHGvEK+W6iAfWZnlwsJsmow=="
- },
"@zag-js/file-upload": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/file-upload/-/file-upload-0.82.1.tgz",
- "integrity": "sha512-6cgJsy9bf2DB0v+CVq1L4g4aCePTpfWsV4C0HC+82K+OSPomiIPsQS87wo4+eAcy3z+80Qh+uglZCFAwkW8W+g==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/file-upload/-/file-upload-1.12.0.tgz",
+ "integrity": "sha512-5VOL/5sEfmZ2PpvZpB+j7dXgTWPgvJk4zo/JjX/UbQdL/NTOMWRfEZVaOZKgAb0Ngo9AvqI8GYWxoUAYLyupDg==",
"requires": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/file-utils": "0.82.1",
- "@zag-js/i18n-utils": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/file-utils": "1.12.0",
+ "@zag-js/i18n-utils": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/file-utils": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/file-utils/-/file-utils-0.82.1.tgz",
- "integrity": "sha512-/u86hMd+E5UCrrY9akDAExkO7sgPA1lXzWC9gSX4LSxHATk7Vo4o5+4LiE1MX4WZRytOhtxAycJzNDVpqzmppQ==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/file-utils/-/file-utils-1.12.0.tgz",
+ "integrity": "sha512-1UGt+juPdyZT2Dzzj6qMvtzdX1tAp/weazlgV4+Vw2kvOn8jn1+l3z0FTmFyA1e2r0n7dvVyhCRq/qw6vGIe/Q==",
"requires": {
- "@zag-js/i18n-utils": "0.82.1"
+ "@zag-js/i18n-utils": "1.12.0"
+ }
+ },
+ "@zag-js/floating-panel": {
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/floating-panel/-/floating-panel-1.12.0.tgz",
+ "integrity": "sha512-9SGV5/OfEU6t4pEVoCbyHr95pH47fYfA4ZWecDJ9I/bZ4Fft2bozAqyjIyoTnj+RPzvHKkqvC6tGmt0j/4DDSQ==",
+ "requires": {
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dismissable": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/popper": "1.12.0",
+ "@zag-js/rect-utils": "1.12.0",
+ "@zag-js/store": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/focus-trap": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/focus-trap/-/focus-trap-0.82.1.tgz",
- "integrity": "sha512-z5OzmR8O3n2043Lwhp1qcizNHXvzc/Xteb3hWmxbX9hR3k0wHJeMXMj3GTDO0FBixRt+d8iHEmt3/8CkI72mqw==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/focus-trap/-/focus-trap-1.12.0.tgz",
+ "integrity": "sha512-T8eNgWDFcSf/oyut0kG3zYji3CqH2GtnivpNSeDsiNdwJ2bHXgeC65HqUExESClBTJz0W74AatruPvFzEhxDxg==",
"requires": {
- "@zag-js/dom-query": "0.82.1"
+ "@zag-js/dom-query": "1.12.0"
}
},
"@zag-js/focus-visible": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/focus-visible/-/focus-visible-0.82.1.tgz",
- "integrity": "sha512-b87FqZO6e9RmTY4msEzwZ3hZ8pRuPd2vbR2b6SlXr6ohtmGKlGgBGO4kmarZN/ClE+7VOnOEqIicatRBEgX9bw==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/focus-visible/-/focus-visible-1.12.0.tgz",
+ "integrity": "sha512-u+6k8fyQ12a0sZvSk02V7JY1OAsxWI6MhfW11OvSixMSHfd82YaWOG2HA/GIAcyaUEKKPqZUtAJZl0ZIZvJG7g==",
"requires": {
- "@zag-js/dom-query": "0.82.1"
+ "@zag-js/dom-query": "1.12.0"
}
},
"@zag-js/highlight-word": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/highlight-word/-/highlight-word-0.82.1.tgz",
- "integrity": "sha512-lS5r3V0l7Z53QyNwkxulYp5QYA9mFkU+3XsZqfM6cBjh+wmGE1xeIwknAmFtYvuYNK37AwT7pp5z0Rm1Ep6WVQ=="
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/highlight-word/-/highlight-word-1.12.0.tgz",
+ "integrity": "sha512-9VLG98hH34NDfubR1SkC5fMUzAYZqkw/4KeJbEzduC9qIrDe58fQkCPITsGKzTz7XncONxwqf+wgfIr0qlwESw=="
},
"@zag-js/hover-card": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/hover-card/-/hover-card-0.82.1.tgz",
- "integrity": "sha512-fp9t/PNXODwxXR1X+VzgYeSpgoJ+M3W/qvuA2stgPI4kEinwKEssSlP2sH6gTmQVZKL8SV1jiNQinVh00NE85g==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/hover-card/-/hover-card-1.12.0.tgz",
+ "integrity": "sha512-hkciHD3rgokvl0MNzUcEOyGcJ1SZtpe2G6rtH9k04pFFUm4nL7TljcFuFzBWESn1fBHcQrzXUl8AWhBcuXl/IA==",
"requires": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dismissable": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/popper": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dismissable": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/popper": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/i18n-utils": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/i18n-utils/-/i18n-utils-0.82.1.tgz",
- "integrity": "sha512-YcTIqka6+/YoH2VRBMnv3CvTjHdUo/NG2nMenAB9Wq0MLTn+TAtcsujenz7ckJcgayVhFAchWNhwK9+/cs1dAw==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/i18n-utils/-/i18n-utils-1.12.0.tgz",
+ "integrity": "sha512-5g7fCeAB8x7mUhsiyfI+w6994HSziX39Wwx4wN+tR424quBNs/EaFALScOMQ0hZjq8VCWFk9DM6+TBNstNLMhw==",
"requires": {
- "@zag-js/dom-query": "0.82.1"
+ "@zag-js/dom-query": "1.12.0"
}
},
"@zag-js/interact-outside": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/interact-outside/-/interact-outside-0.82.1.tgz",
- "integrity": "sha512-WcWJB5kM41fDM6YMGC3ZEPVn1q3Nrm+cAFkllRJrRY4+bUKXmtN8bqDaRKghP+dG5CXz66SiM6xBvDE4nqtK5Q==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/interact-outside/-/interact-outside-1.12.0.tgz",
+ "integrity": "sha512-lcYo84syK3eF0MYESpMBQsfxT4+uzbaA8izZ0HvIC3kSCXLAaDZNagxPcRoB2yaTvWrn3FUw3YmJHKBOW7Vs9w==",
+ "requires": {
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/utils": "1.12.0"
+ }
+ },
+ "@zag-js/listbox": {
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/listbox/-/listbox-1.12.0.tgz",
+ "integrity": "sha512-6z/FGF9zshiIOXeov4NpU4foaeKAKR8hfBo4M8RNwvPUBeqDHCfu+IJlab4mHPB2Rz+4N5rhap2krpqBk9OT5A==",
"requires": {
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/collection": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/focus-visible": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/live-region": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/live-region/-/live-region-0.82.1.tgz",
- "integrity": "sha512-BmSXc41y1uOra/UV1lt8BurWkuwne/+c371IJCK6l+MWsO0ufq1lrjfx4cyFf5yhVcPRkhv/b/0i+7RxfDSK1A=="
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/live-region/-/live-region-1.12.0.tgz",
+ "integrity": "sha512-m9lJ3zDLYIV/MY72ZYc6x1Dm5bnUzhKwObKhuSlpDG+7OzN/Mub5se9SXHe5e4bJ879VcsZQ543/hguo0G1hng=="
},
"@zag-js/menu": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/menu/-/menu-0.82.1.tgz",
- "integrity": "sha512-faAlQZYeWHcGH8nIxBYh7HHfVjSKsHV8yUsbhMD0XkePWM6eB+dPRd/Fc3DeT8ieM8+sUODnTHEuxar0i48v4w==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/menu/-/menu-1.12.0.tgz",
+ "integrity": "sha512-ciKCeQ1heLGt4RcyhB5f9GQB7CE1rP9Zfwp5oHX9FC5rq4nyneI0qhXEYYklE0P9z7+Z6EAH8FyvLORj5j/SpA==",
"requires": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dismissable": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/popper": "0.82.1",
- "@zag-js/rect-utils": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dismissable": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/popper": "1.12.0",
+ "@zag-js/rect-utils": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/number-input": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/number-input/-/number-input-0.82.1.tgz",
- "integrity": "sha512-QIQlxlxM78+TkEhPEGlTbkBR3G2ngm5vhc3BFw4sG6ABMyre8TiIH37EqQB7EGKyAcuz6QwPk3AervHMFKe4YQ==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/number-input/-/number-input-1.12.0.tgz",
+ "integrity": "sha512-8CEdDRPU3v0z2mAO5kVlp4B3IwgLzdBv+1JwrA3nM1NFp5cSzcsPK1gULkwUh0rbMrA3s/dFolPBZHSfldbKEA==",
"requires": {
- "@internationalized/number": "3.6.0",
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@internationalized/number": "3.6.1",
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/pagination": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/pagination/-/pagination-0.82.1.tgz",
- "integrity": "sha512-1Rsd3cSnlewefNB1RBI0ymK5wlgiBcK42H1IrJIhly6+SXDAhp0Oc45ofsCzpfhkQ4be+A9Cb30ayc6J4ZU2kA==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/pagination/-/pagination-1.12.0.tgz",
+ "integrity": "sha512-AoX/Ks2hOAXubZEqnNrgLgalb1+qgTMv9wnahXef7amhEn/L+4wq+p9Nzmk7SXzeqR/p1RweH2lJEnLdAbbP5Q==",
"requires": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/pin-input": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/pin-input/-/pin-input-0.82.1.tgz",
- "integrity": "sha512-P7UN7rIt03YHt05SuK+kZ9mhl4AfvCvaSGB/9KzEq5r6p1D3lc4+0LVkkOvL2EEB8vbGY/y5BNcvaF2jPQPH5Q==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/pin-input/-/pin-input-1.12.0.tgz",
+ "integrity": "sha512-8291cewBFGtUwX1iAObmNUvnp5/31iAYuWIn87vejqG+fVNxPnV9bUYN0nKAXhQTEZo03N/90FxbX8chZSgWtA==",
"requires": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/popover": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/popover/-/popover-0.82.1.tgz",
- "integrity": "sha512-zZ8H/jcjaXcLRX4dBcmandexeKV/5cBOt4AUVEnd3/X5NFFkA2Njz8rpQFcNRZl814NxG4RCchIu8kmonmUKCA==",
- "requires": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/aria-hidden": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dismissable": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/focus-trap": "0.82.1",
- "@zag-js/popper": "0.82.1",
- "@zag-js/remove-scroll": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/popover/-/popover-1.12.0.tgz",
+ "integrity": "sha512-Syp6E/op1g2g7y2ErqGaoMfM6Fln3N027UzxjIFdtNX0i83h04LMO7oyP3+0AcaqRtVuh1cCK0CCjEa7HuSGBA==",
+ "requires": {
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/aria-hidden": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dismissable": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/focus-trap": "1.12.0",
+ "@zag-js/popper": "1.12.0",
+ "@zag-js/remove-scroll": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/popper": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/popper/-/popper-0.82.1.tgz",
- "integrity": "sha512-vQTmVUs6aLGqKmWb+FnLDntsulvd/sCvgndeTmwOHRW8PBwPb86aDnvNrNosBSS+Kk9p6CMJwWZ6CuPWR5Kf7Q==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/popper/-/popper-1.12.0.tgz",
+ "integrity": "sha512-YVdQHdYT32OtIeYmOBpdhhDoZa1aFQA5PrhSMAX9gtEnDIWg3m4bwdwC0Fs7G++zguHjgJ6ve1/QxTs6lHnC7Q==",
"requires": {
- "@floating-ui/dom": "1.6.12",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@floating-ui/dom": "1.6.13",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/presence": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/presence/-/presence-0.82.1.tgz",
- "integrity": "sha512-eZeAkq2s7NYCiNVMvkWL2Or458hZj71u7ygCt6skA18sO1ZksY+qIFqj99leCov+fesz06Hf8bxZz5029t/Wjg==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/presence/-/presence-1.12.0.tgz",
+ "integrity": "sha512-QZm7bf8Xy9D2Y5HqtaODzyL44+A62GuuUked6y1Z0RfJXwVkzjNFqCvqZb8Zf/UvKmReaofczkslL4xzyy/IFA==",
"requires": {
- "@zag-js/core": "0.82.1",
- "@zag-js/types": "0.82.1"
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0"
}
},
"@zag-js/progress": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/progress/-/progress-0.82.1.tgz",
- "integrity": "sha512-Fy1EjUda7o7e/yTKbZgKKayGOsHxkjLG+x0AakHmbR/k2VKbM4QuFHB9RJLlqNd9a+m/BzS1kEKWzCJ7/mXL9Q==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/progress/-/progress-1.12.0.tgz",
+ "integrity": "sha512-jHe11FZwt1cJj/4u4oTAFzR5AEqxJSXt9kO+Jg5yb48UdpZ7L9SL6Layju9fsT8l4yp8XpWZD/cI0kELbP7kvQ==",
"requires": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/qr-code": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/qr-code/-/qr-code-0.82.1.tgz",
- "integrity": "sha512-E1N1o1dPVuhWkcrg6urut2aaCqrc16OeE9VJh1mAGIUknF3p0QScH+ql7J/n9r8WOa21xyF6HLKhnWVPRQmHGg==",
- "requires": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/qr-code/-/qr-code-1.12.0.tgz",
+ "integrity": "sha512-hk5HV5oPVDRyN1yUUshmcE8NPUC0hwT5Rmm1X4bDfYxu8qCAhTsxkpcdOgQoFE720OLFsadHbpWd4PzfUY3LvA==",
+ "requires": {
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0",
"proxy-memoize": "3.0.1",
"uqr": "0.1.2"
}
},
"@zag-js/radio-group": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/radio-group/-/radio-group-0.82.1.tgz",
- "integrity": "sha512-YTqP4Ok2YEmEXCEiNW2tufZ6svt4sh7KHqrHZq81vPAJMKKhVosP6LnZvmt4dVn6tKJ0OU8idwFVtPM5jSAWoA==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/radio-group/-/radio-group-1.12.0.tgz",
+ "integrity": "sha512-ByJq8PMAPmg7w4rWCZwqKJ06CDjY8mMzWbJb8YgOy1Aos/JUUUybVdgFpg1VLntN12plrnff5xzq/67LJawVrA==",
"requires": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/element-rect": "0.82.1",
- "@zag-js/focus-visible": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/focus-visible": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/rating-group": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/rating-group/-/rating-group-0.82.1.tgz",
- "integrity": "sha512-ULl0OA207b6Ilsr2QWt4dbx58hA/NnyCmHpvv1pAYSlH3K0Es5b25B80Cc5jM/3NK3yqoY81OkS9U8lxmpWo+A==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/rating-group/-/rating-group-1.12.0.tgz",
+ "integrity": "sha512-vH2Hu3/rKfP6/EN80V7nMZe06/q+w1jLplojfyiajOaRHn0H+/4oN72Y/6Rd6jk9uAM7Reo/dTLk92BrJ5hyfA==",
"requires": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/react": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/react/-/react-0.82.1.tgz",
- "integrity": "sha512-CZivUTFQ4TdRKTN+9wpWAo0lEZlMnbjJPVn2VJVpcz+eRNUeoVzevkNY/OzAqdV3mp+VtdNabQn1fAz8ngViPQ==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/react/-/react-1.12.0.tgz",
+ "integrity": "sha512-cipenUT8mpF+fNv7gGpnLEMuu6cHHGhKSlrBhPAHv8JABIlsog8TGifz/xTSn8WIbcg0e+LEhfEZG5LRff6dZw==",
"requires": {
- "@zag-js/core": "0.82.1",
- "@zag-js/store": "0.82.1",
- "@zag-js/types": "0.82.1",
- "proxy-compare": "3.0.1"
+ "@zag-js/core": "1.12.0",
+ "@zag-js/store": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/rect-utils": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/rect-utils/-/rect-utils-0.82.1.tgz",
- "integrity": "sha512-gXmvj1wK9FeToOCzvoZ5gycqUNRzfeqd84uwJeG9zA8SVdoyEnoAji8IAynneq8t3LbiNUcu37wjTw0dcWM6ig=="
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/rect-utils/-/rect-utils-1.12.0.tgz",
+ "integrity": "sha512-rREWLVbXV2Sy7Kh+TBuLST3QLUNKi+lYZcyuulIV5X3aEEMfNKvB4svdZonfkPYA5uz1MGRYZoM/K3LY77EEVw=="
},
"@zag-js/remove-scroll": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/remove-scroll/-/remove-scroll-0.82.1.tgz",
- "integrity": "sha512-68cvXvqgNOlucbnGKRyephk8Qg8wb4xpjgUdmF9xQwICdY/uhW2p4ZGJ4471TDCDIlpoBrJPYsWqV2oWH3QNfA==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/remove-scroll/-/remove-scroll-1.12.0.tgz",
+ "integrity": "sha512-a/6NKblhUfNk8GzLPC2sB2nFXnMhTDysDj6T7wkWsYlPYi2IIX8NwbRl0tSoFoe5w1n0o0g4oe4SDViYOFoBUg==",
"requires": {
- "@zag-js/dom-query": "0.82.1"
+ "@zag-js/dom-query": "1.12.0"
}
},
"@zag-js/scroll-snap": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/scroll-snap/-/scroll-snap-0.82.1.tgz",
- "integrity": "sha512-HL3MkBpWx4Cw0+h1UP/PnvLP3Z1T+F5mkeS8HWmiP+KPzhtFiEBRrve+xk7h7BMXifteg2UZy53ZiZfJeGsd3w==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/scroll-snap/-/scroll-snap-1.12.0.tgz",
+ "integrity": "sha512-78j6RNi+enlKjDUY9PfDUyHzfK7vQDpq7Rsw2Glkf2obu5Ye6O9ElwiLyolBL+YUm/+y7KDIJe9zAndG+Hq6kQ==",
"requires": {
- "@zag-js/dom-query": "0.82.1"
+ "@zag-js/dom-query": "1.12.0"
}
},
"@zag-js/select": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/select/-/select-0.82.1.tgz",
- "integrity": "sha512-cc6D8Iz+Ewnx9L0J63QGqC2bbiwzCEcJVE/j4OZDcy4Qk3lqr3qA09uuJbQxAi7yvIeB44DIEt9ryTZPkZbgiw==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/select/-/select-1.12.0.tgz",
+ "integrity": "sha512-GRfQnlG3YdwkD1fAUH/DvfhXIxymB90ZRYcHtuSxF36v2sJIcRhUVZIQvZzGDKX4CvlGVxhobq0w6nHihJh5rA==",
"requires": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/collection": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dismissable": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/popper": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/collection": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dismissable": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/popper": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/signature-pad": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/signature-pad/-/signature-pad-0.82.1.tgz",
- "integrity": "sha512-s8ae88OpAafkpuqimO9beUiVTn3FG+bnWeWnYQOLtNYMCNHzQbVZp9QBNbOoUpNcDT14mx9rfZe98BqfiMohFw==",
- "requires": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/signature-pad/-/signature-pad-1.12.0.tgz",
+ "integrity": "sha512-+01KZSSp2iWZSi8J9j6qTI1iWX1oLXayXJA2RMZhrgjG6i6Aab9SALZA/3ViEl9WLEgedJ/rWZrQsShz+QB66A==",
+ "requires": {
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0",
"perfect-freehand": "^1.2.2"
}
},
"@zag-js/slider": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/slider/-/slider-0.82.1.tgz",
- "integrity": "sha512-qXVvXbDRq6Cla036M9OH6plO7ubefM7k65NJQKjtITDua+VliKQLXj9BrdPLT9K96wWntW+D/TiZXE+JNbR4ow==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/slider/-/slider-1.12.0.tgz",
+ "integrity": "sha512-nyA+67/Wm2/znX1vJMzk10bdm14wJfAU8kYffc8RruqUse4WwSz7Tr0W4jLXTcc+IRZqBc1mrHynfwwOrnvXHA==",
"requires": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/element-size": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/splitter": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/splitter/-/splitter-0.82.1.tgz",
- "integrity": "sha512-eMNncj+pcepYTf+51s4ysDS/tjtKXswpwsSQR0AeNqCE3SW3TGzHOM0+uheyjgv9EmDGDrr3Imdo0PCkq3bqug==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/splitter/-/splitter-1.12.0.tgz",
+ "integrity": "sha512-9twlVGYrjtpQ9yn7MgBGR5H64vd0OKVW1rt5LBsVDEPPkUdhb1gaxHJQSAIYlTfFyiYs72oXXq33uO2Sn7enPA==",
"requires": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/steps": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/steps/-/steps-0.82.1.tgz",
- "integrity": "sha512-N/LVOPbpQGtqpnNsdgZsQytpvXVoJ9Uldo8G38Q7892wwhVx63L0qLaiOK+SkU7kUTueOh109HezZ67nq3sadw==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/steps/-/steps-1.12.0.tgz",
+ "integrity": "sha512-KpIFwISjdLWjDuPKSKK3o68Qm2B1IAsJxYv1BL+6LwAAm31yJN1PEdGFXzvmylLBkAkeUbXQlHMCV7vLgae77w==",
"requires": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/store": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/store/-/store-0.82.1.tgz",
- "integrity": "sha512-uWlVivLZBCuAEXrXOITM1srwfBtAnT8kBYVPElrT5aSO9gkV1YC/g+YdFRol7KKOg12qO561CPKReVfilmtAKg==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/store/-/store-1.12.0.tgz",
+ "integrity": "sha512-AW+EJI1reNzWDEaH/LHnjBFZyLOGJNkf+GYEJ8treRJZXEVIaIjHZtSKQZrFJKAM8JIkrb08vawS/lj1izlimg==",
"requires": {
"proxy-compare": "3.0.1"
}
},
"@zag-js/switch": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/switch/-/switch-0.82.1.tgz",
- "integrity": "sha512-lIZsOs5nG9TkPs75+OK5THprEO0u3NAiLnEJ489KEFautVX/GMwAWvGHNFS7CcCpLZv+EpVKAPAdmGfEphrzhA==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/switch/-/switch-1.12.0.tgz",
+ "integrity": "sha512-ihn+6f4pMJjL1BDheDP5MpWVyIUpyZNo3lDIKrCSunqJflBomHuB3uVR//tkcgG0QsWIQ0b2IoHL1mtbyYn74w==",
"requires": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/focus-visible": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/focus-visible": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/tabs": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/tabs/-/tabs-0.82.1.tgz",
- "integrity": "sha512-1uwNRvy8LyUTCAWjL1kD7BexOZ0sHrZ4OnUwDNuaWbqxUjtzoe+ftvcLXvmwFMmrns7o1SVnjqkgSVKuE4mcDA==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/tabs/-/tabs-1.12.0.tgz",
+ "integrity": "sha512-VRVE2nOTs8jHdAAV2nIskjbeu9n/QEr6k6Sq1srhOztiEPmLMdMF9F6DlMRK2UR+KuNsOqTBV3gsjZj5Av//fw==",
"requires": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/element-rect": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/tags-input": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/tags-input/-/tags-input-0.82.1.tgz",
- "integrity": "sha512-1mY8nCNMQgMwWBV5zX0bUcIgstqKjvFOAuYhGLIxbQPbgX7lP8Kr3nuhABh0oC0KnWaKfOMlItir2k795G4KMQ==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/tags-input/-/tags-input-1.12.0.tgz",
+ "integrity": "sha512-uRUTPPQSm/zV9iZ+/F3Hu8XJMLqZRMnTZZbYS7kEemkpDc3nwUen5XRPwSxIcej77mehvhBPxkPOEdt4nrkZyw==",
"requires": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/auto-resize": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/interact-outside": "0.82.1",
- "@zag-js/live-region": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/auto-resize": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/interact-outside": "1.12.0",
+ "@zag-js/live-region": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/time-picker": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/time-picker/-/time-picker-0.82.1.tgz",
- "integrity": "sha512-nWKx3yyHFBUBPOTDFhi3du4wWlQe8wY0EoeWLQN6bpJSF4qo/BosTZJkUHm//FgUdwdhQBFOAsrlrJ0vL4qvNA==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/time-picker/-/time-picker-1.12.0.tgz",
+ "integrity": "sha512-rEE5Sr+xdij7ZLNeyi/I0DcuFPn0ghSXUPLvp7XJ1rF/qrn8xIR7hR84KqVA1wjrSzuhkr0QyJKqR/k88AxKCg==",
"requires": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dismissable": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/popper": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dismissable": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/popper": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/timer": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/timer/-/timer-0.82.1.tgz",
- "integrity": "sha512-uG4xCrYHgDZJgvW+71ROQX0xIkqMup37ZpNSLS2f5eD5DO1n/9NYLztA1YyeCJyv1aEDsZreeJLJvNDElgXA2A==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/timer/-/timer-1.12.0.tgz",
+ "integrity": "sha512-37iJz2Qcih9+AIevwQkDm/BB4n3wi2xg3vPswjavStlClPHkmBnERey+KcHULmvfRyKNgXfR0oiet5eCZK5bvg==",
"requires": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/toast": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/toast/-/toast-0.82.1.tgz",
- "integrity": "sha512-4dL99zHXQg8j7ReJAR9zLAp5lNKMS4Nm+THnJaKsA0TF5QkELGnsZz47oKhFY0aQn46paxMLVagLqQ0+2i6D1w==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/toast/-/toast-1.12.0.tgz",
+ "integrity": "sha512-G9Emqd5AaUQ9Hl18HQNoOVkGaL7qyUffoAI8+xv+QxJOIobMeRwaoGplOs8f9CDxK+wgl00ZnIa1tJThpq34ZQ==",
"requires": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dismissable": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dismissable": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
+ }
+ },
+ "@zag-js/toggle": {
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/toggle/-/toggle-1.12.0.tgz",
+ "integrity": "sha512-krpJdt3P8/u0AmM6LDrSuCrQWqbaRIGswxg9LoFBCs9AAa+nmhP3LIu0FsNeSZzGWSOGC779ELf1ZnN4z6V+Rw==",
+ "requires": {
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/toggle-group": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/toggle-group/-/toggle-group-0.82.1.tgz",
- "integrity": "sha512-8YaYKFz3ciiQhlTFScrvqH3Ke6UMDQLSgMEsCcERBYatd6TxkJwlFiBzpksIDsZpmloBrylyItJvqmzj9jt6Ig==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/toggle-group/-/toggle-group-1.12.0.tgz",
+ "integrity": "sha512-vw458MNJNcfiMVf54usbWJ6W9079/TtjhyGJdR1p8U73ps0RT/9/rEItAWXyBpoDvHP486zdffyH6HFMIW9c8A==",
"requires": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/tooltip": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/tooltip/-/tooltip-0.82.1.tgz",
- "integrity": "sha512-ewF/1h2INDJlzYnoIigcWFWim56ezhfl7YGKgqLBdxBoRvZHyhRIfR8bbddVZk4k144gXsMVMeXwS6VEt6D0eQ==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/tooltip/-/tooltip-1.12.0.tgz",
+ "integrity": "sha512-sdYSUQp+ziSyKlLPJVN5pythQacMpNYfRximG0ddpZ43iTZp71P9hN0ZvO6RccND+vP7uGNzncEqAIk30emlzw==",
"requires": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/focus-visible": "0.82.1",
- "@zag-js/popper": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/focus-visible": "1.12.0",
+ "@zag-js/popper": "1.12.0",
+ "@zag-js/store": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/tour": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/tour/-/tour-0.82.1.tgz",
- "integrity": "sha512-Oo4ZA3vG2sYEotfrWVXfIV1KW0Z+s91U+2YPtM2sOLnhetEVXxj/AwAruZfvS6WOcTI7D9UBrrQolY94fdZeOA==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/tour/-/tour-1.12.0.tgz",
+ "integrity": "sha512-yTJQPbYE3J5nIR1EbxjBIbGdFgwfFqwEZ22qFOC4vw81tkdi2h4qJcDr/ucML3oeEVmJBmFU4EWzNKcoBATsdw==",
"requires": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dismissable": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/focus-trap": "0.82.1",
- "@zag-js/interact-outside": "0.82.1",
- "@zag-js/popper": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dismissable": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/focus-trap": "1.12.0",
+ "@zag-js/interact-outside": "1.12.0",
+ "@zag-js/popper": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/tree-view": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/tree-view/-/tree-view-0.82.1.tgz",
- "integrity": "sha512-xvYwaL49ffC8nnb+ENgNtkSZE1jMh8tm1E777AqBqnrhJZ28+FA9Sk8YDuWIWhNOV/r4n97jTXqj4SAGCrlAMQ==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/tree-view/-/tree-view-1.12.0.tgz",
+ "integrity": "sha512-p/tKMPqHxx9Xal7zmaNFC/35VZBPtNMXqbpIlT5GVPPge+9NwtWd7a2Nt5RLdvztqfcyZUkfANWkMUnWgXgucA==",
"requires": {
- "@zag-js/anatomy": "0.82.1",
- "@zag-js/collection": "0.82.1",
- "@zag-js/core": "0.82.1",
- "@zag-js/dom-query": "0.82.1",
- "@zag-js/types": "0.82.1",
- "@zag-js/utils": "0.82.1"
+ "@zag-js/anatomy": "1.12.0",
+ "@zag-js/collection": "1.12.0",
+ "@zag-js/core": "1.12.0",
+ "@zag-js/dom-query": "1.12.0",
+ "@zag-js/types": "1.12.0",
+ "@zag-js/utils": "1.12.0"
}
},
"@zag-js/types": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/types/-/types-0.82.1.tgz",
- "integrity": "sha512-Nr/CU/z/SZWDL92P2u9VDZL9JUxY8L1P7dGI0CmDKHlEHk1+vzqg3UnVkUKkZ5eVMNLtloHbrux5X9Gmkl39WQ==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/types/-/types-1.12.0.tgz",
+ "integrity": "sha512-qewKPnURQ9v4BKL0zFrqPIkC77areamYBkO2+DRet2U2WYofrOf4q7T6qIOcF4lLPYvCOv9LnYuw4pAlHyqcgA==",
"requires": {
"csstype": "3.1.3"
}
},
"@zag-js/utils": {
- "version": "0.82.1",
- "resolved": "https://registry.npmjs.org/@zag-js/utils/-/utils-0.82.1.tgz",
- "integrity": "sha512-JUGdEjstrzB0G2AJqzQiURIl6UZ1ONYgby/pqBKX57LO5LxasQXk9oNZh8+ZAvePNC/lKqqTtyyI02YQB4XwkA=="
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@zag-js/utils/-/utils-1.12.0.tgz",
+ "integrity": "sha512-PapS6VDeQtlWyZg3gC4pIY0++2OCAzqrnlRiB7IhEyOYsqi5MiAs+TKVDFXU+eUM77aR8pgtXo10yvdnNw6b+A=="
},
"acorn": {
"version": "8.14.0",
@@ -6507,6 +6615,11 @@
"strip-final-newline": "^3.0.0"
}
},
+ "fast-safe-stringify": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
+ "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA=="
+ },
"fdir": {
"version": "6.4.4",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
diff --git a/frontend/src/components/UserSettings/Appearance.tsx b/frontend/src/components/UserSettings/Appearance.tsx
index a941741630..8ab82f16ce 100644
--- a/frontend/src/components/UserSettings/Appearance.tsx
+++ b/frontend/src/components/UserSettings/Appearance.tsx
@@ -14,7 +14,9 @@ const Appearance = () => {
setTheme(e.value)}
+ onValueChange={(e) => {
+ if (e.value) setTheme(e.value)
+ }}
value={theme}
colorPalette="teal"
>
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000000..3e518fb9a6
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,6 @@
+{
+ "name": "KonditionFastAPI",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {}
+}