feat: add image upload, camera

This commit is contained in:
Sosokker 2025-05-11 02:46:23 +07:00
parent d35ae859e4
commit dac208a397
9 changed files with 623 additions and 252 deletions

View File

@ -14,6 +14,7 @@ export default {
supportsTablet: true supportsTablet: true
}, },
android: { android: {
usesCleartextTraffic: true,
adaptiveIcon: { adaptiveIcon: {
foregroundImage: './assets/images/adaptive-icon.png', foregroundImage: './assets/images/adaptive-icon.png',
backgroundColor: '#ffffff' backgroundColor: '#ffffff'
@ -42,12 +43,7 @@ export default {
typedRoutes: true typedRoutes: true
}, },
extra: { extra: {
FIREBASE_API_KEY: process.env.FIREBASE_API_KEY, GEMINI_API_KEY: process.env.GEMINI_API_KEY,
FIREBASE_AUTH_DOMAIN: process.env.FIREBASE_AUTH_DOMAIN,
FIREBASE_PROJECT_ID: process.env.FIREBASE_PROJECT_ID,
FIREBASE_STORAGE_BUCKET: process.env.FIREBASE_STORAGE_BUCKET,
FIREBASE_MESSAGING_SENDER_ID: process.env.FIREBASE_MESSAGING_SENDER_ID,
FIREBASE_APP_ID: process.env.FIREBASE_APP_ID,
}, },
}, },
}; };

View File

