diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 00000000..b77eeb1b --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,100 @@ +# Splitwiser - AI Coding Guide + +This document provides essential context for AI assistants working with the Splitwiser codebase. + +## Project Overview + +Splitwiser is an expense tracking and splitting application with: +- Backend: FastAPI + MongoDB +- Frontend POC: Streamlit +- Frontend: Expo/React Native (implemented with Google/Firebase authentication) + +The app allows users to create groups, add expenses with flexible splitting options, track balances, and handle settlements. + +## Architecture + +### Backend (FastAPI) +- Located in `/backend/` +- RESTful API using FastAPI with Python 3.9+ +- MongoDB for database (nonrelational schema) +- JWT authentication with refresh token rotation +- Modular design with services: + - `app/auth/`: Authentication & user registration + - `app/user/`: User profile management + - `app/groups/`: Group creation & management + - `app/expenses/`: Expense tracking & splitting + +### Frontend POC (Streamlit) +- Located in `/ui-poc/` +- `Home.py`: Entry point with login/registration +- `pages/`: Contains main application pages + - `Groups.py`: Group management & expense creation + - `Friends.py`: Friend balance tracking + +## Key Development Patterns + +### API Communication +```python +# Example API call with retry logic from Groups.py +response = make_api_request( + method="get", + url=f"{API_URL}/groups/{group_id}", + headers={"Authorization": f"Bearer {st.session_state.access_token}"}, + max_retries=3 +) +``` + +### State Management +- Backend: MongoDB stores persistent data +- Frontend: Streamlit session state manages user session +```python +# Session state initialization (see Home.py) +if "access_token" not in st.session_state: + st.session_state.access_token = None +``` + +### Expense Handling +- Support for different split types: equal, unequal, percentage-based +- Each expense has a payer and multiple splits (recipients) +- Settlements track debt resolution between users + +## Developer Workflows + +### Setup & Running +1. Backend: + ```bash + cd backend + pip install -r requirements.txt + uvicorn main:app --reload + ``` +2. Frontend POC: + ```bash + cd ui-poc + pip install -r requirements.txt + streamlit run Home.py + ``` +3. Test Data Generation: + ```bash + cd ui-poc + python setup_test_data.py + ``` + +### Testing +- Backend tests in `/backend/tests/` +- Run tests with: `cd backend && pytest` +- Test data setup script: `/ui-poc/setup_test_data.py` + +## Critical Files & Dependencies + +- `backend/main.py`: Main FastAPI application entry point +- `backend/app/config.py`: Configuration settings +- `backend/app/database.py`: MongoDB connection +- `ui-poc/Home.py`: Streamlit application entry point +- `ui-poc/openapi.json`: API specification for frontend reference + +## Common Tasks + +- Adding new API endpoint: Add route to appropriate service router file +- Adding new UI component: Modify files in `/ui-poc/pages/` +- Testing data flow: Use the `setup_test_data.py` to create test scenarios +- Troubleshooting auth: Check JWT token handling in `app/auth/security.py` diff --git a/.gitignore b/.gitignore index 552a3680..93c0fa07 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ node_modules/ dist/ web-build/ expo-env.d.ts +eas.json # Native .kotlin/ diff --git a/backend/.env.example b/backend/.env.example index c3edf509..4b0888c4 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -10,7 +10,7 @@ REFRESH_TOKEN_EXPIRE_DAYS=30 # Firebase Config FIREBASE_PROJECT_ID=your-firebase-project-id FIREBASE_SERVICE_ACCOUNT_PATH=./firebase-service-account.json - +FIREBASE_CREDENTIALS_PATH=./firebase-credentials.json # CORS Configuration ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173,http://127.0.0.1:3000,http://127.0.0.1:5173 ALLOW_ALL_ORIGINS=False diff --git a/backend/app/firebase_config.py b/backend/app/firebase_config.py new file mode 100644 index 00000000..3d666390 --- /dev/null +++ b/backend/app/firebase_config.py @@ -0,0 +1,24 @@ +import firebase_admin +from firebase_admin import credentials, auth + +def initialize_firebase(): + """Initialize Firebase Admin SDK with proper error handling.""" + try: + # Use environment variable or fallback to relative path + credentials_path = os.getenv( + 'FIREBASE_CREDENTIALS_PATH', + '../firebase_service_account.json' + ) + + if not Path(credentials_path).exists(): + raise FileNotFoundError(f"Firebase credentials file not found: {credentials_path}") + + cred = credentials.Certificate(credentials_path) + firebase_admin.initialize_app(cred) + print("Firebase Admin SDK initialized successfully") + except Exception as e: + print(f"Failed to initialize Firebase Admin SDK: {e}") + raise + +# Initialize Firebase +initialize_firebase() diff --git a/backend/requirements.txt b/backend/requirements.txt index 5a180f37..14825b14 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,4 +1,4 @@ -fastapi==0.115.12 +fastapi==0.116.1 uvicorn[standard]==0.34.3 python-jose[cryptography]==3.5.0 passlib[bcrypt]==1.7.4 diff --git a/frontend/app.json b/frontend/app.json index 9180dcc7..894d7edb 100644 --- a/frontend/app.json +++ b/frontend/app.json @@ -16,17 +16,41 @@ "**/*" ], "ios": { - "supportsTablet": true - }, "android": { + "supportsTablet": true, + "bundleIdentifier": "splitwiser.app", + "infoPlist": { + "ITSAppUsesNonExemptEncryption": false + } + }, + "android": { "adaptiveIcon": { "foregroundImage": "./assets/adaptive-icon.png", "backgroundColor": "#ffffff" }, "googleServicesFile": "./google-services.json", - "package": "splitwiser.app" + "package": "com.splitwiser.app" }, "web": { "favicon": "./assets/favicon.png" + }, + "plugins": [ + [ + "expo-build-properties", + { + "android": { + "compileSdkVersion": 34, + "targetSdkVersion": 34 + }, + "ios": { + "deploymentTarget": "13.0" + } + } + ] + ], + "extra": { + "eas": { + "projectId": "7d966fbc-616f-44d2-974f-36d61a2f9ae7" + } } } } diff --git a/frontend/assets/adaptive-icon.png b/frontend/assets/adaptive-icon.png new file mode 100644 index 00000000..8adba6d7 --- /dev/null +++ b/frontend/assets/adaptive-icon.png @@ -0,0 +1 @@ +data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAADNQTFRF////AHbJAHbJAHbJAHbJAHbJAHbJAHbJAHbJAHbJAHbJAHbJAHbJAHbJAHbJAHbJ////5qLQzgAAAA90Uk5TABAgMEBQYHCAj5+vv8/f7/PE/rEAAAABYktHRACIBR1IAAAACXBIWXMAAAJ2AAACdgFUT1zhAAAAB3RJTUUH6QIbBAsJIC5JuAAAAKlJREFUeNrt1ksOgCAMRVEQfyr6/ncrLsBocBJDk3Pezu5PmgwhkCRJkiRJkiRJ0u91MgMg7QAwGABGA8BsAFgMAGsXgL3b725YODELnTh3YBYu9O/CqytafxZCCH0ARrpqzpDG1gAFzwAFrwANzwANrwAdzwAdrwAdz0A/DkI8AZ4PGR+A10PGG9DwkPECdDxk3AGdDxlXQPdDxhnQ/5Cpg6HlIRPD63kBZx8bWR7UzYsAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjUtMDItMjdUMDQ6MTE6MDgrMDA6MDCOHFuuAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDI1LTAyLTI3VDA0OjExOjA4KzAwOjAw/0HjEgAAACh0RVh0ZGF0ZTp0aW1lc3RhbXAAMjAyNS0wMi0yN1QwNDoxMTowOSswMDowMKo11+MAAAAASUVORK5CYII= diff --git a/frontend/assets/adaptive-icon/background.png b/frontend/assets/adaptive-icon/background.png new file mode 100644 index 00000000..0a4332a6 Binary files /dev/null and b/frontend/assets/adaptive-icon/background.png differ diff --git a/frontend/assets/adaptive-icon/foreground.png b/frontend/assets/adaptive-icon/foreground.png new file mode 100644 index 00000000..b56ddd29 Binary files /dev/null and b/frontend/assets/adaptive-icon/foreground.png differ diff --git a/frontend/assets/favicon.png b/frontend/assets/favicon.png new file mode 100644 index 00000000..ffd2b4ae Binary files /dev/null and b/frontend/assets/favicon.png differ diff --git a/frontend/assets/google-logo.png b/frontend/assets/google-logo.png new file mode 100644 index 00000000..e7d106c4 Binary files /dev/null and b/frontend/assets/google-logo.png differ diff --git a/frontend/assets/icon.png b/frontend/assets/icon.png new file mode 100644 index 00000000..adc6e21d Binary files /dev/null and b/frontend/assets/icon.png differ diff --git a/frontend/assets/pwa/chrome-icon/chrome-icon-144.png b/frontend/assets/pwa/chrome-icon/chrome-icon-144.png new file mode 100644 index 00000000..8afeeeaa Binary files /dev/null and b/frontend/assets/pwa/chrome-icon/chrome-icon-144.png differ diff --git a/frontend/assets/pwa/chrome-icon/chrome-icon-192.png b/frontend/assets/pwa/chrome-icon/chrome-icon-192.png new file mode 100644 index 00000000..77bb31f9 Binary files /dev/null and b/frontend/assets/pwa/chrome-icon/chrome-icon-192.png differ diff --git a/frontend/assets/pwa/chrome-icon/chrome-icon-512.png b/frontend/assets/pwa/chrome-icon/chrome-icon-512.png new file mode 100644 index 00000000..3ef334e5 Binary files /dev/null and b/frontend/assets/pwa/chrome-icon/chrome-icon-512.png differ diff --git a/frontend/assets/splash.png b/frontend/assets/splash.png new file mode 100644 index 00000000..26c97a31 Binary files /dev/null and b/frontend/assets/splash.png differ diff --git a/frontend/babel.config.js b/frontend/babel.config.js new file mode 100644 index 00000000..98110070 --- /dev/null +++ b/frontend/babel.config.js @@ -0,0 +1,8 @@ + // babel.config.js + module.exports = function(api) { + api.cache(true); + return { + presets: ['babel-preset-expo'], + plugins: ['module:react-native-dotenv'] // Add this line + }; + }; diff --git a/frontend/contexts/AuthContext.tsx b/frontend/contexts/AuthContext.tsx index 7579b798..def4739b 100644 --- a/frontend/contexts/AuthContext.tsx +++ b/frontend/contexts/AuthContext.tsx @@ -1,10 +1,17 @@ -import axios from 'axios'; -import * as SecureStore from 'expo-secure-store'; -import React, { createContext, useContext, useEffect, useState } from 'react'; +import React, { createContext, useContext, useEffect, useState, useCallback } from "react"; +import axios, { AxiosError } from "axios"; +import * as SecureStore from "expo-secure-store"; +import * as WebBrowser from "expo-web-browser"; +import * as Google from "expo-auth-session/providers/google"; +import { GoogleAuthProvider, signInWithCredential } from "firebase/auth"; +import { auth } from "../firebase/firebaseConfig"; -const API_URL = 'https://splitwiser-production.up.railway.app'; // Replace with your actual backend URL +WebBrowser.maybeCompleteAuthSession(); -// Define the shape of our authentication context +// API base URL +const API_URL = process.env.EXPO_PUBLIC_API_URL; + +// Auth context type for better type safety type AuthContextType = { isAuthenticated: boolean; user: any; @@ -12,165 +19,208 @@ type AuthContextType = { refreshToken: string | null; login: (credentials: { email: string; password: string }) => Promise; signup: (userData: { email: string; password: string; name: string }) => Promise; - logout: () => void; + logout: () => Promise; + googleLogin: () => Promise; loading: boolean; }; -// Create the context +// Context creation const AuthContext = createContext(undefined); -// Custom hook to use the auth context +// Hook to access auth context export const useAuth = () => { const context = useContext(AuthContext); - if (!context) { - throw new Error('useAuth must be used within an AuthProvider'); - } + if (!context) throw new Error("useAuth must be used within an AuthProvider"); return context; }; -// Authentication provider component export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { - const [isAuthenticated, setIsAuthenticated] = useState(false); + // Authentication state + const [isAuthenticated, setIsAuthenticated] = useState(false); const [accessToken, setAccessToken] = useState(null); const [refreshToken, setRefreshToken] = useState(null); const [user, setUser] = useState(null); - const [loading, setLoading] = useState(true); + const [loading, setLoading] = useState(true); + + // Google login setup + const [request, response, promptAsync] = Google.useAuthRequest({ + webClientId: process.env.EXPO_PUBLIC_GOOGLE_WEB_CLIENT_ID, + androidClientId: process.env.EXPO_PUBLIC_GOOGLE_ANDROID_CLIENT_ID, + }); + + /** + * Google Sign-In handler + * Triggered automatically when Google sign-in response is received + */ + useEffect(() => { + if (response?.type !== "success") return; + + const handleGoogleLogin = async () => { + try { + const { authentication } = response; + if (!authentication?.accessToken) return; + + // Firebase credential from Google token + const credential = GoogleAuthProvider.credential(null, authentication.accessToken); + const userCredential = await signInWithCredential(auth, credential); + + // Get Firebase ID token & send to backend + const idToken = await userCredential.user.getIdToken(true); + await axios.post(`${API_URL}/auth/login/google`, { id_token: idToken }); - // Set up axios with interceptors for authentication + // Update state + setUser(userCredential.user); + setIsAuthenticated(true); + } catch (error) { + const err = error as AxiosError; + console.error("Google Sign-In Error:", err); + alert(err.response?.data || err.message || "Google sign-in failed. Please try again."); + } + }; + + handleGoogleLogin(); + }, [response]); + + /** + * Axios interceptors: + * 1) Adds Authorization header + * 2) Handles token refresh on 401 + * Cleanup on unmount or token changes + */ useEffect(() => { - // Configure axios defaults axios.defaults.baseURL = API_URL; - // Set up request interceptor - axios.interceptors.request.use( - (config) => { - if (accessToken) { - config.headers.Authorization = `Bearer ${accessToken}`; - } - return config; - }, - (error) => Promise.reject(error) - ); + // Attach token to every request + const reqInterceptor = axios.interceptors.request.use((config) => { + if (accessToken) config.headers.Authorization = `Bearer ${accessToken}`; + return config; + }); - // Set up response interceptor for token refresh - axios.interceptors.response.use( - (response) => response, + // Handle expired tokens and retry requests + const resInterceptor = axios.interceptors.response.use( + (res) => res, async (error) => { const originalRequest = error.config; - - // If 401 response and not already retrying if (error.response?.status === 401 && !originalRequest._retry && refreshToken) { originalRequest._retry = true; - try { - // Call refresh token endpoint - const response = await axios.post('/auth/refresh', { refresh_token: refreshToken }); - - // Update tokens - setAccessToken(response.data.access_token); - if (response.data.refresh_token) { - setRefreshToken(response.data.refresh_token); - await SecureStore.setItemAsync('refreshToken', response.data.refresh_token); + const { data } = await axios.post("/auth/refresh", { refresh_token: refreshToken }); + setAccessToken(data.access_token); + if (data.refresh_token) { + setRefreshToken(data.refresh_token); + await SecureStore.setItemAsync("refreshToken", data.refresh_token); } - - // Retry the original request with new token - originalRequest.headers.Authorization = `Bearer ${response.data.access_token}`; + originalRequest.headers.Authorization = `Bearer ${data.access_token}`; return axios(originalRequest); - } catch (refreshError) { - // If refresh fails, log out the user - logout(); - return Promise.reject(refreshError); + } catch (err) { + await logout(); + return Promise.reject(err); } } - return Promise.reject(error); } ); - // Check for existing session on app load + // Cleanup interceptors + return () => { + axios.interceptors.request.eject(reqInterceptor); + axios.interceptors.response.eject(resInterceptor); + }; + }, [accessToken, refreshToken]); + + /** + * Load tokens on app start + */ + useEffect(() => { const loadTokens = async () => { try { - // Get refresh token from secure storage - const storedRefreshToken = await SecureStore.getItemAsync('refreshToken'); - + const storedRefreshToken = await SecureStore.getItemAsync("refreshToken"); if (storedRefreshToken) { - // Try to get a new access token - const response = await axios.post('/auth/refresh', { refresh_token: storedRefreshToken }); - - // Set authentication state - setAccessToken(response.data.access_token); - setRefreshToken(response.data.refresh_token || storedRefreshToken); - setUser(response.data.user); + const { data } = await axios.post(`${API_URL}/auth/refresh`, { refresh_token: storedRefreshToken }); + setAccessToken(data.access_token); + setRefreshToken(data.refresh_token || storedRefreshToken); + setUser(data.user); setIsAuthenticated(true); - - // Update storage if we got a new refresh token - if (response.data.refresh_token) { - await SecureStore.setItemAsync('refreshToken', response.data.refresh_token); + + // Store updated refresh token if returned + if (data.refresh_token) { + await SecureStore.setItemAsync("refreshToken", data.refresh_token); } } - } catch (error) { - // Clear any invalid tokens - await SecureStore.deleteItemAsync('refreshToken'); + } catch { + await SecureStore.deleteItemAsync("refreshToken"); } finally { setLoading(false); } }; loadTokens(); - }, []); // Email/Password login - const login = async (credentials: { email: string; password: string }) => { + }, []); + + /** + * Email/Password login + */ + const login = useCallback(async (credentials: { email: string; password: string }) => { setLoading(true); try { - const response = await axios.post('/auth/login/email', credentials); - - // Store tokens and user data - setAccessToken(response.data.access_token); - setRefreshToken(response.data.refresh_token); - setUser(response.data.user); + const { data } = await axios.post("/auth/login/email", credentials); + setAccessToken(data.access_token); + setRefreshToken(data.refresh_token); + setUser(data.user); setIsAuthenticated(true); - - // Save refresh token securely - await SecureStore.setItemAsync('refreshToken', response.data.refresh_token); - } catch (error) { - // Re-throw the error so the UI can handle it - throw error; + await SecureStore.setItemAsync("refreshToken", data.refresh_token); } finally { setLoading(false); } - }; // Email/Password signup - const signup = async (userData: { email: string; password: string; name: string }) => { + }, []); + + /** + * Email/Password signup + */ + const signup = useCallback(async (userData: { email: string; password: string; name: string }) => { setLoading(true); try { - const response = await axios.post('/auth/signup/email', userData); - - // Store tokens and user data - setAccessToken(response.data.access_token); - setRefreshToken(response.data.refresh_token); - setUser(response.data.user); + const { data } = await axios.post("/auth/signup/email", userData); + setAccessToken(data.access_token); + setRefreshToken(data.refresh_token); + setUser(data.user); setIsAuthenticated(true); - - // Save refresh token securely - await SecureStore.setItemAsync('refreshToken', response.data.refresh_token); - } catch (error) { - // Re-throw the error so the UI can handle it - throw error; + await SecureStore.setItemAsync("refreshToken", data.refresh_token); } finally { setLoading(false); } - }; + }, []); + + /** + * Trigger Google OAuth + */ + const googleLogin = useCallback(async () => { + await promptAsync(); + }, [promptAsync]); - // Logout - const logout = async () => { - // Clear auth state + /** + * Logout user: + * 1) Clears tokens & state + * 2) Signs out of Google if logged in via Google + */ + const logout = useCallback(async () => { setAccessToken(null); setRefreshToken(null); setUser(null); setIsAuthenticated(false); - - // Clear secure storage - await SecureStore.deleteItemAsync('refreshToken'); - }; - // Provide auth context to children + + // Google logout if provider is google.com + if (user?.providerData?.some((p: any) => p.providerId === "google.com")) { + try { + await auth.signOut(); + } catch (e) { + console.log("Error signing out from Google:", e); + } + } + await SecureStore.deleteItemAsync("refreshToken"); + }, [user]); + + // Context provider return ( = ({ children login, signup, logout, + googleLogin, loading, }} > diff --git a/frontend/firebase/firebaseConfig.ts b/frontend/firebase/firebaseConfig.ts new file mode 100644 index 00000000..2f2dab38 --- /dev/null +++ b/frontend/firebase/firebaseConfig.ts @@ -0,0 +1,30 @@ +// firebaseConfig.ts or firebaseConfig.js +import { initializeApp } from 'firebase/app'; +import { getAuth } from 'firebase/auth'; // Import getAuth for authentication +// Your web app's Firebase configuration +// For Firebase JS SDK v7.20.0 and later, measurementId is optional +const firebaseConfig = { + apiKey: process.env.EXPO_PUBLIC_FIREBASE_API_KEY, + authDomain: process.env.EXPO_PUBLIC_FIREBASE_AUTH_DOMAIN, + projectId: process.env.EXPO_PUBLIC_FIREBASE_PROJECT_ID, + storageBucket: process.env.EXPO_PUBLIC_FIREBASE_STORAGE_BUCKET, + messagingSenderId: process.env.EXPO_PUBLIC_FIREBASE_MESSAGING_SENDER_ID, + appId: process.env.EXPO_PUBLIC_FIREBASE_APP_ID, + // measurementId: process.env.EXPO_PUBLIC_FIREBASE_MEASUREMENT_ID // Only if you enabled Google Analytics +}; +console.log('Env check:', process.env); + +// Validate required configuration +const requiredKeys = ['apiKey', 'authDomain', 'projectId', 'storageBucket', 'messagingSenderId', 'appId'] as const; +const missingKeys = requiredKeys.filter(key => !firebaseConfig[key as keyof typeof firebaseConfig]); +console.log('Firebase configuration:', firebaseConfig); +// Check if any required keys are missing +console.log('Checking Firebase configuration for missing keys...',missingKeys); +if (missingKeys.length > 0){ + throw new Error(`Missing Firebase configuration keys: ${missingKeys.join(', ')}`); +} +// Initialize Firebase +const app = initializeApp(firebaseConfig); + +// Initialize Firebase Authentication and get a reference to the service +export const auth = getAuth(app); diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 564b0d8b..c6de8b40 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -10,13 +10,21 @@ "dependencies": { "@expo/webpack-config": "^19.0.0", "@react-native-async-storage/async-storage": "1.18.2", + "@react-native-google-signin/google-signin": "^11.0.0", "@react-navigation/native": "^6.1.9", "@react-navigation/native-stack": "^6.9.17", "axios": "^1.6.2", + "babel-plugin-inline-dotenv": "^1.7.0", "expo": "~49.0.15", + "expo-auth-session": "~5.0.2", + "expo-build-properties": "~0.8.3", "expo-crypto": "~12.4.1", + "expo-dev-client": "~2.4.13", "expo-secure-store": "~12.3.1", "expo-status-bar": "~1.6.0", + "expo-web-browser": "~12.3.2", + "firebase": "^12.0.0", + "os": "^0.1.2", "react": "18.2.0", "react-dom": "18.2.0", "react-native": "0.72.10", @@ -26,7 +34,9 @@ }, "devDependencies": { "@babel/core": "^7.20.0", + "@expo/ngrok": "^4.1.0", "@types/react": "~18.2.14", + "react-native-dotenv": "^3.4.11", "typescript": "^5.1.3" } }, @@ -2514,6 +2524,197 @@ "write-file-atomic": "^2.3.0" } }, + "node_modules/@expo/ngrok": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@expo/ngrok/-/ngrok-4.1.0.tgz", + "integrity": "sha512-PrtWxBt/SnOF1jZkf7oWznhEPFrmYKKeJPLVRKnEBd/y4eUYfoiNIXOzflIzhdrMubjWVI+pFuPJ6nkjVL95/Q==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@expo/ngrok-bin": "2.3.40", + "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.40", + "resolved": "https://registry.npmjs.org/@expo/ngrok-bin/-/ngrok-bin-2.3.40.tgz", + "integrity": "sha512-40GK1CY1QLPDHSQS7Fd36CJeZgMwtbeezkgp4tzfExVRVtWw30jOBCsM7TBB9IqEmmX7C/XwG47scMQHCnMw8A==", + "dev": true, + "bin": { + "ngrok": "bin/ngrok.js" + }, + "optionalDependencies": { + "@expo/ngrok-bin-darwin-arm64": "2.3.40", + "@expo/ngrok-bin-darwin-x64": "2.3.40", + "@expo/ngrok-bin-freebsd-ia32": "2.3.40", + "@expo/ngrok-bin-freebsd-x64": "2.3.40", + "@expo/ngrok-bin-linux-arm": "2.3.40", + "@expo/ngrok-bin-linux-arm64": "2.3.40", + "@expo/ngrok-bin-linux-ia32": "2.3.40", + "@expo/ngrok-bin-linux-x64": "2.3.40", + "@expo/ngrok-bin-sunos-x64": "2.3.40", + "@expo/ngrok-bin-win32-ia32": "2.3.40", + "@expo/ngrok-bin-win32-x64": "2.3.40" + } + }, + "node_modules/@expo/ngrok-bin-darwin-arm64": { + "version": "2.3.40", + "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-darwin-arm64/-/ngrok-bin-darwin-arm64-2.3.40.tgz", + "integrity": "sha512-Zij81v/bIsVBvgXgYS71xbi/3lqKfVEfr7rId8BsHO3Ec1nQcp/I+729W3KX9PUHzWlXCLxOKZ3uF4jL/TcNbg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@expo/ngrok-bin-darwin-x64": { + "version": "2.3.40", + "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-darwin-x64/-/ngrok-bin-darwin-x64-2.3.40.tgz", + "integrity": "sha512-nqGLfxIjZBoT79VDk5mqaHQKCWkunSi486zGLeB8Ye8Qar1yo4STFwks+DqTbnGD5ItArQz2LzKRVE4YXuJFuw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@expo/ngrok-bin-freebsd-ia32": { + "version": "2.3.40", + "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-freebsd-ia32/-/ngrok-bin-freebsd-ia32-2.3.40.tgz", + "integrity": "sha512-Ji3jZaOuIZO+ege23kZZAAEPUYkF+6mCpghb16b28Is1QHOSl2L4foDnAcWyzSEiBihMicxWltaQyaaxA0fdgw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@expo/ngrok-bin-freebsd-x64": { + "version": "2.3.40", + "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-freebsd-x64/-/ngrok-bin-freebsd-x64-2.3.40.tgz", + "integrity": "sha512-mVnzKGQmOyXimZx6udoiyo3ZTYLZnPShlTySaDP0tqQ0vYz4ZscgvaYpMmDSPrsP/YG2owmKgzmOE2V+ycD8qA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@expo/ngrok-bin-linux-arm": { + "version": "2.3.40", + "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-linux-arm/-/ngrok-bin-linux-arm-2.3.40.tgz", + "integrity": "sha512-Je1QBd7x0hbZa4T3gZbVgD0cSzstpJ7Mu0+dM2lOB+vm3bd603yHtD0RlLdqARJFhPTE1M2zLd68gCEeZ5fRgQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@expo/ngrok-bin-linux-arm64": { + "version": "2.3.40", + "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-linux-arm64/-/ngrok-bin-linux-arm64-2.3.40.tgz", + "integrity": "sha512-S6kbnRqsVXHo/bWNxc0jfq33aQQRsGWjb6e7SvZ2DgXsPFLn27cfK0eHD96uCssARDVhzPsc+VU/B3d8C1DT5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@expo/ngrok-bin-linux-ia32": { + "version": "2.3.40", + "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-linux-ia32/-/ngrok-bin-linux-ia32-2.3.40.tgz", + "integrity": "sha512-gPY5zv5Fu+TkCm5iZolXQbu7e5hc7fTllIKn/zJQxxZs/WCvSxyB5ip6vQcHiavu/kjr0HtNciPX/guXvWENkg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@expo/ngrok-bin-linux-x64": { + "version": "2.3.40", + "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-linux-x64/-/ngrok-bin-linux-x64-2.3.40.tgz", + "integrity": "sha512-yOuwpOmMe6RGnk9ninlM7Zg1EiF81ptFOcFmT61PDOA4gK8/ttZKTMkDQiq0DZdcXUyE0HCr83EglJZTnHIzPA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@expo/ngrok-bin-sunos-x64": { + "version": "2.3.40", + "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-sunos-x64/-/ngrok-bin-sunos-x64-2.3.40.tgz", + "integrity": "sha512-0itEIQ7KsxRbF9nJk6tt0Ey+9TDC5H7krOsy3t7DPx01EvtaiEdMyhmE1XWjBtwr8+BaY9CpEhUWkx4iCcE4cw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ] + }, + "node_modules/@expo/ngrok-bin-win32-ia32": { + "version": "2.3.40", + "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-win32-ia32/-/ngrok-bin-win32-ia32-2.3.40.tgz", + "integrity": "sha512-RAunwOAskfU0R5mYlxxB+bihLJ4nLRx5/x+q5nIq1muYmaqLvGtkQQHZKzgHJANJ7ZIbzfJY57IN2UICpibgIQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@expo/ngrok-bin-win32-x64": { + "version": "2.3.40", + "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-win32-x64/-/ngrok-bin-win32-x64-2.3.40.tgz", + "integrity": "sha512-a8xtUxX/Ftp2ho+/+VR5GCg0ttP9MNzYj58TVjfiKMkl4mVrbFVIzEinRzmy7PhiOWxqGQSCOdzEfa6C2G4nEA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@expo/ngrok/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, "node_modules/@expo/osascript": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/@expo/osascript/-/osascript-2.2.4.tgz", @@ -2889,6 +3090,614 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/@firebase/ai": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@firebase/ai/-/ai-2.0.0.tgz", + "integrity": "sha512-N/aSHjqOpU+KkYU3piMkbcuxzvqsOvxflLUXBAkYAPAz8wjE2Ye3BQDgKHEYuhMmEWqj6LFgEBUN8wwc6dfMTw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/component": "0.7.0", + "@firebase/logger": "0.5.0", + "@firebase/util": "1.13.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/analytics": { + "version": "0.10.18", + "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.18.tgz", + "integrity": "sha512-iN7IgLvM06iFk8BeFoWqvVpRFW3Z70f+Qe2PfCJ7vPIgLPjHXDE774DhCT5Y2/ZU/ZbXPDPD60x/XPWEoZLNdg==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.0", + "@firebase/installations": "0.6.19", + "@firebase/logger": "0.5.0", + "@firebase/util": "1.13.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/analytics-compat": { + "version": "0.2.24", + "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.24.tgz", + "integrity": "sha512-jE+kJnPG86XSqGQGhXXYt1tpTbCTED8OQJ/PQ90SEw14CuxRxx/H+lFbWA1rlFtFSsTCptAJtgyRBwr/f00vsw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/analytics": "0.10.18", + "@firebase/analytics-types": "0.8.3", + "@firebase/component": "0.7.0", + "@firebase/util": "1.13.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/analytics-types": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.3.tgz", + "integrity": "sha512-VrIp/d8iq2g501qO46uGz3hjbDb8xzYMrbu8Tp0ovzIzrvJZ2fvmj649gTjge/b7cCCcjT0H37g1gVtlNhnkbg==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/app": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.14.0.tgz", + "integrity": "sha512-APIAeKvRNFWKJLjIL8wLDjh7u8g6ZjaeVmItyqSjCdEkJj14UuVlus74D8ofsOMWh45HEwxwkd96GYbi+CImEg==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.0", + "@firebase/logger": "0.5.0", + "@firebase/util": "1.13.0", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@firebase/app-check": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.11.0.tgz", + "integrity": "sha512-XAvALQayUMBJo58U/rxW02IhsesaxxfWVmVkauZvGEz3vOAjMEQnzFlyblqkc2iAaO82uJ2ZVyZv9XzPfxjJ6w==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.0", + "@firebase/logger": "0.5.0", + "@firebase/util": "1.13.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/app-check-compat": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.4.0.tgz", + "integrity": "sha512-UfK2Q8RJNjYM/8MFORltZRG9lJj11k0nW84rrffiKvcJxLf1jf6IEjCIkCamykHE73C6BwqhVfhIBs69GXQV0g==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app-check": "0.11.0", + "@firebase/app-check-types": "0.5.3", + "@firebase/component": "0.7.0", + "@firebase/logger": "0.5.0", + "@firebase/util": "1.13.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/app-check-interop-types": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.3.tgz", + "integrity": "sha512-gAlxfPLT2j8bTI/qfe3ahl2I2YcBQ8cFIBdhAQA4I2f3TndcO+22YizyGYuttLHPQEpWkhmpFW60VCFEPg4g5A==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/app-check-types": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.3.tgz", + "integrity": "sha512-hyl5rKSj0QmwPdsAxrI5x1otDlByQ7bvNvVt8G/XPO2CSwE++rmSVf3VEhaeOR4J8ZFaF0Z0NDSmLejPweZ3ng==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/app-compat": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.5.0.tgz", + "integrity": "sha512-nUnNpOeRj0KZzVzHsyuyrmZKKHfykZ8mn40FtG28DeSTWeM5b/2P242Va4bmQpJsy5y32vfv50+jvdckrpzy7Q==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app": "0.14.0", + "@firebase/component": "0.7.0", + "@firebase/logger": "0.5.0", + "@firebase/util": "1.13.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@firebase/app-types": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz", + "integrity": "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/auth": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.11.0.tgz", + "integrity": "sha512-5j7+ua93X+IRcJ1oMDTClTo85l7Xe40WSkoJ+shzPrX7OISlVWLdE1mKC57PSD+/LfAbdhJmvKixINBw2ESK6w==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.0", + "@firebase/logger": "0.5.0", + "@firebase/util": "1.13.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@react-native-async-storage/async-storage": "^1.18.1" + }, + "peerDependenciesMeta": { + "@react-native-async-storage/async-storage": { + "optional": true + } + } + }, + "node_modules/@firebase/auth-compat": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.6.0.tgz", + "integrity": "sha512-J0lGSxXlG/lYVi45wbpPhcWiWUMXevY4fvLZsN1GHh+po7TZVng+figdHBVhFheaiipU8HZyc7ljw1jNojM2nw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/auth": "1.11.0", + "@firebase/auth-types": "0.13.0", + "@firebase/component": "0.7.0", + "@firebase/util": "1.13.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/auth-interop-types": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.4.tgz", + "integrity": "sha512-JPgcXKCuO+CWqGDnigBtvo09HeBs5u/Ktc2GaFj2m01hLarbxthLNm7Fk8iOP1aqAtXV+fnnGj7U28xmk7IwVA==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/auth-types": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.13.0.tgz", + "integrity": "sha512-S/PuIjni0AQRLF+l9ck0YpsMOdE8GO2KU6ubmBB7P+7TJUCQDa3R1dlgYm9UzGbbePMZsp0xzB93f2b/CgxMOg==", + "license": "Apache-2.0", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/component": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.7.0.tgz", + "integrity": "sha512-wR9En2A+WESUHexjmRHkqtaVH94WLNKt6rmeqZhSLBybg4Wyf0Umk04SZsS6sBq4102ZsDBFwoqMqJYj2IoDSg==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/util": "1.13.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@firebase/data-connect": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@firebase/data-connect/-/data-connect-0.3.11.tgz", + "integrity": "sha512-G258eLzAD6im9Bsw+Qm1Z+P4x0PGNQ45yeUuuqe5M9B1rn0RJvvsQCRHXgE52Z+n9+WX1OJd/crcuunvOGc7Vw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.7.0", + "@firebase/logger": "0.5.0", + "@firebase/util": "1.13.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/database": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.1.0.tgz", + "integrity": "sha512-gM6MJFae3pTyNLoc9VcJNuaUDej0ctdjn3cVtILo3D5lpp0dmUHHLFN/pUKe7ImyeB1KAvRlEYxvIHNF04Filg==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.7.0", + "@firebase/logger": "0.5.0", + "@firebase/util": "1.13.0", + "faye-websocket": "0.11.4", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@firebase/database-compat": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.1.0.tgz", + "integrity": "sha512-8nYc43RqxScsePVd1qe1xxvWNf0OBnbwHxmXJ7MHSuuTVYFO3eLyLW3PiCKJ9fHnmIz4p4LbieXwz+qtr9PZDg==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.0", + "@firebase/database": "1.1.0", + "@firebase/database-types": "1.0.16", + "@firebase/logger": "0.5.0", + "@firebase/util": "1.13.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@firebase/database-types": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.16.tgz", + "integrity": "sha512-xkQLQfU5De7+SPhEGAXFBnDryUWhhlFXelEg2YeZOQMCdoe7dL64DDAd77SQsR+6uoXIZY5MB4y/inCs4GTfcw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app-types": "0.9.3", + "@firebase/util": "1.13.0" + } + }, + "node_modules/@firebase/firestore": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.9.0.tgz", + "integrity": "sha512-5zl0+/h1GvlCSLt06RMwqFsd7uqRtnNZt4sW99k2rKRd6k/ECObIWlEnvthm2cuOSnUmwZknFqtmd1qyYSLUuQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.0", + "@firebase/logger": "0.5.0", + "@firebase/util": "1.13.0", + "@firebase/webchannel-wrapper": "1.0.4", + "@grpc/grpc-js": "~1.9.0", + "@grpc/proto-loader": "^0.7.8", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/firestore-compat": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.4.0.tgz", + "integrity": "sha512-4O7v4VFeSEwAZtLjsaj33YrMHMRjplOIYC2CiYsF6o/MboOhrhe01VrTt8iY9Y5EwjRHuRz4pS6jMBT8LfQYJA==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.0", + "@firebase/firestore": "4.9.0", + "@firebase/firestore-types": "3.0.3", + "@firebase/util": "1.13.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/firestore-types": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-3.0.3.tgz", + "integrity": "sha512-hD2jGdiWRxB/eZWF89xcK9gF8wvENDJkzpVFb4aGkzfEaKxVRD1kjz1t1Wj8VZEp2LCB53Yx1zD8mrhQu87R6Q==", + "license": "Apache-2.0", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/functions": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.13.0.tgz", + "integrity": "sha512-2/LH5xIbD8aaLOWSFHAwwAybgSzHIM0dB5oVOL0zZnxFG1LctX2bc1NIAaPk1T+Zo9aVkLKUlB5fTXTkVUQprQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.7.0", + "@firebase/messaging-interop-types": "0.2.3", + "@firebase/util": "1.13.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/functions-compat": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.4.0.tgz", + "integrity": "sha512-VPgtvoGFywWbQqtvgJnVWIDFSHV1WE6Hmyi5EGI+P+56EskiGkmnw6lEqc/MEUfGpPGdvmc4I9XMU81uj766/g==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.0", + "@firebase/functions": "0.13.0", + "@firebase/functions-types": "0.6.3", + "@firebase/util": "1.13.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/functions-types": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.3.tgz", + "integrity": "sha512-EZoDKQLUHFKNx6VLipQwrSMh01A1SaL3Wg6Hpi//x6/fJ6Ee4hrAeswK99I5Ht8roiniKHw4iO0B1Oxj5I4plg==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/installations": { + "version": "0.6.19", + "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.19.tgz", + "integrity": "sha512-nGDmiwKLI1lerhwfwSHvMR9RZuIH5/8E3kgUWnVRqqL7kGVSktjLTWEMva7oh5yxQ3zXfIlIwJwMcaM5bK5j8Q==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.0", + "@firebase/util": "1.13.0", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/installations-compat": { + "version": "0.2.19", + "resolved": "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.19.tgz", + "integrity": "sha512-khfzIY3EI5LePePo7vT19/VEIH1E3iYsHknI/6ek9T8QCozAZshWT9CjlwOzZrKvTHMeNcbpo/VSOSIWDSjWdQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.0", + "@firebase/installations": "0.6.19", + "@firebase/installations-types": "0.5.3", + "@firebase/util": "1.13.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/installations-types": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.3.tgz", + "integrity": "sha512-2FJI7gkLqIE0iYsNQ1P751lO3hER+Umykel+TkLwHj6plzWVxqvfclPUZhcKFVQObqloEBTmpi2Ozn7EkCABAA==", + "license": "Apache-2.0", + "peerDependencies": { + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/logger": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.5.0.tgz", + "integrity": "sha512-cGskaAvkrnh42b3BA3doDWeBmuHFO/Mx5A83rbRDYakPjO9bJtRL3dX7javzc2Rr/JHZf4HlterTW2lUkfeN4g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@firebase/messaging": { + "version": "0.12.23", + "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.23.tgz", + "integrity": "sha512-cfuzv47XxqW4HH/OcR5rM+AlQd1xL/VhuaeW/wzMW1LFrsFcTn0GND/hak1vkQc2th8UisBcrkVcQAnOnKwYxg==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.0", + "@firebase/installations": "0.6.19", + "@firebase/messaging-interop-types": "0.2.3", + "@firebase/util": "1.13.0", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/messaging-compat": { + "version": "0.2.23", + "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.23.tgz", + "integrity": "sha512-SN857v/kBUvlQ9X/UjAqBoQ2FEaL1ZozpnmL1ByTe57iXkmnVVFm9KqAsTfmf+OEwWI4kJJe9NObtN/w22lUgg==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.0", + "@firebase/messaging": "0.12.23", + "@firebase/util": "1.13.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/messaging-interop-types": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.3.tgz", + "integrity": "sha512-xfzFaJpzcmtDjycpDeCUj0Ge10ATFi/VHVIvEEjDNc3hodVBQADZ7BWQU7CuFpjSHE+eLuBI13z5F/9xOoGX8Q==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/performance": { + "version": "0.7.8", + "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.7.8.tgz", + "integrity": "sha512-k6xfNM/CdTl4RaV4gT/lH53NU+wP33JiN0pUeNBzGVNvfXZ3HbCkoISE3M/XaiOwHgded1l6XfLHa4zHgm0Wyg==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.0", + "@firebase/installations": "0.6.19", + "@firebase/logger": "0.5.0", + "@firebase/util": "1.13.0", + "tslib": "^2.1.0", + "web-vitals": "^4.2.4" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/performance-compat": { + "version": "0.2.21", + "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.21.tgz", + "integrity": "sha512-OQfYRsIQiEf9ez1SOMLb5TRevBHNIyA2x1GI1H10lZ432W96AK5r4LTM+SNApg84dxOuHt6RWSQWY7TPWffKXg==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.0", + "@firebase/logger": "0.5.0", + "@firebase/performance": "0.7.8", + "@firebase/performance-types": "0.2.3", + "@firebase/util": "1.13.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/performance-types": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.3.tgz", + "integrity": "sha512-IgkyTz6QZVPAq8GSkLYJvwSLr3LS9+V6vNPQr0x4YozZJiLF5jYixj0amDtATf1X0EtYHqoPO48a9ija8GocxQ==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/remote-config": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.6.6.tgz", + "integrity": "sha512-Yelp5xd8hM4NO1G1SuWrIk4h5K42mNwC98eWZ9YLVu6Z0S6hFk1mxotAdCRmH2luH8FASlYgLLq6OQLZ4nbnCA==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.0", + "@firebase/installations": "0.6.19", + "@firebase/logger": "0.5.0", + "@firebase/util": "1.13.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/remote-config-compat": { + "version": "0.2.19", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.19.tgz", + "integrity": "sha512-y7PZAb0l5+5oIgLJr88TNSelxuASGlXyAKj+3pUc4fDuRIdPNBoONMHaIUa9rlffBR5dErmaD2wUBJ7Z1a513Q==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.0", + "@firebase/logger": "0.5.0", + "@firebase/remote-config": "0.6.6", + "@firebase/remote-config-types": "0.4.0", + "@firebase/util": "1.13.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/remote-config-types": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.4.0.tgz", + "integrity": "sha512-7p3mRE/ldCNYt8fmWMQ/MSGRmXYlJ15Rvs9Rk17t8p0WwZDbeK7eRmoI1tvCPaDzn9Oqh+yD6Lw+sGLsLg4kKg==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/storage": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.14.0.tgz", + "integrity": "sha512-xWWbb15o6/pWEw8H01UQ1dC5U3rf8QTAzOChYyCpafV6Xki7KVp3Yaw2nSklUwHEziSWE9KoZJS7iYeyqWnYFA==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.0", + "@firebase/util": "1.13.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/storage-compat": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.4.0.tgz", + "integrity": "sha512-vDzhgGczr1OfcOy285YAPur5pWDEvD67w4thyeCUh6Ys0izN9fNYtA1MJERmNBfqjqu0lg0FM5GLbw0Il21M+g==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.0", + "@firebase/storage": "0.14.0", + "@firebase/storage-types": "0.8.3", + "@firebase/util": "1.13.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/storage-types": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.3.tgz", + "integrity": "sha512-+Muk7g9uwngTpd8xn9OdF/D48uiQ7I1Fae7ULsWPuKoCH3HU7bfFPhxtJYzyhjdniowhuDpQcfPmuNRAqZEfvg==", + "license": "Apache-2.0", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/util": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.13.0.tgz", + "integrity": "sha512-0AZUyYUfpMNcztR5l09izHwXkZpghLgCUaAGjtMwXnCg3bj4ml5VgiwqOMOxJ+Nw4qN/zJAaOQBcJ7KGkWStqQ==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@firebase/webchannel-wrapper": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.4.tgz", + "integrity": "sha512-6m8+P+dE/RPl4OPzjTxcTbQ0rGeRyeTvAi9KwIffBVCiAMKrfXfLZaqD1F+m8t4B5/Q5aHsMozOgirkH1F5oMQ==", + "license": "Apache-2.0" + }, "node_modules/@gar/promisify": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", @@ -2902,6 +3711,37 @@ "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, + "node_modules/@grpc/grpc-js": { + "version": "1.9.15", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.15.tgz", + "integrity": "sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/proto-loader": "^0.7.8", + "@types/node": ">=12.12.47" + }, + "engines": { + "node": "^8.13.0 || >=10.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.15", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz", + "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==", + "license": "Apache-2.0", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/@hapi/hoek": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", @@ -3274,6 +4114,70 @@ "node": ">=14" } }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "license": "BSD-3-Clause" + }, "node_modules/@react-native-async-storage/async-storage": { "version": "1.18.2", "resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-1.18.2.tgz", @@ -4616,6 +5520,22 @@ "node": ">= 8" } }, + "node_modules/@react-native-google-signin/google-signin": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@react-native-google-signin/google-signin/-/google-signin-11.0.0.tgz", + "integrity": "sha512-J8BFFGr5HcEWE2gRaVSJ0rQN8ztxYIr2wdWMG4PS1vhQZJqUzmRr6vpu12O1GppItq8ai7AA0rl/ja7hSytSXA==", + "license": "MIT", + "peerDependencies": { + "expo": ">=47.0.0", + "react": "*", + "react-native": "*" + }, + "peerDependenciesMeta": { + "expo": { + "optional": true + } + } + }, "node_modules/@react-native/assets-registry": { "version": "0.72.0", "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.72.0.tgz", @@ -4779,6 +5699,19 @@ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==" }, + "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==", + "dev": true, + "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", @@ -4795,6 +5728,19 @@ "@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==", + "dev": true, + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@trysound/sax": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", @@ -4820,6 +5766,19 @@ "@types/node": "*" } }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", @@ -4907,6 +5866,13 @@ "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==" }, + "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==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/http-errors": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", @@ -4946,6 +5912,16 @@ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", @@ -4998,6 +5974,16 @@ "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/retry": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", @@ -5718,6 +6704,23 @@ "node": ">=8" } }, + "node_modules/babel-plugin-inline-dotenv": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/babel-plugin-inline-dotenv/-/babel-plugin-inline-dotenv-1.7.0.tgz", + "integrity": "sha512-FUrXEwY9R9ZO8l0NFHqXFBzHjjMvdIJlELrzJoE6imDsj6Vh7g9svm7iHPHVYPbHmt6BPB2eCAJkwvG6kr5byg==", + "license": "ISC", + "dependencies": { + "dotenv": "^16.0.0" + }, + "peerDependencies": { + "dotenv-expand": ">= 5.0.0" + }, + "peerDependenciesMeta": { + "dotenv-expand": { + "optional": true + } + } + }, "node_modules/babel-plugin-module-resolver": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/babel-plugin-module-resolver/-/babel-plugin-module-resolver-5.0.2.tgz", @@ -6210,6 +7213,64 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "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==", + "dev": true, + "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==", + "dev": true, + "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/cacheable-request/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cacheable-request/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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -6589,6 +7650,19 @@ "node": ">=6" } }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -6644,6 +7718,18 @@ "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" }, + "node_modules/compare-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-urls/-/compare-urls-2.0.0.tgz", + "integrity": "sha512-eCJcWn2OYFEIqbm70ta7LQowJOOZZqq1a2YbbFCFI1uwSvj+TWMwXVn7vPR1ceFNcAIt5RSTDbwdlX82gYLTkA==", + "license": "MIT", + "dependencies": { + "normalize-url": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/compare-versions": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", @@ -7388,6 +8474,35 @@ "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==", + "dev": true, + "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==", + "dev": true, + "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", @@ -7432,7 +8547,17 @@ "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", "engines": { - "node": ">=0.8" + "node": ">=0.8" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" } }, "node_modules/define-data-property": { @@ -8097,6 +9222,67 @@ "url-parse": "^1.5.9" } }, + "node_modules/expo-auth-session": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/expo-auth-session/-/expo-auth-session-5.0.2.tgz", + "integrity": "sha512-hzuIGATiyZ4ICuzSnCTTQLUA74eHGd1aaPydsSAQEAkMnNT2bMoIYLq1rp971xF+eqWz0lzMVboRYTnxuvEKJg==", + "license": "MIT", + "dependencies": { + "expo-constants": "~14.4.2", + "expo-crypto": "~12.4.0", + "expo-linking": "~5.0.0", + "expo-web-browser": "~12.3.0", + "invariant": "^2.2.4", + "qs": "^6.11.0" + } + }, + "node_modules/expo-build-properties": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/expo-build-properties/-/expo-build-properties-0.8.3.tgz", + "integrity": "sha512-kEDDuAadHqJTkvCGK4fXYHVrePiJO1DjyW95AicmwuGwQvGJydYFbuoauf9ybAU+4UH4arhbce8gHI3ZpIj3Jw==", + "license": "MIT", + "dependencies": { + "ajv": "^8.11.0", + "semver": "^7.5.3" + }, + "peerDependencies": { + "expo": "*" + } + }, + "node_modules/expo-build-properties/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/expo-build-properties/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/expo-build-properties/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-constants": { "version": "14.4.2", "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-14.4.2.tgz", @@ -8120,6 +9306,95 @@ "expo": "*" } }, + "node_modules/expo-dev-client": { + "version": "2.4.13", + "resolved": "https://registry.npmjs.org/expo-dev-client/-/expo-dev-client-2.4.13.tgz", + "integrity": "sha512-EBNJlPntw+DZy7mKxYvpdrmE2GU4YjcEpxSLpwNn2GDwy7e2xXAC2k/25E13BGy3yKPLo1iBXNgB01uleIDdVg==", + "license": "MIT", + "dependencies": { + "expo-dev-launcher": "2.4.15", + "expo-dev-menu": "3.2.4", + "expo-dev-menu-interface": "1.3.0", + "expo-manifests": "~0.7.0", + "expo-updates-interface": "~0.10.0" + }, + "peerDependencies": { + "expo": "*" + } + }, + "node_modules/expo-dev-launcher": { + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/expo-dev-launcher/-/expo-dev-launcher-2.4.15.tgz", + "integrity": "sha512-6oF4NsxlKwuafnyIZvVtMp4OTxRu4Arsw6qJ9s4jDjZuGJtGwgEj9ux3R0YLkDPs8xhsK9Awp0q17RqbQzs1qg==", + "license": "MIT", + "dependencies": { + "expo-dev-menu": "3.2.3", + "resolve-from": "^5.0.0", + "semver": "^7.5.3" + }, + "peerDependencies": { + "expo": "*" + } + }, + "node_modules/expo-dev-launcher/node_modules/expo-dev-menu": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/expo-dev-menu/-/expo-dev-menu-3.2.3.tgz", + "integrity": "sha512-DneF3okTC4AAfAZgaOIylQ/UngSO8SnUT6bRV6nHhJU/jQS1OIP1cZoNW23I100+2yj6x6mobL21PxyiI5VA8g==", + "license": "MIT", + "dependencies": { + "expo-dev-menu-interface": "1.3.0", + "semver": "^7.5.3" + }, + "peerDependencies": { + "expo": "*" + } + }, + "node_modules/expo-dev-launcher/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-dev-menu": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/expo-dev-menu/-/expo-dev-menu-3.2.4.tgz", + "integrity": "sha512-jPvEY4xGTsiVL6A8M6xThNG+tgCHKlDaWqmWHT+wy2EXgFf/7zE0daVYoFms0KJ1XtZc+/DmDRgIPTR86qIGTg==", + "license": "MIT", + "dependencies": { + "expo-dev-menu-interface": "1.3.0", + "semver": "^7.5.3" + }, + "peerDependencies": { + "expo": "*" + } + }, + "node_modules/expo-dev-menu-interface": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expo-dev-menu-interface/-/expo-dev-menu-interface-1.3.0.tgz", + "integrity": "sha512-WtRP7trQ2lizJJTTFXUSGGn1deIeHaYej0sUynvu/uC69VrSP4EeSnYOxbmEO29kuT/MsQBMGu0P/AkMQOqCOg==", + "license": "MIT", + "peerDependencies": { + "expo": "*" + } + }, + "node_modules/expo-dev-menu/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-file-system": { "version": "15.4.5", "resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-15.4.5.tgz", @@ -8142,6 +9417,12 @@ "expo": "*" } }, + "node_modules/expo-json-utils": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/expo-json-utils/-/expo-json-utils-0.7.1.tgz", + "integrity": "sha512-L0lyH8diXQtV0q5BLbFlcoxTqPF5im79xDHPhybB0j36xYdm65hjwRJ4yMrPIN5lR18hj48FUZeONiDHRyEvIg==", + "license": "MIT" + }, "node_modules/expo-keep-awake": { "version": "12.3.0", "resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-12.3.0.tgz", @@ -8150,6 +9431,28 @@ "expo": "*" } }, + "node_modules/expo-linking": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/expo-linking/-/expo-linking-5.0.2.tgz", + "integrity": "sha512-SPQus0+tYGx9c69Uw4wmdo3rkKX8vRT1vyJz/mvkpSlZN986s0NmP/V0M5vDv5Zv2qZzVdqJyuITFe0Pg5aI+A==", + "license": "MIT", + "dependencies": { + "@types/qs": "^6.9.7", + "expo-constants": "~14.4.2", + "invariant": "^2.2.4", + "qs": "^6.11.0", + "url-parse": "^1.5.9" + } + }, + "node_modules/expo-manifests": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/expo-manifests/-/expo-manifests-0.7.2.tgz", + "integrity": "sha512-xlhL0XI2zw3foJ0q2Ra4ieBhU0V2yz+Rv6GpVEaaIHFlIC/Dbx+mKrX5dgenZEMERr/MG7sRJaRbAVB2PaAYhA==", + "license": "MIT", + "dependencies": { + "expo-json-utils": "~0.7.0" + } + }, "node_modules/expo-modules-autolinking": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-1.5.1.tgz", @@ -8361,6 +9664,28 @@ "resolved": "https://registry.npmjs.org/expo-status-bar/-/expo-status-bar-1.6.0.tgz", "integrity": "sha512-e//Oi2WPdomMlMDD3skE4+1ZarKCJ/suvcB4Jo/nO427niKug5oppcPNYO+csR6y3ZglGuypS+3pp/hJ+Xp6fQ==" }, + "node_modules/expo-updates-interface": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/expo-updates-interface/-/expo-updates-interface-0.10.1.tgz", + "integrity": "sha512-I6JMR7EgjXwckrydDmrkBEX/iw750dcqpzQVsjznYWfi0HTEOxajLHB90fBFqQkUV5i5s4Fd3hYQ1Cn0oMzUbA==", + "license": "MIT", + "peerDependencies": { + "expo": "*" + } + }, + "node_modules/expo-web-browser": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/expo-web-browser/-/expo-web-browser-12.3.2.tgz", + "integrity": "sha512-ohBf+vnRnGzlTleY8EQ2XQU0vRdRwqMJtKkzM9MZRPDOK5QyJYPJjpk6ixGhxdeoUG2Ogj0InvhhgX9NUn4jkg==", + "license": "MIT", + "dependencies": { + "compare-urls": "^2.0.0", + "url": "^0.11.0" + }, + "peerDependencies": { + "expo": "*" + } + }, "node_modules/express": { "version": "4.21.2", "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", @@ -8719,6 +10044,42 @@ "micromatch": "^4.0.2" } }, + "node_modules/firebase": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-12.0.0.tgz", + "integrity": "sha512-KV+OrMJpi2uXlqL2zaCcXb7YuQbY/gMIWT1hf8hKeTW1bSumWaHT5qfmn0WTpHwKQa3QEVOtZR2ta9EchcmYuw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/ai": "2.0.0", + "@firebase/analytics": "0.10.18", + "@firebase/analytics-compat": "0.2.24", + "@firebase/app": "0.14.0", + "@firebase/app-check": "0.11.0", + "@firebase/app-check-compat": "0.4.0", + "@firebase/app-compat": "0.5.0", + "@firebase/app-types": "0.9.3", + "@firebase/auth": "1.11.0", + "@firebase/auth-compat": "0.6.0", + "@firebase/data-connect": "0.3.11", + "@firebase/database": "1.1.0", + "@firebase/database-compat": "2.1.0", + "@firebase/firestore": "4.9.0", + "@firebase/firestore-compat": "0.4.0", + "@firebase/functions": "0.13.0", + "@firebase/functions-compat": "0.4.0", + "@firebase/installations": "0.6.19", + "@firebase/installations-compat": "0.2.19", + "@firebase/messaging": "0.12.23", + "@firebase/messaging-compat": "0.2.23", + "@firebase/performance": "0.7.8", + "@firebase/performance-compat": "0.2.21", + "@firebase/remote-config": "0.6.6", + "@firebase/remote-config-compat": "0.2.19", + "@firebase/storage": "0.14.0", + "@firebase/storage-compat": "0.4.0", + "@firebase/util": "1.13.0" + } + }, "node_modules/flow-enums-runtime": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/flow-enums-runtime/-/flow-enums-runtime-0.0.5.tgz", @@ -9133,6 +10494,32 @@ "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==", + "dev": true, + "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", @@ -9407,6 +10794,13 @@ "entities": "^2.0.0" } }, + "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==", + "dev": true, + "license": "BSD-2-Clause" + }, "node_modules/http-deceiver": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", @@ -9487,6 +10881,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -9534,6 +10942,12 @@ "postcss": "^8.1.0" } }, + "node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==", + "license": "ISC" + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -10040,6 +11454,15 @@ "node": ">=8" } }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -10644,6 +12067,13 @@ "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==", + "dev": true, + "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", @@ -10706,6 +12136,16 @@ "graceful-fs": "^4.1.6" } }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -10855,6 +12295,12 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "license": "MIT" + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -11091,6 +12537,12 @@ "node": ">=6" } }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -11110,6 +12562,16 @@ "tslib": "^2.0.3" } }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true, + "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", @@ -11833,6 +13295,16 @@ "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/mini-css-extract-plugin": { "version": "2.9.2", "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.2.tgz", @@ -12233,6 +13705,43 @@ "node": ">=0.10.0" } }, + "node_modules/normalize-url": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-2.0.1.tgz", + "integrity": "sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==", + "license": "MIT", + "dependencies": { + "prepend-http": "^2.0.0", + "query-string": "^5.0.1", + "sort-keys": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/normalize-url/node_modules/query-string": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", + "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", + "license": "MIT", + "dependencies": { + "decode-uri-component": "^0.2.0", + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url/node_modules/strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/npm-package-arg": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-7.0.0.tgz", @@ -12472,6 +13981,12 @@ "node": ">=4" } }, + "node_modules/os": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/os/-/os-0.1.2.tgz", + "integrity": "sha512-ZoXJkvAnljwvc56MbvhtKVWmSkzV712k42Is2mA0+0KTSRakq5XXuXpjZjgAt9ctzl51ojhQWakQQpmOvXWfjQ==", + "license": "MIT" + }, "node_modules/os-homedir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", @@ -12514,6 +14029,16 @@ "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", @@ -13460,6 +14985,15 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, + "node_modules/prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", @@ -13547,6 +15081,30 @@ "react-is": "^16.13.1" } }, + "node_modules/protobufjs": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.3.tgz", + "integrity": "sha512-sildjKwVqOI2kmFDiXQ6aEB0fjYTafpEvIBs8tOR8qI4spuL9OPROLVu2qZqi/xgCfsHIwVqlaF8JBjWFHnKbw==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -13573,6 +15131,12 @@ "once": "^1.3.1" } }, + "node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "license": "MIT" + }, "node_modules/qrcode-terminal": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.11.0.tgz", @@ -13644,6 +15208,19 @@ } ] }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -13817,6 +15394,32 @@ "react": "18.2.0" } }, + "node_modules/react-native-dotenv": { + "version": "3.4.11", + "resolved": "https://registry.npmjs.org/react-native-dotenv/-/react-native-dotenv-3.4.11.tgz", + "integrity": "sha512-6vnIE+WHABSeHCaYP6l3O1BOEhWxKH6nHAdV7n/wKn/sciZ64zPPp2NUdEUf1m7g4uuzlLbjgr+6uDt89q2DOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "dotenv": "^16.4.5" + }, + "peerDependencies": { + "@babel/runtime": "^7.20.6" + } + }, + "node_modules/react-native-dotenv/node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/react-native-safe-area-context": { "version": "4.6.3", "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.6.3.tgz", @@ -14203,6 +15806,13 @@ "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==", + "dev": true, + "license": "MIT" + }, "node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -14211,6 +15821,19 @@ "node": ">=8" } }, + "node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dev": true, + "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", @@ -14914,6 +16537,18 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/sort-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz", + "integrity": "sha512-/dPCrG1s3ePpWm6yBbxZq5Be1dXGLyLn9Z791chDC3NFrpkVbWGzkBwPN1knaciexFXgRJ7hzdnwZ4stHSDmjg==", + "license": "MIT", + "dependencies": { + "is-plain-obj": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/source-list-map": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", @@ -16193,6 +17828,19 @@ "node": ">=6" } }, + "node_modules/url": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.4.tgz", + "integrity": "sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==", + "license": "MIT", + "dependencies": { + "punycode": "^1.4.1", + "qs": "^6.12.3" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/url-join": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.0.tgz", @@ -16317,6 +17965,12 @@ "defaults": "^1.0.3" } }, + "node_modules/web-vitals": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz", + "integrity": "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==", + "license": "Apache-2.0" + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index c83bb6b6..f7d0def9 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,13 +11,21 @@ "dependencies": { "@expo/webpack-config": "^19.0.0", "@react-native-async-storage/async-storage": "1.18.2", + "@react-native-google-signin/google-signin": "^11.0.0", "@react-navigation/native": "^6.1.9", "@react-navigation/native-stack": "^6.9.17", "axios": "^1.6.2", + "babel-plugin-inline-dotenv": "^1.7.0", "expo": "~49.0.15", + "expo-auth-session": "~5.0.2", + "expo-build-properties": "~0.8.3", "expo-crypto": "~12.4.1", + "expo-dev-client": "~2.4.13", "expo-secure-store": "~12.3.1", "expo-status-bar": "~1.6.0", + "expo-web-browser": "~12.3.2", + "firebase": "^12.0.0", + "os": "^0.1.2", "react": "18.2.0", "react-dom": "18.2.0", "react-native": "0.72.10", @@ -27,7 +35,9 @@ }, "devDependencies": { "@babel/core": "^7.20.0", + "@expo/ngrok": "^4.1.0", "@types/react": "~18.2.14", + "react-native-dotenv": "^3.4.11", "typescript": "^5.1.3" }, "private": true diff --git a/frontend/screens/LoginScreen.tsx b/frontend/screens/LoginScreen.tsx index 75e94e73..fa84900d 100644 --- a/frontend/screens/LoginScreen.tsx +++ b/frontend/screens/LoginScreen.tsx @@ -1,5 +1,5 @@ -import { StatusBar } from 'expo-status-bar'; -import React, { useState } from 'react'; +import { StatusBar } from "expo-status-bar"; +import React, { useState } from "react"; import { ActivityIndicator, Alert, @@ -10,69 +10,49 @@ import { Text, TextInput, TouchableOpacity, - View -} from 'react-native'; -import { useAuth } from '../contexts/AuthContext'; + View, + Image, +} from "react-native"; +import { useAuth } from "../contexts/AuthContext"; +import * as AuthSession from "expo-auth-session"; const LoginScreen: React.FC = () => { - // States for form input and mode - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - const [name, setName] = useState(''); + const redirectUri = AuthSession.makeRedirectUri({}); + if(__DEV__){ + console.log("Redirect URI:", redirectUri); + } + + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [name, setName] = useState(""); const [isSignUp, setIsSignUp] = useState(false); - // Get auth functions - const { login, signup, loading } = useAuth(); // Handle form submission + + const { login, signup, googleLogin, loading } = useAuth(); + const handleSubmit = async () => { - // Basic form validation - if (!email.trim()) { - Alert.alert('Error', 'Please enter your email'); - return; - } - - if (!password.trim()) { - Alert.alert('Error', 'Please enter your password'); - return; - } - - // Basic email format validation + if (!email.trim()) return Alert.alert("Error", "Please enter your email"); + if (!password.trim()) return Alert.alert("Error", "Please enter your password"); + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; - if (!emailRegex.test(email)) { - Alert.alert('Error', 'Please enter a valid email address'); - return; - } - + if (!emailRegex.test(email)) return Alert.alert("Error", "Please enter a valid email address"); + try { if (isSignUp) { - if (!name.trim()) { - Alert.alert('Error', 'Please enter your name'); - return; - } + if (!name.trim()) return Alert.alert("Error", "Please enter your name"); await signup({ email, password, name }); } else { await login({ email, password }); } } catch (error: any) { - console.error('Authentication error:', error); - - // Extract error message from different possible error structures - let errorMessage = 'Failed to authenticate'; - - if (error.response?.data?.detail) { - errorMessage = error.response.data.detail; - } else if (error.response?.data?.message) { - errorMessage = error.response.data.message; - } else if (error.message) { - errorMessage = error.message; - } - - Alert.alert('Authentication Error', errorMessage); + console.error("Authentication error:", error); + let errorMessage = error.response?.data?.detail || error.response?.data?.message || error.message || "Failed to authenticate"; + Alert.alert("Authentication Error", errorMessage); } }; - return ( - @@ -82,10 +62,10 @@ const LoginScreen: React.FC = () => { Splitwiser Split expenses with friends easily - + - {isSignUp ? 'Create Account' : 'Welcome Back'} - + {isSignUp ? "Create Account" : "Welcome Back"} + {isSignUp && ( { autoCapitalize="words" /> )} - + { keyboardType="email-address" autoCapitalize="none" /> - + { onChangeText={setPassword} secureTextEntry /> - - {loading ? ( - - ) : ( - - {isSignUp ? 'Sign Up' : 'Login'} - - )} - - setIsSignUp(!isSignUp)} - > + + + {loading ? : {isSignUp ? "Sign Up" : "Login"}} + + + setIsSignUp(!isSignUp)}> - {isSignUp - ? 'Already have an account? Login' - : 'Don\'t have an account? Sign Up' - } + {isSignUp ? "Already have an account? Login" : "Don't have an account? Sign Up"} + + OR + + {/* Fixed Google Login Button */} + + + Continue with Google + @@ -192,7 +166,7 @@ const styles = StyleSheet.create({ paddingHorizontal: 15, fontSize: 16, backgroundColor: '#f5f5f5', - }, button: { + }, button: { backgroundColor: '#2e7d32', // Green color height: 50, borderRadius: 8, @@ -205,14 +179,38 @@ const styles = StyleSheet.create({ fontSize: 18, fontWeight: '600', }, + orText: { + textAlign: 'center', + color: '#999', + marginVertical: 15, + }, + googleButton: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + backgroundColor: '#f5f5f5', + height: 50, + borderRadius: 8, + borderWidth: 1, + borderColor: '#ddd', + paddingHorizontal: 15, + }, switchMode: { marginTop: 20, alignItems: 'center', }, switchModeText: { color: '#2e7d32', // Green color - fontSize: 15, + }, + googleIcon: { + width: 24, + height: 24, + marginRight: 10, + }, + googleButtonText: { + color: '#2e7d32', + fontSize: 16, + fontWeight: '600', }, }); - export default LoginScreen; diff --git a/nixpacks.toml b/nixpacks.toml index d33c9431..4bb0dbbb 100644 --- a/nixpacks.toml +++ b/nixpacks.toml @@ -1,23 +1,21 @@ [phases.setup] nixPkgs = ["python312", "pip"] -# Completely override the install command to avoid duplicate processes +# Use default Python behavior but ensure we use the right requirements file [phases.install] cmds = [ - "python -m pip install --upgrade pip", + "pip install --upgrade pip", "pip install -r requirements.txt" + # (Optional) upgrade pip via the Python executable to avoid PATH ambiguity + "python -m pip install --upgrade pip", + # Install backend requirements first; fall back to root requirements.txt + "pip install -r backend/requirements.txt || pip install -r requirements.txt" ] -# Skip Python's default install phase entirely -[phases.python:install] -dependsOn = ["install"] -cmds = [] - [phases.build] cmds = [ - "echo 'Build phase complete'", - # Copy backend files to the root for simpler execution - "cp -r backend/* ." + "echo 'Build phase: Copying backend files'", + "cp -r backend/* . || echo 'Backend files already in place'" ] [start] @@ -28,7 +26,5 @@ cmd = "uvicorn main:app --host 0.0.0.0 --port $PORT" PYTHONPATH = "/app" PYTHONUNBUFFERED = "1" -# Specify we're using plain pip without a venv +# Let Nixpacks handle virtual environment creation automatically [nixpacks] -plan-path = "disabled" -create-venv = false diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..43f22b88 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "Splitwiser", + "lockfileVersion": 3, + "requires": true, + "packages": {} +}