From d3bf5498ad1b45891614145efc0fd83166e49df5 Mon Sep 17 00:00:00 2001 From: xavidop Date: Wed, 23 Jul 2025 16:12:17 +0200 Subject: [PATCH] feat: video generation, image to image generation and a bunch of new features --- .firebaserc | 2 +- README.md | 61 +- apphosting.yaml | 2 - firebase.json | 72 +- next.config.ts | 21 + package-lock.json | 2000 +++++++++-------- package.json | 11 +- src/ai/dev.ts | 2 +- src/ai/flows/generate-card-video.ts | 191 ++ .../flows/generate-pokemon-card-from-photo.ts | 151 ++ src/ai/flows/generate-pokemon-card.ts | 36 +- src/ai/flows/scan-pokemon-card.ts | 89 +- src/ai/flows/summarize-card-information.ts | 50 - src/ai/genkit.ts | 31 +- src/app/api/generate-card-from-photo/route.ts | 34 + src/app/api/generate-card/route.ts | 4 +- src/app/api/generate-video/route.ts | 33 + .../collection/[cardId]/edit/page.tsx | 131 +- .../dashboard/collection/[cardId]/page.tsx | 461 ++++ src/app/dashboard/collection/page.tsx | 130 +- .../generate-from-photo/edit/page.tsx | 331 +++ .../dashboard/generate-from-photo/page.tsx | 131 ++ src/app/dashboard/generate/edit/page.tsx | 307 +++ src/app/dashboard/generate/page.tsx | 38 +- src/app/dashboard/page.tsx | 23 +- src/app/dashboard/scan/page.tsx | 8 +- src/app/dashboard/settings/page.tsx | 249 ++ src/components/cards/CardForm.tsx | 2 +- src/components/cards/CardGenerator.tsx | 81 +- .../cards/CardGeneratorFormOnly.tsx | 399 ++++ src/components/cards/CardItem.tsx | 394 +++- src/components/cards/CardScanner.tsx | 173 +- src/components/cards/PhotoCardGenerator.tsx | 501 +++++ .../cards/PhotoCardGeneratorFormOnly.tsx | 439 ++++ src/components/layout/Navbar.tsx | 125 +- src/components/providers/AuthProvider.tsx | 18 +- src/components/ui/share-button.tsx | 195 ++ src/lib/firebase.ts | 6 +- src/lib/firestore.ts | 300 ++- src/types/index.ts | 42 +- src/utils/downloadUtils.ts | 111 + src/utils/generationParamsUtils.ts | 67 + src/utils/imageUtils.ts | 102 + src/utils/shareUtils.ts | 236 ++ src/utils/storageUtils.ts | 121 + storage.rules | 24 + 46 files changed, 6606 insertions(+), 1329 deletions(-) create mode 100644 src/ai/flows/generate-card-video.ts create mode 100644 src/ai/flows/generate-pokemon-card-from-photo.ts delete mode 100644 src/ai/flows/summarize-card-information.ts create mode 100644 src/app/api/generate-card-from-photo/route.ts create mode 100644 src/app/api/generate-video/route.ts create mode 100644 src/app/dashboard/collection/[cardId]/page.tsx create mode 100644 src/app/dashboard/generate-from-photo/edit/page.tsx create mode 100644 src/app/dashboard/generate-from-photo/page.tsx create mode 100644 src/app/dashboard/generate/edit/page.tsx create mode 100644 src/app/dashboard/settings/page.tsx create mode 100644 src/components/cards/CardGeneratorFormOnly.tsx create mode 100644 src/components/cards/PhotoCardGenerator.tsx create mode 100644 src/components/cards/PhotoCardGeneratorFormOnly.tsx create mode 100644 src/components/ui/share-button.tsx create mode 100644 src/utils/downloadUtils.ts create mode 100644 src/utils/generationParamsUtils.ts create mode 100644 src/utils/imageUtils.ts create mode 100644 src/utils/shareUtils.ts create mode 100644 src/utils/storageUtils.ts create mode 100644 storage.rules diff --git a/.firebaserc b/.firebaserc index c5d92c2..e598d96 100644 --- a/.firebaserc +++ b/.firebaserc @@ -2,4 +2,4 @@ "projects": { "default": "cardex-1mapj" } -} \ No newline at end of file +} diff --git a/README.md b/README.md index ecb0ba9..34731a9 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ A modern web application for managing your PokΓ©mon card collection with AI-powe - πŸ€– **AI Card Scanning**: Use Gemini Vision to automatically identify PokΓ©mon cards from photos - ✨ **AI Card Generation**: Create custom PokΓ©mon cards using Google Imagen4 +- 🎬 **AI Video Generation**: Bring your cards to life with animated videos using Google Veo 2.0 +- πŸ“Έ **Photo-to-Card**: Transform your own photos into Pokemon cards using AI image generation - πŸ” **Secure Authentication**: Firebase Authentication with email and Google login - πŸ“± **Responsive Design**: Modern UI built with Next.js and Tailwind CSS - πŸ’Ύ **Cloud Storage**: Real-time data synchronization with Firestore @@ -30,6 +32,8 @@ graph TB J[Firestore Database] --> K[Real-time Updates] L[Gemini AI] --> M[Vision API] M --> N[Card Recognition] + O[Google Veo 2.0] --> P[Video Generation] + P --> Q[Card Animation] end subgraph "Development Tools" @@ -41,6 +45,7 @@ graph TB A --> H A --> J A --> L + A --> O O --> L ``` @@ -50,12 +55,15 @@ graph TB src/ β”œβ”€β”€ ai/ β”‚ └── flows/ -β”‚ └── scan-pokemon-card.ts # AI card scanning logic +β”‚ β”œβ”€β”€ scan-pokemon-card.ts # AI card scanning logic +β”‚ β”œβ”€β”€ generate-pokemon-card.ts # AI card generation logic +β”‚ β”œβ”€β”€ generate-pokemon-card-from-photo.ts # Photo-to-card logic +β”‚ └── generate-card-video.ts # AI video generation logic β”œβ”€β”€ app/ β”‚ β”œβ”€β”€ dashboard/ -β”‚ β”‚ β”œβ”€β”€ collection/ # Card collection pages -β”‚ β”‚ └── scan/ # Card scanning page -β”‚ └── page.tsx # Root page with auth routing +β”‚ β”‚ β”œβ”€β”€ collection/ # Card collection pages +β”‚ β”‚ └── scan/ # Card scanning page +β”‚ └── page.tsx # Root page with auth routing β”œβ”€β”€ components/ β”‚ β”œβ”€β”€ cards/ β”‚ β”‚ β”œβ”€β”€ CardForm.tsx # Reusable card form @@ -77,7 +85,8 @@ src/ - Node.js 18+ - Firebase project with Firestore and Authentication enabled -- Google AI API key for Gemini Vision and Imagen4 +- Google AI API key for Gemini Vision, Imagen4, and Veo 2.0 +- OpenAI API key for DALL-E 3 and GPT-4o (for photo-based card generation) ### Installation @@ -102,6 +111,7 @@ NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=your_project.appspot.com NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=your_sender_id NEXT_PUBLIC_FIREBASE_APP_ID=your_app_id GOOGLE_GENAI_API_KEY=your_gemini_api_key +OPENAI_API_KEY=your_openai_api_key ``` 4. Run the development server: @@ -134,6 +144,31 @@ npm run genkit:dev 4. Preview the generated card 5. Save it to your collection if you like it +### Photo-to-Card Generation +1. Navigate to the "Photo Card" page +2. Upload a reference photo that will inspire the card design +3. Fill out the Pokemon details: + - Pokemon name and type + - Style description explaining how to adapt the photo + - Card properties and optional stats +4. Click "Generate Pokemon Card from Photo" to create a card using your photo as reference +5. Preview the generated card that combines your photo's style with Pokemon card design +6. Save it to your collection + +### Video Generation +1. Navigate to any card in your collection +2. Click "Make Card Live" to generate an animated video +3. The AI will create a 5-second video where: + - The Pokemon comes to life and moves within the card frame + - Sparkles, glowing effects, and type-specific elemental effects are added + - The background has subtle movement and atmospheric effects + - The card maintains a gentle holographic shimmer +4. Video generation may take several minutes - the page will refresh automatically +5. Once complete, you can: + - Play the video directly in the card view + - Download the video to your device + - Share the animated card with others + ### Collection Management - View all your cards in a responsive grid layout - Edit card details by clicking on any card @@ -147,17 +182,29 @@ Firestore Collection: users/{userId}/pokemon_cards/{cardId} β”œβ”€β”€ name: string β”œβ”€β”€ set: string β”œβ”€β”€ rarity: string -β”œβ”€β”€ imageDataUrl: string (base64 encoded image) +β”œβ”€β”€ imageUrl: string (Firebase Storage URL) +β”œβ”€β”€ videoUrl?: string (Firebase Storage URL for animated video) +β”œβ”€β”€ videoGenerationStatus?: 'generating' | 'completed' | 'failed' +β”œβ”€β”€ videoPrompt?: string (AI prompt used for video generation) β”œβ”€β”€ userId: string β”œβ”€β”€ createdAt: timestamp └── updatedAt: timestamp ``` +Firebase Storage Structure: +``` +/users/{userId}/cards/{sanitized_card_name}_{timestamp}.png +/users/{userId}/videos/{sanitized_card_name}_{timestamp}.mp4 +``` + ## AI Integration -The app uses Google's AI services through the Genkit framework to: +The app uses multiple AI services through the Genkit framework: - **Gemini Vision API**: Analyze uploaded card images and extract card information - **Imagen4**: Generate custom Pokemon card artwork based on user parameters +- **Google Veo 2.0**: Create animated videos of Pokemon cards with magical effects and movements +- **OpenAI DALL-E 3**: Generate Pokemon cards based on reference photos with advanced image analysis +- **GPT-4o**: Analyze reference photos to extract visual elements for enhanced card generation - Provide structured data for user review and confirmation ## Contributing diff --git a/apphosting.yaml b/apphosting.yaml index cfe2f3f..2702f0f 100644 --- a/apphosting.yaml +++ b/apphosting.yaml @@ -18,7 +18,5 @@ env: secret: NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID - variable: NEXT_PUBLIC_FIREBASE_APP_ID secret: NEXT_PUBLIC_FIREBASE_APP_ID - - variable: GOOGLE_GENAI_API_KEY - secret: GOOGLE_GENAI_API_KEY - variable: NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID secret: NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID diff --git a/firebase.json b/firebase.json index 1f8f90b..0622454 100644 --- a/firebase.json +++ b/firebase.json @@ -1,40 +1,42 @@ { + "firestore": { + "rules": "firestore.rules", + "indexes": "firestore.indexes.json" + }, + "apphosting": { + "backendId": "studio", + "rootDir": "/", + "ignore": [ + "node_modules", + ".git", + "firebase-debug.log", + "firebase-debug.*.log", + "functions" + ] + }, + "emulators": { + "auth": { + "port": 9099, + "host": "0.0.0.0" + }, "firestore": { - "rules": "firestore.rules", - "indexes": "firestore.indexes.json" + "port": 8080, + "host": "0.0.0.0" }, - "apphosting": { - "backendId": "studio", - "rootDir": "/", - "ignore": [ - "node_modules", - ".git", - "firebase-debug.log", - "firebase-debug.*.log", - "functions" - ] + "hub": { + "port": 4400, + "host": "0.0.0.0" }, - "emulators": { - "auth": { - "port": 9099, - "host": "0.0.0.0" - }, - "firestore": { - "port": 8080, - "host": "0.0.0.0" - }, - "hub": { - "port": 4400, - "host": "0.0.0.0" - }, - "ui": { - "enabled": true, - "host": "0.0.0.0" - }, - "logging": { - "host": "0.0.0.0" - }, - "singleProjectMode": true - } + "ui": { + "enabled": true, + "host": "0.0.0.0" + }, + "logging": { + "host": "0.0.0.0" + }, + "singleProjectMode": true + }, + "storage": { + "rules": "storage.rules" } - \ No newline at end of file +} diff --git a/next.config.ts b/next.config.ts index 9d8de36..0b57d5e 100644 --- a/next.config.ts +++ b/next.config.ts @@ -16,6 +16,27 @@ const nextConfig: NextConfig = { port: '', pathname: '/**', }, + // Firebase Storage production + { + protocol: 'https', + hostname: 'firebasestorage.googleapis.com', + port: '', + pathname: '/**', + }, + // Firebase Storage emulator (for development) + { + protocol: 'http', + hostname: 'localhost', + port: '9199', + pathname: '/**', + }, + // Firebase Storage emulator alternative hostname + { + protocol: 'http', + hostname: '127.0.0.1', + port: '9199', + pathname: '/**', + }, ], }, }; diff --git a/package-lock.json b/package-lock.json index 69a0e34..fe8790a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,8 +8,8 @@ "name": "nextn", "version": "0.1.0", "dependencies": { - "@genkit-ai/googleai": "^1.8.0", - "@genkit-ai/next": "^1.8.0", + "@genkit-ai/googleai": "^1.15.1", + "@genkit-ai/next": "^1.15.1", "@google/genai": "^1.10.0", "@hookform/resolvers": "^4.1.3", "@radix-ui/react-accordion": "^1.2.3", @@ -38,10 +38,11 @@ "clsx": "^2.1.1", "date-fns": "^3.6.0", "dotenv": "^16.5.0", - "firebase": "^11.8.1", - "genkit": "^1.8.0", + "firebase": "^11.10.0", + "genkit": "^1.15.1", "lucide-react": "^0.475.0", "next": "15.3.3", + "openai": "^5.10.1", "patch-package": "^8.0.0", "react": "^18.3.1", "react-day-picker": "^8.10.1", @@ -57,7 +58,7 @@ "@types/react": "^18", "@types/react-dom": "^18", "concurrently": "^9.2.0", - "genkit-cli": "^1.8.0", + "genkit-cli": "^1.15.1", "postcss": "^8", "tailwindcss": "^3.4.1", "typescript": "^5" @@ -75,9 +76,9 @@ } }, "node_modules/@asteasolutions/zod-to-openapi": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/@asteasolutions/zod-to-openapi/-/zod-to-openapi-7.3.0.tgz", - "integrity": "sha512-7tE/r1gXwMIvGnXVUdIqUhCU1RevEFC4Jk6Bussa0fk1ecbnnINkZzj1EOAJyE/M3AI25DnHT/zKQL1/FPFi8Q==", + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@asteasolutions/zod-to-openapi/-/zod-to-openapi-7.3.4.tgz", + "integrity": "sha512-/2rThQ5zPi9OzVwes6U7lK1+Yvug0iXu25olp7S0XsYmOqnyMfxH7gdSQjn/+DSOHRg7wnotwGJSyL+fBKdnEA==", "dev": true, "license": "MIT", "dependencies": { @@ -129,9 +130,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.3.tgz", - "integrity": "sha512-W8bFfPA8DowP8l//sxjJLSLkD8iEjMc7cBVyP+u4cEv9sM7mdUCkgsj+t0n/BWPFtv7WWCN5Yzj0N6FJNUUqBQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz", + "integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==", "cpu": [ "ppc64" ], @@ -146,9 +147,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.3.tgz", - "integrity": "sha512-PuwVXbnP87Tcff5I9ngV0lmiSu40xw1At6i3GsU77U7cjDDB4s0X2cyFuBiDa1SBk9DnvWwnGvVaGBqoFWPb7A==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz", + "integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==", "cpu": [ "arm" ], @@ -163,9 +164,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.3.tgz", - "integrity": "sha512-XelR6MzjlZuBM4f5z2IQHK6LkK34Cvv6Rj2EntER3lwCBFdg6h2lKbtRjpTTsdEjD/WSe1q8UyPBXP1x3i/wYQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz", + "integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==", "cpu": [ "arm64" ], @@ -180,9 +181,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.3.tgz", - "integrity": "sha512-ogtTpYHT/g1GWS/zKM0cc/tIebFjm1F9Aw1boQ2Y0eUQ+J89d0jFY//s9ei9jVIlkYi8AfOjiixcLJSGNSOAdQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz", + "integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==", "cpu": [ "x64" ], @@ -197,9 +198,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.3.tgz", - "integrity": "sha512-eESK5yfPNTqpAmDfFWNsOhmIOaQA59tAcF/EfYvo5/QWQCzXn5iUSOnqt3ra3UdzBv073ykTtmeLJZGt3HhA+w==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz", + "integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==", "cpu": [ "arm64" ], @@ -214,9 +215,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.3.tgz", - "integrity": "sha512-Kd8glo7sIZtwOLcPbW0yLpKmBNWMANZhrC1r6K++uDR2zyzb6AeOYtI6udbtabmQpFaxJ8uduXMAo1gs5ozz8A==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz", + "integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==", "cpu": [ "x64" ], @@ -231,9 +232,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.3.tgz", - "integrity": "sha512-EJiyS70BYybOBpJth3M0KLOus0n+RRMKTYzhYhFeMwp7e/RaajXvP+BWlmEXNk6uk+KAu46j/kaQzr6au+JcIw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz", + "integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==", "cpu": [ "arm64" ], @@ -248,9 +249,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.3.tgz", - "integrity": "sha512-Q+wSjaLpGxYf7zC0kL0nDlhsfuFkoN+EXrx2KSB33RhinWzejOd6AvgmP5JbkgXKmjhmpfgKZq24pneodYqE8Q==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz", + "integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==", "cpu": [ "x64" ], @@ -265,9 +266,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.3.tgz", - "integrity": "sha512-dUOVmAUzuHy2ZOKIHIKHCm58HKzFqd+puLaS424h6I85GlSDRZIA5ycBixb3mFgM0Jdh+ZOSB6KptX30DD8YOQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz", + "integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==", "cpu": [ "arm" ], @@ -282,9 +283,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.3.tgz", - "integrity": "sha512-xCUgnNYhRD5bb1C1nqrDV1PfkwgbswTTBRbAd8aH5PhYzikdf/ddtsYyMXFfGSsb/6t6QaPSzxtbfAZr9uox4A==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz", + "integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==", "cpu": [ "arm64" ], @@ -299,9 +300,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.3.tgz", - "integrity": "sha512-yplPOpczHOO4jTYKmuYuANI3WhvIPSVANGcNUeMlxH4twz/TeXuzEP41tGKNGWJjuMhotpGabeFYGAOU2ummBw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz", + "integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==", "cpu": [ "ia32" ], @@ -316,9 +317,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.3.tgz", - "integrity": "sha512-P4BLP5/fjyihmXCELRGrLd793q/lBtKMQl8ARGpDxgzgIKJDRJ/u4r1A/HgpBpKpKZelGct2PGI4T+axcedf6g==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz", + "integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==", "cpu": [ "loong64" ], @@ -333,9 +334,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.3.tgz", - "integrity": "sha512-eRAOV2ODpu6P5divMEMa26RRqb2yUoYsuQQOuFUexUoQndm4MdpXXDBbUoKIc0iPa4aCO7gIhtnYomkn2x+bag==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz", + "integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==", "cpu": [ "mips64el" ], @@ -350,9 +351,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.3.tgz", - "integrity": "sha512-ZC4jV2p7VbzTlnl8nZKLcBkfzIf4Yad1SJM4ZMKYnJqZFD4rTI+pBG65u8ev4jk3/MPwY9DvGn50wi3uhdaghg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz", + "integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==", "cpu": [ "ppc64" ], @@ -367,9 +368,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.3.tgz", - "integrity": "sha512-LDDODcFzNtECTrUUbVCs6j9/bDVqy7DDRsuIXJg6so+mFksgwG7ZVnTruYi5V+z3eE5y+BJZw7VvUadkbfg7QA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz", + "integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==", "cpu": [ "riscv64" ], @@ -384,9 +385,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.3.tgz", - "integrity": "sha512-s+w/NOY2k0yC2p9SLen+ymflgcpRkvwwa02fqmAwhBRI3SC12uiS10edHHXlVWwfAagYSY5UpmT/zISXPMW3tQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz", + "integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==", "cpu": [ "s390x" ], @@ -401,9 +402,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.3.tgz", - "integrity": "sha512-nQHDz4pXjSDC6UfOE1Fw9Q8d6GCAd9KdvMZpfVGWSJztYCarRgSDfOVBY5xwhQXseiyxapkiSJi/5/ja8mRFFA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz", + "integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==", "cpu": [ "x64" ], @@ -418,9 +419,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.3.tgz", - "integrity": "sha512-1QaLtOWq0mzK6tzzp0jRN3eccmN3hezey7mhLnzC6oNlJoUJz4nym5ZD7mDnS/LZQgkrhEbEiTn515lPeLpgWA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz", + "integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==", "cpu": [ "arm64" ], @@ -435,9 +436,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.3.tgz", - "integrity": "sha512-i5Hm68HXHdgv8wkrt+10Bc50zM0/eonPb/a/OFVfB6Qvpiirco5gBA5bz7S2SHuU+Y4LWn/zehzNX14Sp4r27g==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz", + "integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==", "cpu": [ "x64" ], @@ -452,9 +453,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.3.tgz", - "integrity": "sha512-zGAVApJEYTbOC6H/3QBr2mq3upG/LBEXr85/pTtKiv2IXcgKV0RT0QA/hSXZqSvLEpXeIxah7LczB4lkiYhTAQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz", + "integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==", "cpu": [ "arm64" ], @@ -469,9 +470,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.3.tgz", - "integrity": "sha512-fpqctI45NnCIDKBH5AXQBsD0NDPbEFczK98hk/aa6HJxbl+UtLkJV2+Bvy5hLSLk3LHmqt0NTkKNso1A9y1a4w==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz", + "integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==", "cpu": [ "x64" ], @@ -485,10 +486,27 @@ "node": ">=18" } }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz", + "integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.3.tgz", - "integrity": "sha512-ROJhm7d8bk9dMCUZjkS8fgzsPAZEjtRJqCAmVgB0gMrvG7hfmPmz9k1rwO4jSiblFjYmNvbECL9uhaPzONMfgA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz", + "integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==", "cpu": [ "x64" ], @@ -503,9 +521,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.3.tgz", - "integrity": "sha512-YWcow8peiHpNBiIXHwaswPnAXLsLVygFwCB3A7Bh5jRkIBFWHGmNQ48AlX4xDvQNoMZlPYzjVOQDYEzWCqufMQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz", + "integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==", "cpu": [ "arm64" ], @@ -520,9 +538,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.3.tgz", - "integrity": "sha512-qspTZOIGoXVS4DpNqUYUs9UxVb04khS1Degaw/MnfMe7goQ3lTfQ13Vw4qY/Nj0979BGvMRpAYbs/BAxEvU8ew==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz", + "integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==", "cpu": [ "ia32" ], @@ -537,9 +555,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.3.tgz", - "integrity": "sha512-ICgUR+kPimx0vvRzf+N/7L7tVSQeE3BYY+NhHRHXS1kBuPO7z2+7ea2HbhDyZdTephgvNvKrlDDKUexuCVBVvg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz", + "integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==", "cpu": [ "x64" ], @@ -554,14 +572,15 @@ } }, "node_modules/@firebase/ai": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@firebase/ai/-/ai-1.3.0.tgz", - "integrity": "sha512-qBxJTtl9hpgZr050kVFTRADX6I0Ss6mEQyp/JEkBgKwwxixKnaRNqEDGFba4OKNL7K8E4Y7LlA/ZW6L8aCKH4A==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@firebase/ai/-/ai-1.4.1.tgz", + "integrity": "sha512-bcusQfA/tHjUjBTnMx6jdoPMpDl3r8K15Z+snHz9wq0Foox0F/V+kNLXucEOHoTL2hTc9l+onZCyBJs2QoIC3g==", + "license": "Apache-2.0", "dependencies": { "@firebase/app-check-interop-types": "0.3.3", - "@firebase/component": "0.6.17", + "@firebase/component": "0.6.18", "@firebase/logger": "0.4.4", - "@firebase/util": "1.12.0", + "@firebase/util": "1.12.1", "tslib": "^2.1.0" }, "engines": { @@ -573,14 +592,15 @@ } }, "node_modules/@firebase/analytics": { - "version": "0.10.16", - "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.16.tgz", - "integrity": "sha512-cMtp19He7Fd6uaj/nDEul+8JwvJsN8aRSJyuA1QN3QrKvfDDp+efjVurJO61sJpkVftw9O9nNMdhFbRcTmTfRQ==", + "version": "0.10.17", + "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.17.tgz", + "integrity": "sha512-n5vfBbvzduMou/2cqsnKrIes4auaBjdhg8QNA2ZQZ59QgtO2QiwBaXQZQE4O4sgB0Ds1tvLgUUkY+pwzu6/xEg==", + "license": "Apache-2.0", "dependencies": { - "@firebase/component": "0.6.17", - "@firebase/installations": "0.6.17", + "@firebase/component": "0.6.18", + "@firebase/installations": "0.6.18", "@firebase/logger": "0.4.4", - "@firebase/util": "1.12.0", + "@firebase/util": "1.12.1", "tslib": "^2.1.0" }, "peerDependencies": { @@ -588,14 +608,15 @@ } }, "node_modules/@firebase/analytics-compat": { - "version": "0.2.22", - "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.22.tgz", - "integrity": "sha512-VogWHgwkdYhjWKh8O1XU04uPrRaiDihkWvE/EMMmtWtaUtVALnpLnUurc3QtSKdPnvTz5uaIGKlW84DGtSPFbw==", + "version": "0.2.23", + "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.23.tgz", + "integrity": "sha512-3AdO10RN18G5AzREPoFgYhW6vWXr3u+OYQv6pl3CX6Fky8QRk0AHurZlY3Q1xkXO0TDxIsdhO3y65HF7PBOJDw==", + "license": "Apache-2.0", "dependencies": { - "@firebase/analytics": "0.10.16", + "@firebase/analytics": "0.10.17", "@firebase/analytics-types": "0.8.3", - "@firebase/component": "0.6.17", - "@firebase/util": "1.12.0", + "@firebase/component": "0.6.18", + "@firebase/util": "1.12.1", "tslib": "^2.1.0" }, "peerDependencies": { @@ -605,16 +626,18 @@ "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==" + "integrity": "sha512-VrIp/d8iq2g501qO46uGz3hjbDb8xzYMrbu8Tp0ovzIzrvJZ2fvmj649gTjge/b7cCCcjT0H37g1gVtlNhnkbg==", + "license": "Apache-2.0" }, "node_modules/@firebase/app": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.13.0.tgz", - "integrity": "sha512-Vj3MST245nq+V5UmmfEkB3isIgPouyUr8yGJlFeL9Trg/umG5ogAvrjAYvQ8gV7daKDoQSRnJKWI2JFpQqRsuQ==", + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.13.2.tgz", + "integrity": "sha512-jwtMmJa1BXXDCiDx1vC6SFN/+HfYG53UkfJa6qeN5ogvOunzbFDO3wISZy5n9xgYFUrEP6M7e8EG++riHNTv9w==", + "license": "Apache-2.0", "dependencies": { - "@firebase/component": "0.6.17", + "@firebase/component": "0.6.18", "@firebase/logger": "0.4.4", - "@firebase/util": "1.12.0", + "@firebase/util": "1.12.1", "idb": "7.1.1", "tslib": "^2.1.0" }, @@ -623,13 +646,14 @@ } }, "node_modules/@firebase/app-check": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.10.0.tgz", - "integrity": "sha512-AZlRlVWKcu8BH4Yf8B5EI8sOi2UNGTS8oMuthV45tbt6OVUTSQwFPIEboZzhNJNKY+fPsg7hH8vixUWFZ3lrhw==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.10.1.tgz", + "integrity": "sha512-MgNdlms9Qb0oSny87pwpjKush9qUwCJhfmTJHDfrcKo4neLGiSeVE4qJkzP7EQTIUFKp84pbTxobSAXkiuQVYQ==", + "license": "Apache-2.0", "dependencies": { - "@firebase/component": "0.6.17", + "@firebase/component": "0.6.18", "@firebase/logger": "0.4.4", - "@firebase/util": "1.12.0", + "@firebase/util": "1.12.1", "tslib": "^2.1.0" }, "engines": { @@ -640,15 +664,16 @@ } }, "node_modules/@firebase/app-check-compat": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.25.tgz", - "integrity": "sha512-3zrsPZWAKfV7DVC20T2dgfjzjtQnSJS65OfMOiddMUtJL1S5i0nAZKsdX0bOEvvrd0SBIL8jYnfpfDeQRnhV3w==", + "version": "0.3.26", + "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.26.tgz", + "integrity": "sha512-PkX+XJMLDea6nmnopzFKlr+s2LMQGqdyT2DHdbx1v1dPSqOol2YzgpgymmhC67vitXVpNvS3m/AiWQWWhhRRPQ==", + "license": "Apache-2.0", "dependencies": { - "@firebase/app-check": "0.10.0", + "@firebase/app-check": "0.10.1", "@firebase/app-check-types": "0.5.3", - "@firebase/component": "0.6.17", + "@firebase/component": "0.6.18", "@firebase/logger": "0.4.4", - "@firebase/util": "1.12.0", + "@firebase/util": "1.12.1", "tslib": "^2.1.0" }, "engines": { @@ -661,22 +686,25 @@ "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==" + "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==" + "integrity": "sha512-hyl5rKSj0QmwPdsAxrI5x1otDlByQ7bvNvVt8G/XPO2CSwE++rmSVf3VEhaeOR4J8ZFaF0Z0NDSmLejPweZ3ng==", + "license": "Apache-2.0" }, "node_modules/@firebase/app-compat": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.4.0.tgz", - "integrity": "sha512-LjLUrzbUgTa/sCtPoLKT2C7KShvLVHS3crnU1Du02YxnGVLE0CUBGY/NxgfR/Zg84mEbj1q08/dgesojxjn0dA==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.4.2.tgz", + "integrity": "sha512-LssbyKHlwLeiV8GBATyOyjmHcMpX/tFjzRUCS1jnwGAew1VsBB4fJowyS5Ud5LdFbYpJeS+IQoC+RQxpK7eH3Q==", + "license": "Apache-2.0", "dependencies": { - "@firebase/app": "0.13.0", - "@firebase/component": "0.6.17", + "@firebase/app": "0.13.2", + "@firebase/component": "0.6.18", "@firebase/logger": "0.4.4", - "@firebase/util": "1.12.0", + "@firebase/util": "1.12.1", "tslib": "^2.1.0" }, "engines": { @@ -686,17 +714,19 @@ "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==" + "integrity": "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==", + "license": "Apache-2.0" }, "node_modules/@firebase/auth-compat": { - "version": "0.5.26", - "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.5.26.tgz", - "integrity": "sha512-4baB7tR0KukyGzrlD25aeO4t0ChLifwvDQXTBiVJE9WWwJEOjkZpHmoU9Iww0+Vdalsq4sZ3abp6YTNjHyB1dA==", + "version": "0.5.28", + "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.5.28.tgz", + "integrity": "sha512-HpMSo/cc6Y8IX7bkRIaPPqT//Jt83iWy5rmDWeThXQCAImstkdNo3giFLORJwrZw2ptiGkOij64EH1ztNJzc7Q==", + "license": "Apache-2.0", "dependencies": { - "@firebase/auth": "1.10.6", + "@firebase/auth": "1.10.8", "@firebase/auth-types": "0.13.0", - "@firebase/component": "0.6.17", - "@firebase/util": "1.12.0", + "@firebase/component": "0.6.18", + "@firebase/util": "1.12.1", "tslib": "^2.1.0" }, "engines": { @@ -707,13 +737,14 @@ } }, "node_modules/@firebase/auth-compat/node_modules/@firebase/auth": { - "version": "1.10.6", - "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.10.6.tgz", - "integrity": "sha512-cFbo2FymQltog4atI9cKTO6CxKxS0dOMXslTQrlNZRH7qhDG44/d7QeI6GXLweFZtrnlecf52ESnNz1DU6ek8w==", + "version": "1.10.8", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.10.8.tgz", + "integrity": "sha512-GpuTz5ap8zumr/ocnPY57ZanX02COsXloY6Y/2LYPAuXYiaJRf6BAGDEdRq1BMjP93kqQnKNuKZUTMZbQ8MNYA==", + "license": "Apache-2.0", "dependencies": { - "@firebase/component": "0.6.17", + "@firebase/component": "0.6.18", "@firebase/logger": "0.4.4", - "@firebase/util": "1.12.0", + "@firebase/util": "1.12.1", "tslib": "^2.1.0" }, "engines": { @@ -732,23 +763,26 @@ "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==" + "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.6.17", - "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.17.tgz", - "integrity": "sha512-M6DOg7OySrKEFS8kxA3MU5/xc37fiOpKPMz6cTsMUcsuKB6CiZxxNAvgFta8HGRgEpZbi8WjGIj6Uf+TpOhyzg==", + "version": "0.6.18", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.18.tgz", + "integrity": "sha512-n28kPCkE2dL2U28fSxZJjzPPVpKsQminJ6NrzcKXAI0E/lYC8YhfwpyllScqVEvAI3J2QgJZWYgrX+1qGI+SQQ==", + "license": "Apache-2.0", "dependencies": { - "@firebase/util": "1.12.0", + "@firebase/util": "1.12.1", "tslib": "^2.1.0" }, "engines": { @@ -756,14 +790,15 @@ } }, "node_modules/@firebase/data-connect": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@firebase/data-connect/-/data-connect-0.3.9.tgz", - "integrity": "sha512-B5tGEh5uQrQeH0i7RvlU8kbZrKOJUmoyxVIX4zLA8qQJIN6A7D+kfBlGXtSwbPdrvyaejcRPcbOtqsDQ9HPJKw==", + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/@firebase/data-connect/-/data-connect-0.3.10.tgz", + "integrity": "sha512-VMVk7zxIkgwlVQIWHOKFahmleIjiVFwFOjmakXPd/LDgaB/5vzwsB5DWIYo+3KhGxWpidQlR8geCIn39YflJIQ==", + "license": "Apache-2.0", "dependencies": { "@firebase/auth-interop-types": "0.2.4", - "@firebase/component": "0.6.17", + "@firebase/component": "0.6.18", "@firebase/logger": "0.4.4", - "@firebase/util": "1.12.0", + "@firebase/util": "1.12.1", "tslib": "^2.1.0" }, "peerDependencies": { @@ -771,15 +806,16 @@ } }, "node_modules/@firebase/database": { - "version": "1.0.19", - "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.19.tgz", - "integrity": "sha512-khE+MIYK+XlIndVn/7mAQ9F1fwG5JHrGKaG72hblCC6JAlUBDd3SirICH6SMCf2PQ0iYkruTECth+cRhauacyQ==", + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.20.tgz", + "integrity": "sha512-H9Rpj1pQ1yc9+4HQOotFGLxqAXwOzCHsRSRjcQFNOr8lhUt6LeYjf0NSRL04sc4X0dWe8DsCvYKxMYvFG/iOJw==", + "license": "Apache-2.0", "dependencies": { "@firebase/app-check-interop-types": "0.3.3", "@firebase/auth-interop-types": "0.2.4", - "@firebase/component": "0.6.17", + "@firebase/component": "0.6.18", "@firebase/logger": "0.4.4", - "@firebase/util": "1.12.0", + "@firebase/util": "1.12.1", "faye-websocket": "0.11.4", "tslib": "^2.1.0" }, @@ -788,15 +824,16 @@ } }, "node_modules/@firebase/database-compat": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.0.10.tgz", - "integrity": "sha512-3sjl6oGaDDYJw/Ny0E5bO6v+KM3KoD4Qo/sAfHGdRFmcJ4QnfxOX9RbG9+ce/evI3m64mkPr24LlmTDduqMpog==", + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.0.11.tgz", + "integrity": "sha512-itEsHARSsYS95+udF/TtIzNeQ0Uhx4uIna0sk4E0wQJBUnLc/G1X6D7oRljoOuwwCezRLGvWBRyNrugv/esOEw==", + "license": "Apache-2.0", "dependencies": { - "@firebase/component": "0.6.17", - "@firebase/database": "1.0.19", - "@firebase/database-types": "1.0.14", + "@firebase/component": "0.6.18", + "@firebase/database": "1.0.20", + "@firebase/database-types": "1.0.15", "@firebase/logger": "0.4.4", - "@firebase/util": "1.12.0", + "@firebase/util": "1.12.1", "tslib": "^2.1.0" }, "engines": { @@ -804,22 +841,24 @@ } }, "node_modules/@firebase/database-types": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.14.tgz", - "integrity": "sha512-8a0Q1GrxM0akgF0RiQHliinhmZd+UQPrxEmUv7MnQBYfVFiLtKOgs3g6ghRt/WEGJHyQNslZ+0PocIwNfoDwKw==", + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.15.tgz", + "integrity": "sha512-XWHJ0VUJ0k2E9HDMlKxlgy/ZuTa9EvHCGLjaKSUvrQnwhgZuRU5N3yX6SZ+ftf2hTzZmfRkv+b3QRvGg40bKNw==", + "license": "Apache-2.0", "dependencies": { "@firebase/app-types": "0.9.3", - "@firebase/util": "1.12.0" + "@firebase/util": "1.12.1" } }, "node_modules/@firebase/firestore": { - "version": "4.7.16", - "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.7.16.tgz", - "integrity": "sha512-5OpvlwYVUTLEnqewOlXmtIpH8t2ISlZHDW0NDbKROM2D0ATMqFkMHdvl+/wz9zOAcb8GMQYlhCihOnVAliUbpQ==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.8.0.tgz", + "integrity": "sha512-QSRk+Q1/CaabKyqn3C32KSFiOdZpSqI9rpLK5BHPcooElumOBooPFa6YkDdiT+/KhJtel36LdAacha9BptMj2A==", + "license": "Apache-2.0", "dependencies": { - "@firebase/component": "0.6.17", + "@firebase/component": "0.6.18", "@firebase/logger": "0.4.4", - "@firebase/util": "1.12.0", + "@firebase/util": "1.12.1", "@firebase/webchannel-wrapper": "1.0.3", "@grpc/grpc-js": "~1.9.0", "@grpc/proto-loader": "^0.7.8", @@ -833,14 +872,15 @@ } }, "node_modules/@firebase/firestore-compat": { - "version": "0.3.51", - "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.51.tgz", - "integrity": "sha512-E5iubPhS6aAM7oSsHMx/FGBwfA2nbEHaK/hCs+MD3l3N7rHKnq4SYCGmVu/AraSJaMndZR1I37N9A/BH7aCq5A==", + "version": "0.3.53", + "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.53.tgz", + "integrity": "sha512-qI3yZL8ljwAYWrTousWYbemay2YZa+udLWugjdjju2KODWtLG94DfO4NALJgPLv8CVGcDHNFXoyQexdRA0Cz8Q==", + "license": "Apache-2.0", "dependencies": { - "@firebase/component": "0.6.17", - "@firebase/firestore": "4.7.16", + "@firebase/component": "0.6.18", + "@firebase/firestore": "4.8.0", "@firebase/firestore-types": "3.0.3", - "@firebase/util": "1.12.0", + "@firebase/util": "1.12.1", "tslib": "^2.1.0" }, "engines": { @@ -854,6 +894,7 @@ "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" @@ -863,6 +904,7 @@ "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" @@ -872,15 +914,16 @@ } }, "node_modules/@firebase/functions": { - "version": "0.12.8", - "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.12.8.tgz", - "integrity": "sha512-p+ft6dQW0CJ3BLLxeDb5Hwk9ARw01kHTZjLqiUdPRzycR6w7Z75ThkegNmL6gCss3S0JEpldgvehgZ3kHybVhA==", + "version": "0.12.9", + "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.12.9.tgz", + "integrity": "sha512-FG95w6vjbUXN84Ehezc2SDjGmGq225UYbHrb/ptkRT7OTuCiQRErOQuyt1jI1tvcDekdNog+anIObihNFz79Lg==", + "license": "Apache-2.0", "dependencies": { "@firebase/app-check-interop-types": "0.3.3", "@firebase/auth-interop-types": "0.2.4", - "@firebase/component": "0.6.17", + "@firebase/component": "0.6.18", "@firebase/messaging-interop-types": "0.2.3", - "@firebase/util": "1.12.0", + "@firebase/util": "1.12.1", "tslib": "^2.1.0" }, "engines": { @@ -891,14 +934,15 @@ } }, "node_modules/@firebase/functions-compat": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.25.tgz", - "integrity": "sha512-V0JKUw5W/7aznXf9BQ8LIYHCX6zVCM8Hdw7XUQ/LU1Y9TVP8WKRCnPB/qdPJ0xGjWWn7fhtwIYbgEw/syH4yTQ==", + "version": "0.3.26", + "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.26.tgz", + "integrity": "sha512-A798/6ff5LcG2LTWqaGazbFYnjBW8zc65YfID/en83ALmkhu2b0G8ykvQnLtakbV9ajrMYPn7Yc/XcYsZIUsjA==", + "license": "Apache-2.0", "dependencies": { - "@firebase/component": "0.6.17", - "@firebase/functions": "0.12.8", + "@firebase/component": "0.6.18", + "@firebase/functions": "0.12.9", "@firebase/functions-types": "0.6.3", - "@firebase/util": "1.12.0", + "@firebase/util": "1.12.1", "tslib": "^2.1.0" }, "engines": { @@ -911,15 +955,17 @@ "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==" + "integrity": "sha512-EZoDKQLUHFKNx6VLipQwrSMh01A1SaL3Wg6Hpi//x6/fJ6Ee4hrAeswK99I5Ht8roiniKHw4iO0B1Oxj5I4plg==", + "license": "Apache-2.0" }, "node_modules/@firebase/installations": { - "version": "0.6.17", - "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.17.tgz", - "integrity": "sha512-zfhqCNJZRe12KyADtRrtOj+SeSbD1H/K8J24oQAJVv/u02eQajEGlhZtcx9Qk7vhGWF5z9dvIygVDYqLL4o1XQ==", + "version": "0.6.18", + "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.18.tgz", + "integrity": "sha512-NQ86uGAcvO8nBRwVltRL9QQ4Reidc/3whdAasgeWCPIcrhOKDuNpAALa6eCVryLnK14ua2DqekCOX5uC9XbU/A==", + "license": "Apache-2.0", "dependencies": { - "@firebase/component": "0.6.17", - "@firebase/util": "1.12.0", + "@firebase/component": "0.6.18", + "@firebase/util": "1.12.1", "idb": "7.1.1", "tslib": "^2.1.0" }, @@ -928,14 +974,15 @@ } }, "node_modules/@firebase/installations-compat": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.17.tgz", - "integrity": "sha512-J7afeCXB7yq25FrrJAgbx8mn1nG1lZEubOLvYgG7ZHvyoOCK00sis5rj7TgDrLYJgdj/SJiGaO1BD3BAp55TeA==", + "version": "0.2.18", + "resolved": "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.18.tgz", + "integrity": "sha512-aLFohRpJO5kKBL/XYL4tN+GdwEB/Q6Vo9eZOM/6Kic7asSUgmSfGPpGUZO1OAaSRGwF4Lqnvi1f/f9VZnKzChw==", + "license": "Apache-2.0", "dependencies": { - "@firebase/component": "0.6.17", - "@firebase/installations": "0.6.17", + "@firebase/component": "0.6.18", + "@firebase/installations": "0.6.18", "@firebase/installations-types": "0.5.3", - "@firebase/util": "1.12.0", + "@firebase/util": "1.12.1", "tslib": "^2.1.0" }, "peerDependencies": { @@ -946,6 +993,7 @@ "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" } @@ -954,6 +1002,7 @@ "version": "0.4.4", "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.4.tgz", "integrity": "sha512-mH0PEh1zoXGnaR8gD1DeGeNZtWFKbnz9hDO91dIml3iou1gpOnLqXQ2dJfB71dj6dpmUjcQ6phY3ZZJbjErr9g==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.1.0" }, @@ -962,14 +1011,15 @@ } }, "node_modules/@firebase/messaging": { - "version": "0.12.21", - "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.21.tgz", - "integrity": "sha512-bYJ2Evj167Z+lJ1ach6UglXz5dUKY1zrJZd15GagBUJSR7d9KfiM1W8dsyL0lDxcmhmA/sLaBYAAhF1uilwN0g==", + "version": "0.12.22", + "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.22.tgz", + "integrity": "sha512-GJcrPLc+Hu7nk+XQ70Okt3M1u1eRr2ZvpMbzbc54oTPJZySHcX9ccZGVFcsZbSZ6o1uqumm8Oc7OFkD3Rn1/og==", + "license": "Apache-2.0", "dependencies": { - "@firebase/component": "0.6.17", - "@firebase/installations": "0.6.17", + "@firebase/component": "0.6.18", + "@firebase/installations": "0.6.18", "@firebase/messaging-interop-types": "0.2.3", - "@firebase/util": "1.12.0", + "@firebase/util": "1.12.1", "idb": "7.1.1", "tslib": "^2.1.0" }, @@ -978,13 +1028,14 @@ } }, "node_modules/@firebase/messaging-compat": { - "version": "0.2.21", - "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.21.tgz", - "integrity": "sha512-1yMne+4BGLbHbtyu/VyXWcLiefUE1+K3ZGfVTyKM4BH4ZwDFRGoWUGhhx+tKRX4Tu9z7+8JN67SjnwacyNWK5g==", + "version": "0.2.22", + "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.22.tgz", + "integrity": "sha512-5ZHtRnj6YO6f/QPa/KU6gryjmX4Kg33Kn4gRpNU6M1K47Gm8kcQwPkX7erRUYEH1mIWptfvjvXMHWoZaWjkU7A==", + "license": "Apache-2.0", "dependencies": { - "@firebase/component": "0.6.17", - "@firebase/messaging": "0.12.21", - "@firebase/util": "1.12.0", + "@firebase/component": "0.6.18", + "@firebase/messaging": "0.12.22", + "@firebase/util": "1.12.1", "tslib": "^2.1.0" }, "peerDependencies": { @@ -994,17 +1045,19 @@ "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==" + "integrity": "sha512-xfzFaJpzcmtDjycpDeCUj0Ge10ATFi/VHVIvEEjDNc3hodVBQADZ7BWQU7CuFpjSHE+eLuBI13z5F/9xOoGX8Q==", + "license": "Apache-2.0" }, "node_modules/@firebase/performance": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.7.6.tgz", - "integrity": "sha512-AsOz74dSTlyQGlnnbLWXiHFAsrxhpssPOsFFi4HgOJ5DjzkK7ZdZ/E9uMPrwFoXJyMVoybGRuqsL/wkIbFITsA==", + "version": "0.7.7", + "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.7.7.tgz", + "integrity": "sha512-JTlTQNZKAd4+Q5sodpw6CN+6NmwbY72av3Lb6wUKTsL7rb3cuBIhQSrslWbVz0SwK3x0ZNcqX24qtRbwKiv+6w==", + "license": "Apache-2.0", "dependencies": { - "@firebase/component": "0.6.17", - "@firebase/installations": "0.6.17", + "@firebase/component": "0.6.18", + "@firebase/installations": "0.6.18", "@firebase/logger": "0.4.4", - "@firebase/util": "1.12.0", + "@firebase/util": "1.12.1", "tslib": "^2.1.0", "web-vitals": "^4.2.4" }, @@ -1013,15 +1066,16 @@ } }, "node_modules/@firebase/performance-compat": { - "version": "0.2.19", - "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.19.tgz", - "integrity": "sha512-4cU0T0BJ+LZK/E/UwFcvpBCVdkStgBMQwBztM9fJPT6udrEUk3ugF5/HT+E2Z22FCXtIaXDukJbYkE/c3c6IHw==", + "version": "0.2.20", + "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.20.tgz", + "integrity": "sha512-XkFK5NmOKCBuqOKWeRgBUFZZGz9SzdTZp4OqeUg+5nyjapTiZ4XoiiUL8z7mB2q+63rPmBl7msv682J3rcDXIQ==", + "license": "Apache-2.0", "dependencies": { - "@firebase/component": "0.6.17", + "@firebase/component": "0.6.18", "@firebase/logger": "0.4.4", - "@firebase/performance": "0.7.6", + "@firebase/performance": "0.7.7", "@firebase/performance-types": "0.2.3", - "@firebase/util": "1.12.0", + "@firebase/util": "1.12.1", "tslib": "^2.1.0" }, "peerDependencies": { @@ -1031,17 +1085,19 @@ "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==" + "integrity": "sha512-IgkyTz6QZVPAq8GSkLYJvwSLr3LS9+V6vNPQr0x4YozZJiLF5jYixj0amDtATf1X0EtYHqoPO48a9ija8GocxQ==", + "license": "Apache-2.0" }, "node_modules/@firebase/remote-config": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.6.4.tgz", - "integrity": "sha512-ZyLJRT46wtycyz2+opEkGaoFUOqRQjt/0NX1WfUISOMCI/PuVoyDjqGpq24uK+e8D5NknyTpiXCVq5dowhScmg==", + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.6.5.tgz", + "integrity": "sha512-fU0c8HY0vrVHwC+zQ/fpXSqHyDMuuuglV94VF6Yonhz8Fg2J+KOowPGANM0SZkLvVOYpTeWp3ZmM+F6NjwWLnw==", + "license": "Apache-2.0", "dependencies": { - "@firebase/component": "0.6.17", - "@firebase/installations": "0.6.17", + "@firebase/component": "0.6.18", + "@firebase/installations": "0.6.18", "@firebase/logger": "0.4.4", - "@firebase/util": "1.12.0", + "@firebase/util": "1.12.1", "tslib": "^2.1.0" }, "peerDependencies": { @@ -1049,15 +1105,16 @@ } }, "node_modules/@firebase/remote-config-compat": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.17.tgz", - "integrity": "sha512-KelsBD0sXSC0u3esr/r6sJYGRN6pzn3bYuI/6pTvvmZbjBlxQkRabHAVH6d+YhLcjUXKIAYIjZszczd1QJtOyA==", + "version": "0.2.18", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.18.tgz", + "integrity": "sha512-YiETpldhDy7zUrnS8e+3l7cNs0sL7+tVAxvVYU0lu7O+qLHbmdtAxmgY+wJqWdW2c9nDvBFec7QiF58pEUu0qQ==", + "license": "Apache-2.0", "dependencies": { - "@firebase/component": "0.6.17", + "@firebase/component": "0.6.18", "@firebase/logger": "0.4.4", - "@firebase/remote-config": "0.6.4", + "@firebase/remote-config": "0.6.5", "@firebase/remote-config-types": "0.4.0", - "@firebase/util": "1.12.0", + "@firebase/util": "1.12.1", "tslib": "^2.1.0" }, "peerDependencies": { @@ -1067,15 +1124,17 @@ "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==" + "integrity": "sha512-7p3mRE/ldCNYt8fmWMQ/MSGRmXYlJ15Rvs9Rk17t8p0WwZDbeK7eRmoI1tvCPaDzn9Oqh+yD6Lw+sGLsLg4kKg==", + "license": "Apache-2.0" }, "node_modules/@firebase/storage": { - "version": "0.13.12", - "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.13.12.tgz", - "integrity": "sha512-5JmoFS01MYjW1XMQa5F5rD/kvMwBN10QF03bmcuJWq4lg+BJ3nRgL3sscWnyJPhwM/ZCyv2eRwcfzESVmsYkdQ==", + "version": "0.13.14", + "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.13.14.tgz", + "integrity": "sha512-xTq5ixxORzx+bfqCpsh+o3fxOsGoDjC1nO0Mq2+KsOcny3l7beyBhP/y1u5T6mgsFQwI1j6oAkbT5cWdDBx87g==", + "license": "Apache-2.0", "dependencies": { - "@firebase/component": "0.6.17", - "@firebase/util": "1.12.0", + "@firebase/component": "0.6.18", + "@firebase/util": "1.12.1", "tslib": "^2.1.0" }, "engines": { @@ -1086,14 +1145,15 @@ } }, "node_modules/@firebase/storage-compat": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.22.tgz", - "integrity": "sha512-29j6JgXTjQ76sOIkxmTNHQfYA/hDTeV9qGbn0jolynPXSg/AmzCB0CpCoCYrS0ja0Flgmy1hkA3XYDZ/eiV1Cg==", + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.24.tgz", + "integrity": "sha512-XHn2tLniiP7BFKJaPZ0P8YQXKiVJX+bMyE2j2YWjYfaddqiJnROJYqSomwW6L3Y+gZAga35ONXUJQju6MB6SOQ==", + "license": "Apache-2.0", "dependencies": { - "@firebase/component": "0.6.17", - "@firebase/storage": "0.13.12", + "@firebase/component": "0.6.18", + "@firebase/storage": "0.13.14", "@firebase/storage-types": "0.8.3", - "@firebase/util": "1.12.0", + "@firebase/util": "1.12.1", "tslib": "^2.1.0" }, "engines": { @@ -1107,16 +1167,18 @@ "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.12.0", - "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.12.0.tgz", - "integrity": "sha512-Z4rK23xBCwgKDqmzGVMef+Vb4xso2j5Q8OG0vVL4m4fA5ZjPMYQazu8OJJC3vtQRC3SQ/Pgx/6TPNVsCd70QRw==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.12.1.tgz", + "integrity": "sha512-zGlBn/9Dnya5ta9bX/fgEoNC3Cp8s6h+uYPYaDieZsFOAdHP/ExzQ/eaDgxD3GOROdPkLKpvKY0iIzr9adle0w==", "hasInstallScript": true, + "license": "Apache-2.0", "dependencies": { "tslib": "^2.1.0" }, @@ -1127,7 +1189,8 @@ "node_modules/@firebase/webchannel-wrapper": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.3.tgz", - "integrity": "sha512-2xCRM9q9FlzGZCdgDMJwc0gyUkWFtkosy7Xxr6sFgQwn+wMNIWd7xIvYNauU1r64B5L5rsGKy/n9TKJ0aAFeqQ==" + "integrity": "sha512-2xCRM9q9FlzGZCdgDMJwc0gyUkWFtkosy7Xxr6sFgQwn+wMNIWd7xIvYNauU1r64B5L5rsGKy/n9TKJ0aAFeqQ==", + "license": "Apache-2.0" }, "node_modules/@floating-ui/core": { "version": "1.6.9", @@ -1164,12 +1227,12 @@ "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==" }, "node_modules/@genkit-ai/ai": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@genkit-ai/ai/-/ai-1.8.0.tgz", - "integrity": "sha512-TIhFgQCThdVOyrk6qiVF8dPfz4XmL3RxE9OCidhqcpGrGE5YeRvle+nWIbkNIojdLURQf3/dBxNdaqZZ7B3msQ==", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@genkit-ai/ai/-/ai-1.15.2.tgz", + "integrity": "sha512-TG9QrY+x3AsddfJLeoBir8Dh4TupYy5gLrNkcABr3iZwrXxaBW6vamWRYw7ulgC0R0s5xcbqO63+ASDxSKzOug==", "license": "Apache-2.0", "dependencies": { - "@genkit-ai/core": "1.8.0", + "@genkit-ai/core": "1.15.2", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.11.19", "colorette": "^2.0.20", @@ -1177,21 +1240,22 @@ "json5": "^2.2.3", "node-fetch": "^3.3.2", "partial-json": "^0.1.7", + "uri-templates": "^0.2.0", "uuid": "^10.0.0" } }, "node_modules/@genkit-ai/core": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@genkit-ai/core/-/core-1.8.0.tgz", - "integrity": "sha512-XvK/Gq7fi8pFCJftzby/6EWoVBj5EUSai/9/104Y699wbub3qreoIGH2DN/CKOUzt0gD2i7fHeYlyTAnJ+TPbw==", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@genkit-ai/core/-/core-1.15.2.tgz", + "integrity": "sha512-I+Ma88LYUUQmgo9cfp29d1bjjDnsWx01FyYPGPf5Q3nvPWTQcRzVRno2vk93qN6lqfUfwbO6J84Hmm8hVSinQg==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/context-async-hooks": "^1.25.0", - "@opentelemetry/core": "^1.25.0", - "@opentelemetry/sdk-metrics": "^1.25.0", + "@opentelemetry/context-async-hooks": "~1.25.0", + "@opentelemetry/core": "~1.25.0", + "@opentelemetry/sdk-metrics": "~1.25.0", "@opentelemetry/sdk-node": "^0.52.0", - "@opentelemetry/sdk-trace-base": "^1.25.0", + "@opentelemetry/sdk-trace-base": "~1.25.0", "@types/json-schema": "^7.0.15", "ajv": "^8.12.0", "ajv-formats": "^3.0.1", @@ -1207,9 +1271,9 @@ } }, "node_modules/@genkit-ai/googleai": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@genkit-ai/googleai/-/googleai-1.8.0.tgz", - "integrity": "sha512-1XzH5hSiQ1HFEm0V8YuRlL90YIRBLVNjs+rOpGqeflecmP2kNBq9+wnBwGzvQl7vHpF5DOC9MjJRMFwrOZWhgg==", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@genkit-ai/googleai/-/googleai-1.15.2.tgz", + "integrity": "sha512-Xu72/Ug1YSDBykiNTJCLMmVvangCS+fF8hRQ8fSKEM1/dTH0Se8vw75JtRnlHGcoQb/Enw8x0sJAG9UPVS3J1A==", "license": "Apache-2.0", "dependencies": { "@google/generative-ai": "^0.24.0", @@ -1217,36 +1281,36 @@ "node-fetch": "^3.3.2" }, "peerDependencies": { - "genkit": "^1.8.0" + "genkit": "^1.15.2" } }, "node_modules/@genkit-ai/next": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@genkit-ai/next/-/next-1.8.0.tgz", - "integrity": "sha512-IdIaLVePYc58p5mFtjhpsF5U+al/hQLKDszcXoLOkolCf3uxjJv4o21iWl2I0JLgvaMNZru+wbyWpgYUmY0xWw==", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@genkit-ai/next/-/next-1.15.2.tgz", + "integrity": "sha512-tInYu3CYi/48IzCchh9xm16fYqoCR8G33U9DmatwJLv81/fEztKzP0DgRbp5ksc8ppHmKbbDsoePULajHIE0Dg==", "license": "Apache-2.0", "peerDependencies": { - "genkit": "1.8.0", + "genkit": "1.15.2", "next": "^15.0.0", "zod": "^3.24.1" } }, "node_modules/@genkit-ai/telemetry-server": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@genkit-ai/telemetry-server/-/telemetry-server-1.8.0.tgz", - "integrity": "sha512-qkDSuV+ShdecGtAOO4YIBS0yi4QimgBNZ0wPWj1w4SX+9f4wdhMLPcf4Z81QtP+ZCEcZ8RqH0jvw9CIUfolb9g==", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@genkit-ai/telemetry-server/-/telemetry-server-1.15.2.tgz", + "integrity": "sha512-5RwiyCcEhVbmia5zpBSfLEHgvEUXvozRMN7LRo4r3CwzUXLwOWOOccc2Ad5QIvNZsEJN1YKvNQbYv66R6DQKWw==", "dev": true, "license": "Apache-2.0", "dependencies": { "@asteasolutions/zod-to-openapi": "^7.0.0", - "@genkit-ai/tools-common": "1.8.0", + "@genkit-ai/tools-common": "1.15.2", "@google-cloud/firestore": "^7.6.0", - "@opentelemetry/api": "^1.9.0", - "@opentelemetry/context-async-hooks": "^1.25.0", - "@opentelemetry/core": "^1.25.0", - "@opentelemetry/sdk-metrics": "^1.25.0", + "@opentelemetry/api": "~1.9.0", + "@opentelemetry/context-async-hooks": "~1.25.0", + "@opentelemetry/core": "~1.25.0", + "@opentelemetry/sdk-metrics": "~1.25.0", "@opentelemetry/sdk-node": "^0.52.0", - "@opentelemetry/sdk-trace-base": "^1.25.0", + "@opentelemetry/sdk-trace-base": "~1.25.0", "async-mutex": "^0.5.0", "express": "^4.21.0", "lockfile": "^1.0.4", @@ -1254,9 +1318,9 @@ } }, "node_modules/@genkit-ai/tools-common": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@genkit-ai/tools-common/-/tools-common-1.8.0.tgz", - "integrity": "sha512-W2EZTM9l/i5pOwtfxmFafHvBzln7WvHylzO8FbCe8f24LQILesamBCcYgFNtE6l1e386/2OR1BbKGc/oGmbzlA==", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@genkit-ai/tools-common/-/tools-common-1.15.2.tgz", + "integrity": "sha512-dU+raj9TMfNI2SC2WJhO2VVAOpbYbZeU0pELNzrMsz3KILOcODZrdt3sOmNaaQT8Ti6CZLYjtpslw89nsc7TeQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1312,9 +1376,9 @@ } }, "node_modules/@google-cloud/firestore": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.11.0.tgz", - "integrity": "sha512-88uZ+jLsp1aVMj7gh3EKYH1aulTAMFAp8sH/v5a9w8q8iqSG27RiWLoxSAFr/XocZ9hGiWH1kEnBw+zl3xAgNA==", + "version": "7.11.3", + "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.11.3.tgz", + "integrity": "sha512-qsM3/WHpawF07SRVvEJJVRwhYzM7o9qtuksyuqnrMig6fxIrwWnsezECWsG/D5TyYru51Fv5c/RTqNDQ2yU+4w==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1863,6 +1927,363 @@ "url": "https://opencollective.com/js-sdsl" } }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.16.0.tgz", + "integrity": "sha512-8ofX7gkZcLj9H9rSd50mCgm3SSF8C7XoclxJuLoV0Cz3rEQ1tv9MZRYYvJtm9n1BiEQQMzSmE/w2AEkNacLYfg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.6", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.0.1", + "express-rate-limit": "^7.5.0", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.23.8", + "zod-to-json-schema": "^3.24.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/body-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "devOptional": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/raw-body": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", + "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.6.3", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/@next/env": { "version": "15.3.3", "resolved": "https://registry.npmjs.org/@next/env/-/env-15.3.3.tgz", @@ -2050,9 +2471,9 @@ } }, "node_modules/@opentelemetry/context-async-hooks": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.1.tgz", - "integrity": "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==", + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.25.1.tgz", + "integrity": "sha512-UW/ge9zjvAEmRWVapOP0qyCvPulWU6cQxGxDbWEFfGOj1VBBZAuOqTo3X6yWmDTD3Xe15ysCZChHncr2xFMIfQ==", "license": "Apache-2.0", "engines": { "node": ">=14" @@ -2062,12 +2483,12 @@ } }, "node_modules/@opentelemetry/core": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", - "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/semantic-conventions": "1.28.0" + "@opentelemetry/semantic-conventions": "1.25.1" }, "engines": { "node": ">=14" @@ -2096,63 +2517,6 @@ "@opentelemetry/api": "^1.0.0" } }, - "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/core": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", - "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/semantic-conventions": "1.25.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/resources": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", - "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.25.1", - "@opentelemetry/semantic-conventions": "1.25.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/sdk-trace-base": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", - "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.25.1", - "@opentelemetry/resources": "1.25.1", - "@opentelemetry/semantic-conventions": "1.25.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", - "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=14" - } - }, "node_modules/@opentelemetry/exporter-trace-otlp-http": { "version": "0.52.1", "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.52.1.tgz", @@ -2172,63 +2536,6 @@ "@opentelemetry/api": "^1.0.0" } }, - "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/core": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", - "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/semantic-conventions": "1.25.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/resources": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", - "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.25.1", - "@opentelemetry/semantic-conventions": "1.25.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/sdk-trace-base": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", - "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.25.1", - "@opentelemetry/resources": "1.25.1", - "@opentelemetry/semantic-conventions": "1.25.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", - "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=14" - } - }, "node_modules/@opentelemetry/exporter-trace-otlp-proto": { "version": "0.52.1", "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.52.1.tgz", @@ -2248,63 +2555,6 @@ "@opentelemetry/api": "^1.0.0" } }, - "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/core": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", - "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/semantic-conventions": "1.25.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/resources": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", - "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.25.1", - "@opentelemetry/semantic-conventions": "1.25.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/sdk-trace-base": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", - "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.25.1", - "@opentelemetry/resources": "1.25.1", - "@opentelemetry/semantic-conventions": "1.25.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", - "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=14" - } - }, "node_modules/@opentelemetry/exporter-zipkin": { "version": "1.25.1", "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-1.25.1.tgz", @@ -2323,63 +2573,6 @@ "@opentelemetry/api": "^1.0.0" } }, - "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/core": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", - "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/semantic-conventions": "1.25.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/resources": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", - "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.25.1", - "@opentelemetry/semantic-conventions": "1.25.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/sdk-trace-base": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", - "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.25.1", - "@opentelemetry/resources": "1.25.1", - "@opentelemetry/semantic-conventions": "1.25.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", - "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=14" - } - }, "node_modules/@opentelemetry/instrumentation": { "version": "0.52.1", "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.52.1.tgz", @@ -2416,30 +2609,6 @@ "@opentelemetry/api": "^1.0.0" } }, - "node_modules/@opentelemetry/otlp-exporter-base/node_modules/@opentelemetry/core": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", - "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/semantic-conventions": "1.25.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/otlp-exporter-base/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", - "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=14" - } - }, "node_modules/@opentelemetry/otlp-grpc-exporter-base": { "version": "0.52.1", "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.52.1.tgz", @@ -2458,30 +2627,6 @@ "@opentelemetry/api": "^1.0.0" } }, - "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/core": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", - "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/semantic-conventions": "1.25.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", - "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=14" - } - }, "node_modules/@opentelemetry/otlp-transformer": { "version": "0.52.1", "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.52.1.tgz", @@ -2503,80 +2648,6 @@ "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/core": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", - "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/semantic-conventions": "1.25.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/resources": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", - "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.25.1", - "@opentelemetry/semantic-conventions": "1.25.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-metrics": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz", - "integrity": "sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.25.1", - "@opentelemetry/resources": "1.25.1", - "lodash.merge": "^4.6.2" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-trace-base": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", - "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.25.1", - "@opentelemetry/resources": "1.25.1", - "@opentelemetry/semantic-conventions": "1.25.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", - "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=14" - } - }, "node_modules/@opentelemetry/propagator-b3": { "version": "1.25.1", "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.25.1.tgz", @@ -2592,30 +2663,6 @@ "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@opentelemetry/propagator-b3/node_modules/@opentelemetry/core": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", - "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/semantic-conventions": "1.25.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/propagator-b3/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", - "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=14" - } - }, "node_modules/@opentelemetry/propagator-jaeger": { "version": "1.25.1", "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.25.1.tgz", @@ -2631,38 +2678,14 @@ "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@opentelemetry/propagator-jaeger/node_modules/@opentelemetry/core": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", - "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/semantic-conventions": "1.25.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/propagator-jaeger/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", - "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=14" - } - }, "node_modules/@opentelemetry/resources": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", - "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "1.30.1", - "@opentelemetry/semantic-conventions": "1.28.0" + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" }, "engines": { "node": ">=14" @@ -2688,54 +2711,15 @@ "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, - "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/core": { + "node_modules/@opentelemetry/sdk-metrics": { "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", - "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/semantic-conventions": "1.25.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/resources": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", - "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz", + "integrity": "sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "1.25.1", - "@opentelemetry/semantic-conventions": "1.25.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", - "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=14" - } - }, - "node_modules/@opentelemetry/sdk-metrics": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.30.1.tgz", - "integrity": "sha512-q9zcZ0Okl8jRgmy7eNW3Ku1XSgg3sDLa5evHZpCwjspw7E8Is4K/haRPDJrBcX3YSn/Y7gUvFnByNYEKQNbNog==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.30.1", - "@opentelemetry/resources": "1.30.1" + "@opentelemetry/resources": "1.25.1", + "lodash.merge": "^4.6.2" }, "engines": { "node": ">=14" @@ -2771,55 +2755,7 @@ "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/core": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", - "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/semantic-conventions": "1.25.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/resources": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", - "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.25.1", - "@opentelemetry/semantic-conventions": "1.25.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-metrics": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz", - "integrity": "sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.25.1", - "@opentelemetry/resources": "1.25.1", - "lodash.merge": "^4.6.2" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-trace-base": { + "node_modules/@opentelemetry/sdk-trace-base": { "version": "1.25.1", "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", @@ -2836,32 +2772,6 @@ "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", - "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=14" - } - }, - "node_modules/@opentelemetry/sdk-trace-base": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.30.1.tgz", - "integrity": "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.30.1", - "@opentelemetry/resources": "1.30.1", - "@opentelemetry/semantic-conventions": "1.28.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, "node_modules/@opentelemetry/sdk-trace-node": { "version": "1.25.1", "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.25.1.tgz", @@ -2882,67 +2792,7 @@ "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/context-async-hooks": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.25.1.tgz", - "integrity": "sha512-UW/ge9zjvAEmRWVapOP0qyCvPulWU6cQxGxDbWEFfGOj1VBBZAuOqTo3X6yWmDTD3Xe15ysCZChHncr2xFMIfQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/core": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", - "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/semantic-conventions": "1.25.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/resources": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", - "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.25.1", - "@opentelemetry/semantic-conventions": "1.25.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/sdk-trace-base": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", - "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.25.1", - "@opentelemetry/resources": "1.25.1", - "@opentelemetry/semantic-conventions": "1.25.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/semantic-conventions": { + "node_modules/@opentelemetry/semantic-conventions": { "version": "1.25.1", "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", @@ -2951,15 +2801,6 @@ "node": ">=14" } }, - "node_modules/@opentelemetry/semantic-conventions": { - "version": "1.28.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", - "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", - "license": "Apache-2.0", - "engines": { - "node": ">=14" - } - }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -4322,15 +4163,16 @@ } }, "node_modules/@types/request/node_modules/form-data": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.3.tgz", - "integrity": "sha512-XHIrMD0NpDrNM/Ckf7XJiBbLl57KEhT3+i3yY+eWm+cqYZJQTZrKo8Y8AWKnuV5GT4scfuUGt9LzNoIx3dU1nQ==", + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.5.tgz", + "integrity": "sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A==", "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.35", "safe-buffer": "^5.2.1" }, @@ -4587,9 +4429,9 @@ } }, "node_modules/axios": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", - "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz", + "integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==", "dev": true, "license": "MIT", "dependencies": { @@ -5740,9 +5582,9 @@ } }, "node_modules/esbuild": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.3.tgz", - "integrity": "sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz", + "integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -5753,31 +5595,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.3", - "@esbuild/android-arm": "0.25.3", - "@esbuild/android-arm64": "0.25.3", - "@esbuild/android-x64": "0.25.3", - "@esbuild/darwin-arm64": "0.25.3", - "@esbuild/darwin-x64": "0.25.3", - "@esbuild/freebsd-arm64": "0.25.3", - "@esbuild/freebsd-x64": "0.25.3", - "@esbuild/linux-arm": "0.25.3", - "@esbuild/linux-arm64": "0.25.3", - "@esbuild/linux-ia32": "0.25.3", - "@esbuild/linux-loong64": "0.25.3", - "@esbuild/linux-mips64el": "0.25.3", - "@esbuild/linux-ppc64": "0.25.3", - "@esbuild/linux-riscv64": "0.25.3", - "@esbuild/linux-s390x": "0.25.3", - "@esbuild/linux-x64": "0.25.3", - "@esbuild/netbsd-arm64": "0.25.3", - "@esbuild/netbsd-x64": "0.25.3", - "@esbuild/openbsd-arm64": "0.25.3", - "@esbuild/openbsd-x64": "0.25.3", - "@esbuild/sunos-x64": "0.25.3", - "@esbuild/win32-arm64": "0.25.3", - "@esbuild/win32-ia32": "0.25.3", - "@esbuild/win32-x64": "0.25.3" + "@esbuild/aix-ppc64": "0.25.8", + "@esbuild/android-arm": "0.25.8", + "@esbuild/android-arm64": "0.25.8", + "@esbuild/android-x64": "0.25.8", + "@esbuild/darwin-arm64": "0.25.8", + "@esbuild/darwin-x64": "0.25.8", + "@esbuild/freebsd-arm64": "0.25.8", + "@esbuild/freebsd-x64": "0.25.8", + "@esbuild/linux-arm": "0.25.8", + "@esbuild/linux-arm64": "0.25.8", + "@esbuild/linux-ia32": "0.25.8", + "@esbuild/linux-loong64": "0.25.8", + "@esbuild/linux-mips64el": "0.25.8", + "@esbuild/linux-ppc64": "0.25.8", + "@esbuild/linux-riscv64": "0.25.8", + "@esbuild/linux-s390x": "0.25.8", + "@esbuild/linux-x64": "0.25.8", + "@esbuild/netbsd-arm64": "0.25.8", + "@esbuild/netbsd-x64": "0.25.8", + "@esbuild/openbsd-arm64": "0.25.8", + "@esbuild/openbsd-x64": "0.25.8", + "@esbuild/openharmony-arm64": "0.25.8", + "@esbuild/sunos-x64": "0.25.8", + "@esbuild/win32-arm64": "0.25.8", + "@esbuild/win32-ia32": "0.25.8", + "@esbuild/win32-x64": "0.25.8" } }, "node_modules/escalade": { @@ -5844,6 +5687,29 @@ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.3.tgz", + "integrity": "sha512-nVpZkTMM9rF6AQ9gPJpFsNAMt48wIzB5TQgiTLdHiuO8XEDhUgZEhqKlZWXbIzo9VmJ/HvysHqEaVeD5v9TPvA==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/express": { "version": "4.21.2", "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", @@ -5890,6 +5756,22 @@ "url": "https://opencollective.com/express" } }, + "node_modules/express-rate-limit": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz", + "integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -5993,6 +5875,13 @@ "node": ">= 6" } }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "devOptional": true, + "license": "MIT" + }, "node_modules/fast-uri": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", @@ -6021,6 +5910,7 @@ "version": "0.11.4", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "license": "Apache-2.0", "dependencies": { "websocket-driver": ">=0.5.1" }, @@ -6120,48 +6010,50 @@ } }, "node_modules/firebase": { - "version": "11.8.1", - "resolved": "https://registry.npmjs.org/firebase/-/firebase-11.8.1.tgz", - "integrity": "sha512-oetXhPCvJZM4DVL/n/06442emMU+KzM0JLZjszpwlU6mqdFZqBwumBxn6hQkLukJyU5wsjihZHUY8HEAE2micg==", + "version": "11.10.0", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-11.10.0.tgz", + "integrity": "sha512-nKBXoDzF0DrXTBQJlZa+sbC5By99ysYU1D6PkMRYknm0nCW7rJly47q492Ht7Ndz5MeYSBuboKuhS1e6mFC03w==", + "license": "Apache-2.0", "dependencies": { - "@firebase/ai": "1.3.0", - "@firebase/analytics": "0.10.16", - "@firebase/analytics-compat": "0.2.22", - "@firebase/app": "0.13.0", - "@firebase/app-check": "0.10.0", - "@firebase/app-check-compat": "0.3.25", - "@firebase/app-compat": "0.4.0", + "@firebase/ai": "1.4.1", + "@firebase/analytics": "0.10.17", + "@firebase/analytics-compat": "0.2.23", + "@firebase/app": "0.13.2", + "@firebase/app-check": "0.10.1", + "@firebase/app-check-compat": "0.3.26", + "@firebase/app-compat": "0.4.2", "@firebase/app-types": "0.9.3", - "@firebase/auth": "1.10.6", - "@firebase/auth-compat": "0.5.26", - "@firebase/data-connect": "0.3.9", - "@firebase/database": "1.0.19", - "@firebase/database-compat": "2.0.10", - "@firebase/firestore": "4.7.16", - "@firebase/firestore-compat": "0.3.51", - "@firebase/functions": "0.12.8", - "@firebase/functions-compat": "0.3.25", - "@firebase/installations": "0.6.17", - "@firebase/installations-compat": "0.2.17", - "@firebase/messaging": "0.12.21", - "@firebase/messaging-compat": "0.2.21", - "@firebase/performance": "0.7.6", - "@firebase/performance-compat": "0.2.19", - "@firebase/remote-config": "0.6.4", - "@firebase/remote-config-compat": "0.2.17", - "@firebase/storage": "0.13.12", - "@firebase/storage-compat": "0.3.22", - "@firebase/util": "1.12.0" + "@firebase/auth": "1.10.8", + "@firebase/auth-compat": "0.5.28", + "@firebase/data-connect": "0.3.10", + "@firebase/database": "1.0.20", + "@firebase/database-compat": "2.0.11", + "@firebase/firestore": "4.8.0", + "@firebase/firestore-compat": "0.3.53", + "@firebase/functions": "0.12.9", + "@firebase/functions-compat": "0.3.26", + "@firebase/installations": "0.6.18", + "@firebase/installations-compat": "0.2.18", + "@firebase/messaging": "0.12.22", + "@firebase/messaging-compat": "0.2.22", + "@firebase/performance": "0.7.7", + "@firebase/performance-compat": "0.2.20", + "@firebase/remote-config": "0.6.5", + "@firebase/remote-config-compat": "0.2.18", + "@firebase/storage": "0.13.14", + "@firebase/storage-compat": "0.3.24", + "@firebase/util": "1.12.1" } }, "node_modules/firebase/node_modules/@firebase/auth": { - "version": "1.10.6", - "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.10.6.tgz", - "integrity": "sha512-cFbo2FymQltog4atI9cKTO6CxKxS0dOMXslTQrlNZRH7qhDG44/d7QeI6GXLweFZtrnlecf52ESnNz1DU6ek8w==", + "version": "1.10.8", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.10.8.tgz", + "integrity": "sha512-GpuTz5ap8zumr/ocnPY57ZanX02COsXloY6Y/2LYPAuXYiaJRf6BAGDEdRq1BMjP93kqQnKNuKZUTMZbQ8MNYA==", + "license": "Apache-2.0", "dependencies": { - "@firebase/component": "0.6.17", + "@firebase/component": "0.6.18", "@firebase/logger": "0.4.4", - "@firebase/util": "1.12.0", + "@firebase/util": "1.12.1", "tslib": "^2.1.0" }, "engines": { @@ -6221,15 +6113,16 @@ } }, "node_modules/form-data": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { @@ -6379,25 +6272,26 @@ } }, "node_modules/genkit": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/genkit/-/genkit-1.8.0.tgz", - "integrity": "sha512-+zLKLyZLqUfFtJWfYdRgx0soO8NaROYbu/ofmWOjhD9Dkc94q2pCqGMnFXZhSrVvgHNJz7Jn8AkI/DaOWj6PNw==", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/genkit/-/genkit-1.15.2.tgz", + "integrity": "sha512-GvssmZb6W9O+TskFsags3m5PjWWY7UD5/FvH+n2YDZhwdNS+hpAM8KcUmk7gulcX+EhB9+ZenMzxpRwE+NJf2g==", "license": "Apache-2.0", "dependencies": { - "@genkit-ai/ai": "1.8.0", - "@genkit-ai/core": "1.8.0", + "@genkit-ai/ai": "1.15.2", + "@genkit-ai/core": "1.15.2", "uuid": "^10.0.0" } }, "node_modules/genkit-cli": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/genkit-cli/-/genkit-cli-1.8.0.tgz", - "integrity": "sha512-FBl3lfYID7FztXo3zUlnRSld3JHr4+6gmqgUkNcwaVlZBh8FB1yhryo0BfqNGER4et8FMr1nsH8oBslc1t7uBw==", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/genkit-cli/-/genkit-cli-1.15.2.tgz", + "integrity": "sha512-Ql4rnapmyhEsHFzNhWg0KMceZDwc9dDaJeWpoh0SHUjSwhLHg1ZoIIOExUzUTZmkDYPnxH3q1GFQZc6ozxlxbA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@genkit-ai/telemetry-server": "1.8.0", - "@genkit-ai/tools-common": "1.8.0", + "@genkit-ai/telemetry-server": "1.15.2", + "@genkit-ai/tools-common": "1.15.2", + "@modelcontextprotocol/sdk": "^1.13.1", "axios": "^1.7.7", "colorette": "^2.0.20", "commander": "^11.1.0", @@ -6499,9 +6393,9 @@ } }, "node_modules/get-tsconfig": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz", - "integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", + "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6558,9 +6452,9 @@ } }, "node_modules/google-gax": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.6.0.tgz", - "integrity": "sha512-zKKLeLfcYBVOzzM48Brtn4EQkKcTli9w6c1ilzFK2NbJvcd4ATD8/XqFExImvE/W5IwMlKKwa5qqVufji3ioNQ==", + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.6.1.tgz", + "integrity": "sha512-V6eky/xz2mcKfAd1Ioxyd6nmA61gao3n01C+YeuIwu3vzM9EDR6wcVzMSIbLMDXWeoi9SHYctXuKYC5uJUT3eQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -6749,7 +6643,8 @@ "node_modules/http-parser-js": { "version": "0.5.10", "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz", - "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==" + "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==", + "license": "MIT" }, "node_modules/http-proxy-agent": { "version": "5.0.0", @@ -6780,9 +6675,9 @@ } }, "node_modules/http-proxy-agent/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6852,7 +6747,8 @@ "node_modules/idb": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", - "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==" + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==", + "license": "ISC" }, "node_modules/ieee754": { "version": "1.2.1", @@ -7132,6 +7028,13 @@ "node": ">=8" } }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "devOptional": true, + "license": "MIT" + }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -7899,14 +7802,35 @@ "node": ">=8" } }, + "node_modules/openai": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/openai/-/openai-5.10.1.tgz", + "integrity": "sha512-fq6xVfv1/gpLbsj8fArEt3b6B9jBxdhAK+VJ+bDvbUvNd+KTLlA3bnDeYZaBsGH9LUhJ1M1yXfp9sEyBLMx6eA==", + "license": "Apache-2.0", + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, "node_modules/openapi3-ts": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/openapi3-ts/-/openapi3-ts-4.4.0.tgz", - "integrity": "sha512-9asTNB9IkKEzWMcHmVZE7Ts3kC9G7AFHfs8i7caD8HbI76gEjdkId4z/AkP83xdZsH7PLAnnbl47qZkXuxpArw==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/openapi3-ts/-/openapi3-ts-4.5.0.tgz", + "integrity": "sha512-jaL+HgTq2Gj5jRcfdutgRGLosCy/hT8sQf6VOy+P+g36cZOjI1iukdPnijC+4CmeRzg/jEllJUboEic2FhxhtQ==", "dev": true, "license": "MIT", "dependencies": { - "yaml": "^2.5.0" + "yaml": "^2.8.0" } }, "node_modules/ora": { @@ -8132,6 +8056,16 @@ "node": ">= 6" } }, + "node_modules/pkce-challenge": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz", + "integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, "node_modules/postcss": { "version": "8.5.2", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.2.tgz", @@ -8365,6 +8299,16 @@ "once": "^1.3.1" } }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/qs": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", @@ -8818,6 +8762,58 @@ "node": "*" } }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/router/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/router/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/router/node_modules/path-to-regexp": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=16" + } + }, "node_modules/run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -9509,9 +9505,9 @@ } }, "node_modules/teeny-request/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "dev": true, "license": "MIT", "dependencies": { @@ -9700,9 +9696,9 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/tsx": { - "version": "4.19.4", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.4.tgz", - "integrity": "sha512-gK5GVzDkJK1SI1zwHf32Mqxf2tSJkNx+eYcNly5+nHvWqXUJYUkWBQtKauoESz3ymezAI++ZwT855x5p5eop+Q==", + "version": "4.20.3", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.3.tgz", + "integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==", "dev": true, "license": "MIT", "dependencies": { @@ -9816,6 +9812,22 @@ "node": ">= 0.8" } }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "devOptional": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/uri-templates": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/uri-templates/-/uri-templates-0.2.0.tgz", + "integrity": "sha512-EWkjYEN0L6KOfEoOH6Wj4ghQqU7eBZMJqRHQnxQAq+dSEzRPClkWjf8557HkWQXF6BrAUoLSAyy9i3RVTliaNg==", + "license": "http://geraintluff.github.io/tv4/LICENSE.txt" + }, "node_modules/use-callback-ref": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", @@ -9935,7 +9947,8 @@ "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==" + "integrity": "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==", + "license": "Apache-2.0" }, "node_modules/webidl-conversions": { "version": "3.0.1", @@ -9946,6 +9959,7 @@ "version": "0.7.4", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "license": "Apache-2.0", "dependencies": { "http-parser-js": ">=0.5.1", "safe-buffer": ">=5.1.0", @@ -9959,6 +9973,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "license": "Apache-2.0", "engines": { "node": ">=0.8.0" } @@ -10179,14 +10194,15 @@ } }, "node_modules/yaml": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", - "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", + "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", + "license": "ISC", "bin": { "yaml": "bin.mjs" }, "engines": { - "node": ">= 14" + "node": ">= 14.6" } }, "node_modules/yargs": { diff --git a/package.json b/package.json index 10ca185..cf5765f 100644 --- a/package.json +++ b/package.json @@ -15,8 +15,8 @@ "dev:full": "concurrently \"npm run emulators\" \"npm run dev\"" }, "dependencies": { - "@genkit-ai/googleai": "^1.8.0", - "@genkit-ai/next": "^1.8.0", + "@genkit-ai/googleai": "^1.15.1", + "@genkit-ai/next": "^1.15.1", "@google/genai": "^1.10.0", "@hookform/resolvers": "^4.1.3", "@radix-ui/react-accordion": "^1.2.3", @@ -45,10 +45,11 @@ "clsx": "^2.1.1", "date-fns": "^3.6.0", "dotenv": "^16.5.0", - "firebase": "^11.8.1", - "genkit": "^1.8.0", + "firebase": "^11.10.0", + "genkit": "^1.15.1", "lucide-react": "^0.475.0", "next": "15.3.3", + "openai": "^5.10.1", "patch-package": "^8.0.0", "react": "^18.3.1", "react-day-picker": "^8.10.1", @@ -64,7 +65,7 @@ "@types/react": "^18", "@types/react-dom": "^18", "concurrently": "^9.2.0", - "genkit-cli": "^1.8.0", + "genkit-cli": "^1.15.1", "postcss": "^8", "tailwindcss": "^3.4.1", "typescript": "^5" diff --git a/src/ai/dev.ts b/src/ai/dev.ts index 85d9248..439be0c 100644 --- a/src/ai/dev.ts +++ b/src/ai/dev.ts @@ -3,4 +3,4 @@ config(); import '@/ai/flows/summarize-card-information.ts'; import '@/ai/flows/scan-pokemon-card.ts'; -import '@/ai/flows/generate-pokemon-card.ts'; \ No newline at end of file +import '@/ai/flows/generate-pokemon-card.ts'; diff --git a/src/ai/flows/generate-card-video.ts b/src/ai/flows/generate-card-video.ts new file mode 100644 index 0000000..1d5503c --- /dev/null +++ b/src/ai/flows/generate-card-video.ts @@ -0,0 +1,191 @@ +// This file handles video generation for Pokemon cards using Veo 3 model +'use server'; + +/** + * @fileOverview Uses Google Veo 3 to generate animated videos of Pokemon cards. + * Note: This is a placeholder implementation until Veo 3 API is fully available + */ + +import { GoogleGenAI } from '@google/genai'; +import { getUserApiKeys } from '@/lib/firestore'; + +export interface GenerateCardVideoInput { + cardImageUrl: string; + pokemonName: string; + pokemonType: string; + customPrompt?: string; + userId?: string; +} + +export interface GenerateCardVideoOutput { + videoBase64?: string; + prompt?: string; + error?: string; +} + +export async function generateCardVideo(input: GenerateCardVideoInput): Promise { + try { + console.log('Starting video generation for:', input.pokemonName); + + // Get user API keys first if userId is provided + let apiKey: string | undefined; + + if (input.userId) { + try { + const userApiKeys = await getUserApiKeys(input.userId); + apiKey = userApiKeys?.geminiApiKey; + } catch (error) { + console.warn('Failed to get user API keys:', error); + } + } + + // Fallback to environment variables if no user API key + if (!apiKey) { + apiKey = process.env.GOOGLE_GENAI_API_KEY || process.env.GEMINI_API_KEY; + } + + if (!apiKey) { + return { error: 'Google AI API key is required. Please set your Gemini API key in settings or contact support.' }; + } + + // Create the animation prompt + const animationPrompt = input.customPrompt || + `Take this existing Pokemon card image of ${input.pokemonName}, a ${input.pokemonType}-type Pokemon, and create a magical animated video where: + - The Pokemon comes to life and moves naturally within the card frame + - Add sparkles, glowing effects, and ${input.pokemonType}-type elemental effects + - The background has subtle movement and atmospheric effects + - The card itself has a gentle holographic shimmer + - Keep the card frame intact and readable + - Make it feel like the Pokemon is truly alive and magical + - Use the provided card image as the base for animation`; + + console.log('Generated animation prompt:', animationPrompt); + + // Fetch and convert image to base64 + console.log('Fetching image from URL:', input.cardImageUrl); + const imageResponse = await fetch(input.cardImageUrl); + if (!imageResponse.ok) { + return { error: `Failed to fetch image: ${imageResponse.statusText}` }; + } + + const imageBuffer = await imageResponse.arrayBuffer(); + const base64Image = Buffer.from(imageBuffer).toString('base64'); + const mimeType = imageResponse.headers.get('content-type') || 'image/jpeg'; + + console.log('Image converted to base64, mime type:', mimeType); + + // Initialize Google GenAI client + const ai = new GoogleGenAI({ apiKey }); + + // Start video generation operation + console.log('Starting Veo video generation...'); + let operation = await ai.models.generateVideos({ + model: 'veo-2.0-generate-001', + prompt: animationPrompt, + image: { + mimeType: mimeType, + imageBytes: base64Image + }, + config: { + numberOfVideos: 1, + aspectRatio: '9:16', + durationSeconds: 5, // Set duration to 5 seconds + } + }); + + // Poll for completion + console.log('Waiting for video generation to complete...'); + let pollCount = 0; + const maxPolls = 60; // Maximum 10 minutes (60 * 10 seconds) + + while (!operation.done && pollCount < maxPolls) { + await new Promise(resolve => setTimeout(resolve, 10000)); // Wait 10 seconds + try { + operation = await ai.operations.getVideosOperation({operation: operation}); + pollCount++; + console.log(`Video generation status: ${operation.done ? 'completed' : 'in progress'} (poll ${pollCount}/${maxPolls})`); + } catch (pollError) { + console.error('Error polling for video status:', pollError); + break; + } + } + + if (!operation.done) { + return { + error: 'Video generation timed out after 10 minutes. The process is taking longer than expected. Please try again later.' + }; + } + + // Check if video was generated successfully + if (!operation.response?.generatedVideos?.[0]?.video) { + console.error('No video in operation response:', operation.response); + return { + error: 'No video generated by Veo model' + }; + } + + const generatedVideo = operation.response.generatedVideos[0].video; + console.log('Video generation completed. Video data available:', Object.keys(generatedVideo)); + + // Handle video data - we need to download it if it's a URI since HTML video can't handle authenticated URLs + if (generatedVideo.uri) { + console.log('Video generated with URI, downloading video data...'); + + try { + // Download the video using the correct API key parameter format + const downloadUrl = `${generatedVideo.uri}&key=${apiKey}`; + console.log('Downloading from URL:', downloadUrl); + + const videoResponse = await fetch(downloadUrl); + + if (!videoResponse.ok) { + console.error('Failed to download video:', videoResponse.status, videoResponse.statusText); + return { + error: `Failed to download generated video: ${videoResponse.status} ${videoResponse.statusText}` + }; + } + + // Convert to base64 for storage/transmission + const videoBuffer = await videoResponse.arrayBuffer(); + const videoBase64 = Buffer.from(videoBuffer).toString('base64'); + + console.log('Video successfully downloaded and converted to base64'); + + return { + videoBase64: videoBase64, + prompt: animationPrompt, + }; + + } catch (downloadError) { + console.error('Error downloading video:', downloadError); + return { + error: `Failed to download video: ${downloadError instanceof Error ? downloadError.message : 'Unknown error'}` + }; + } + + } else if (generatedVideo.videoBytes) { + console.log('Video generated with direct bytes'); + return { + videoBase64: generatedVideo.videoBytes, + prompt: animationPrompt, + }; + } else { + console.error('No usable video data in response'); + return { + error: 'Video was generated but no accessible video data was provided' + }; + } + + } catch (error) { + console.error('Error in video generation setup:', error); + + let errorMessage = 'Failed to set up video generation'; + if (error instanceof Error) { + errorMessage = error.message; + } + + return { + error: errorMessage, + }; + } +} diff --git a/src/ai/flows/generate-pokemon-card-from-photo.ts b/src/ai/flows/generate-pokemon-card-from-photo.ts new file mode 100644 index 0000000..0431f69 --- /dev/null +++ b/src/ai/flows/generate-pokemon-card-from-photo.ts @@ -0,0 +1,151 @@ +// This file is automatically generated - edits will be lost! +'use server'; + +/** + * @fileOverview Uses Google Imagen4 to generate Pokemon cards based on an uploaded photo reference. + * + * - generatePokemonCardFromPhoto - A function that handles the photo-based card generation process. + * - GenerateFromPhotoInput - The input type for the generatePokemonCardFromPhoto function. + * - GenerateFromPhotoOutput - The return type for the generatePokemonCardFromPhoto function. + */ + +import { ai } from '@/ai/genkit'; +import { z } from 'genkit'; +import OpenAI from 'openai'; +import { getUserApiKeys } from '@/lib/firestore'; + +const GenerateFromPhotoInputSchema = z.object({ + userId: z.string().min(1, "User ID is required"), + photoDataUri: z + .string() + .describe( + "A reference photo as a data URI that must include a MIME type and use Base64 encoding. Expected format: 'data:;base64,'." + ), + pokemonName: z.string().min(1, "Pokemon name is required"), + pokemonType: z.string().min(1, "Pokemon type is required"), + styleDescription: z.string().min(10, "Style description is required - describe how to adapt the photo"), + language: z.enum(['english', 'japanese', 'chinese', 'korean', 'spanish', 'french', 'german', 'italian']).default('english'), + hp: z.number().min(10).max(999).optional().default(130), + attackName1: z.string().optional().default("Quick Attack"), + attackDamage1: z.number().min(0).max(999).optional().default(60), + attackName2: z.string().optional().default("Special Move"), + attackDamage2: z.number().min(0).max(999).optional().default(90), + weakness: z.string().optional().default("Fighting"), + resistance: z.string().optional().default("Psychic"), + retreatCost: z.number().min(0).max(5).optional().default(2), +}); + +export type GenerateFromPhotoInput = z.infer; + +const GenerateFromPhotoOutputSchema = z.object({ + imageBase64: z.string().describe("The generated card image as base64 string").optional(), + prompt: z.string().describe("The prompt used to generate the card").optional(), + error: z.string().describe("Error message if the card could not be generated").optional() +}); + +export type GenerateFromPhotoOutput = z.infer; + +export async function generatePokemonCardFromPhoto(input: GenerateFromPhotoInput): Promise { + return generateFromPhotoFlow(input); +} + +const generateFromPhotoFlow = ai.defineFlow( + { + name: 'generateFromPhotoFlow', + inputSchema: GenerateFromPhotoInputSchema, + outputSchema: GenerateFromPhotoOutputSchema, + }, + async (params: GenerateFromPhotoInput) => { + try { + // Get user's API keys from Firestore + const userApiKeys = await getUserApiKeys(params.userId); + const apiKey = userApiKeys?.openaiApiKey || process.env.OPENAI_API_KEY; + + if (!apiKey) { + return { + error: 'OpenAI API key is required. Please configure your API key in Settings or set OPENAI_API_KEY environment variable.' + }; + } + + const openai = new OpenAI({ + apiKey: apiKey, + }); + + // Generate the detailed prompt for the Pokemon card based on photo + console.log("Generating Pokemon card using reference photo..."); + + // Generate the enhanced prompt for the Pokemon card + const enhancedPrompt = generatePhotoBasedCardPrompt(params); + console.log("Generated prompt for image generation:", enhancedPrompt); + + // Use OpenAI responses.create with reference image for image generation + const response = await openai.responses.create({ + model: "gpt-4.1", + input: [ + { + role: "user", + content: [ + { type: "input_text", text: enhancedPrompt }, + { + type: "input_image", + image_url: params.photoDataUri, + detail: "high" + }, + ], + }, + ], + tools: [{ type: "image_generation" }], + }); + + const imageData = response.output + .filter((output) => output.type === "image_generation_call") + .map((output) => output.result); + + if (imageData.length > 0) { + const imageBase64 = imageData[0]; + return { + imageBase64: imageBase64 || "", + prompt: enhancedPrompt, + }; + } else { + console.log("No image generated, response output:", response.output); + return { error: 'No image generated from the reference photo.' }; + } + } catch (error: any) { + console.error('Error generating Pokemon card from photo:', error); + return { error: `Failed to generate Pokemon card: ${error?.message || 'Unknown error'}` }; + } + } +); + +function generatePhotoBasedCardPrompt(params: GenerateFromPhotoInput): string { + const { + pokemonName, + pokemonType, + styleDescription, + language, + hp = 130, + attackName1 = "Quick Attack", + attackDamage1 = 60, + attackName2 = "Special Move", + attackDamage2 = 90, + weakness = "Fighting", + resistance = "Psychic", + retreatCost = 2 + } = params; + + const languageInstruction = language !== 'english' ? `Write all card information in ${language}. The Pokemon name, attacks and description should be translated accordingly.` : ''; + + return `A regular classic PokΓ©mon trading card (not full art), featuring the PokΓ©mon "${pokemonName}". Use the reference image as inspiration and adapt it to create a Pokemon card illustration. Use the base photo, but in Studio Ghibli style, ${styleDescription} + +The card has a standard vertical layout with a detailed illustration in the main area. The Pokemon "${pokemonName}", a ${pokemonType} type, should be depicted as the main subject, incorporating elements and style from the reference photo. + +The card layout includes: The top left corner displays "${pokemonName}" in a stylized font, with "HP ${hp}" next to it in red. Below the PokΓ©mon's name, the ${pokemonType} type symbol is clearly visible. In the lower section, the card has two attacks listed. The first attack is ${attackName1} and should be written in ${language}, deals ${attackDamage1} damage. The second attack is ${attackName2} and should be written in ${language}, deals ${attackDamage2} damage. + +Below the attacks, the Weakness is ${weakness} (x2), Resistance is ${resistance} (-30), and Retreat Cost shows ${retreatCost} energy symbols. The bottom edge of the card features a thin line of text indicating the rarity and copyright information. + +${languageInstruction} + +The overall style should match official PokΓ©mon TCG card design with proper fonts, layout, and professional quality artwork, while incorporating the visual style and elements from the reference photo. +Make sure the information is displayed clearly and the Pokemon illustration feels natural within the adapted photographic style.`; +} diff --git a/src/ai/flows/generate-pokemon-card.ts b/src/ai/flows/generate-pokemon-card.ts index ac4d20a..0345df8 100644 --- a/src/ai/flows/generate-pokemon-card.ts +++ b/src/ai/flows/generate-pokemon-card.ts @@ -12,8 +12,10 @@ import { ai } from '@/ai/genkit'; import { z } from 'genkit'; import { GoogleGenAI } from '@google/genai'; +import { getUserApiKeys } from '@/lib/firestore'; const GeneratePokemonCardInputSchema = z.object({ + userId: z.string().min(1, "User ID is required"), pokemonName: z.string().min(1, "Pokemon name is required"), pokemonType: z.string().min(1, "Pokemon type is required"), isIllustrationRare: z.boolean(), @@ -52,28 +54,32 @@ const generatePokemonCardFlow = ai.defineFlow( outputSchema: GeneratePokemonCardOutputSchema, }, async (params: GeneratePokemonCardInput) => { - const apiKey = process.env.GOOGLE_GENAI_API_KEY || process.env.GEMINI_API_KEY; - - if (!apiKey) { - return { error: 'Google AI API key is required. Please set GOOGLE_GENAI_API_KEY or GEMINI_API_KEY environment variable.' }; - } - - const genAI = new GoogleGenAI({ - apiKey: apiKey, - }); - - // Generate the detailed prompt for the Pokemon card - const prompt = generateCardPrompt(params); - console.log("Generated prompt:", prompt); - try { + // Get user's API keys from Firestore + const userApiKeys = await getUserApiKeys(params.userId); + const apiKey = userApiKeys?.geminiApiKey || process.env.GOOGLE_GENAI_API_KEY || process.env.GEMINI_API_KEY; + + if (!apiKey) { + return { + error: 'Gemini API key is required. Please configure your API key in Settings or set GOOGLE_GENAI_API_KEY environment variable.' + }; + } + + const genAI = new GoogleGenAI({ + apiKey: apiKey, + }); + + // Generate the detailed prompt for the Pokemon card + const prompt = generateCardPrompt(params); + console.log("Generated prompt:", prompt); + const response = await genAI.models.generateImages({ model: 'models/imagen-4.0-ultra-generate-preview-06-06', prompt: prompt, config: { numberOfImages: 1, outputMimeType: 'image/jpeg', - aspectRatio: '3:4', // Pokemon cards are roughly 3:4 aspect ratio + aspectRatio: '3:4' // Pokemon cards are roughly 3:4 aspect ratio }, }); diff --git a/src/ai/flows/scan-pokemon-card.ts b/src/ai/flows/scan-pokemon-card.ts index cb1bcb5..b4a9c3b 100644 --- a/src/ai/flows/scan-pokemon-card.ts +++ b/src/ai/flows/scan-pokemon-card.ts @@ -9,7 +9,7 @@ * - ScanPokemonCardOutput - The return type for the scanPokemonCard function. */ -import {ai} from '@/ai/genkit'; +import {ai, createUserAI} from '@/ai/genkit'; import {z} from 'genkit'; const ScanPokemonCardInputSchema = z.object({ @@ -18,6 +18,7 @@ const ScanPokemonCardInputSchema = z.object({ .describe( "A photo of a Pokemon card, as a data URI that must include a MIME type and use Base64 encoding. Expected format: 'data:;base64,'." ), + userId: z.string().describe("The user ID for personalized API key usage.").optional(), }); export type ScanPokemonCardInput = z.infer; @@ -36,40 +37,60 @@ export async function scanPokemonCard(input: ScanPokemonCardInput): Promise { - try { - const {output} = await scanPokemonCardPrompt(input); - return output!; - } catch (error: any) { - console.error('Error during card scanning:', error); - return {error: 'Failed to scan Pokemon card.'}; + const scanPokemonCardFlow = userAI.defineFlow( + { + name: 'scanPokemonCardFlow', + inputSchema: ScanPokemonCardInputSchema, + outputSchema: ScanPokemonCardOutputSchema, + }, + async (input: ScanPokemonCardInput) => { + try { + const {output} = await scanPokemonCardPrompt(input); + return output!; + } catch (error: any) { + console.error('Error during card scanning:', error); + return {error: 'Failed to scan Pokemon card.'}; + } } + ); + + return scanPokemonCardFlow; +} + +async function scanPokemonCardFlow(input: ScanPokemonCardInput): Promise { + try { + // Get user-specific AI instance if userId provided + const userAI = input.userId ? await createUserAI(input.userId) : ai; + + // Create the flow with the appropriate AI instance + const flow = await createScanPromptAndFlow(userAI); + + // Execute the flow + return await flow(input); + } catch (error: any) { + console.error('Error in scan flow setup:', error); + return {error: 'Failed to scan Pokemon card.'}; } -); +} diff --git a/src/ai/flows/summarize-card-information.ts b/src/ai/flows/summarize-card-information.ts deleted file mode 100644 index d619e9d..0000000 --- a/src/ai/flows/summarize-card-information.ts +++ /dev/null @@ -1,50 +0,0 @@ -// Summarize Card Information Flow -'use server'; - -/** - * @fileOverview Summarizes the key information of a Pokemon card. - * - * - summarizeCardInformation - A function that summarizes the card information. - * - SummarizeCardInformationInput - The input type for the summarizeCardInformation function. - * - SummarizeCardInformationOutput - The return type for the summarizeCardInformation function. - */ - -import {ai} from '@/ai/genkit'; -import {z} from 'genkit'; - -const SummarizeCardInformationInputSchema = z.object({ - name: z.string().describe('The name of the Pokemon card.'), - set: z.string().describe('The set the card belongs to.'), - rarity: z.string().describe('The rarity of the card.'), -}); -export type SummarizeCardInformationInput = z.infer; - -const SummarizeCardInformationOutputSchema = z.object({ - summary: z.string().describe('A summary of the card information.'), -}); -export type SummarizeCardInformationOutput = z.infer; - -export async function summarizeCardInformation( - input: SummarizeCardInformationInput -): Promise { - return summarizeCardInformationFlow(input); -} - -const summarizeCardInformationPrompt = ai.definePrompt({ - name: 'summarizeCardInformationPrompt', - input: {schema: SummarizeCardInformationInputSchema}, - output: {schema: SummarizeCardInformationOutputSchema}, - prompt: `Summarize the key information of the following Pokemon card, including its name, set, and rarity:\n\nName: {{{name}}}\nSet: {{{set}}}\nRarity: {{{rarity}}}`, -}); - -const summarizeCardInformationFlow = ai.defineFlow( - { - name: 'summarizeCardInformationFlow', - inputSchema: SummarizeCardInformationInputSchema, - outputSchema: SummarizeCardInformationOutputSchema, - }, - async input => { - const {output} = await summarizeCardInformationPrompt(input); - return output!; - } -); diff --git a/src/ai/genkit.ts b/src/ai/genkit.ts index aa06071..921cfc6 100644 --- a/src/ai/genkit.ts +++ b/src/ai/genkit.ts @@ -1,17 +1,40 @@ import {genkit} from 'genkit'; import {googleAI} from '@genkit-ai/googleai'; +import { getUserApiKeys } from '@/lib/firestore'; -// Validate API key exists -const apiKey = process.env.GOOGLE_GENAI_API_KEY; +// Default genkit instance with environment variables +const defaultApiKey = process.env.GOOGLE_GENAI_API_KEY; -if (!apiKey) { +if (!defaultApiKey) { console.error('Missing Google AI API key. Please set GOOGLE_GENAI_API_KEY or GEMINI_API_KEY environment variable.'); throw new Error('Google AI API key is required for Genkit configuration'); } export const ai = genkit({ plugins: [ - googleAI({apiKey}) + googleAI({apiKey: defaultApiKey}) ], model: 'googleai/gemini-2.5-pro-preview-05-06', }); + +// Function to create genkit instance with user API key +export async function createUserAI(userId: string) { + try { + const userApiKeys = await getUserApiKeys(userId); + const userApiKey = userApiKeys?.geminiApiKey; + + if (userApiKey) { + return genkit({ + plugins: [ + googleAI({apiKey: userApiKey}) + ], + model: 'googleai/gemini-2.5-pro-preview-05-06', + }); + } + } catch (error) { + console.warn('Failed to get user API keys, falling back to default:', error); + } + + // Fallback to default AI instance + return ai; +} diff --git a/src/app/api/generate-card-from-photo/route.ts b/src/app/api/generate-card-from-photo/route.ts new file mode 100644 index 0000000..37dd680 --- /dev/null +++ b/src/app/api/generate-card-from-photo/route.ts @@ -0,0 +1,34 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { generatePokemonCardFromPhoto } from '@/ai/flows/generate-pokemon-card-from-photo'; + +export async function POST(request: NextRequest) { + try { + const body = await request.json(); + + // Validate required fields - including userId + const requiredFields = ['userId', 'photoDataUri', 'pokemonName', 'pokemonType', 'styleDescription']; + const missingFields = requiredFields.filter(field => !body[field]); + + if (missingFields.length > 0) { + return NextResponse.json( + { error: `Missing required fields: ${missingFields.join(', ')}` }, + { status: 400 } + ); + } + + // Call the AI flow + const result = await generatePokemonCardFromPhoto(body); + + if (result.error) { + return NextResponse.json({ error: result.error }, { status: 500 }); + } + + return NextResponse.json(result); + } catch (error) { + console.error('API route error:', error); + return NextResponse.json( + { error: 'Internal server error' }, + { status: 500 } + ); + } +} diff --git a/src/app/api/generate-card/route.ts b/src/app/api/generate-card/route.ts index 326028e..158e2c2 100644 --- a/src/app/api/generate-card/route.ts +++ b/src/app/api/generate-card/route.ts @@ -5,8 +5,8 @@ export async function POST(request: NextRequest) { try { const body = await request.json(); - // Validate required fields - const requiredFields = ['pokemonName', 'pokemonType', 'backgroundDescription', 'pokemonDescription']; + // Validate required fields - including userId + const requiredFields = ['userId', 'pokemonName', 'pokemonType', 'backgroundDescription', 'pokemonDescription']; const missingFields = requiredFields.filter(field => !body[field]); if (missingFields.length > 0) { diff --git a/src/app/api/generate-video/route.ts b/src/app/api/generate-video/route.ts new file mode 100644 index 0000000..2aed781 --- /dev/null +++ b/src/app/api/generate-video/route.ts @@ -0,0 +1,33 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { generateVideoForExistingCard } from '@/lib/firestore'; + +export async function POST(request: NextRequest) { + try { + const { userId, cardId } = await request.json(); + + if (!userId || !cardId) { + return NextResponse.json( + { error: 'User ID and Card ID are required' }, + { status: 400 } + ); + } + + // Start video generation in the background + await generateVideoForExistingCard(userId, cardId); + + return NextResponse.json({ + success: true, + message: 'Video generation started successfully' + }); + + } catch (error) { + console.error('Error in video generation API:', error); + + const errorMessage = error instanceof Error ? error.message : 'Internal server error'; + + return NextResponse.json( + { error: errorMessage }, + { status: 500 } + ); + } +} diff --git a/src/app/dashboard/collection/[cardId]/edit/page.tsx b/src/app/dashboard/collection/[cardId]/edit/page.tsx index 655b8d6..0253092 100644 --- a/src/app/dashboard/collection/[cardId]/edit/page.tsx +++ b/src/app/dashboard/collection/[cardId]/edit/page.tsx @@ -7,7 +7,11 @@ import { getCardById, updateCardInCollection } from '@/lib/firestore'; import type { PokemonCard } from '@/types'; import CardForm, { type CardFormInputs } from '@/components/cards/CardForm'; import { useToast } from '@/hooks/use-toast'; -import { Loader2, AlertTriangle } from 'lucide-react'; +import { Loader2, AlertTriangle, Download, FileDown } from 'lucide-react'; +import { downloadCardImage, downloadCardVideo } from '@/utils/downloadUtils'; +import { Button } from '@/components/ui/button'; +import ShareButton from '@/components/ui/share-button'; +import { serializeGenerationParams, serializePhotoGenerationParams } from '@/utils/generationParamsUtils'; export default function EditCardPage() { const { user, loading: authLoading } = useAuth(); @@ -39,6 +43,38 @@ export default function EditCardPage() { .then(fetchedCard => { if (fetchedCard) { setCard(fetchedCard); + + // Check if this is an AI-generated card and redirect to appropriate generator + if (fetchedCard.isGenerated) { + if (fetchedCard.isPhotoGenerated && fetchedCard.photoGenerationParams) { + // Redirect to photo generator with parameters + const params = serializePhotoGenerationParams(fetchedCard.photoGenerationParams); + // Add original card data + params.append('originalCardId', fetchedCard.id); + params.append('originalCardName', fetchedCard.name); + params.append('originalCardImageUrl', fetchedCard.imageUrl); + router.replace(`/dashboard/generate-from-photo/edit?${params.toString()}`); + return; + } else if (fetchedCard.generationParams) { + // Redirect to regular generator with parameters + const params = serializeGenerationParams(fetchedCard.generationParams); + // Add original card data + params.append('originalCardId', fetchedCard.id); + params.append('originalCardName', fetchedCard.name); + params.append('originalCardImageUrl', fetchedCard.imageUrl); + router.replace(`/dashboard/generate/edit?${params.toString()}`); + return; + } else { + // AI-generated card but no parameters stored - show a message + toast({ + title: "Unable to Edit", + description: "This AI-generated card was created before parameter storage was implemented. Please generate a new card.", + variant: "destructive" + }); + router.replace('/dashboard/collection'); + return; + } + } } else { setError('Card not found or you do not have permission to edit it.'); toast({ title: "Error", description: "Card not found.", variant: "destructive" }); @@ -58,16 +94,13 @@ export default function EditCardPage() { setIsSubmitting(true); try { - const updatedData: Partial = { + const updatedData = { name: data.name, set: data.set, rarity: data.rarity, - // imageDataUrl is part of CardFormInputs but might not change if not re-uploaded - // For simplicity, we assume imageDataUrl is handled if it's part of the form - // and if image upload was part of the edit form (which it isn't currently for simplicity) - // For this schema, imageDataUrl is part of CardFormInputs, so it will be included. - // If image editing is not part of this form, ensure data.imageDataUrl is the original one. - imageDataUrl: data.imageDataUrl, + // Only include imageDataUrl if it's different from the original imageUrl + // This allows the backend to determine if a new image upload is needed + ...(data.imageDataUrl !== card.imageUrl && { imageDataUrl: data.imageDataUrl }) }; await updateCardInCollection(user.uid, card.id, updatedData); toast({ title: 'Card Updated', description: `${data.name} has been updated.` }); @@ -81,6 +114,55 @@ export default function EditCardPage() { } }; + const handleDownloadCard = async () => { + if (!card) return; + + try { + await downloadCardImage(card.imageUrl, card.name); + toast({ + title: 'Download Started', + description: `${card.name} is being downloaded.`, + }); + } catch (error) { + console.error('Download error:', error); + toast({ + title: 'Download Failed', + description: 'Failed to download the card. Please try again.', + variant: 'destructive', + }); + } + }; + + const handleDownloadVideo = async () => { + if (!card?.videoUrl) return; + + try { + await downloadCardVideo(card.videoUrl, card.name); + toast({ + title: 'Video Download Started', + description: `${card.name} video is being downloaded.`, + }); + } catch (error) { + console.error('Video download error:', error); + toast({ + title: 'Video Download Failed', + description: 'Failed to download the video. Please try again.', + variant: 'destructive', + }); + } + }; + + // Helper function to check if video is ready for playback + const isVideoReady = () => { + return card?.videoUrl && ( + card.videoGenerationStatus === 'completed' || + card.videoUrl.includes('firebasestorage.googleapis.com') || + card.videoUrl.includes('firebaseapp.com') || + card.videoUrl.includes('googleapis.com/storage') || + card.videoUrl.includes('localhost:9199') // Support emulator URLs + ); + }; + if (loading) { return (
@@ -106,7 +188,38 @@ export default function EditCardPage() { return (
-

Edit Card: {card.name}

+
+

Edit Card: {card.name}

+
+ + {isVideoReady() && ( + + )} + {card.isGenerated && ( + + )} +
+
(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [showVideo, setShowVideo] = useState(false); + const [isGeneratingVideo, setIsGeneratingVideo] = useState(false); + const { toast } = useToast(); + + useEffect(() => { + if (authLoading) return; + + if (!user) { + router.replace('/login'); + return; + } + + if (user && cardId) { + setLoading(true); + setError(null); + getCardById(user.uid, cardId) + .then(fetchedCard => { + if (fetchedCard) { + setCard(fetchedCard); + } else { + setError('Card not found or you do not have permission to view it.'); + toast({ title: "Error", description: "Card not found.", variant: "destructive" }); + } + }) + .catch(err => { + console.error("Error fetching card:", err); + setError((err as Error).message || 'Failed to load card details.'); + toast({ title: "Error", description: "Failed to load card details.", variant: "destructive" }); + }) + .finally(() => setLoading(false)); + } + }, [user, cardId, router, toast, authLoading]); + + // Helper function to check if video is ready for playback + const isVideoReady = () => { + return card?.videoUrl && ( + card.videoGenerationStatus === 'completed' || + card.videoUrl.includes('firebasestorage.googleapis.com') || + card.videoUrl.includes('firebaseapp.com') || + card.videoUrl.includes('googleapis.com/storage') || + card.videoUrl.includes('localhost:9199') // Support emulator URLs + ); + }; + + const handleDownloadCard = async () => { + if (!card) return; + + try { + await downloadCardImage(card.imageUrl, card.name); + toast({ + title: 'Download Started', + description: `${card.name} is being downloaded.`, + }); + } catch (error) { + console.error('Download error:', error); + toast({ + title: 'Download Failed', + description: 'Failed to download the card. Please try again.', + variant: 'destructive', + }); + } + }; + + const handleDownloadVideo = async () => { + if (!card?.videoUrl) return; + + try { + await downloadCardVideo(card.videoUrl, card.name); + toast({ + title: 'Video Download Started', + description: `${card.name} video is being downloaded.`, + }); + } catch (error) { + console.error('Video download error:', error); + toast({ + title: 'Video Download Failed', + description: 'Failed to download the video. Please try again.', + variant: 'destructive', + }); + } + }; + + const handleGenerateVideo = async () => { + if (!card?.id) return; + + setIsGeneratingVideo(true); + + // Update the card status locally to show the banner + setCard(prev => prev ? { ...prev, videoGenerationStatus: 'generating' } : null); + + try { + await generateVideoForExistingCard(card.userId, card.id); + toast({ + title: 'Video Generation Started', + description: `Video generation for ${card.name} has been started. This may take a few minutes.`, + }); + } catch (error) { + console.error('Video generation error:', error); + toast({ + title: 'Video Generation Failed', + description: 'Failed to start video generation. Please try again.', + variant: 'destructive', + }); + + // Revert the status if there was an error + setCard(prev => prev ? { ...prev, videoGenerationStatus: 'failed' } : null); + } finally { + setIsGeneratingVideo(false); + } + }; + + const getVideoStatusBadge = () => { + if (!card?.videoGenerationStatus || card.videoGenerationStatus === 'pending') return null; + + const statusConfig = { + generating: { icon: Clock, text: 'Generating Video...', variant: 'secondary' as const }, + completed: { + icon: Video, + text: isVideoReady() ? 'Live Video Ready' : 'Video Processing...', + variant: 'default' as const + }, + failed: { icon: AlertCircle, text: 'Video Failed', variant: 'destructive' as const }, + }; + + const config = statusConfig[card.videoGenerationStatus]; + if (!config) return null; + + const { icon: Icon, text, variant } = config; + + return ( + + + {text} + + ); + }; + + if (loading) { + return ( +
+ +
+ ); + } + + if (error) { + return ( +
+ +

Error Loading Card

+

{error}

+ +
+ ); + } + + if (!card) { + return ( +
+

Card not found

+ +
+ ); + } + + return ( +
+ {/* Header */} +
+
+ +
+

{card.name}

+

Set: {card.set} | Rarity: {card.rarity}

+
+
+
+ {card.isGenerated && ( + + + AI Generated + + )} + {getVideoStatusBadge()} +
+
+ +
+ {/* Card Display */} + + +
+ {showVideo && card.videoUrl ? ( +
+ {card.videoUrl.includes('firebasestorage.googleapis.com') || + card.videoUrl.includes('firebaseapp.com') || + card.videoUrl.includes('googleapis.com/storage') || + card.videoUrl.includes('localhost:9199') ? ( +
+ ) : ( + <> + {card.name} + {isVideoReady() && ( + + )} + + )} +
+
+
+ + {/* Card Actions & Info */} +
+ {/* Video Generation Status */} + {card.videoGenerationStatus === 'generating' && ( + + +
+
+
+

+ Generating Video... +

+

+ This may take a few minutes. The page will refresh automatically. +

+
+
+
+
+ )} + + {/* Actions */} + + + Actions + + Download, edit, or generate video for your card + + + + {/* Download Card */} + + + {/* Download Video */} + {isVideoReady() && ( + + )} + + {/* Generate Video */} + {!card.videoUrl && card.videoGenerationStatus !== 'generating' && ( + + )} + + {/* Retry Video Generation */} + {card.videoGenerationStatus === 'failed' && ( + + )} + + {/* Edit Card */} + + + {/* Share Card */} + {card.isGenerated && ( + + )} + + + + {/* Card Information */} + + + Card Information + + +
+
+ Set: +

{card.set}

+
+
+ Rarity: +

{card.rarity}

+
+ {card.isGenerated && ( + <> +
+ Type: +

AI Generated

+
+ {card.isPhotoGenerated && ( +
+ Source: +

Photo-based

+
+ )} + + )} + {card.videoUrl && ( +
+ Video Status: +

+ {getVideoStatusBadge()} +

+
+ )} +
+
+
+
+
+
+ ); +} diff --git a/src/app/dashboard/collection/page.tsx b/src/app/dashboard/collection/page.tsx index 3eafe2f..25d0b96 100644 --- a/src/app/dashboard/collection/page.tsx +++ b/src/app/dashboard/collection/page.tsx @@ -6,7 +6,7 @@ import { getUserCards, deleteCardFromCollection } from '@/lib/firestore'; import type { PokemonCard } from '@/types'; import CardItem from '@/components/cards/CardItem'; import { Button } from '@/components/ui/button'; -import { Loader2, PlusCircle, AlertTriangle, Sparkles } from 'lucide-react'; +import { Loader2, PlusCircle, AlertTriangle, Sparkles, Camera, RefreshCw } from 'lucide-react'; import Link from 'next/link'; import { useToast } from '@/hooks/use-toast'; @@ -26,12 +26,55 @@ export default function CollectionPage() { } }, [user]); + // Set up periodic refresh for cards that are generating videos + useEffect(() => { + const hasGeneratingVideos = cards.some(card => card.videoGenerationStatus === 'generating'); + + if (!hasGeneratingVideos) return; + + console.log('Setting up automatic refresh for video generation...'); + const interval = setInterval(() => { + console.log('Refreshing cards to check for video generation updates...'); + fetchCards(); + }, 5000); // Check every 15 seconds (more frequent for better UX) + + return () => { + console.log('Clearing automatic refresh interval'); + clearInterval(interval); + }; + }, [cards, user]); + const fetchCards = async () => { if (!user) return; setLoading(true); setError(null); try { const userCards = await getUserCards(user.uid); + + // Check for video generation status changes (only for background updates) + userCards.forEach(newCard => { + const existingCard = cards.find(c => c.id === newCard.id); + if (existingCard) { + // Only show notifications for status changes from database updates (not manual updates) + if (existingCard.videoGenerationStatus === 'generating' && + newCard.videoGenerationStatus === 'completed' && + newCard.videoUrl) { + toast({ + title: 'Video Ready!', + description: `Your video for ${newCard.name} is now available.`, + }); + } + else if (existingCard.videoGenerationStatus === 'generating' && + newCard.videoGenerationStatus === 'failed') { + toast({ + title: 'Video Generation Failed', + description: `Failed to generate video for ${newCard.name}. You can try again.`, + variant: 'destructive', + }); + } + } + }); + setCards(userCards); } catch (e) { console.error("Error fetching cards:", e); @@ -57,6 +100,47 @@ export default function CollectionPage() { } }; + const handleUpdateCard = (updatedCard: PokemonCard) => { + setCards(prevCards => { + const updated = prevCards.map(card => { + if (card.id === updatedCard.id) { + const oldStatus = card.videoGenerationStatus; + const newStatus = updatedCard.videoGenerationStatus; + + // Check if video generation status changed to completed + if (oldStatus === 'generating' && + newStatus === 'completed' && + updatedCard.videoUrl) { + toast({ + title: 'Video Ready!', + description: `Your video for ${updatedCard.name} is now available.`, + }); + } + // Check if video generation failed + else if (oldStatus === 'generating' && + newStatus === 'failed') { + toast({ + title: 'Video Generation Failed', + description: `Failed to generate video for ${updatedCard.name}. You can try again.`, + variant: 'destructive', + }); + } + // Show immediate feedback when generation starts + else if (!oldStatus && newStatus === 'generating') { + toast({ + title: 'Video Generation Started', + description: `Generating video for ${updatedCard.name}. This page will refresh automatically.`, + }); + } + + return updatedCard; + } + return card; + }); + return updated; + }); + }; + if (loading) { return (
@@ -79,7 +163,18 @@ export default function CollectionPage() { return (
-

My Card Collection

+
+

My Card Collection

+ +
+ {/* Video Generation Status Banner */} + {(() => { + const generatingVideos = cards.filter(card => card.videoGenerationStatus === 'generating'); + if (generatingVideos.length > 0) { + return ( +
+
+
+
+

+ Generating {generatingVideos.length} video{generatingVideos.length > 1 ? 's' : ''} +

+

+ {generatingVideos.map(card => card.name).join(', ')} - This page will refresh automatically +

+
+
+
+ ); + } + return null; + })()} + {cards.length === 0 ? (
@@ -105,6 +223,11 @@ export default function CollectionPage() { Generate a Card +
) : ( -
+
{cards.map(card => ( ))} diff --git a/src/app/dashboard/generate-from-photo/edit/page.tsx b/src/app/dashboard/generate-from-photo/edit/page.tsx new file mode 100644 index 0000000..09a866d --- /dev/null +++ b/src/app/dashboard/generate-from-photo/edit/page.tsx @@ -0,0 +1,331 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import { useSearchParams } from 'next/navigation'; +import { useAuth } from '@/hooks/useAuth'; +import { useToast } from '@/hooks/use-toast'; +import { PhotoCardGeneratorFormOnly } from '@/components/cards/PhotoCardGeneratorFormOnly'; +import { addCardToCollection } from '@/lib/firestore'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { Button } from '@/components/ui/button'; +import { Loader2, Save } from 'lucide-react'; +import { useRouter } from 'next/navigation'; +import { compressBase64Image, getBase64Size, formatBytes } from '@/utils/imageUtils'; +import { parsePhotoGenerationParams } from '@/utils/generationParamsUtils'; +import type { GenerateFromPhotoInput } from '@/components/cards/PhotoCardGeneratorFormOnly'; +import Image from 'next/image'; + +export default function EditGenerateFromPhotoPage() { + const { user } = useAuth(); + const { toast } = useToast(); + const router = useRouter(); + const searchParams = useSearchParams(); + const [generatedCard, setGeneratedCard] = useState<{ imageBase64: string; prompt: string; params?: any } | null>(null); + const [originalCard, setOriginalCard] = useState<{ id: string; name: string; imageUrl: string } | null>(null); + const [isSaving, setIsSaving] = useState(false); + const [initialValues, setInitialValues] = useState | undefined>(undefined); + const [showComparison, setShowComparison] = useState(true); + + useEffect(() => { + // Parse parameters from URL search params + const params = parsePhotoGenerationParams(searchParams); + setInitialValues(params); + + // Extract original card data + const originalCardId = searchParams.get('originalCardId'); + const originalCardName = searchParams.get('originalCardName'); + const originalCardImageUrl = searchParams.get('originalCardImageUrl'); + + if (originalCardId && originalCardName && originalCardImageUrl) { + setOriginalCard({ + id: originalCardId, + name: originalCardName, + imageUrl: originalCardImageUrl, + }); + } + }, [searchParams]); + + const handleCardGenerated = (imageBase64: string, prompt: string, params: any) => { + setGeneratedCard({ imageBase64, prompt, params }); + }; + + const handleSaveToCollection = async () => { + if (!user || !generatedCard) return; + + setIsSaving(true); + try { + // Convert base64 to data URL + const originalImageDataUrl = `data:image/jpeg;base64,${generatedCard.imageBase64}`; + + // Check original size + const originalSize = getBase64Size(generatedCard.imageBase64); + console.log(`Original image size: ${formatBytes(originalSize)}`); + + // Compress the image if it's too large (> 800KB to leave some buffer) + let finalImageDataUrl = originalImageDataUrl; + if (originalSize > 800 * 1024) { + console.log('Compressing image for Firestore storage...'); + try { + finalImageDataUrl = await compressBase64Image(originalImageDataUrl, 400, 560, 0.7); + const compressedSize = getBase64Size(finalImageDataUrl.split(',')[1]); + console.log(`Compressed image size: ${formatBytes(compressedSize)}`); + + // If still too large, compress more aggressively + if (compressedSize > 800 * 1024) { + console.log('Further compressing image...'); + finalImageDataUrl = await compressBase64Image(originalImageDataUrl, 300, 420, 0.5); + const finalSize = getBase64Size(finalImageDataUrl.split(',')[1]); + console.log(`Final compressed size: ${formatBytes(finalSize)}`); + } + } catch (compressionError) { + console.warn('Image compression failed, using original:', compressionError); + // If compression fails, we'll try with the original and let Firestore handle the error + } + } + + // Exclude photoDataUri from params when saving to Firestore (too large for document storage) + const { photoDataUri, ...paramsWithoutPhoto } = generatedCard.params; + + await addCardToCollection(user.uid, { + name: 'Photo-Generated Pokemon Card', + set: 'AI Photo Generated', + rarity: 'Photo Special', + imageDataUrl: finalImageDataUrl, + isGenerated: true, + isPhotoGenerated: true, + prompt: generatedCard.prompt, + photoGenerationParams: paramsWithoutPhoto, + }); + + toast({ + title: 'Card Saved', + description: 'Your photo-generated Pokemon card has been added to your collection!', + }); + + // Clear the generated card after saving + setGeneratedCard(null); + router.push('/dashboard/collection'); + } catch (error) { + console.error('Error saving card:', error); + toast({ + title: 'Error', + description: 'Failed to save card to collection. Please try again.', + variant: 'destructive', + }); + } finally { + setIsSaving(false); + } + }; + + if (initialValues === undefined) { + return
Loading...
; + } + + return ( +
+
+

Edit Pokemon Card Generator from Photo

+

+ Modify your photo-based Pokemon card parameters and regenerate the card +

+
+ +
+ {/* Form Section - Takes up 2 columns */} +
+ +
+ + {/* Card Preview Section - Takes up 1 column */} +
+
+
+

Card Preview

+ {generatedCard && originalCard && ( +
+ + +
+ )} +
+ {generatedCard && originalCard ? ( + showComparison ? ( + /* Show comparison when both cards exist and comparison is enabled */ + + + + Comparison + + + Compare your original card with the newly generated one + + + + {/* Original Card */} +
+

Original Card

+
+ {originalCard.name} +
+
+ + {/* Divider */} +
+
+ VS +
+
+ + {/* New Generated Card */} +
+

New Generated Card

+
+ Generated Pokemon Card +
+
+ + +
+
+ ) : ( + /* Show only new card when comparison is disabled */ + + + New Generated Card + + Your updated photo-inspired Pokemon card is ready! + + + +
+ Generated Pokemon Card +
+ +
+
+ ) + ) : generatedCard ? ( + /* Show only new card if no original */ + + + New Generated Card + + Your updated photo-inspired Pokemon card is ready! + + + +
+ Generated Pokemon Card +
+ +
+
+ ) : originalCard ? ( + /* Show only original card */ + + + Original Card + + {originalCard.name} - This card will be replaced when you generate a new one + + + +
+ {originalCard.name} +
+
+
+ ) : ( + /* No cards available */ + + +
+

Generate a new card to see it here

+
+
+
+ )} +
+
+
+
+ ); +} diff --git a/src/app/dashboard/generate-from-photo/page.tsx b/src/app/dashboard/generate-from-photo/page.tsx new file mode 100644 index 0000000..b2be7da --- /dev/null +++ b/src/app/dashboard/generate-from-photo/page.tsx @@ -0,0 +1,131 @@ +'use client'; + +import { useState } from 'react'; +import { useAuth } from '@/hooks/useAuth'; +import { useToast } from '@/hooks/use-toast'; +import { PhotoCardGenerator } from '@/components/cards/PhotoCardGenerator'; +import { addCardToCollection } from '@/lib/firestore'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { Button } from '@/components/ui/button'; +import { Loader2, Save } from 'lucide-react'; +import { useRouter } from 'next/navigation'; +import { compressBase64Image, getBase64Size, formatBytes } from '@/utils/imageUtils'; + +export default function GenerateFromPhotoPage() { + const { user } = useAuth(); + const { toast } = useToast(); + const router = useRouter(); + const [generatedCard, setGeneratedCard] = useState<{ imageBase64: string; prompt: string; params?: any } | null>(null); + const [isSaving, setIsSaving] = useState(false); + + const handleCardGenerated = (imageBase64: string, prompt: string, params: any) => { + setGeneratedCard({ imageBase64, prompt, params }); + }; + + const handleSaveToCollection = async () => { + if (!user || !generatedCard) return; + + setIsSaving(true); + try { + // Convert base64 to data URL + const originalImageDataUrl = `data:image/jpeg;base64,${generatedCard.imageBase64}`; + + // Check original size + const originalSize = getBase64Size(generatedCard.imageBase64); + console.log(`Original image size: ${formatBytes(originalSize)}`); + + // Compress the image if it's too large (> 800KB to leave some buffer) + let finalImageDataUrl = originalImageDataUrl; + if (originalSize > 800 * 1024) { + console.log('Compressing image for Firestore storage...'); + try { + finalImageDataUrl = await compressBase64Image(originalImageDataUrl, 400, 560, 0.7); + const compressedSize = getBase64Size(finalImageDataUrl.split(',')[1]); + console.log(`Compressed image size: ${formatBytes(compressedSize)}`); + + // If still too large, compress more aggressively + if (compressedSize > 800 * 1024) { + console.log('Further compressing image...'); + finalImageDataUrl = await compressBase64Image(originalImageDataUrl, 300, 420, 0.5); + const finalSize = getBase64Size(finalImageDataUrl.split(',')[1]); + console.log(`Final compressed size: ${formatBytes(finalSize)}`); + } + } catch (compressionError) { + console.warn('Image compression failed, using original:', compressionError); + // If compression fails, we'll try with the original and let Firestore handle the error + } + } + + // Exclude photoDataUri from params when saving to Firestore (too large for document storage) + const { photoDataUri, ...paramsWithoutPhoto } = generatedCard.params; + + await addCardToCollection(user.uid, { + name: 'Photo-Generated Pokemon Card', + set: 'AI Photo Generated', + rarity: 'Photo Special', + imageDataUrl: finalImageDataUrl, + isGenerated: true, + isPhotoGenerated: true, + prompt: generatedCard.prompt, + photoGenerationParams: paramsWithoutPhoto, + }); + + toast({ + title: 'Card Saved', + description: 'Your photo-generated Pokemon card has been added to your collection!', + }); + + // Clear the generated card after saving + setGeneratedCard(null); + router.push('/dashboard/collection'); + } catch (error) { + console.error('Error saving card:', error); + toast({ + title: 'Error', + description: 'Failed to save card to collection. Please try again.', + variant: 'destructive', + }); + } finally { + setIsSaving(false); + } + }; + + return ( +
+
+

Pokemon Card Generator from Photo

+

+ Upload your own photo and transform it into a custom Pokemon card using AI-powered image generation +

+
+ + + + {generatedCard && ( + + + Save Your Card + + Your photo-inspired Pokemon card is ready! Save it to your collection. + + + + + + + )} +
+ ); +} diff --git a/src/app/dashboard/generate/edit/page.tsx b/src/app/dashboard/generate/edit/page.tsx new file mode 100644 index 0000000..c2cad60 --- /dev/null +++ b/src/app/dashboard/generate/edit/page.tsx @@ -0,0 +1,307 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import { useSearchParams } from 'next/navigation'; +import { CardGeneratorFormOnly } from '@/components/cards/CardGeneratorFormOnly'; +import { Button } from '@/components/ui/button'; +import { useAuth } from '@/hooks/useAuth'; +import { addCardToCollection } from '@/lib/firestore'; +import { useToast } from '@/hooks/use-toast'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { compressBase64Image, getBase64Size, formatBytes } from '@/utils/imageUtils'; +import { parseGenerationParams } from '@/utils/generationParamsUtils'; +import type { GeneratePokemonCardInput } from '@/components/cards/CardGeneratorFormOnly'; +import Image from 'next/image'; + +export default function EditGenerateCardPage() { + const { user } = useAuth(); + const { toast } = useToast(); + const searchParams = useSearchParams(); + const [generatedCard, setGeneratedCard] = useState<{ imageBase64: string; prompt: string; params?: any } | null>(null); + const [originalCard, setOriginalCard] = useState<{ id: string; name: string; imageUrl: string } | null>(null); + const [isSaving, setIsSaving] = useState(false); + const [initialValues, setInitialValues] = useState | undefined>(undefined); + const [showComparison, setShowComparison] = useState(true); + + useEffect(() => { + // Parse parameters from URL search params + const params = parseGenerationParams(searchParams); + setInitialValues(params); + + // Extract original card data + const originalCardId = searchParams.get('originalCardId'); + const originalCardName = searchParams.get('originalCardName'); + const originalCardImageUrl = searchParams.get('originalCardImageUrl'); + + if (originalCardId && originalCardName && originalCardImageUrl) { + setOriginalCard({ + id: originalCardId, + name: originalCardName, + imageUrl: originalCardImageUrl, + }); + } + }, [searchParams]); + + const handleCardGenerated = (imageBase64: string, prompt: string, params: any) => { + setGeneratedCard({ imageBase64, prompt, params }); + }; + + const handleSaveToCollection = async () => { + if (!user || !generatedCard) return; + + setIsSaving(true); + try { + // Convert base64 to data URL + let imageDataUrl = `data:image/jpeg;base64,${generatedCard.imageBase64}`; + + // Check image size and compress if needed + const originalSize = getBase64Size(generatedCard.imageBase64); + console.log(`Original image size: ${formatBytes(originalSize)}`); + + // Firestore has a 1MB limit for document fields, so compress if needed + if (originalSize > 800000) { // 800KB threshold to be safe + console.log('Image is too large, compressing...'); + try { + // Try first level compression + let compressedBase64 = await compressBase64Image(generatedCard.imageBase64, 400, 560, 0.7); + let compressedSize = getBase64Size(compressedBase64); + console.log(`Compressed image size (first attempt): ${formatBytes(compressedSize)}`); + + // If still too large, compress more aggressively + if (compressedSize > 800000) { + console.log('Still too large, applying aggressive compression...'); + compressedBase64 = await compressBase64Image(generatedCard.imageBase64, 300, 420, 0.5); + compressedSize = getBase64Size(compressedBase64); + console.log(`Final compressed image size: ${formatBytes(compressedSize)}`); + } + + imageDataUrl = `data:image/jpeg;base64,${compressedBase64}`; + } catch (compressionError) { + console.error('Error compressing image:', compressionError); + // Fall back to original image + } + } + + await addCardToCollection(user.uid, { + name: 'Generated Pokemon Card', + set: 'AI Generated', + rarity: 'Special', + imageDataUrl: imageDataUrl, + isGenerated: true, + prompt: generatedCard.prompt, + generationParams: generatedCard.params, + }); + + toast({ + title: 'Card Saved', + description: 'Your generated Pokemon card has been added to your collection!', + }); + + // Clear the generated card after saving + setGeneratedCard(null); + } catch (error) { + console.error('Error saving card:', error); + toast({ + title: 'Error', + description: 'Failed to save card to collection. Please try again.', + variant: 'destructive', + }); + } finally { + setIsSaving(false); + } + }; + + if (initialValues === undefined) { + return
Loading...
; + } + + return ( +
+
+

Edit Pokemon Card Generator

+

+ Modify your Pokemon card parameters and regenerate the card +

+
+ +
+ {/* Form Section - Takes up 2 columns */} +
+ +
+ + {/* Card Preview Section - Takes up 1 column */} +
+
+
+

Card Preview

+ {generatedCard && originalCard && ( +
+ + +
+ )} +
+ {generatedCard && originalCard ? ( + showComparison ? ( + /* Show comparison when both cards exist and comparison is enabled */ + + + + Comparison + + + Compare your original card with the newly generated one + + + + {/* Original Card */} +
+

Original Card

+
+ {originalCard.name} +
+
+ + {/* Divider */} +
+
+ VS +
+
+ + {/* New Generated Card */} +
+

New Generated Card

+
+ Generated Pokemon Card +
+
+ + +
+
+ ) : ( + /* Show only new card when comparison is disabled */ + + + New Generated Card + + Your updated Pokemon card is ready! + + + +
+ Generated Pokemon Card +
+ +
+
+ ) + ) : generatedCard ? ( + /* Show only new card if no original */ + + + New Generated Card + + Your updated Pokemon card is ready! + + + +
+ Generated Pokemon Card +
+ +
+
+ ) : originalCard ? ( + /* Show only original card */ + + + Original Card + + {originalCard.name} - This card will be replaced when you generate a new one + + + +
+ {originalCard.name} +
+
+
+ ) : ( + /* No cards available */ + + +
+

Generate a new card to see it here

+
+
+
+ )} +
+
+
+
+ ); +} diff --git a/src/app/dashboard/generate/page.tsx b/src/app/dashboard/generate/page.tsx index 1d23896..9420a41 100644 --- a/src/app/dashboard/generate/page.tsx +++ b/src/app/dashboard/generate/page.tsx @@ -7,15 +7,16 @@ import { useAuth } from '@/hooks/useAuth'; import { addCardToCollection } from '@/lib/firestore'; import { useToast } from '@/hooks/use-toast'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { compressBase64Image, getBase64Size, formatBytes } from '@/utils/imageUtils'; export default function GenerateCardPage() { const { user } = useAuth(); const { toast } = useToast(); - const [generatedCard, setGeneratedCard] = useState<{ imageBase64: string; prompt: string } | null>(null); + const [generatedCard, setGeneratedCard] = useState<{ imageBase64: string; prompt: string; params?: any } | null>(null); const [isSaving, setIsSaving] = useState(false); - const handleCardGenerated = (imageBase64: string, prompt: string) => { - setGeneratedCard({ imageBase64, prompt }); + const handleCardGenerated = (imageBase64: string, prompt: string, params: any) => { + setGeneratedCard({ imageBase64, prompt, params }); }; const handleSaveToCollection = async () => { @@ -24,7 +25,35 @@ export default function GenerateCardPage() { setIsSaving(true); try { // Convert base64 to data URL - const imageDataUrl = `data:image/jpeg;base64,${generatedCard.imageBase64}`; + let imageDataUrl = `data:image/jpeg;base64,${generatedCard.imageBase64}`; + + // Check image size and compress if needed + const originalSize = getBase64Size(generatedCard.imageBase64); + console.log(`Original image size: ${formatBytes(originalSize)}`); + + // Firestore has a 1MB limit for document fields, so compress if needed + if (originalSize > 800000) { // 800KB threshold to be safe + console.log('Image is too large, compressing...'); + try { + // Try first level compression + let compressedBase64 = await compressBase64Image(generatedCard.imageBase64, 400, 560, 0.7); + let compressedSize = getBase64Size(compressedBase64); + console.log(`Compressed image size (first attempt): ${formatBytes(compressedSize)}`); + + // If still too large, compress more aggressively + if (compressedSize > 800000) { + console.log('Still too large, applying aggressive compression...'); + compressedBase64 = await compressBase64Image(generatedCard.imageBase64, 300, 420, 0.5); + compressedSize = getBase64Size(compressedBase64); + console.log(`Final compressed image size: ${formatBytes(compressedSize)}`); + } + + imageDataUrl = `data:image/jpeg;base64,${compressedBase64}`; + } catch (compressionError) { + console.error('Error compressing image:', compressionError); + // Fall back to original image + } + } await addCardToCollection(user.uid, { name: 'Generated Pokemon Card', @@ -33,6 +62,7 @@ export default function GenerateCardPage() { imageDataUrl: imageDataUrl, isGenerated: true, prompt: generatedCard.prompt, + generationParams: generatedCard.params, }); toast({ diff --git a/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx index cb98b82..956683f 100644 --- a/src/app/dashboard/page.tsx +++ b/src/app/dashboard/page.tsx @@ -4,7 +4,7 @@ import { useRouter } from 'next/navigation'; import Link from 'next/link'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; -import { ScanLine, Sparkles, BookOpen } from 'lucide-react'; +import { ScanLine, Sparkles, BookOpen, Camera } from 'lucide-react'; export default function DashboardPage() { const router = useRouter(); @@ -18,7 +18,7 @@ export default function DashboardPage() {

-
+
@@ -57,6 +57,25 @@ export default function DashboardPage() { + + + + + Photo Cards + + + Transform your photos into Pokemon cards using AI + + + + + + + diff --git a/src/app/dashboard/scan/page.tsx b/src/app/dashboard/scan/page.tsx index ceacf98..51d14b9 100644 --- a/src/app/dashboard/scan/page.tsx +++ b/src/app/dashboard/scan/page.tsx @@ -2,7 +2,13 @@ import CardScanner from '@/components/cards/CardScanner'; export default function ScanCardPage() { return ( -
+
+
+

Scan PokΓ©mon Card

+

+ Upload an image of your PokΓ©mon card to automatically identify and add it to your collection +

+
); diff --git a/src/app/dashboard/settings/page.tsx b/src/app/dashboard/settings/page.tsx new file mode 100644 index 0000000..d6e681e --- /dev/null +++ b/src/app/dashboard/settings/page.tsx @@ -0,0 +1,249 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { useAuth } from '@/hooks/useAuth'; +import { getUserProfile, updateUserApiKeys } from '@/lib/firestore'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { Alert, AlertDescription } from '@/components/ui/alert'; +import { Loader2, Key, Save, Eye, EyeOff } from 'lucide-react'; +import { useToast } from '@/hooks/use-toast'; +import type { UserApiKeys } from '@/types'; + +export default function SettingsPage() { + const { user } = useAuth(); + const { toast } = useToast(); + const [loading, setLoading] = useState(true); + const [saving, setSaving] = useState(false); + const [apiKeys, setApiKeys] = useState({ + geminiApiKey: '', + openaiApiKey: '', + }); + const [showGeminiKey, setShowGeminiKey] = useState(false); + const [showOpenAIKey, setShowOpenAIKey] = useState(false); + + useEffect(() => { + if (user) { + loadUserSettings(); + } + }, [user]); + + const loadUserSettings = async () => { + if (!user) return; + + try { + setLoading(true); + const profile = await getUserProfile(user.uid); + if (profile?.apiKeys) { + setApiKeys(profile.apiKeys); + } + } catch (error) { + console.error('Error loading user settings:', error); + toast({ + title: 'Error', + description: 'Failed to load your settings', + variant: 'destructive', + }); + } finally { + setLoading(false); + } + }; + + const handleSave = async () => { + if (!user) return; + + // Basic validation + if (!apiKeys.geminiApiKey && !apiKeys.openaiApiKey) { + toast({ + title: 'Warning', + description: 'Please provide at least one API key to enable card generation', + variant: 'destructive', + }); + return; + } + + try { + setSaving(true); + await updateUserApiKeys(user.uid, apiKeys); + toast({ + title: 'Success', + description: 'Your API keys have been saved successfully', + }); + } catch (error) { + console.error('Error saving API keys:', error); + toast({ + title: 'Error', + description: 'Failed to save your API keys', + variant: 'destructive', + }); + } finally { + setSaving(false); + } + }; + + const handleInputChange = (field: keyof UserApiKeys, value: string) => { + setApiKeys(prev => ({ + ...prev, + [field]: value, + })); + }; + + if (loading) { + return ( +
+ +
+ ); + } + + return ( +
+
+

Settings

+

+ Configure your API keys to enable AI-powered card generation +

+
+ +
+ {/* API Keys Section */} + + + + + API Keys + + + Configure your API keys to enable different AI features. Your keys are securely stored and encrypted. + + + + + + Security Note: Your API keys are stored securely in your user profile. + Only you can access them. We recommend using keys with appropriate usage limits. + + + + {/* Gemini API Key */} +
+ +
+ handleInputChange('geminiApiKey', e.target.value)} + className="pr-10" + /> + +
+

+ Used for regular Pokemon card generation and video generation using Google's Imagen and Veo models. + Get your key from the Google AI Studio. +

+
+ + {/* OpenAI API Key */} +
+ +
+ handleInputChange('openaiApiKey', e.target.value)} + className="pr-10" + /> + +
+

+ Used for photo-based Pokemon card generation using GPT-4o and DALL-E. + Get your key from the OpenAI Platform. +

+
+ +
+ +
+
+
+ + {/* Feature Availability */} + + + Feature Availability + + Which features are available based on your configured API keys + + + +
+
+
+
+

Regular Card Generation

+

Requires Gemini API key

+
+
+
+
+
+

Photo-based Generation

+

Requires OpenAI API key

+
+
+
+
+
+

Video Generation

+

Requires Gemini API key

+
+
+
+ + +
+
+ ); +} diff --git a/src/components/cards/CardForm.tsx b/src/components/cards/CardForm.tsx index 4eefebf..4fc8ecd 100644 --- a/src/components/cards/CardForm.tsx +++ b/src/components/cards/CardForm.tsx @@ -55,7 +55,7 @@ export default function CardForm({ name: initialData?.name || '', set: initialData?.set || '', rarity: initialData?.rarity || '', - imageDataUrl: imageDataUrlFromScan || (initialData as PokemonCard)?.imageDataUrl || '', + imageDataUrl: imageDataUrlFromScan || (initialData as PokemonCard)?.imageUrl || '', }, }); diff --git a/src/components/cards/CardGenerator.tsx b/src/components/cards/CardGenerator.tsx index 9655266..a79d9de 100644 --- a/src/components/cards/CardGenerator.tsx +++ b/src/components/cards/CardGenerator.tsx @@ -12,7 +12,10 @@ import { Switch } from '@/components/ui/switch'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Separator } from '@/components/ui/separator'; -import { Loader2 } from 'lucide-react'; +import { Loader2, Download } from 'lucide-react'; +import { downloadCardFromBase64 } from '@/utils/downloadUtils'; +import { useToast } from '@/hooks/use-toast'; +import { useAuth } from '@/hooks/useAuth'; export interface GeneratePokemonCardInput { pokemonName: string; @@ -70,10 +73,13 @@ const cardGeneratorSchema = z.object({ type CardGeneratorForm = z.infer; interface CardGeneratorProps { - onCardGenerated?: (imageBase64: string, prompt: string) => void; + onCardGenerated?: (imageBase64: string, prompt: string, params: GeneratePokemonCardInput) => void; + initialValues?: Partial; } -export function CardGenerator({ onCardGenerated }: CardGeneratorProps) { +export function CardGenerator({ onCardGenerated, initialValues }: CardGeneratorProps) { + const { user } = useAuth(); + const { toast } = useToast(); const [isGenerating, setIsGenerating] = useState(false); const [generatedCard, setGeneratedCard] = useState<{ imageBase64: string; prompt: string } | null>(null); const [error, setError] = useState(''); @@ -87,27 +93,51 @@ export function CardGenerator({ onCardGenerated }: CardGeneratorProps) { } = useForm({ resolver: zodResolver(cardGeneratorSchema), defaultValues: { - pokemonName: '', - pokemonType: '', - isIllustrationRare: false, - isHolo: false, - backgroundDescription: '', - pokemonDescription: '', - language: 'english', - hp: 130, - attackName1: 'Quick Attack', - attackDamage1: 60, - attackName2: 'Special Move', - attackDamage2: 90, - weakness: 'Fighting', - resistance: 'Psychic', - retreatCost: 2, + pokemonName: initialValues?.pokemonName || '', + pokemonType: initialValues?.pokemonType || '', + isIllustrationRare: initialValues?.isIllustrationRare || false, + isHolo: initialValues?.isHolo || false, + backgroundDescription: initialValues?.backgroundDescription || '', + pokemonDescription: initialValues?.pokemonDescription || '', + language: initialValues?.language || 'english', + hp: initialValues?.hp || 130, + attackName1: initialValues?.attackName1 || 'Quick Attack', + attackDamage1: initialValues?.attackDamage1 || 60, + attackName2: initialValues?.attackName2 || 'Special Move', + attackDamage2: initialValues?.attackDamage2 || 90, + weakness: initialValues?.weakness || 'Fighting', + resistance: initialValues?.resistance || 'Psychic', + retreatCost: initialValues?.retreatCost || 2, }, }); + const handleDownloadCard = () => { + if (!generatedCard) return; + + try { + downloadCardFromBase64(generatedCard.imageBase64, watchedValues.pokemonName || 'pokemon_card'); + toast({ + title: 'Download Started', + description: 'Your Pokemon card is being downloaded.', + }); + } catch (error) { + console.error('Download error:', error); + toast({ + title: 'Download Failed', + description: 'Failed to download the card. Please try again.', + variant: 'destructive', + }); + } + }; + const watchedValues = watch(); const onSubmit = async (data: CardGeneratorForm) => { + if (!user) { + setError('You must be logged in to generate cards'); + return; + } + setIsGenerating(true); setError(''); setGeneratedCard(null); @@ -118,7 +148,10 @@ export function CardGenerator({ onCardGenerated }: CardGeneratorProps) { headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify(data), + body: JSON.stringify({ + ...data, + userId: user.uid, + }), }); const result = await response.json(); @@ -138,7 +171,7 @@ export function CardGenerator({ onCardGenerated }: CardGeneratorProps) { imageBase64: result.imageBase64, prompt: result.prompt, }); - onCardGenerated?.(result.imageBase64, result.prompt); + onCardGenerated?.(result.imageBase64, result.prompt, data); } } catch (err) { console.error('Error generating card:', err); @@ -399,6 +432,14 @@ export function CardGenerator({ onCardGenerated }: CardGeneratorProps) { className="w-full h-auto rounded-lg shadow-lg" />
+
) : (
diff --git a/src/components/cards/CardGeneratorFormOnly.tsx b/src/components/cards/CardGeneratorFormOnly.tsx new file mode 100644 index 0000000..5de0a66 --- /dev/null +++ b/src/components/cards/CardGeneratorFormOnly.tsx @@ -0,0 +1,399 @@ +'use client'; + +import { useState } from 'react'; +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { z } from 'zod'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import { Textarea } from '@/components/ui/textarea'; +import { Switch } from '@/components/ui/switch'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { Separator } from '@/components/ui/separator'; +import { Loader2 } from 'lucide-react'; +import { useToast } from '@/hooks/use-toast'; +import { useAuth } from '@/hooks/useAuth'; + +export interface GeneratePokemonCardInput { + pokemonName: string; + pokemonType: string; + isIllustrationRare: boolean; + isHolo: boolean; + backgroundDescription: string; + pokemonDescription: string; + language: 'english' | 'japanese' | 'chinese' | 'korean' | 'spanish' | 'french' | 'german' | 'italian'; + hp?: number; + attackName1?: string; + attackDamage1?: number; + attackName2?: string; + attackDamage2?: number; + weakness?: string; + resistance?: string; + retreatCost?: number; +} + +const pokemonTypes = [ + 'Normal', 'Fire', 'Water', 'Electric', 'Grass', 'Ice', 'Fighting', 'Poison', + 'Ground', 'Flying', 'Psychic', 'Bug', 'Rock', 'Ghost', 'Dragon', 'Dark', + 'Steel', 'Fairy' +]; + +const languages = [ + { value: 'english', label: 'English' }, + { value: 'japanese', label: 'Japanese' }, + { value: 'chinese', label: 'Chinese' }, + { value: 'korean', label: 'Korean' }, + { value: 'spanish', label: 'Spanish' }, + { value: 'french', label: 'French' }, + { value: 'german', label: 'German' }, + { value: 'italian', label: 'Italian' }, +] as const; + +const cardGeneratorSchema = z.object({ + pokemonName: z.string().min(1, 'Pokemon name is required'), + pokemonType: z.string().min(1, 'Pokemon type is required'), + isIllustrationRare: z.boolean(), + isHolo: z.boolean(), + backgroundDescription: z.string().min(10, 'Background description must be at least 10 characters'), + pokemonDescription: z.string().min(10, 'Pokemon description must be at least 10 characters'), + language: z.enum(['english', 'japanese', 'chinese', 'korean', 'spanish', 'french', 'german', 'italian']), + hp: z.number().min(10).max(999).optional(), + attackName1: z.string().optional(), + attackDamage1: z.number().min(0).max(999).optional(), + attackName2: z.string().optional(), + attackDamage2: z.number().min(0).max(999).optional(), + weakness: z.string().optional(), + resistance: z.string().optional(), + retreatCost: z.number().min(0).max(5).optional(), +}); + +type CardGeneratorForm = z.infer; + +interface CardGeneratorFormOnlyProps { + onCardGenerated?: (imageBase64: string, prompt: string, params: GeneratePokemonCardInput) => void; + initialValues?: Partial; +} + +export function CardGeneratorFormOnly({ onCardGenerated, initialValues }: CardGeneratorFormOnlyProps) { + const { user } = useAuth(); + const [isGenerating, setIsGenerating] = useState(false); + const [error, setError] = useState(''); + const { toast } = useToast(); + + const { + register, + handleSubmit, + watch, + setValue, + formState: { errors }, + } = useForm({ + resolver: zodResolver(cardGeneratorSchema), + defaultValues: { + pokemonName: initialValues?.pokemonName || '', + pokemonType: initialValues?.pokemonType || '', + isIllustrationRare: initialValues?.isIllustrationRare || false, + isHolo: initialValues?.isHolo || false, + backgroundDescription: initialValues?.backgroundDescription || '', + pokemonDescription: initialValues?.pokemonDescription || '', + language: initialValues?.language || 'english', + hp: initialValues?.hp || 130, + attackName1: initialValues?.attackName1 || 'Quick Attack', + attackDamage1: initialValues?.attackDamage1 || 60, + attackName2: initialValues?.attackName2 || 'Special Move', + attackDamage2: initialValues?.attackDamage2 || 90, + weakness: initialValues?.weakness || 'Fighting', + resistance: initialValues?.resistance || 'Psychic', + retreatCost: initialValues?.retreatCost || 2, + }, + }); + + const watchedValues = watch(); + + const onSubmit = async (data: CardGeneratorForm) => { + if (!user) { + setError('You must be logged in to generate cards'); + return; + } + + setIsGenerating(true); + setError(''); + + try { + const response = await fetch('/api/generate-card', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + ...data, + userId: user.uid, + }), + }); + + const result = await response.json(); + + if (!response.ok) { + setError(result.error || 'Failed to generate card'); + return; + } + + if (result.error) { + setError(result.error); + return; + } + + if (result.imageBase64 && result.prompt) { + onCardGenerated?.(result.imageBase64, result.prompt, data); + toast({ + title: 'Card Generated!', + description: 'Your Pokemon card has been generated successfully.', + }); + } + } catch (err) { + console.error('Error generating card:', err); + setError('Failed to generate card. Please try again.'); + } finally { + setIsGenerating(false); + } + }; + + return ( + + + Create Your Pokemon Card + + Fill in the details to generate a custom Pokemon TCG card using AI + + + +
+ {/* Basic Pokemon Info */} +
+

Basic Information

+ +
+
+ + + {errors.pokemonName && ( +

{errors.pokemonName.message}

+ )} +
+ +
+ + + {errors.pokemonType && ( +

{errors.pokemonType.message}

+ )} +
+
+ +
+ + +
+
+ + + + {/* Card Properties */} +
+

Card Properties

+ +
+
+
+ +

Horizontal layout with full illustration

+
+ setValue('isIllustrationRare', checked)} + /> +
+ +
+
+ +

Rainbow shimmer effect

+
+ setValue('isHolo', checked)} + /> +
+
+
+ + + + {/* Descriptions */} +
+

Descriptions

+ +
+ +