@ -1,8 +1,14 @@
import { IconSymbol } from "@/components/ui/IconSymbol"; import { IconSymbol } from "@/components/ui/IconSymbol";
import { getFoods, insertGenAIResult } from "@/services/data/foods";
import { uploadImageToSupabase } from "@/services/data/imageUpload";
import { callGenAIonImage } from "@/services/gemini";
import { supabase } from "@/services/supabase";
import { Feather, FontAwesome, Ionicons } from "@expo/vector-icons"; import { Feather, FontAwesome, Ionicons } from "@expo/vector-icons";
import { useQuery } from "@tanstack/react-query";
import * as FileSystem from "expo-file-system";
import * as ImagePicker from "expo-image-picker"; import * as ImagePicker from "expo-image-picker";
import { router } from "expo-router"; import { router } from "expo-router";
import React, { useState } from "react"; import React, { useMemo, useState } from "react";
import { import {
Alert, Alert,
Image, Image,
@ -15,148 +21,96 @@ import {
View, View,
} from "react-native"; } from "react-native";
// Sample recipe data const useFoodsQuery = () => {
const foodHighlights = [ return useQuery({
{ queryKey: ["highlight-foods"],
id: 1, queryFn: async () => {
name: "Pad Kra Pao Moo Sab with Eggs", const { data, error } = await getFoods(undefined, true, undefined, 4);
image: require("@/assets/images/food/padkrapao.jpg"), if (error) throw error;
description: "Thai stir-fry with ground pork and holy basil", return data || [];
time: "30 Mins",
calories: "520 kcal",
}, },
{ staleTime: 1000 * 60 * 5,
id: 2, });
name: "Jjajangmyeon", };
image: require("@/assets/images/food/jjajangmyeon.jpg"),
description: "Korean black bean noodles", const runImagePipeline = async (
time: "45 Mins", imageBase64: string,
calories: "650 kcal", imageType: string,
}, userId: string
{ ) => {
id: 3, const imageUri = await uploadImageToSupabase(imageBase64, imageType, userId);
name: "Ramen", const genAIResult = await callGenAIonImage(imageUri);
image: require("@/assets/images/food/ramen.jpg"), if (genAIResult.error) throw genAIResult.error;
description: "Japanese noodle soup", const { data: genAIResultData } = genAIResult;
time: "60 Mins", if (!genAIResultData) throw new Error("GenAI result is null");
calories: "480 kcal", await insertGenAIResult(genAIResultData, userId, imageUri);
}, };
{
id: 4, const processImage = async (
name: "Beef Wellington", asset: ImagePicker.ImagePickerAsset,
image: require("@/assets/images/food/beef.jpg"), userId: string
description: "Tender beef wrapped in puff pastry", ) => {
time: "90 Mins", const base64 = await FileSystem.readAsStringAsync(asset.uri, {
calories: "750 kcal", encoding: "base64",
}, });
]; const imageType = asset.mimeType || "image/jpeg";
await runImagePipeline(base64, imageType, userId);
};
const navigateToFoodDetail = (foodId: string) => { const navigateToFoodDetail = (foodId: string) => {
router.push({ pathname: "/recipe-detail", params: { id: foodId } }); router.push({ pathname: "/recipe-detail", params: { id: foodId } });
}; };
const handleImageSelection = async (
pickerFn:
| typeof ImagePicker.launchCameraAsync
| typeof ImagePicker.launchImageLibraryAsync
) => {
const result = await pickerFn({
mediaTypes: ["images"],
allowsEditing: true,
aspect: [1, 1],
quality: 1,
});
if (!result.canceled) {
try {
const { data, error } = await supabase.auth.getUser();
if (error || !data?.user?.id) throw new Error("Cannot get user id");
const userId = data.user.id;
await processImage(result.assets[0], userId);
} catch (err) {
Alert.alert(
"Image Processing Failed",
(err as Error).message || "Unknown error"
);
}
router.push({
pathname: "/recipe-detail",
params: {
title: "My New Recipe",
image: result.assets[0].uri,
},
});
}
};
export default function HomeScreen() { export default function HomeScreen() {
const [searchQuery, setSearchQuery] = useState(""); const [searchQuery, setSearchQuery] = useState("");
const [filteredRecipes, setFilteredRecipes] = useState(foodHighlights); const { data: foodsData = [], isLoading, error } = useFoodsQuery();
// Handle search const filteredFoods = useMemo(() => {
const handleSearch = (text: string): void => { return searchQuery
setSearchQuery(text); ? foodsData.filter((food) =>
if (text) { food.name.toLowerCase().includes(searchQuery.toLowerCase())
const filtered = foodHighlights.filter((food) => )
food.name.toLowerCase().includes(text.toLowerCase()) : foodsData;
); }, [foodsData, searchQuery]);
setFilteredRecipes(filtered);
} else {
setFilteredRecipes(foodHighlights);
}
};
// Handle camera
const takePhoto = async () => {
const { status } = await ImagePicker.requestCameraPermissionsAsync();
if (status !== "granted") {
Alert.alert(
"Permission needed",
"Please grant camera permissions to use this feature."
);
return;
}
const result = await ImagePicker.launchCameraAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [1, 1],
quality: 1,
});
if (!result.canceled) {
// Navigate to recipe detail with the captured image
router.push({
pathname: "/recipe-detail",
params: {
title: "My New Recipe",
image: result.assets[0].uri,
},
});
}
};
// Handle gallery
const pickImage = async () => {
const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
if (status !== "granted") {
Alert.alert(
"Permission needed",
"Please grant media library permissions to use this feature."
);
return;
}
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [1, 1],
quality: 1,
});
if (!result.canceled) {
// Navigate to recipe detail with the selected image
router.push({
pathname: "/recipe-detail",
params: {
title: "My New Recipe",
image: result.assets[0].uri,
},
});
}
};
// Navigate to recipe detail
interface Recipe {
id: number;
title: string;
image: string;
color: string;
}
const goToRecipeDetail = (recipe: Recipe): void => {
router.push({
pathname: "/recipe-detail",
params: {
title: recipe.title,
image: recipe.image,
},
});
};
return ( return (
<SafeAreaView className="flex-1 bg-white"> <SafeAreaView className="flex-1 bg-white">
<StatusBar barStyle="dark-content" /> <StatusBar barStyle="dark-content" />
{/* Header - Fixed at top */}
<View className="flex-row justify-between items-center px-6 pt-4 pb-2"> <View className="flex-row justify-between items-center px-6 pt-4 pb-2">
<Text className="text-3xl font-bold">Hi! Mr. Chef</Text> <Text className="text-3xl font-bold">Hi! Mr. Chef</Text>
<View className="bg-[#ffd60a] p-3 rounded-lg"> <View className="bg-[#ffd60a] p-3 rounded-lg">
@ -164,13 +118,11 @@ export default function HomeScreen() {
</View> </View>
</View> </View>
{/* Scrollable Content */}
<ScrollView <ScrollView
className="flex-1" className="flex-1"
showsVerticalScrollIndicator={false} showsVerticalScrollIndicator={false}
contentContainerStyle={{ paddingBottom: 100 }} contentContainerStyle={{ paddingBottom: 100 }}
> >
{/* Show your dishes */}
<View className="px-6 mb-6"> <View className="px-6 mb-6">
<View className="flex-row items-center mb-4"> <View className="flex-row items-center mb-4">
<Text className="text-2xl font-bold mr-2">Show your dishes</Text> <Text className="text-2xl font-bold mr-2">Show your dishes</Text>
@ -182,7 +134,7 @@ export default function HomeScreen() {
className="flex-1" className="flex-1"
placeholder="Search..." placeholder="Search..."
value={searchQuery} value={searchQuery}
onChangeText={handleSearch} onChangeText={setSearchQuery}
/> />
<View className="bg-[#ffd60a] p-2 rounded-full"> <View className="bg-[#ffd60a] p-2 rounded-full">
<Feather name="send" size={20} color="black" /> <Feather name="send" size={20} color="black" />
@ -192,7 +144,18 @@ export default function HomeScreen() {
<View className="flex-row justify-between"> <View className="flex-row justify-between">
<TouchableOpacity <TouchableOpacity
className="bg-[#ffd60a] p-4 rounded-xl w-[48%]" className="bg-[#ffd60a] p-4 rounded-xl w-[48%]"
onPress={takePhoto} onPress={async () => {
const { status } =
await ImagePicker.requestCameraPermissionsAsync();
if (status !== "granted") {
Alert.alert(
"Permission needed",
"Please grant camera permissions."
);
return;
}
await handleImageSelection(ImagePicker.launchCameraAsync);
}}
> >
<View className="items-center"> <View className="items-center">
<FontAwesome name="camera" size={24} color="black" /> <FontAwesome name="camera" size={24} color="black" />
@ -202,10 +165,20 @@ export default function HomeScreen() {
</Text> </Text>
</View> </View>
</TouchableOpacity> </TouchableOpacity>
<TouchableOpacity <TouchableOpacity
className="bg-[#f9be25] p-4 rounded-xl w-[48%]" className="bg-[#f9be25] p-4 rounded-xl w-[48%]"
onPress={pickImage} onPress={async () => {
const { status } =
await ImagePicker.requestMediaLibraryPermissionsAsync();
if (status !== "granted") {
Alert.alert(
"Permission needed",
"Please grant gallery permissions."
);
return;
}
await handleImageSelection(ImagePicker.launchImageLibraryAsync);
}}
> >
<View className="items-center"> <View className="items-center">
<Feather name="image" size={24} color="black" /> <Feather name="image" size={24} color="black" />
@ -216,32 +189,46 @@ export default function HomeScreen() {
</View> </View>
</TouchableOpacity> </TouchableOpacity>
</View> </View>
</View>
{/* Highlights Section */}
<View className="px-6 mb-6"> <View className="px-6 mb-6">
<View className="flex-row items-center mb-4"> <View className="flex-row items-center mb-4">
<Text className="text-2xl font-bold mr-2">Highlights</Text> <Text className="text-2xl font-bold mr-2">Highlights</Text>
<FontAwesome name="star" size={20} color="#ffd60a" /> <Ionicons name="star-outline" size={20} color="#bb0718" />
</View> </View>
<View className="w-full"> {isLoading ? (
{foodHighlights.map((food) => ( <Text className="text-center text-gray-500">
Loading highlights...
</Text>
) : error ? (
<Text className="text-center text-red-600">
Failed to load highlights
</Text>
) : filteredFoods.length === 0 ? (
<Text className="text-center text-gray-400">
No highlights available
</Text>
) : (
<View className="flex-row justify-between">
{filteredFoods.map((food, idx) => (
<TouchableOpacity <TouchableOpacity
key={food.id} key={food.id}
className="flex-row bg-white rounded-xl mb-3 shadow-sm overflow-hidden" className="bg-white rounded-xl shadow-md flex-1 mr-4"
style={{ style={{
shadowColor: "#000", marginRight: idx === filteredFoods.length - 1 ? 0 : 12,
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 2,
}} }}
onPress={() => navigateToFoodDetail(String(food.id))} onPress={() => navigateToFoodDetail(food.id)}
> >
{food.image_url ? (
<Image <Image
source={food.image} source={{ uri: food.image_url }}
className="w-[88px] h-[88px] rounded-l-xl" className="w-full h-32 rounded-t-xl"
resizeMode="cover" resizeMode="cover"
/> />
) : (
<View className="w-full h-32 rounded-t-xl bg-gray-200 items-center justify-center">
<Text className="text-gray-400">No Image</Text>
</View>
)}
<View className="flex-1 p-3 justify-between"> <View className="flex-1 p-3 justify-between">
<Text <Text
className="text-base font-bold text-[#333] mb-1" className="text-base font-bold text-[#333] mb-1"
@ -249,20 +236,19 @@ export default function HomeScreen() {
> >
{food.name} {food.name}
</Text> </Text>
<Text className="text-sm text-[#666] mb-2" numberOfLines={1}> <Text
{food.description} className="text-sm text-[#666] mb-2"
numberOfLines={1}
>
{food.description || "No description"}
</Text> </Text>
<View className="flex-row justify-between"> <View className="flex-row justify-between">
<View className="flex-row items-center"> <View className="flex-row items-center">
<IconSymbol name="clock" size={12} color="#666666" /> <IconSymbol name="clock" size={12} color="#666" />
<Text className="text-xs text-[#666] ml-1"> <Text className="text-xs text-[#666] ml-1">
{food.time} {food.time_to_cook_minutes
</Text> ? `${food.time_to_cook_minutes} min`
</View> : "-"}
<View className="flex-row items-center">
<IconSymbol name="flame" size={12} color="#666666" />
<Text className="text-xs text-[#666] ml-1">
{food.calories}
</Text> </Text>
</View> </View>
</View> </View>
@ -270,8 +256,9 @@ export default function HomeScreen() {
</TouchableOpacity> </TouchableOpacity>
))} ))}
</View> </View>
)}
</View>
</View> </View>
{/* Extra space at bottom */} {/* Extra space at bottom */}
<View className="h-20"></View> <View className="h-20"></View>
</ScrollView> </ScrollView>

319
package-lock.json generated
View File

@ -11,17 +11,23 @@
"@dev-plugins/react-query": "^0.3.1", "@dev-plugins/react-query": "^0.3.1",
"@expo/ngrok": "^4.1.3", "@expo/ngrok": "^4.1.3",
"@expo/vector-icons": "^14.1.0", "@expo/vector-icons": "^14.1.0",
"@google/genai": "^0.13.0",
"@react-native-async-storage/async-storage": "2.1.2", "@react-native-async-storage/async-storage": "2.1.2",
"@react-navigation/bottom-tabs": "^7.3.10", "@react-navigation/bottom-tabs": "^7.3.10",
"@react-navigation/elements": "^2.3.8", "@react-navigation/elements": "^2.3.8",
"@react-navigation/native": "^7.1.6", "@react-navigation/native": "^7.1.6",
"@supabase/supabase-js": "2.49.5-next.1", "@supabase/supabase-js": "2.49.5-next.1",
"base64-arraybuffer": "^1.0.2",
"cors": "^2.8.5",
"dotenv": "^16.5.0",
"expo": "^53.0.9", "expo": "^53.0.9",
"expo-blur": "~14.1.4", "expo-blur": "~14.1.4",
"expo-constants": "~17.1.6", "expo-constants": "~17.1.6",
"expo-file-system": "~18.1.9",
"expo-font": "~13.3.1", "expo-font": "~13.3.1",
"expo-haptics": "~14.1.4", "expo-haptics": "~14.1.4",
"expo-image": "~2.1.7", "expo-image": "~2.1.7",
"expo-image-manipulator": "~13.1.6",
"expo-image-picker": "~16.1.4", "expo-image-picker": "~16.1.4",
"expo-linking": "~7.1.4", "expo-linking": "~7.1.4",
"expo-router": "~5.0.6", "expo-router": "~5.0.6",
@ -31,6 +37,8 @@
"expo-symbols": "~0.4.4", "expo-symbols": "~0.4.4",
"expo-system-ui": "~5.0.7", "expo-system-ui": "~5.0.7",
"expo-web-browser": "~14.1.6", "expo-web-browser": "~14.1.6",
"express": "^5.1.0",
"mime": "^4.0.7",
"nativewind": "^4.1.23", "nativewind": "^4.1.23",
"react": "19.0.0", "react": "19.0.0",
"react-dom": "19.0.0", "react-dom": "19.0.0",
@ -1863,6 +1871,18 @@
"getenv": "^1.0.0" "getenv": "^1.0.0"
} }
}, },
"node_modules/@expo/env/node_modules/dotenv": {
"version": "16.4.7",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
"integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://dotenvx.com"
}
},
"node_modules/@expo/fingerprint": { "node_modules/@expo/fingerprint": {
"version": "0.12.4", "version": "0.12.4",
"resolved": "https://registry.npmjs.org/@expo/fingerprint/-/fingerprint-0.12.4.tgz", "resolved": "https://registry.npmjs.org/@expo/fingerprint/-/fingerprint-0.12.4.tgz",
@ -1996,6 +2016,18 @@
"balanced-match": "^1.0.0" "balanced-match": "^1.0.0"
} }
}, },
"node_modules/@expo/metro-config/node_modules/dotenv": {
"version": "16.4.7",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
"integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://dotenvx.com"
}
},
"node_modules/@expo/metro-config/node_modules/minimatch": { "node_modules/@expo/metro-config/node_modules/minimatch": {
"version": "9.0.5", "version": "9.0.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
@ -2334,6 +2366,21 @@
"@babel/highlight": "^7.10.4" "@babel/highlight": "^7.10.4"
} }
}, },
"node_modules/@google/genai": {
"version": "0.13.0",
"resolved": "https://registry.npmjs.org/@google/genai/-/genai-0.13.0.tgz",
"integrity": "sha512-eaEncWt875H7046T04mOpxpHJUM+jLIljEf+5QctRyOeChylE/nhpwm1bZWTRWoOu/t46R9r+PmgsJFhTpE7tQ==",
"license": "Apache-2.0",
"dependencies": {
"google-auth-library": "^9.14.2",
"ws": "^8.18.0",
"zod": "^3.22.4",
"zod-to-json-schema": "^3.22.4"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@humanfs/core": { "node_modules/@humanfs/core": {
"version": "0.19.1", "version": "0.19.1",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
@ -4538,6 +4585,15 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
}, },
"node_modules/base64-arraybuffer": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
"license": "MIT",
"engines": {
"node": ">= 0.6.0"
}
},
"node_modules/base64-js": { "node_modules/base64-js": {
"version": "1.5.1", "version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
@ -4594,6 +4650,15 @@
"node": ">=0.6" "node": ">=0.6"
} }
}, },
"node_modules/bignumber.js": {
"version": "9.3.0",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.0.tgz",
"integrity": "sha512-EM7aMFTXbptt/wZdMlBv2t8IViwQL+h6SLHosp8Yf0dqJMTnY6iL32opnAB6kAdL0SZPuvcAzFr31o0c/R3/RA==",
"license": "MIT",
"engines": {
"node": "*"
}
},
"node_modules/binary-extensions": { "node_modules/binary-extensions": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
@ -4610,7 +4675,6 @@
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
"integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==",
"dev": true,
"dependencies": { "dependencies": {
"bytes": "^3.1.2", "bytes": "^3.1.2",
"content-type": "^1.0.5", "content-type": "^1.0.5",
@ -4728,6 +4792,12 @@
"ieee754": "^1.1.13" "ieee754": "^1.1.13"
} }
}, },
"node_modules/buffer-equal-constant-time": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
"license": "BSD-3-Clause"
},
"node_modules/buffer-from": { "node_modules/buffer-from": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
@ -4788,7 +4858,6 @@
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"dev": true,
"dependencies": { "dependencies": {
"es-errors": "^1.3.0", "es-errors": "^1.3.0",
"function-bind": "^1.1.2" "function-bind": "^1.1.2"
@ -4801,7 +4870,6 @@
"version": "1.0.4", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
"dev": true,
"dependencies": { "dependencies": {
"call-bind-apply-helpers": "^1.0.2", "call-bind-apply-helpers": "^1.0.2",
"get-intrinsic": "^1.3.0" "get-intrinsic": "^1.3.0"
@ -5234,7 +5302,6 @@
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
"integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==",
"dev": true,
"dependencies": { "dependencies": {
"safe-buffer": "5.2.1" "safe-buffer": "5.2.1"
}, },
@ -5246,7 +5313,6 @@
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
"dev": true,
"engines": { "engines": {
"node": ">= 0.6" "node": ">= 0.6"
} }
@ -5260,7 +5326,6 @@
"version": "0.7.2", "version": "0.7.2",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
"dev": true,
"engines": { "engines": {
"node": ">= 0.6" "node": ">= 0.6"
} }
@ -5269,7 +5334,6 @@
"version": "1.2.2", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
"integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
"dev": true,
"engines": { "engines": {
"node": ">=6.6.0" "node": ">=6.6.0"
} }
@ -5296,7 +5360,7 @@
"version": "2.8.5", "version": "2.8.5",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
"dev": true, "license": "MIT",
"dependencies": { "dependencies": {
"object-assign": "^4", "object-assign": "^4",
"vary": "^1" "vary": "^1"
@ -5653,9 +5717,10 @@
} }
}, },
"node_modules/dotenv": { "node_modules/dotenv": {
"version": "16.4.7", "version": "16.5.0",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz",
"integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==",
"license": "BSD-2-Clause",
"engines": { "engines": {
"node": ">=12" "node": ">=12"
}, },
@ -5681,7 +5746,6 @@
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
"dev": true,
"dependencies": { "dependencies": {
"call-bind-apply-helpers": "^1.0.1", "call-bind-apply-helpers": "^1.0.1",
"es-errors": "^1.3.0", "es-errors": "^1.3.0",
@ -5696,6 +5760,15 @@
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
}, },
"node_modules/ecdsa-sig-formatter": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
"integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
"license": "Apache-2.0",
"dependencies": {
"safe-buffer": "^5.0.1"
}
},
"node_modules/ee-first": { "node_modules/ee-first": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@ -5821,7 +5894,6 @@
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
"dev": true,
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
} }
@ -5830,7 +5902,6 @@
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"dev": true,
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
} }
@ -5866,7 +5937,6 @@
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
"dev": true,
"dependencies": { "dependencies": {
"es-errors": "^1.3.0" "es-errors": "^1.3.0"
}, },
@ -6517,6 +6587,18 @@
"expo": "*" "expo": "*"
} }
}, },
"node_modules/expo-image-manipulator": {
"version": "13.1.7",
"resolved": "https://registry.npmjs.org/expo-image-manipulator/-/expo-image-manipulator-13.1.7.tgz",
"integrity": "sha512-DBy/Xdd0E/yFind14x36XmwfWuUxOHI/oH97/giKjjPaRc2dlyjQ3tuW3x699hX6gAs9Sixj5WEJ1qNf3c8sag==",
"license": "MIT",
"dependencies": {
"expo-image-loader": "~5.1.0"
},
"peerDependencies": {
"expo": "*"
}
},
"node_modules/expo-image-picker": { "node_modules/expo-image-picker": {
"version": "16.1.4", "version": "16.1.4",
"resolved": "https://registry.npmjs.org/expo-image-picker/-/expo-image-picker-16.1.4.tgz", "resolved": "https://registry.npmjs.org/expo-image-picker/-/expo-image-picker-16.1.4.tgz",
@ -6710,7 +6792,7 @@
"version": "5.1.0", "version": "5.1.0",
"resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
"integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
"dev": true, "license": "MIT",
"dependencies": { "dependencies": {
"accepts": "^2.0.0", "accepts": "^2.0.0",
"body-parser": "^2.2.0", "body-parser": "^2.2.0",
@ -6767,7 +6849,6 @@
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
"integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
"dev": true,
"dependencies": { "dependencies": {
"mime-types": "^3.0.0", "mime-types": "^3.0.0",
"negotiator": "^1.0.0" "negotiator": "^1.0.0"
@ -6780,7 +6861,6 @@
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
"integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==",
"dev": true,
"dependencies": { "dependencies": {
"debug": "^4.4.0", "debug": "^4.4.0",
"encodeurl": "^2.0.0", "encodeurl": "^2.0.0",
@ -6797,7 +6877,6 @@
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
"integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
"dev": true,
"dependencies": { "dependencies": {
"mime-db": "^1.54.0" "mime-db": "^1.54.0"
}, },
@ -6809,7 +6888,6 @@
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
"integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
"dev": true,
"engines": { "engines": {
"node": ">= 0.6" "node": ">= 0.6"
} }
@ -6818,7 +6896,6 @@
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz",
"integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==",
"dev": true,
"dependencies": { "dependencies": {
"debug": "^4.3.5", "debug": "^4.3.5",
"encodeurl": "^2.0.0", "encodeurl": "^2.0.0",
@ -6840,7 +6917,6 @@
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz",
"integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==",
"dev": true,
"dependencies": { "dependencies": {
"encodeurl": "^2.0.0", "encodeurl": "^2.0.0",
"escape-html": "^1.0.3", "escape-html": "^1.0.3",
@ -6851,6 +6927,12 @@
"node": ">= 18" "node": ">= 18"
} }
}, },
"node_modules/extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
"license": "MIT"
},
"node_modules/fast-deep-equal": { "node_modules/fast-deep-equal": {
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@ -7130,7 +7212,6 @@
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
"dev": true,
"engines": { "engines": {
"node": ">= 0.6" "node": ">= 0.6"
} }
@ -7148,7 +7229,6 @@
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
"integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
"dev": true,
"engines": { "engines": {
"node": ">= 0.8" "node": ">= 0.8"
} }
@ -7208,6 +7288,49 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/gaxios": {
"version": "6.7.1",
"resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz",
"integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==",
"license": "Apache-2.0",
"dependencies": {
"extend": "^3.0.2",
"https-proxy-agent": "^7.0.1",
"is-stream": "^2.0.0",
"node-fetch": "^2.6.9",
"uuid": "^9.0.1"
},
"engines": {
"node": ">=14"
}
},
"node_modules/gaxios/node_modules/uuid": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"license": "MIT",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/gcp-metadata": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz",
"integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==",
"license": "Apache-2.0",
"dependencies": {
"gaxios": "^6.1.1",
"google-logging-utils": "^0.0.2",
"json-bigint": "^1.0.0"
},
"engines": {
"node": ">=14"
}
},
"node_modules/gensync": { "node_modules/gensync": {
"version": "1.0.0-beta.2", "version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
@ -7228,7 +7351,6 @@
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
"dev": true,
"dependencies": { "dependencies": {
"call-bind-apply-helpers": "^1.0.2", "call-bind-apply-helpers": "^1.0.2",
"es-define-property": "^1.0.1", "es-define-property": "^1.0.1",
@ -7260,7 +7382,6 @@
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
"dev": true,
"dependencies": { "dependencies": {
"dunder-proto": "^1.0.1", "dunder-proto": "^1.0.1",
"es-object-atoms": "^1.0.0" "es-object-atoms": "^1.0.0"
@ -7396,11 +7517,36 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/google-auth-library": {
"version": "9.15.1",
"resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz",
"integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==",
"license": "Apache-2.0",
"dependencies": {
"base64-js": "^1.3.0",
"ecdsa-sig-formatter": "^1.0.11",
"gaxios": "^6.1.1",
"gcp-metadata": "^6.1.0",
"gtoken": "^7.0.0",
"jws": "^4.0.0"
},
"engines": {
"node": ">=14"
}
},
"node_modules/google-logging-utils": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz",
"integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==",
"license": "Apache-2.0",
"engines": {
"node": ">=14"
}
},
"node_modules/gopd": { "node_modules/gopd": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
"dev": true,
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
}, },
@ -7443,6 +7589,19 @@
"integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
"dev": true "dev": true
}, },
"node_modules/gtoken": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz",
"integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==",
"license": "MIT",
"dependencies": {
"gaxios": "^6.0.0",
"jws": "^4.0.0"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/has-bigints": { "node_modules/has-bigints": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz",
@ -7503,7 +7662,6 @@
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
"dev": true,
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
}, },
@ -7634,7 +7792,6 @@
"version": "0.6.3", "version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"dev": true,
"dependencies": { "dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0" "safer-buffer": ">= 2.1.2 < 3.0.0"
}, },
@ -7772,7 +7929,6 @@
"version": "1.9.1", "version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
"dev": true,
"engines": { "engines": {
"node": ">= 0.10" "node": ">= 0.10"
} }
@ -8071,8 +8227,7 @@
"node_modules/is-promise": { "node_modules/is-promise": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="
"dev": true
}, },
"node_modules/is-regex": { "node_modules/is-regex": {
"version": "1.2.1", "version": "1.2.1",
@ -8119,6 +8274,18 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/is-stream": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
"license": "MIT",
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-string": { "node_modules/is-string": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz",
@ -8502,6 +8669,15 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/json-bigint": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
"integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
"license": "MIT",
"dependencies": {
"bignumber.js": "^9.0.0"
}
},
"node_modules/json-buffer": { "node_modules/json-buffer": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
@ -8550,6 +8726,27 @@
"node": ">=4.0" "node": ">=4.0"
} }
}, },
"node_modules/jwa": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
"integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
"license": "MIT",
"dependencies": {
"buffer-equal-constant-time": "^1.0.1",
"ecdsa-sig-formatter": "1.0.11",
"safe-buffer": "^5.0.1"
}
},
"node_modules/jws": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
"integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
"license": "MIT",
"dependencies": {
"jwa": "^2.0.0",
"safe-buffer": "^5.0.1"
}
},
"node_modules/keyv": { "node_modules/keyv": {
"version": "4.5.4", "version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
@ -9010,7 +9207,6 @@
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
"dev": true,
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
} }
@ -9019,7 +9215,6 @@
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
"integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
"dev": true,
"engines": { "engines": {
"node": ">= 0.8" "node": ">= 0.8"
} }
@ -9033,7 +9228,6 @@
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
"integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
"dev": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
}, },
@ -9396,14 +9590,18 @@
} }
}, },
"node_modules/mime": { "node_modules/mime": {
"version": "1.6.0", "version": "4.0.7",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.7.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "integrity": "sha512-2OfDPL+e03E0LrXaGYOtTFIYhiuzep94NSsuhrNULq+stylcJedcHdzHtz0atMUuGwJfFYs0YL5xeC/Ca2x0eQ==",
"funding": [
"https://github.com/sponsors/broofa"
],
"license": "MIT",
"bin": { "bin": {
"mime": "cli.js" "mime": "bin/cli.js"
}, },
"engines": { "engines": {
"node": ">=4" "node": ">=16"
} }
}, },
"node_modules/mime-db": { "node_modules/mime-db": {
@ -9705,7 +9903,6 @@
"version": "1.13.4", "version": "1.13.4",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
"dev": true,
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
}, },
@ -10150,7 +10347,6 @@
"version": "8.2.0", "version": "8.2.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz",
"integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==",
"dev": true,
"engines": { "engines": {
"node": ">=16" "node": ">=16"
} }
@ -10490,7 +10686,6 @@
"version": "2.0.7", "version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
"dev": true,
"dependencies": { "dependencies": {
"forwarded": "0.2.0", "forwarded": "0.2.0",
"ipaddr.js": "1.9.1" "ipaddr.js": "1.9.1"
@ -10528,7 +10723,6 @@
"version": "6.14.0", "version": "6.14.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
"integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
"dev": true,
"dependencies": { "dependencies": {
"side-channel": "^1.1.0" "side-channel": "^1.1.0"
}, },
@ -10606,7 +10800,6 @@
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz",
"integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==",
"dev": true,
"dependencies": { "dependencies": {
"bytes": "3.1.2", "bytes": "3.1.2",
"http-errors": "2.0.0", "http-errors": "2.0.0",
@ -11316,7 +11509,6 @@
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
"integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
"dev": true,
"dependencies": { "dependencies": {
"debug": "^4.4.0", "debug": "^4.4.0",
"depd": "^2.0.0", "depd": "^2.0.0",
@ -11424,8 +11616,7 @@
"node_modules/safer-buffer": { "node_modules/safer-buffer": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
"dev": true
}, },
"node_modules/sax": { "node_modules/sax": {
"version": "1.4.1", "version": "1.4.1",
@ -11542,6 +11733,18 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/send/node_modules/mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"license": "MIT",
"bin": {
"mime": "cli.js"
},
"engines": {
"node": ">=4"
}
},
"node_modules/serialize-error": { "node_modules/serialize-error": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz", "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz",
@ -11585,6 +11788,18 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/serve-static/node_modules/mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"license": "MIT",
"bin": {
"mime": "cli.js"
},
"engines": {
"node": ">=4"
}
},
"node_modules/serve-static/node_modules/send": { "node_modules/serve-static/node_modules/send": {
"version": "0.19.0", "version": "0.19.0",
"resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
@ -11724,7 +11939,6 @@
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
"dev": true,
"dependencies": { "dependencies": {
"es-errors": "^1.3.0", "es-errors": "^1.3.0",
"object-inspect": "^1.13.3", "object-inspect": "^1.13.3",
@ -11743,7 +11957,6 @@
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
"dev": true,
"dependencies": { "dependencies": {
"es-errors": "^1.3.0", "es-errors": "^1.3.0",
"object-inspect": "^1.13.3" "object-inspect": "^1.13.3"
@ -11759,7 +11972,6 @@
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
"dev": true,
"dependencies": { "dependencies": {
"call-bound": "^1.0.2", "call-bound": "^1.0.2",
"es-errors": "^1.3.0", "es-errors": "^1.3.0",
@ -11777,7 +11989,6 @@
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
"dev": true,
"dependencies": { "dependencies": {
"call-bound": "^1.0.2", "call-bound": "^1.0.2",
"es-errors": "^1.3.0", "es-errors": "^1.3.0",
@ -12560,7 +12771,6 @@
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
"integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
"dev": true,
"dependencies": { "dependencies": {
"content-type": "^1.0.5", "content-type": "^1.0.5",
"media-typer": "^1.1.0", "media-typer": "^1.1.0",
@ -12574,7 +12784,6 @@
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
"integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
"dev": true,
"dependencies": { "dependencies": {
"mime-db": "^1.54.0" "mime-db": "^1.54.0"
}, },
@ -13350,7 +13559,6 @@
"version": "3.24.4", "version": "3.24.4",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.24.4.tgz", "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.4.tgz",
"integrity": "sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg==", "integrity": "sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg==",
"dev": true,
"funding": { "funding": {
"url": "https://github.com/sponsors/colinhacks" "url": "https://github.com/sponsors/colinhacks"
} }
@ -13359,7 +13567,6 @@
"version": "3.24.5", "version": "3.24.5",
"resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz", "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz",
"integrity": "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==", "integrity": "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==",
"dev": true,
"peerDependencies": { "peerDependencies": {
"zod": "^3.24.1" "zod": "^3.24.1"
} }

View File

@ -14,17 +14,23 @@
"@dev-plugins/react-query": "^0.3.1", "@dev-plugins/react-query": "^0.3.1",
"@expo/ngrok": "^4.1.3", "@expo/ngrok": "^4.1.3",
"@expo/vector-icons": "^14.1.0", "@expo/vector-icons": "^14.1.0",
"@google/genai": "^0.13.0",
"@react-native-async-storage/async-storage": "2.1.2", "@react-native-async-storage/async-storage": "2.1.2",
"@react-navigation/bottom-tabs": "^7.3.10", "@react-navigation/bottom-tabs": "^7.3.10",
"@react-navigation/elements": "^2.3.8", "@react-navigation/elements": "^2.3.8",
"@react-navigation/native": "^7.1.6", "@react-navigation/native": "^7.1.6",
"@supabase/supabase-js": "2.49.5-next.1", "@supabase/supabase-js": "2.49.5-next.1",
"base64-arraybuffer": "^1.0.2",
"cors": "^2.8.5",
"dotenv": "^16.5.0",
"expo": "^53.0.9", "expo": "^53.0.9",
"expo-blur": "~14.1.4", "expo-blur": "~14.1.4",
"expo-constants": "~17.1.6", "expo-constants": "~17.1.6",
"expo-file-system": "~18.1.9",
"expo-font": "~13.3.1", "expo-font": "~13.3.1",
"expo-haptics": "~14.1.4", "expo-haptics": "~14.1.4",
"expo-image": "~2.1.7", "expo-image": "~2.1.7",
"expo-image-manipulator": "~13.1.6",
"expo-image-picker": "~16.1.4", "expo-image-picker": "~16.1.4",
"expo-linking": "~7.1.4", "expo-linking": "~7.1.4",
"expo-router": "~5.0.6", "expo-router": "~5.0.6",
@ -34,6 +40,8 @@
"expo-symbols": "~0.4.4", "expo-symbols": "~0.4.4",
"expo-system-ui": "~5.0.7", "expo-system-ui": "~5.0.7",
"expo-web-browser": "~14.1.6", "expo-web-browser": "~14.1.6",
"express": "^5.1.0",
"mime": "^4.0.7",
"nativewind": "^4.1.23", "nativewind": "^4.1.23",
"react": "19.0.0", "react": "19.0.0",
"react-dom": "19.0.0", "react-dom": "19.0.0",

View File

@ -1,5 +1,5 @@
import { supabase } from "@/services/supabase"; import { supabase } from "@/services/supabase";
import { Foods, LikedFood, SavedFood } from "@/types"; import { Foods, GenAIResult, LikedFood, SavedFood } from "@/types";
import { PostgrestError } from "@supabase/supabase-js"; import { PostgrestError } from "@supabase/supabase-js";
/** /**
@ -126,3 +126,90 @@ export const getIngredients = async (foodId: string): Promise<{ data: Ingredient
.eq("food_id", foodId) .eq("food_id", foodId)
return { data, error }; return { data, error };
}; };
/**
* Inserts a new food into the database.
*
* @param genAIResult - The result from the GenAI API.
* @param userId - The ID of the user who created the food.
* @param imageUrl - The URL of the image of the food.
* @returns A promise that resolves to an object containing the ID of the inserted food and any error that occurred.
*/
export const insertGenAIResult = async (
genAIResult: GenAIResult,
userId: string,
imageUrl: string
): Promise<{ data: string | null; error: PostgrestError | null }> => {
const client = supabase;
const now = new Date().toISOString();
const { foods, ingredients, nutrients, cooking_steps } = genAIResult;
const { data: foodInsert, error: foodError } = await client
.from("foods")
.insert({
name: foods.name,
description: foods.description,
time_to_cook_minutes: foods.time_to_cook_minutes,
skill_level: foods.skill_level,
ingredient_count: foods.ingredient_count,
calories: foods.calories,
image_url: imageUrl,
is_shared: false,
created_by: userId,
created_at: now,
})
.select("id")
.single();
if (foodError || !foodInsert) {
return { data: null, error: foodError };
}
const foodId = foodInsert.id;
const { error: nutrientError } = await client.from("nutrients").insert({
food_id: foodId,
...nutrients,
created_at: now,
});
if (nutrientError) {
return { data: null, error: nutrientError };
}
const ingredientInsert = ingredients.map((i) => ({
food_id: foodId,
name: i.name,
emoji: i.emoji,
created_at: now,
}));
const { error: ingredientError } = await client
.from("ingredients")
.insert(ingredientInsert);
if (ingredientError) {
return { data: null, error: ingredientError };
}
const stepInsert = cooking_steps.map((step) => ({
food_id: foodId,
step_order: step.step_order,
title: step.title,
description: step.description,
created_at: now,
}));
const { error: stepError } = await client
.from("cooking_steps")
.insert(stepInsert);
if (stepError) {
return { data: null, error: stepError };
}
return { data: foodId, error: null };
};

View File

@ -0,0 +1,36 @@
import { decode } from "base64-arraybuffer";
import { supabase } from "../supabase";
export async function uploadImageToSupabase(imageBase64: string, imageType: string, userId: string): Promise<string> {
if (!userId) {
throw new Error("User ID is required.");
}
const filePath = `${userId}/${new Date().getTime()}.${imageType === "image" ? "png" : "jpg"}`;
const contentType = imageType === "image" ? "image/png" : "image/jpeg";
const { error: uploadError } = await supabase
.storage
.from("food")
.upload(filePath, decode(imageBase64), {
contentType: contentType,
cacheControl: "3600",
upsert: false,
});
if (uploadError) {
console.error("[UPLOAD ERROR]", uploadError);
throw uploadError;
}
const { data, error } = await supabase
.storage
.from("food")
.createSignedUrl(filePath, 31536000);
if (error) {
console.error("[GET PUBLIC URL ERROR]", error);
throw error;
}
return data.signedUrl;
}

10
services/gemini.ts Normal file
View File

@ -0,0 +1,10 @@
import { GenAIResult } from '../types';
import { supabase } from './supabase';
export async function callGenAIonImage(imageUrl: string): Promise<{ data: GenAIResult | null; error: Error | null }> {
const { data, error } = await supabase.functions.invoke('gemini-food-analyze', {
body: { imageUrl: imageUrl },
})
return { data, error }
}

View File

@ -1,12 +1,25 @@
import AsyncStorage from '@react-native-async-storage/async-storage'; import * as SecureStore from 'expo-secure-store';
// import AsyncStorage from '@react-native-async-storage/async-storage';
import { createClient } from '@supabase/supabase-js'; import { createClient } from '@supabase/supabase-js';
const supabaseUrl = process.env.EXPO_PUBLIC_SUPABASE_PROJECT_URL as string; const supabaseUrl = process.env.EXPO_PUBLIC_SUPABASE_PROJECT_URL as string;
const supabaseAnonKey = process.env.EXPO_PUBLIC_SUPABASE_ANON_KEY as string; const supabaseAnonKey = process.env.EXPO_PUBLIC_SUPABASE_ANON_KEY as string;
const ExpoSecureStoreAdapter = {
getItem: (key: string) => {
return SecureStore.getItemAsync(key);
},
setItem: (key: string, value: string) => {
SecureStore.setItemAsync(key, value);
},
removeItem: (key: string) => {
SecureStore.deleteItemAsync(key);
},
};
export const supabase = createClient(supabaseUrl, supabaseAnonKey, { export const supabase = createClient(supabaseUrl, supabaseAnonKey, {
auth: { auth: {
storage: AsyncStorage, storage: ExpoSecureStoreAdapter as any,
autoRefreshToken: true, autoRefreshToken: true,
persistSession: true, persistSession: true,
detectSessionInUrl: false, detectSessionInUrl: false,

View File

@ -33,4 +33,31 @@ interface CookingStep {
description: string description: string
} }
export { CookingStep, Foods, LikedFood, SavedFood }; interface GenAIResult {
foods: {
name: string;
description: string;
time_to_cook_minutes: number;
skill_level: "Easy" | "Medium" | "Hard";
ingredient_count: number;
calories: number;
};
cooking_steps: {
title: string;
description: string;
step_order: number;
}[];
ingredients: {
name: string;
emoji: string;
}[];
nutrients: {
fat_g: number;
fiber_g: number;
protein_g: number;
carbs_g: number;
};
}
export { CookingStep, Foods, GenAIResult, LikedFood, SavedFood };