diff --git a/.env.example b/.env.example index a515a3c..b87d6aa 100644 --- a/.env.example +++ b/.env.example @@ -1,12 +1,24 @@ # Firebase Web SDK config (public keys, but keep local env private) -FIREBASE_API_KEY=your_api_key -FIREBASE_AUTH_DOMAIN=your_project.firebaseapp.com -FIREBASE_DATABASE_URL=https://your_project-default-rtdb.firebaseio.com -FIREBASE_PROJECT_ID=your_project_id -FIREBASE_STORAGE_BUCKET=your_project.firebasestorage.app -FIREBASE_MESSAGING_SENDER_ID=your_messaging_sender_id -FIREBASE_APP_ID=your_app_id -FIREBASE_MEASUREMENT_ID=your_measurement_id - -# Optional +# Note: All VITE_ prefixed variables are exposed to client-side code +VITE_FIREBASE_API_KEY=your_api_key +VITE_FIREBASE_AUTH_DOMAIN=your_project.firebaseapp.com +VITE_FIREBASE_DATABASE_URL=https://your_project-default-rtdb.firebaseio.com +VITE_FIREBASE_PROJECT_ID=your_project_id +VITE_FIREBASE_STORAGE_BUCKET=your_project.firebasestorage.app +VITE_FIREBASE_MESSAGING_SENDER_ID=your_messaging_sender_id +VITE_FIREBASE_APP_ID=your_app_id +VITE_FIREBASE_MEASUREMENT_ID=your_measurement_id + +# Vercel Blob (used by /upload and /api/upload) +# Get this token from Vercel Dashboard > Storage > Blob > your_store > Settings +BLOB_READ_WRITE_TOKEN=vercel_blob_rw_xxxxxxxxxxxx + +# Server config PORT=4001 + +# Required for encrypted admin secrets storage/rotation +ADMIN_SECRETS_KEY=replace_with_a_long_random_secret + +# Gemini API key for server-side chatbot orchestration +# Keep this server-side. Do not expose it to the browser. +GEMINI_API_KEY=your_gemini_api_key diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..960a9dd --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,53 @@ +# Project Guidelines + +## Repository Overview +- This is a React + Vite portfolio, blog, and admin project. +- Entry points and routing live in `src/main.jsx` and `src/App.jsx`. +- Main routes: + - `/` and `/home`: portfolio (`src/Pages/Portfolio/Portfolio.jsx`) + - `/blog`: public blog (`src/Pages/Blog/Blog.jsx`) + - `/admin`: admin dashboard/auth (`src/Pages/Admin/`) +- Backend runtime and API endpoints are in `server.js`. + +## Code Style +- Keep implementation framework-consistent with existing React function components and hooks. +- Prefer small, direct state updates and existing component patterns. +- Preserve existing naming and data shape contracts in hooks and API responses. +- Reuse existing CSS variables and section patterns from `src/index.css` and page styles. + +## Architecture +- Portfolio UI behavior belongs in `src/Pages/` and `src/Components/`. +- Shared data access belongs in `src/hooks/useBlogPosts.js`. +- Auth state belongs in `src/contexts/AuthContext.jsx`. +- Chatbot UI adapter belongs in `src/Components/Chatbot/Chatbot.jsx`. +- Chatbot orchestration, memory, and Gemini runtime belong in `server.js` and `chatbot/`. +- Keep server responsibilities focused on config serving, chatbot endpoints, upload endpoint, and static hosting. + +## Build And Run +- Install dependencies: `npm install` +- Local development (frontend + backend): `npm run dev` +- Backend only: `npm start` +- Lint/type-check: `npm run lint` +- Runtime smoke test: `npm test` +- Build production assets: `npm run build` + +## Conventions +- Keep Firebase web config flow intact via `/firebase-config.js` generated by `server.js`. +- Keep Realtime Database compatibility for `blogPosts`, `portfolioProjects`, and `portfolioCredentials`. +- Preserve safe rendering and escaping patterns in blog/admin/chatbot flows. +- Keep chatbot prompts externalized in `chatbot/prompts/` (do not hardcode in React components). + +## Environment And Pitfalls +- Local development requires `.env` values matching `.env.example`. +- Uploads require `BLOB_READ_WRITE_TOKEN`. +- Optional Firebase Admin features require `FIREBASE_SERVICE_ACCOUNT` or `serviceAccountKey.json`. +- Chatbot generation requires `GEMINI_API_KEY`. +- Use HTTP-served pages (`npm run dev` or `npm start`), not `file://`. + +## Key References +- `README.md` +- `server.js` +- `chatbot/ARCHITECTURE.md` +- `chatbot/STAGE_1_TODO.md` +- `chatbot/api/contract.md` +- `src/Components/Chatbot/Chatbot.jsx` diff --git a/.github/prompts/audit.prompt.md b/.github/prompts/audit.prompt.md new file mode 100644 index 0000000..3e18512 --- /dev/null +++ b/.github/prompts/audit.prompt.md @@ -0,0 +1,18 @@ +--- +name: audit +description: "Use when: you need to audit and update project dependencies" +argument-hint: "Optional: specify a package name to audit a specific dependency" +--- + +# Goal: Audit and update project dependencies + +Your goal is to check project dependencies, check for lint errors, check outdated dependencies and update them. + +1. Run `npm audit` to check for vulnerabilities. +2. Run `npm audit fix` to fix vulnerabilities. +3. Run `npm audit fix --force` if no. 2 doesn't fix vulnerabilities to disable Recommended protections. +4. Run `npm outdated` to check for outdated dependencies. +5. Update dependencies using `npm update` or manually update the version in `package.json` and run `npm install`. +6. Run `npm run lint` to check for lint errors and fix them. +7. Run `npm run build` to ensure the project builds successfully after updates. +8. Run tests if available to ensure nothing is broken after updates. \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..d5cee7d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,44 @@ +--- +name: CI + +"on": + push: + branches: + - main + - master + - develop + - 'feature/**' + pull_request: + +permissions: + contents: read + +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + quality: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Lint + run: npm run lint + + - name: Smoke test + run: npm test + + - name: Build + run: npm run build diff --git a/.gitignore b/.gitignore index 883812f..c8d04ba 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,9 @@ node_modules/ +dist/ +coverage/ +*.log +.vercel/ +.DS_Store serviceAccountKey.json firebase-config.js .env diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..1540bfb --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "css.lint.unknownAtRules": "ignore", + "scss.lint.unknownAtRules": "ignore", + "less.lint.unknownAtRules": "ignore" +} diff --git a/README.md b/README.md index 5e54e83..e8dfe74 100644 --- a/README.md +++ b/README.md @@ -1,397 +1,127 @@ -# Dee Portfolio & Blog Platform +# Dee Portfolio Platform -A fully responsive personal portfolio website with an integrated Firebase-powered blog admin panel. Features a modern admin dashboard for content management, Firebase Storage media uploads, scheduled post publishing, and a public blog with modal post views. +React + Vite portfolio site with a Firebase-backed blog/admin and a Gemini-powered portfolio assistant. -## πŸš€ Tech Stack - -### Frontend -- **HTML5 + CSS3** – Semantic markup with modern responsive design -- **Vanilla JavaScript** – Zero framework dependencies for maximum performance -- **Firebase SDK** – Authentication, Realtime Database, Storage integration - -### Backend -- **Node.js + Express** – Optional upload proxy server (for Firebase Storage) -- **Firebase Admin SDK** – Server-side Firebase operations -- **Multer** – File upload handling - -### Services -- **Firebase Authentication** – Email/password & GitHub OAuth -- **Firebase Realtime Database** – Blog post storage and retrieval -- **Cloudinary** – Media file hosting for images and videos - -## πŸ“ Project Structure - -``` -dee/ -β”œβ”€β”€ index.html # Main portfolio page -β”œβ”€β”€ blog.html # Public blog with post cards and modal viewer -β”œβ”€β”€ admin.html # Admin dashboard (requires auth) -β”œβ”€β”€ styles.css # Global styles (1200+ lines) -β”œβ”€β”€ script.js # Portfolio animations and interactions -β”œβ”€β”€ blogscript.js # Blog data loading from Firebase -β”œβ”€β”€ adminscript.js # Admin panel logic (auth, CRUD, uploads) -β”œβ”€β”€ server.js # Optional Node.js upload proxy -β”œβ”€β”€ package.json # Node dependencies for upload server -β”œβ”€β”€ package-lock.json # Locked dependency versions -β”œβ”€β”€ .env.example # Environment variable template -β”œβ”€β”€ serviceAccountKey.json # Firebase Admin credentials (gitignored) -β”œβ”€β”€ cors.json # Firebase Storage CORS configuration -β”œβ”€β”€ media/ # Images, videos, icons, favicon -└── docs/ # Project documentation - β”œβ”€β”€ README_ADMIN.md - β”œβ”€β”€ ADMIN_IMPROVEMENTS.md - β”œβ”€β”€ ADMIN_QUICK_START.md - β”œβ”€β”€ FIREBASE_SETUP.md - β”œβ”€β”€ FIREBASE_CONFIG_LOCATION.md - └── SYSTEM_CSS_DESIGN.md -``` - -## ✨ Features - -### Portfolio Site -- **Hero Section** – Animated background video with profile overlay -- **About Section** – Bio, stats, and animated code editor -- **Projects Carousel** – Horizontal slider with media support (images/videos) -- **Skills Grid** – Progress bars and technology badges -- **Contact Section** – Direct links to email, GitHub, LinkedIn -- **Responsive Design** – Mobile-first approach with hamburger navigation - -### Blog System -- **Post Cards** – Grid layout with featured images/videos -- **Category Filtering** – Filter posts by category or view all -- **Tag Cloud** – Visual tag navigation -- **Post Modal** – Full-screen overlay for reading complete posts -- **Inline Code Markup** – Render `` `code` `` in titles and excerpts -- **Media Support** – Display images or videos (15-30s clips) -- **Firebase Integration** – Real-time post loading from database - -### Admin Panel -- **Secure Authentication** – Email/password and GitHub OAuth with Firebase Auth -- **Post Composer** – Rich form with live preview -- **Media Upload** – Cloudinary integration for images/videos -- **Scheduled Publishing** – Set future publish dates -- **Post Management** – Edit, delete, search, and filter posts -- **Status Tracking** – Published, scheduled, or draft states -- **Category & Tags** – Multiple tag support with visual chips -- **Responsive Layout** – Works on desktop and mobile -- **Real-time Preview** – See how posts will appear on blog - -## πŸ”§ Setup Instructions - -### 1. Clone and Install - -## πŸ”§ Setup Instructions - -### 1. Clone and Install - -```bash -git clone -cd dee -npm install # Only needed if using upload server -``` - -### 2. Firebase Configuration - -#### Create Firebase Project -1. Go to [Firebase Console](https://console.firebase.google.com/) -2. Create a new project: `dee-s-site` (or your preferred name) -3. Enable **Authentication** β†’ Email/Password & GitHub OAuth providers -4. Enable **Realtime Database** β†’ Start in test mode - -#### Get Firebase Config -1. Go to **Project Settings** β†’ **General** -2. Scroll to **Your apps** β†’ Click web icon () to add a web app -3. Copy the `firebaseConfig` values -4. Create `.env` from `.env.example` and fill the matching `FIREBASE_*` variables -5. Start the Node server (`npm start`) so it serves `/firebase-config.js` to the frontend - -#### Set Database Rules -1. Go to **Realtime Database** β†’ **Rules** -2. Replace with: - -```json -{ - "rules": { - "users": { - "$uid": { - ".read": "$uid === auth.uid", - ".write": "$uid === auth.uid" - } - }, - "blogPosts": { - ".read": true, - ".write": "auth != null" - } - } -} -``` - -3. Click **Publish** - -### 3. Cloudinary Setup (for Media Uploads) - -1. Sign up at [Cloudinary](https://cloudinary.com/users/register_free) (free tier) -2. Go to your Dashboard and copy your **Cloud Name** -3. Go to **Settings** β†’ **Upload** -4. Create an **Upload Preset**: - - Name: `blog_uploads` - - Signing Mode: **Unsigned** - - Click **Save** -5. Update `adminscript.js` line 233: - ```javascript - const cloudinaryUrl = 'https://api.cloudinary.com/v1_1/YOUR_CLOUD_NAME/upload'; - ``` - Replace `YOUR_CLOUD_NAME` with your actual Cloudinary cloud name - -### 4. GitHub OAuth Setup (Optional) - -To enable GitHub sign-in for the admin panel: - - Save as `serviceAccountKey.json` in project root - -2. Set CORS rules (requires `gsutil`): - ```bash - gsutil cors set cors.json gs://your-bucket-name.appspot.com - ``` - -3. Update `adminscript.js` to use the upload server (see `server.js`) - -### 5. Run Local Development Server - -```bash -# Start app server (serves pages, upload API, and /firebase-config.js) -npm start -``` - -### 6. First Login - -1. Open http://localhost:4001/admin.html -2. Create an account with email/password -3. Start creating blog posts! - -## 🎯 Usage Guide - -### Creating a Blog Post - -1. **Navigate** to `admin.html` and sign in -2. **Fill out the form**: - - Title (supports `` `inline code` ``) - - Category (tutorials/projects/tips) - - Date (defaults to today) - - Excerpt (short description, 150 chars recommended) - - Content (full post body) - - Featured Media: - - Option A: Paste media URL - - Option B: Upload image/video (auto-uploads to Cloudinary) - - Tags (press Enter after each tag) - - Publish Time (optional: schedule for future) -3. **Preview** updates live as you type -4. **Click "Create Post"** to publish immediately or schedule -5. **View on blog** at `blog.html` - -### Managing Posts - -- **Edit**: Click "Edit" on any post in the Manage Posts section -- **Delete**: Click "Delete" and confirm in the modal -- **Search**: Type in the search box to filter by title/tags -- **Filter**: Use dropdowns to filter by category or status - -### Reading Posts (Public Blog) - -1. Open `blog.html` -2. Browse post cards with featured media -3. Click **"Read More β†’"** to open full post modal -4. Close modal by: - - Clicking the Γ— button - - Pressing Escape - - Clicking outside the modal - -## πŸ” Security Configuration - -### Firebase Realtime Database Rules - -```json -{ - "rules": { - "users": { - "$uid": { - ".read": "$uid === auth.uid", - ".write": "$uid === auth.uid" - } - }, - "blogPosts": { - ".read": true, - ".write": "auth != null" - } - } -} -``` - -**Explanation**: -- `users/{uid}` – Only authenticated users can read/write their own data -- `blogPosts` – Anyone can read (public blog), only authenticated users can write - -### Cloudinary Configuration - -Cloudinary handles media uploads securely with: -- **Upload Preset**: `blog_uploads` set to Unsigned mode -- **CDN Delivery**: Automatic CORS and optimization -- **Free Tier**: Up to 25GB storage, 25GB bandwidth per month - -## 🎨 Customization - -### Branding -- Update name in `index.html` line 16: `` -- Update footer in all pages: Line ~130+ in each HTML file -- Replace logo in `media/favicon.svg` - -### Colors -Edit CSS variables in `styles.css` (lines 9-25): - -```css -:root { - --primary-color: #1e3a8a; - --secondary-color: #1e40af; - --accent-color: #2563eb; - --text-primary: #1f2937; - --bg-primary: #ffffff; - /* ... */ -} -``` - -### Default Blog Posts -Edit the array in `blog.html` (lines 150-228) to customize placeholder posts - -## πŸ“Š Data Structure - -### Blog Post Object - -```javascript -{ - id: "generated-firebase-key", - title: "Post Title", - slug: "post-title", - excerpt: "Brief summary...", - content: "Full post content...", - category: "tutorials", - tags: ["JavaScript", "React"], - author: "Dee", - date: "2026-02-15", - image: "https://...", // Deprecated, use mediaUrl - mediaUrl: "https://...", // Cloudinary secure URL - mediaType: "image", // "image" or "video" - status: "published", // "published", "scheduled", "draft" - publishAt: "", // ISO date for scheduled posts - createdAt: "2026-02-15T10:30:00Z", - updatedAt: "2026-02-15T10:30:00Z", - publishedAt: "2026-02-15T10:30:00Z" -} -``` - -## πŸ› Troubleshooting - -### Posts Not Appearing on Blog -1. Check Firebase Database rules allow public read on `blogPosts` -2. Verify post `status` is `"published"` (not `"scheduled"` or `"draft"`) -3. Check browser console (F12) for permission errors -4. Ensure Firebase config is correct in `blogscript.js` - -### Upload Failing -1. **Cloudinary errors**: Verify Cloud Name and upload preset name -2. **"Upload preset not found"**: Make sure preset is named `blog_uploads` and is Unsigned -3. **CORS errors**: Cloudinary handles CORS automatically -4. **Network errors**: Check internet connection and Cloudinary config - -### Permission Denied -1. Update Firebase Database rules (see Security Configuration above) -2. Sign out and sign back in to refresh auth token -3. Check Firebase Auth is enabled in console - -### Modal Not Opening -1. Check browser console for JavaScript errors -2. Verify `postsCollection` array is populated -3. Hard refresh (Ctrl+Shift+R) to clear cache - -## πŸ“Έ Media Guidelines - -### Images -- **Format**: JPG, PNG, WebP, or AVIF -- **Size**: 1200Γ—800px recommended for featured images -- **File size**: <500KB (use compression tools) -- **Ratio**: 3:2 aspect ratio looks best in cards - -### Videos -- **Format**: MP4, WebM, MOV -- **Duration**: 15-30 seconds for featured clips -- **Resolution**: 1280Γ—720 or 1920Γ—1080 -- **File size**: <10MB recommended -- **Codec**: H.264 for broad compatibility - -### Icons & Logos -- **Format**: SVG preferred (or PNG with transparency) -- **Size**: 64Γ—64 to 512Γ—512 pixels -- **Colors**: Match brand palette in `styles.css` - -## πŸš€ Deployment - -### Static Hosting (Vercel, Netlify, GitHub Pages) - -1. Push code to GitHub -2. Connect repository to hosting platform -3. Configure build settings: - - **Build command**: (none, static site) - - **Publish directory**: `/` (root) -4. Add environment variables (if any) -5. Deploy! - -### Firebase Hosting +## Quick Start ```bash -# Install Firebase CLI -npm install -g firebase-tools - -# Login to Firebase -firebase login - -# Initialize hosting -firebase init hosting - -# Deploy -firebase deploy --only hosting +npm install +npm run dev ``` -## πŸ“š Documentation - -| File | Purpose | -|------|---------| -| [docs/README_ADMIN.md](docs/README_ADMIN.md) | Admin panel architecture and workflows | -| [docs/ADMIN_IMPROVEMENTS.md](docs/ADMIN_IMPROVEMENTS.md) | Admin enhancement notes and updates | -| [docs/ADMIN_QUICK_START.md](docs/ADMIN_QUICK_START.md) | 5-minute setup checklist | -| [docs/FIREBASE_SETUP.md](docs/FIREBASE_SETUP.md) | Detailed Firebase configuration | -| [docs/FIREBASE_CONFIG_LOCATION.md](docs/FIREBASE_CONFIG_LOCATION.md) | Where to place Firebase credentials and config | -| [docs/SYSTEM_CSS_DESIGN.md](docs/SYSTEM_CSS_DESIGN.md) | UI components and visual guidelines | - -## 🀝 Contributing - -This is a personal portfolio project, but suggestions and improvements are welcome: - -1. Fork the repository -2. Create a feature branch (`git checkout -b feature/amazing-feature`) -3. Commit your changes (`git commit -m 'Add amazing feature'`) -4. Push to the branch (`git push origin feature/amazing-feature`) -5. Open a Pull Request - -## πŸ“ License - -See [LICENSE](LICENSE). Free to use and adapt for personal projects. Attribution appreciated but not required. - -## πŸ’‘ Credits - -- **Developer**: Dee (Agoma Divine E.) -- **Framework**: None (Vanilla JS for maximum performance) -- **Icons**: Hand-crafted SVGs -- **Fonts**: Inter (Google Fonts) - ---- - -**Built with ❀️ using vanilla HTML, CSS, and JavaScript** - +Default local ports: +- Frontend (Vite): `http://localhost:8000` +- Backend (Express): `http://localhost:4001` + +## System Overview + +This repository is split into clear layers: + +1. `src/`: +- React UI layer for portfolio, blog, admin, and chatbot panel. + +2. `server.js`: +- Express runtime layer for static hosting, Firebase config delivery, uploads, and chatbot orchestration APIs. + +3. `chatbot/`: +- Chatbot product layer (architecture, prompts, knowledge, schemas, API contract notes, UI behavior docs). + +4. `media/`: +- Static media assets (images, PDFs, certificates, etc.). + +5. `.github/workflows/`: +- CI and deployment automation. + +## Project Parts (What Each Area Does) + +| Path | Responsibility | +|---|---| +| `src/main.jsx` | React app bootstrap and router mount | +| `src/App.jsx` | Route table (`/`, `/home`, `/blog`, `/admin`) | +| `src/Pages/Portfolio/` | Main portfolio page composition | +| `src/Pages/Hero/` | Hero section + animated background presentation | +| `src/Pages/About/` | Bio/positioning content section | +| `src/Pages/Credentials/` | Credential/certification rendering + modal view | +| `src/Pages/Projects/` | Featured projects carousel and project links | +| `src/Pages/Skills/` | Skills and tools presentation | +| `src/Pages/Contact/` | Contact links and form UI | +| `src/Pages/Footer/` | Footer links and identity | +| `src/Pages/Blog/` | Public blog feed and post modal rendering | +| `src/Pages/Admin/` | Admin auth, dashboard, content management, secret rotation UI | +| `src/Components/Navbar/` | Main site navigation | +| `src/Components/Chatbot/` | Chatbot UI adapter (session bootstrap, message send, actions, citations) | +| `src/contexts/AuthContext.jsx` | Firebase auth state/provider | +| `src/hooks/useBlogPosts.js` | Realtime data hooks for posts, projects, credentials | +| `src/utils/blogUtils.js` | Blog normalization/filtering/date helpers | +| `src/utils/scrollToSection.js` | Shared smooth-scroll helper | +| `src/firebase.js` | Firebase client initialization | +| `server.js` | API server, upload endpoint, chatbot orchestration, static host | +| `chatbot/prompts/` | Externalized chatbot prompt layers | +| `chatbot/knowledge/` | Grounding data used for retrieval | +| `chatbot/api/contract.md` | Chatbot endpoint/request-response contract | +| `chatbot/ui/` | UI state vocabulary and structured message guidance | +| `chatbot/schemas/` | Response/citation/session schema drafts | +| `.github/workflows/ci.yml` | Lint + smoke test + build checks | +| `.github/workflows/deploy-vercel.yml` | Vercel preview/production deployments | + +## Chatbot Runtime (Current) + +Implemented: +- `POST /api/chatbot/session` +- `POST /api/chatbot/message` +- `POST /api/chatbot/summarize` +- `GET /api/chatbot/config/status` + +Behavior: +- Server-side Gemini call and fallback behavior +- Prompt layering from `chatbot/prompts/*.md` +- Retrieval grounding from `chatbot/knowledge/portfolio-profile.json` +- In-memory session context (`recentTurns`, rolling summary, pinned facts) +- Structured UI responses (`summary`, `sections`, `citations`, `suggestedActions`, `meta`) + +## Environment Variables + +Required (core app): +- `VITE_FIREBASE_API_KEY` +- `VITE_FIREBASE_AUTH_DOMAIN` +- `VITE_FIREBASE_DATABASE_URL` +- `VITE_FIREBASE_PROJECT_ID` +- `VITE_FIREBASE_STORAGE_BUCKET` +- `VITE_FIREBASE_MESSAGING_SENDER_ID` +- `VITE_FIREBASE_APP_ID` + +Required (chatbot generation): +- `GEMINI_API_KEY` + +Required (uploads): +- `BLOB_READ_WRITE_TOKEN` + +Optional (admin/server features): +- `FIREBASE_SERVICE_ACCOUNT` +- `ADMIN_SECRETS_KEY` +- `ADMIN_UIDS` + +## Scripts + +- `npm run dev`: run frontend + backend concurrently +- `npm run dev:client`: run Vite only +- `npm run dev:server`: run Express only +- `npm run lint`: TypeScript no-emit checks +- `npm test`: runtime smoke test script +- `npm run build`: production build to `dist/` +- `npm start`: run Express server + +## CI/CD + +- CI: `.github/workflows/ci.yml` +- Deploy: `.github/workflows/deploy-vercel.yml` + +Expected deploy secrets: +- `VERCEL_TOKEN` +- `VERCEL_ORG_ID` +- `VERCEL_PROJECT_ID` + +## Notes + +- The chatbot session memory is runtime in-memory and resets on server restart. +- `docs/` and zip-derived standalone admin-console artifacts were removed during cleanup. diff --git a/admin.html b/admin.html deleted file mode 100644 index 3e26fb9..0000000 --- a/admin.html +++ /dev/null @@ -1,281 +0,0 @@ - - - - - - - - - Blog Admin - Dee - - - - - - - - -
- - - - - -
-
-

Sign In

-

Access your blog admin dashboard

- -
-
- - -
- -
- - -
- -
- -
-
-
-
- - - - - - - - - - - - - - diff --git a/adminscript.js b/adminscript.js deleted file mode 100644 index 54615de..0000000 --- a/adminscript.js +++ /dev/null @@ -1,625 +0,0 @@ -'use strict'; - -// Firebase configuration for the admin panel is loaded from /firebase-config.js -const firebaseConfig = window.FIREBASE_CONFIG; -if (!firebaseConfig) { - throw new Error('Missing Firebase config. Add Firebase values to .env and run the Node server to serve /firebase-config.js.'); -} - -firebase.initializeApp(firebaseConfig); -const auth = firebase.auth(); -const database = firebase.database(); -const blogPostsRef = database.ref('blogPosts'); -const storage = firebase.storage(); - -let currentUser = null; -let deletePostId = null; -let allPosts = []; -const imageFileInput = document.getElementById('post-image-file'); -let imageFilePreviewUrl = ''; - -function escapeHtml(value = '') { - return String(value) - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"') - .replace(/'/g, '''); -} - -function inlineCodeMarkup(value) { - const escaped = escapeHtml(value || ''); - return escaped.replace(/`([^`]+)`/g, '$1'); -} - -function setPreviewHtml(element, text) { - if (!element) return; - element.innerHTML = inlineCodeMarkup(text); -} - -const PLACEHOLDER_MEDIA_URL = 'https://via.placeholder.com/400x250?text=Blog+Post'; -const mediaStatusEl = document.getElementById('media-upload-status'); -let imageFileMimeType = 'image'; - -function detectMediaTypeFromUrl(url) { - if (!url) return 'image'; - return /\.(mp4|webm|ogg|mov|m4v)(\?|$)/i.test(url) ? 'video' : 'image'; -} - -function setMediaStatus(message) { - if (mediaStatusEl) { - mediaStatusEl.textContent = message; - } -} - -function renderPreviewMedia(src, type) { - const container = document.getElementById('preview-media'); - if (!container) return; - const safeSrc = escapeHtml(src || PLACEHOLDER_MEDIA_URL); - if (type === 'video') { - container.innerHTML = ` - - `; - } else { - container.innerHTML = ` - Preview media - `; - } -} - -const authForm = document.getElementById('auth-form'); -if (authForm) { - authForm.addEventListener('submit', async (e) => { - e.preventDefault(); - const email = document.getElementById('auth-email').value.trim(); - const password = document.getElementById('auth-password').value; - const submitBtn = document.getElementById('auth-submit'); - - submitBtn.classList.add('loading'); - submitBtn.disabled = true; - - try { - const result = await auth.signInWithEmailAndPassword(email, password); - console.log('βœ“ Email/password sign-in successful:', result.user.email); - showAlert(`Welcome, ${email}!`); - } catch (error) { - console.error('Sign-in error:', error.code, error.message); - showAlert(error.message, 'error'); - submitBtn.classList.remove('loading'); - submitBtn.disabled = false; - } - }); -} -// Set up auth state listener -auth.onAuthStateChanged((user) => { - currentUser = user; - console.log('Auth state changed:', user ? `Logged in as ${user.email}` : 'Logged out'); - - if (user) { - document.getElementById('auth-section').classList.add('hidden'); - document.getElementById('admin-section').classList.remove('hidden'); - - // Scroll to top to show admin section - window.scrollTo(0, 0); - - document.getElementById('user-email').textContent = user.email; - document.getElementById('user-avatar').textContent = user.email.charAt(0).toUpperCase(); - - listenForPosts(); - ensureScheduledPostsPublished(); - setInterval(ensureScheduledPostsPublished, 60000); - console.log('Admin section displayed for:', user.email); - } else { - document.getElementById('auth-section').classList.remove('hidden'); - document.getElementById('admin-section').classList.add('hidden'); - window.scrollTo(0, 0); - } -}); - -// Set up image file input listener -if (imageFileInput) { - imageFileInput.addEventListener('change', handleImageFileChange); -} - -// Sign out function -window.signOut = async function signOut() { - try { - await auth.signOut(); - showAlert('Signed out successfully!'); - } catch (error) { - showAlert(error.message, 'error'); - } -}; - -function showAlert(message, type = 'success') { - const alertsContainer = document.getElementById('alerts'); - const alert = document.createElement('div'); - alert.className = `alert alert-${type}`; - alert.textContent = message; - alertsContainer.appendChild(alert); - setTimeout(() => alert.remove(), 4000); -} - -function updatePreview() { - const title = document.getElementById('post-title')?.value?.trim() || 'Post title'; - const excerpt = document.getElementById('post-excerpt')?.value?.trim() || 'Post excerpt will appear here once you start typing.'; - const category = document.getElementById('post-category')?.value || 'Category'; - const dateValue = document.getElementById('post-date')?.value; - const imageValue = document.getElementById('post-image')?.value?.trim(); - const tags = getTags(); - - const previewTitle = document.getElementById('preview-title'); - const previewExcerpt = document.getElementById('preview-excerpt'); - const previewMeta = document.getElementById('preview-meta'); - const previewTags = document.getElementById('preview-tags'); - - if (previewTitle) setPreviewHtml(previewTitle, title); - if (previewExcerpt) setPreviewHtml(previewExcerpt, excerpt); - - const formattedDate = dateValue ? formatDate(dateValue) : 'Date'; - if (previewMeta) previewMeta.textContent = `${category || 'Category'} β€’ ${formattedDate}`; - - const previewSrc = imageFilePreviewUrl || imageValue || PLACEHOLDER_MEDIA_URL; - const previewType = imageFilePreviewUrl ? imageFileMimeType : detectMediaTypeFromUrl(imageValue); - renderPreviewMedia(previewSrc, previewType); - - if (previewTags) { - previewTags.innerHTML = ''; - tags.forEach((tag) => { - const tagEl = document.createElement('span'); - tagEl.className = 'preview-tag'; - tagEl.textContent = tag; - previewTags.appendChild(tagEl); - }); - } -} - -function handleImageFileChange() { - const file = imageFileInput?.files?.[0]; - if (!file) { - imageFilePreviewUrl = ''; - imageFileMimeType = 'image'; - updatePreview(); - setMediaStatus(''); - return; - } - - imageFileMimeType = file.type.startsWith('video/') ? 'video' : 'image'; - const reader = new FileReader(); - reader.onload = () => { - imageFilePreviewUrl = reader.result; - updatePreview(); - }; - reader.readAsDataURL(file); - document.getElementById('post-image').value = ''; - setMediaStatus(''); -} - -async function uploadFeaturedImage(file) { - if (!file) { - throw new Error('No image selected'); - } - if (!currentUser) { - throw new Error('Sign in before uploading images.'); - } - - const mediaKind = file.type.startsWith('video/') ? 'video' : 'image'; - setMediaStatus(`Uploading ${file.name}...`); - - const formData = new FormData(); - formData.append('file', file); - formData.append('upload_preset', 'blog_uploads'); - - const cloudinaryUrl = 'https://api.cloudinary.com/v1_1/ddtfrh6az/upload'; - - const response = await fetch(cloudinaryUrl, { - method: 'POST', - body: formData - }); - - if (!response.ok) { - const error = await response.json(); - throw new Error(error.error?.message || 'Upload failed'); - } - - const result = await response.json(); - imageFileMimeType = result.resource_type === 'video' ? 'video' : 'image'; - setMediaStatus(`Uploaded: ${file.name} (${mediaKind})`); - return result.secure_url; -} - -function formatDate(dateString) { - const options = { year: 'numeric', month: 'short', day: 'numeric' }; - return new Date(dateString).toLocaleDateString('en-US', options); -} - -function generateSlug(title) { - return title.toLowerCase() - .replace(/[^\w\s-]/g, '') - .replace(/\s+/g, '-') - .replace(/-+/g, '-') - .trim(); -} - -function getTags() { - const tagsInput = document.getElementById('tags-input'); - return Array.from(tagsInput.querySelectorAll('.tag')).map((tag) => - tag.textContent.replace('Γ—', '').trim() - ); -} - -function renderTags(tags = []) { - const tagsInput = document.getElementById('tags-input'); - const tagInput = document.getElementById('tag-input'); - - tagsInput.querySelectorAll('.tag').forEach((tag) => tag.remove()); - - tags.forEach((tag) => { - const tagEl = document.createElement('div'); - tagEl.className = 'tag'; - tagEl.innerHTML = `${tag} `; - tagEl.querySelector('button').onclick = () => { - tagEl.remove(); - updatePreview(); - }; - tagsInput.insertBefore(tagEl, tagInput); - }); - - updatePreview(); -} - -const tagInputEl = document.getElementById('tag-input'); -if (tagInputEl) { - tagInputEl.addEventListener('keydown', (e) => { - if (e.key === 'Enter') { - e.preventDefault(); - const value = e.target.value.trim(); - if (value) { - renderTags([...getTags(), value]); - e.target.value = ''; - } - } - }); -} - -function listenForPosts() { - if (!currentUser) return; - - const postsRef = database.ref(`users/${currentUser.uid}/posts`); - postsRef.on('value', (snapshot) => { - const postsObj = snapshot.val() || {}; - const posts = Object.keys(postsObj) - .map((key) => ({ id: key, ...postsObj[key] })) - .sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt)); - - allPosts = posts; - applyPostFilters(); - }); -} - -async function ensureScheduledPostsPublished() { - if (!currentUser) return; - - const now = new Date(); - const postsRef = database.ref(`users/${currentUser.uid}/posts`); - const snapshot = await postsRef.once('value'); - const postsObj = snapshot.val() || {}; - - await Promise.all( - Object.entries(postsObj).map(async ([id, post]) => { - if (!post || post.status !== 'scheduled' || !post.publishAt) return; - const publishDate = new Date(post.publishAt); - if (Number.isNaN(publishDate.getTime())) return; - if (publishDate <= now) { - const updates = { - status: 'published', - publishedAt: now.toISOString() - }; - const multi = {}; - multi[`users/${currentUser.uid}/posts/${id}`] = { ...post, ...updates }; - multi[`blogPosts/${id}`] = { ...post, ...updates }; - await database.ref().update(multi); - } - }) - ); -} - -function renderPosts(posts, totalCount = posts.length) { - const container = document.getElementById('posts-container'); - const count = posts.length; - const postsCountEl = document.getElementById('posts-count'); - if (postsCountEl) { - postsCountEl.textContent = totalCount; - } - const statCountEl = document.getElementById('stat-posts-count'); - if (statCountEl) { - statCountEl.textContent = totalCount; - } - const lastSyncEl = document.getElementById('stat-last-sync'); - if (lastSyncEl) { - lastSyncEl.textContent = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); - } - - if (count === 0) { - container.innerHTML = '
No posts yet. Create your first post!
'; - return; - } - - container.innerHTML = posts.map((post) => ` -
-
${post.title}
-
- ${post.category} β€’ ${formatDate(post.date)}${post.status === 'scheduled' ? ' β€’ Scheduled' : ''} -
-

${post.excerpt || ''}

-
- - -
-
- `).join(''); -} - -function applyPostFilters() { - const searchInput = document.getElementById('posts-search'); - const filterSelect = document.getElementById('posts-filter'); - const statusSelect = document.getElementById('posts-status'); - - const query = searchInput ? searchInput.value.trim().toLowerCase() : ''; - const category = filterSelect ? filterSelect.value : 'all'; - const status = statusSelect ? statusSelect.value : 'all'; - - const filtered = allPosts.filter((post) => { - const matchesCategory = category === 'all' || post.category === category; - const matchesStatus = status === 'all' || post.status === status || (!post.status && status === 'published'); - const matchesQuery = !query - || post.title?.toLowerCase().includes(query) - || post.excerpt?.toLowerCase().includes(query) - || (post.tags || []).some((tag) => tag.toLowerCase().includes(query)); - return matchesCategory && matchesStatus && matchesQuery; - }); - - renderPosts(filtered, allPosts.length); -} - -const postDateInput = document.getElementById('post-date'); -if (postDateInput) { - postDateInput.valueAsDate = new Date(); -} - -updatePreview(); - -const postForm = document.getElementById('post-form'); -if (postForm) { - postForm.addEventListener('submit', async (e) => { - e.preventDefault(); - - if (!currentUser) { - showAlert('You must be signed in to create posts', 'error'); - return; - } - - const postId = postForm.dataset.editId; - const title = document.getElementById('post-title').value; - const imageInput = document.getElementById('post-image'); - let imageUrlValue = imageInput?.value?.trim() || ''; - const imageFile = imageFileInput?.files?.[0]; - if (imageFile) { - imageUrlValue = await uploadFeaturedImage(imageFile); - if (imageInput) { - imageInput.value = imageUrlValue; - } - imageFilePreviewUrl = ''; - updatePreview(); - } - const submitBtn = document.getElementById('submit-btn'); - const publishAtInput = document.getElementById('post-publish-at'); - const publishAtValue = publishAtInput ? publishAtInput.value : ''; - const publishAt = publishAtValue ? new Date(publishAtValue) : null; - const isScheduled = publishAt && !Number.isNaN(publishAt.getTime()) && publishAt > new Date(); - - submitBtn.classList.add('loading'); - submitBtn.disabled = true; - - const resolvedMediaUrl = imageUrlValue || PLACEHOLDER_MEDIA_URL; - const resolvedMediaType = imageFile ? imageFileMimeType : detectMediaTypeFromUrl(resolvedMediaUrl); - - const postData = { - title, - slug: generateSlug(title), - excerpt: document.getElementById('post-excerpt').value, - content: document.getElementById('post-content').value, - category: document.getElementById('post-category').value, - tags: getTags(), - author: 'Dee', - date: document.getElementById('post-date').value, - image: resolvedMediaUrl, - updatedAt: new Date().toISOString(), - publishAt: publishAt ? publishAt.toISOString() : '', - status: isScheduled ? 'scheduled' : 'published' - }; - postData.mediaUrl = resolvedMediaUrl; - postData.mediaType = resolvedMediaType; - - try { - if (postId) { - if (!postData.publishedAt && postData.status === 'published') { - postData.publishedAt = new Date().toISOString(); - } - const updates = {}; - updates[`users/${currentUser.uid}/posts/${postId}`] = postData; - updates[`blogPosts/${postId}`] = postData; - await database.ref().update(updates); - showAlert(isScheduled ? 'Post scheduled successfully!' : 'Post updated successfully!'); - } else { - postData.createdAt = new Date().toISOString(); - if (!isScheduled) { - postData.publishedAt = new Date().toISOString(); - } - const newPostRef = database.ref(`users/${currentUser.uid}/posts`).push(); - const newPostId = newPostRef.key; - const updates = {}; - updates[`users/${currentUser.uid}/posts/${newPostId}`] = postData; - updates[`blogPosts/${newPostId}`] = postData; - await database.ref().update(updates); - showAlert(isScheduled ? 'Post scheduled successfully!' : 'Post created successfully!'); - } - - postForm.reset(); - postForm.dataset.editId = ''; - document.getElementById('form-title').textContent = 'Compose Post'; - document.getElementById('submit-btn').textContent = 'Create Post'; - document.getElementById('cancel-edit-btn').style.display = 'none'; - document.getElementById('post-date').valueAsDate = new Date(); - const publishAtInput = document.getElementById('post-publish-at'); - if (publishAtInput) { - publishAtInput.value = ''; - } - renderTags(); - if (imageFileInput) { - imageFileInput.value = ''; - } - imageFilePreviewUrl = ''; - updatePreview(); - } catch (error) { - if (error && (error.code === 'PERMISSION_DENIED' || /PERMISSION_DENIED/i.test(error.message))) { - showAlert('Permission denied. Update your Firebase Realtime Database rules to allow writes to users/{uid}/posts and blogPosts.', 'error'); - } else { - showAlert(error.message, 'error'); - } - } finally { - submitBtn.classList.remove('loading'); - submitBtn.disabled = false; - } - }); - - postForm.addEventListener('reset', () => { - if (imageFileInput) { - imageFileInput.value = ''; - } - imageFilePreviewUrl = ''; - imageFileMimeType = 'image'; - setMediaStatus(''); - updatePreview(); - }); -} - -window.editPost = function editPost(postId) { - if (!currentUser) return; - - database.ref(`users/${currentUser.uid}/posts/${postId}`).once('value', (snapshot) => { - const post = snapshot.val(); - if (!post) return; - - document.getElementById('post-title').value = post.title; - document.getElementById('post-category').value = post.category; - document.getElementById('post-date').value = post.date; - document.getElementById('post-excerpt').value = post.excerpt; - document.getElementById('post-content').value = post.content; - document.getElementById('post-image').value = post.image || ''; - const publishAtInput = document.getElementById('post-publish-at'); - if (publishAtInput) { - publishAtInput.value = post.publishAt ? post.publishAt.slice(0, 16) : ''; - } - - renderTags(post.tags || []); - - postForm.dataset.editId = postId; - document.getElementById('form-title').textContent = 'Edit Post'; - document.getElementById('submit-btn').textContent = 'Update Post'; - document.getElementById('cancel-edit-btn').style.display = 'block'; - - updatePreview(); - document.querySelector('.form-section').scrollIntoView({ behavior: 'smooth' }); - }); -}; - -window.openDeleteModal = function openDeleteModal(postId) { - deletePostId = postId; - document.getElementById('delete-modal').classList.add('active'); -}; - -window.closeDeleteModal = function closeDeleteModal() { - deletePostId = null; - document.getElementById('delete-modal').classList.remove('active'); -}; - -window.confirmDelete = async function confirmDelete() { - if (!deletePostId || !currentUser) return; - - try { - const updates = {}; - updates[`users/${currentUser.uid}/posts/${deletePostId}`] = null; - updates[`blogPosts/${deletePostId}`] = null; - await database.ref().update(updates); - showAlert('Post deleted successfully!'); - closeDeleteModal(); - } catch (error) { - showAlert(error.message, 'error'); - } -}; - -const cancelEditBtn = document.getElementById('cancel-edit-btn'); -if (cancelEditBtn) { - cancelEditBtn.addEventListener('click', () => { - postForm.reset(); - postForm.dataset.editId = ''; - document.getElementById('form-title').textContent = 'Compose Post'; - document.getElementById('submit-btn').textContent = 'Create Post'; - cancelEditBtn.style.display = 'none'; - document.getElementById('post-date').valueAsDate = new Date(); - const publishAtInput = document.getElementById('post-publish-at'); - if (publishAtInput) { - publishAtInput.value = ''; - } - renderTags(); - }); -} - -['post-title', 'post-excerpt', 'post-category', 'post-date', 'post-image'].forEach((id) => { - const input = document.getElementById(id); - if (input) { - input.addEventListener('input', updatePreview); - input.addEventListener('change', updatePreview); - } -}); - -const postsSearchInput = document.getElementById('posts-search'); -if (postsSearchInput) { - postsSearchInput.addEventListener('input', applyPostFilters); -} - -const postsFilterSelect = document.getElementById('posts-filter'); -if (postsFilterSelect) { - postsFilterSelect.addEventListener('change', applyPostFilters); -} - -const postsStatusSelect = document.getElementById('posts-status'); -if (postsStatusSelect) { - postsStatusSelect.addEventListener('change', applyPostFilters); -} - -const adminNavToggle = document.getElementById('admin-nav-toggle'); -const adminNavLinks = document.getElementById('admin-nav-links'); -if (adminNavToggle && adminNavLinks) { - adminNavToggle.addEventListener('click', () => { - adminNavLinks.classList.toggle('open'); - adminNavToggle.classList.toggle('open'); - }); - - adminNavLinks.querySelectorAll('a').forEach((link) => { - link.addEventListener('click', () => { - adminNavLinks.classList.remove('open'); - adminNavToggle.classList.remove('open'); - }); - }); -} - -const deleteModal = document.getElementById('delete-modal'); -if (deleteModal) { - deleteModal.addEventListener('click', (e) => { - if (e.target.id === 'delete-modal') { - closeDeleteModal(); - } - }); -} diff --git a/blog.html b/blog.html deleted file mode 100644 index 2c8a11f..0000000 --- a/blog.html +++ /dev/null @@ -1,445 +0,0 @@ - - - - - - - - - Developer - - - - - - - - - - - - - - - -
-
- -
- -
- - -
-

- -
- - - -
-
- - -
-
- - - - -
-
- -
-
-
-
- - - - - - - - - - - diff --git a/blogscript.js b/blogscript.js deleted file mode 100644 index 3c6bde5..0000000 --- a/blogscript.js +++ /dev/null @@ -1,142 +0,0 @@ -'use strict'; - -const firebaseConfig = window.FIREBASE_CONFIG; -if (!firebaseConfig) { - throw new Error('Missing Firebase config. Add Firebase values to .env and run the Node server to serve /firebase-config.js.'); -} - -if (!firebase.apps.length) { - firebase.initializeApp(firebaseConfig); -} - -const database = firebase.database(); -const blogPostsRef = database.ref('blogPosts'); -const defaultBlogPosts = window.DEFAULT_BLOG_POSTS || []; -const PLACEHOLDER_MEDIA_URL = 'https://via.placeholder.com/640x360?text=Blog+Preview'; - -const mediaTypePattern = /\.(mp4|webm|ogg|mov|m4v)(?:\?|$)/i; - -function determineMediaTypeFromUrl(url) { - if (!url) return 'image'; - return mediaTypePattern.test(url.split('?')[0]) ? 'video' : 'image'; -} - -function normalizePost(post) { - const resolvedMediaUrl = post.mediaUrl || post.image || PLACEHOLDER_MEDIA_URL; - const resolvedMediaType = post.mediaType || determineMediaTypeFromUrl(resolvedMediaUrl); - return { - ...post, - mediaUrl: resolvedMediaUrl, - mediaType: resolvedMediaType - }; -} - -async function promoteScheduledPosts() { - const now = new Date(); - const snapshot = await blogPostsRef.once('value'); - const postsObj = snapshot.val() || {}; - - await Promise.all( - Object.entries(postsObj).map(async ([id, post]) => { - if (!post || post.status !== 'scheduled' || !post.publishAt) return; - const publishDate = new Date(post.publishAt); - if (Number.isNaN(publishDate.getTime())) return; - if (publishDate <= now) { - await blogPostsRef.child(id).update({ - status: 'published', - publishedAt: now.toISOString() - }); - } - }) - ); -} - -async function fetchAllPosts() { - const snapshot = await blogPostsRef.once('value'); - const postsObj = snapshot.val() || {}; - return Object.keys(postsObj).map((key) => ({ id: key, ...postsObj[key] })); -} - -function filterPublished(posts) { - return posts.filter((post) => !post.status || post.status === 'published'); -} - -function sortPosts(posts) { - const getTimestamp = (post) => { - const source = post.date || post.publishedAt || post.createdAt; - const time = new Date(source).getTime(); - return Number.isNaN(time) ? 0 : time; - }; - return posts.slice().sort((a, b) => getTimestamp(b) - getTimestamp(a)); -} - -function createSeedPost(post) { - const timestamp = new Date().toISOString(); - const seeded = { - ...post, - tags: Array.isArray(post.tags) ? post.tags : [], - status: 'published', - createdAt: post.createdAt || timestamp, - updatedAt: post.updatedAt || timestamp, - publishedAt: post.publishedAt || timestamp, - publishAt: post.publishAt || '' - }; - return normalizePost(seeded); -} - -async function seedDefaultPosts(existingPosts) { - if (!defaultBlogPosts.length) { - return false; - } - - const normalizedExisting = existingPosts.map(normalizePost); - const existingSlugs = new Set(normalizedExisting.map((post) => post.slug)); - const missingTemplatePosts = defaultBlogPosts.filter((post) => post.slug && !existingSlugs.has(post.slug)); - - if (!missingTemplatePosts.length) { - return false; - } - - const updates = {}; - missingTemplatePosts.forEach((post) => { - const id = blogPostsRef.push().key; - if (!id) return; - updates[id] = createSeedPost(post); - }); - - if (!Object.keys(updates).length) { - return false; - } - - await blogPostsRef.update(updates); - return true; -} - -async function loadPreparedPosts() { - let posts = await fetchAllPosts(); - return sortPosts(filterPublished(posts.map(normalizePost))); -} - -// Start loading posts immediately instead of waiting for DOMContentLoaded -async function initializeBlog() { - const renderTarget = typeof window.updateBlogPosts === 'function' ? window.updateBlogPosts : renderPosts; - - try { - const posts = await loadPreparedPosts(); - renderTarget(posts); - database.goOffline(); - } catch (error) { - console.error('Failed to load Firebase posts', error); - if (defaultBlogPosts.length) { - renderTarget(defaultBlogPosts); - } - } -} - -// Initialize immediately if DOM is ready, otherwise wait for DOMContentLoaded -if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', initializeBlog, { once: true }); -} else { - // DOM is already ready, start loading posts immediately - initializeBlog(); -} diff --git a/chatbot/ARCHITECTURE.md b/chatbot/ARCHITECTURE.md new file mode 100644 index 0000000..6324add --- /dev/null +++ b/chatbot/ARCHITECTURE.md @@ -0,0 +1,515 @@ +# Chatbot Architecture + +## Purpose + +This directory is the source of truth for the portfolio AI assistant. + +The assistant is intended to: + +- answer questions about Dee, the portfolio, projects, skills, blog, and contact channels +- summarize any major area of the website in different tones and levels of depth +- maintain meaningful session memory so conversations are not stateless +- use Google Gemini as the LLM provider +- ground its claims in portfolio knowledge rather than relying on model guesswork +- expose an editable prompt and behavior configuration so tone and strategy can be tuned over time +- return structured responses with visible reasoning stages, source references, and clear next actions + +This module must remain separate from the React presentation layer in `src/`. +The React app should render the UI and call the chatbot APIs, but the chatbot behavior, prompt design, memory model, knowledge model, and orchestration rules should live here. + +## Scope + +## Current Implementation Snapshot + +As implemented in this repository today: + +- `server.js` owns Gemini orchestration and chatbot APIs +- `src/Components/Chatbot/Chatbot.jsx` is an API-driven frontend adapter +- prompts are loaded from `chatbot/prompts/` +- grounded knowledge is loaded from `chatbot/knowledge/portfolio-profile.json` +- session memory is server-side and in-memory for runtime sessions +- response payloads include structured sections, citations, suggested actions, and provider diagnostics + +Known limitation: + +- memory is not yet durable across server restarts + +This chatbot module owns: + +- prompt files +- knowledge and retrieval definitions +- memory strategy +- API contract +- response schema +- assistant behavior rules +- implementation notes for admin configurability + +This chatbot module does not own: + +- site-wide routing +- generic portfolio layout +- unrelated server endpoints +- admin authentication model + +## Product Goals + +The assistant should act like a portfolio strategist, not a generic help bot. + +It should be able to: + +- explain what Dee does in plain language +- persuade recruiters, clients, or collaborators using grounded portfolio evidence +- summarize projects, skills, and experience with different levels of detail +- answer follow-up questions while preserving session context +- identify what the user is likely asking for even when the request is vague +- suggest the next best action, such as viewing projects, contacting Dee, or opening the resume + +## Non-Goals + +The first implementation should not attempt: + +- model fine-tuning in the literal training sense +- exposing raw chain-of-thought to users +- cross-device user identity memory without an explicit product decision +- autonomous browsing or open web search unless intentionally added later +- unrestricted claims that are not grounded in site content + +## Core Constraints + +1. The assistant must not be stateless. +2. The assistant must preserve conversation context across many turns within a session. +3. The system prompt must be editable without editing core UI files. +4. The assistant must use Gemini through server-side orchestration. +5. The assistant must be grounded in real portfolio content. +6. The UI must visibly communicate phases such as thinking, searching, summarizing, and responding. +7. The assistant must be able to reference the sources or sections behind its claims. + +## Implementation Interpretation + +"Fine tuned" in this project means prompt-tuned plus retrieval-grounded plus memory-aware. + +It does not mean training a custom Gemini model. + +The correct implementation is: + +- Gemini model invocation on the server +- curated system prompt +- portfolio knowledge retrieval +- short-term and rolling-summary memory +- structured response output +- configurable tone and persuasion rules + +## Directory Plan + +The intended structure for this directory is: + +```text +chatbot/ +β”œβ”€β”€ ARCHITECTURE.md +β”œβ”€β”€ STAGE_1_TODO.md +β”œβ”€β”€ prompts/ +β”‚ β”œβ”€β”€ system.md +β”‚ β”œβ”€β”€ tone.md +β”‚ β”œβ”€β”€ retrieval.md +β”‚ β”œβ”€β”€ summarization.md +β”‚ └── safety.md +β”œβ”€β”€ knowledge/ +β”‚ β”œβ”€β”€ portfolio-profile.json +β”‚ β”œβ”€β”€ sections/ +β”‚ β”œβ”€β”€ projects/ +β”‚ β”œβ”€β”€ skills/ +β”‚ β”œβ”€β”€ blog/ +β”‚ └── contact/ +β”œβ”€β”€ schemas/ +β”‚ β”œβ”€β”€ response.schema.json +β”‚ β”œβ”€β”€ session.schema.json +β”‚ └── citation.schema.json +β”œβ”€β”€ retrieval/ +β”‚ β”œβ”€β”€ chunking.md +β”‚ β”œβ”€β”€ ranking.md +β”‚ └── sources.md +β”œβ”€β”€ memory/ +β”‚ β”œβ”€β”€ strategy.md +β”‚ β”œβ”€β”€ summarization.md +β”‚ └── limits.md +β”œβ”€β”€ api/ +β”‚ β”œβ”€β”€ contract.md +β”‚ └── examples.md +└── ui/ + β”œβ”€β”€ states.md + └── message-structure.md +``` + +Not every file needs to exist on day one. The structure defines ownership and future direction. + +## System Components + +### 1. Frontend Adapter + +The current component in `src/Components/Chatbot/Chatbot.jsx` should become a thin client. + +Responsibilities: + +- render the panel, messages, status indicators, citations, and actions +- send user input to the server +- maintain client-local transient state such as open or closed panel and optimistic message rendering +- render session output returned by the server + +It should not own: + +- business logic for answer generation +- retrieval logic +- prompt text +- long-lived memory decisions + +### 2. Server Orchestrator + +The server should manage all Gemini access. + +Responsibilities: + +- receive chat requests +- load prompt configuration +- load session memory +- retrieve relevant portfolio context +- assemble a model-ready prompt +- call Gemini +- normalize the model response into a strict UI shape +- update memory after each turn + +This should be added to `server.js` first, with the option to extract to dedicated modules later. + +### 3. Knowledge Layer + +The assistant should answer from curated portfolio knowledge. + +Knowledge inputs should include: + +- hero copy +- about content +- credentials and certifications +- project descriptions and outcomes +- skills and technologies +- contact details and links +- blog summaries and topical themes +- resume metadata + +Each knowledge chunk should carry: + +- `id` +- `type` +- `title` +- `summary` +- `content` +- `tags` +- `source` +- `priority` +- `updatedAt` + +### 4. Memory Layer + +The assistant must keep context beyond a single turn. + +Recommended model: + +- recent turns window for precise conversational continuity +- rolling summary memory for long sessions +- optional pinned facts gathered from the conversation + +Session memory should live server-side. + +The browser may cache the visible transcript, but it should not be the sole source of memory. + +### 5. Admin Configuration Layer + +The assistant should be tunable without direct source edits. + +Editable settings should eventually include: + +- Gemini API configuration +- base system prompt +- tone rules +- persuasion guidance +- maximum context window strategy +- citation behavior +- summarization style + +The active admin surface for settings and future chatbot diagnostics is under `src/Pages/Admin/`. + +## Knowledge Model + +The assistant must be able to answer and summarize using curated site facts. + +Recommended source categories: + +- `identity` +- `positioning` +- `skills` +- `projects` +- `credentials` +- `contact` +- `blog` +- `resume` +- `social-links` + +Recommended retrieval behavior: + +1. classify the user request +2. determine relevant content domains +3. retrieve the top ranked chunks +4. assemble context with concise source descriptors +5. ask Gemini to answer only from retrieved context and known session memory unless explicitly labeled as a general opinion + +## Memory Model + +Session memory should support long conversations without unbounded token growth. + +Recommended session shape: + +```json +{ + "sessionId": "string", + "createdAt": "ISO date", + "updatedAt": "ISO date", + "recentTurns": [], + "rollingSummary": "string", + "pinnedFacts": [], + "userIntentProfile": { + "audience": "recruiter|client|collaborator|general|unknown", + "goal": "string" + } +} +``` + +Memory policy: + +- keep the last several detailed turns +- periodically summarize older turns into a compact memory block +- preserve explicit user goals and high-value context +- do not fabricate facts about the user or Dee + +## Prompt Model + +The assistant prompt should be split into layers instead of one giant string. + +Recommended prompt layers: + +1. base system prompt +2. tone and brand voice prompt +3. retrieval grounding instructions +4. citation and answer formatting rules +5. current session memory summary +6. current retrieved knowledge chunks +7. latest user turn + +This makes tuning safer and easier. + +## API Contract + +Initial server endpoints should be minimal and explicit. + +### `POST /api/chatbot/session` + +Purpose: + +- create a new chat session + +Response: + +- `sessionId` +- initial assistant greeting +- initial UI capabilities + +### `POST /api/chatbot/message` + +Purpose: + +- send a user message into an existing session + +Request body: + +```json +{ + "sessionId": "string", + "message": "string", + "pageContext": { + "route": "/", + "section": "skills" + } +} +``` + +Response body: + +```json +{ + "sessionId": "string", + "status": "completed", + "assistantMessage": { + "id": "string", + "role": "assistant", + "summary": "string", + "sections": [ + { + "label": "Summary", + "content": "string" + } + ], + "citations": [ + { + "label": "Skills", + "sourceId": "skills-core", + "anchor": "skills" + } + ], + "suggestedActions": [ + { + "type": "scroll", + "label": "View Skills", + "target": "skills" + } + ], + "meta": { + "stages": ["thinking", "searching", "drafting"], + "memoryUpdated": true + } + } +} +``` + +### `POST /api/chatbot/summarize` + +Purpose: + +- generate structured summaries of the portfolio, a project, a skill set, or Dee's positioning for a given audience + +### `GET /api/chatbot/config/status` + +Purpose: + +- expose whether Gemini and chatbot prompt configuration are available + +## Response Schema + +The response should be render-friendly and not just a blob of text. + +Recommended message shape: + +```json +{ + "id": "string", + "role": "assistant", + "summary": "one-sentence answer", + "sections": [ + { + "label": "Why Dee Fits", + "content": "grounded explanation" + }, + { + "label": "Evidence", + "content": "skills, projects, credentials" + } + ], + "citations": [ + { + "label": "Projects", + "sourceId": "projects-featured", + "anchor": "projects" + } + ], + "suggestedActions": [ + { + "type": "scroll", + "label": "See Projects", + "target": "projects" + } + ], + "meta": { + "intent": "recruiter-fit", + "confidence": "high", + "usedMemory": true, + "usedRetrieval": true + } +} +``` + +## UI State Model + +The UI should display assistant workflow stages rather than pretending replies are instantaneous. + +Recommended stages: + +- `idle` +- `thinking` +- `searching` +- `summarizing` +- `drafting` +- `completed` +- `error` + +Recommended visible elements: + +- stage indicator +- structured response cards +- citations and source pills +- suggested next actions +- memory-aware continuity between turns +- collapsible details if the answer is long + +## Persuasion Rules + +The assistant may be persuasive, but only in grounded ways. + +Allowed: + +- reframing Dee's experience for different audiences +- emphasizing relevant skills and projects +- connecting capabilities to user goals +- summarizing strengths confidently + +Not allowed: + +- inventing experience +- overstating results without evidence +- claiming certifications, roles, or outcomes not present in the portfolio knowledge + +## Reference Rules + +The assistant should be able to reference what it says. + +For this portfolio, references should point to: + +- section names +- project records +- skill groups +- credential entries +- blog entries +- resume metadata + +It should not pretend to cite invisible sources. + +## Open Decisions + +The following are still product decisions and should be confirmed before deeper implementation: + +1. Should session memory persist only during one browser session, or across visits? +2. Should admin prompt edits be stored in files, environment-backed secrets, database records, or a hybrid model? +3. Should the assistant have explicit audience modes such as recruiter, client, and collaborator? +4. Should summarization be exposed as dedicated quick actions in the UI? +5. Should the assistant use strict citations for every answer, or only when claims are made? + +## Stage 1 Definition + +Stage 1 is architecture and infrastructure preparation. + +Stage 1 should deliver: + +- chatbot module scaffolding +- prompt file scaffolding +- knowledge source plan +- API contract definition +- response schema definition +- session and memory strategy definition +- no production Gemini orchestration yet unless needed for validation + +Stage 1 should not yet attempt final visual polish or advanced admin editing. \ No newline at end of file diff --git a/chatbot/api/contract.md b/chatbot/api/contract.md new file mode 100644 index 0000000..d6b7b4c --- /dev/null +++ b/chatbot/api/contract.md @@ -0,0 +1,92 @@ +# API Contract Draft + +## Endpoint: POST /api/chatbot/session + +Creates a new assistant session. + +Response shape: + +```json +{ + "sessionId": "string", + "message": { + "id": "string", + "role": "assistant", + "summary": "Welcome message", + "sections": [], + "citations": [], + "suggestedActions": [], + "meta": { + "intent": "welcome", + "usedMemory": false, + "usedRetrieval": false + } + } +} +``` + +## Endpoint: POST /api/chatbot/message + +Sends a user message into an existing session. + +Request shape: + +```json +{ + "sessionId": "string", + "message": "string", + "pageContext": { + "route": "/", + "section": "skills" + } +} +``` + +Response shape: + +```json +{ + "sessionId": "string", + "status": "completed", + "assistantMessage": { + "id": "string", + "role": "assistant", + "summary": "string", + "sections": [], + "citations": [], + "suggestedActions": [], + "meta": { + "intent": "string", + "usedMemory": true, + "usedRetrieval": true, + "stageTrace": ["thinking", "searching", "drafting"] + } + } +} +``` + +## Endpoint: POST /api/chatbot/summarize + +Produces structured summaries for a declared target. + +Request shape: + +```json +{ + "sessionId": "string", + "target": "portfolio|dee|skills|project|blog", + "audience": "recruiter|client|collaborator|general", + "subjectId": "optional-string" +} +``` + +## Endpoint: GET /api/chatbot/config/status + +Returns whether chatbot runtime configuration is ready. + +Status checks should eventually cover: + +- Gemini API key presence +- prompt availability +- knowledge availability +- session capability availability \ No newline at end of file diff --git a/chatbot/knowledge/README.md b/chatbot/knowledge/README.md new file mode 100644 index 0000000..02f5e66 --- /dev/null +++ b/chatbot/knowledge/README.md @@ -0,0 +1,62 @@ +# Knowledge Contract + +## Purpose + +The assistant should answer portfolio-specific questions from normalized knowledge records rather than raw page scraping or prompt-only memory. + +## Source Domains + +- identity +- positioning +- about +- skills +- projects +- credentials +- contact +- blog +- resume +- social-links + +## Normalized Chunk Shape + +Each knowledge record should follow this shape: + +```json +{ + "id": "skills-core", + "type": "skills", + "title": "Core engineering skills", + "summary": "High-signal summary used during retrieval.", + "content": "Full normalized content for the assistant.", + "tags": ["python", "react", "llm"], + "source": { + "kind": "site-section", + "path": "src/Pages/Skills/Skills.jsx", + "anchor": "skills" + }, + "priority": 0.9, + "updatedAt": "2026-03-20T00:00:00.000Z" +} +``` + +## Citation Requirements + +Every retrievable record should expose: + +- a stable `id` +- a human-readable `title` +- a `source.kind` +- a `source.anchor` +- a usable path or logical source reference + +## Initial Inventory Targets + +Stage 2 should extract and normalize these first: + +- Dee identity and positioning summary +- skills overview +- featured projects +- contact methods +- resume link and summary +- key credentials +- blog overview \ No newline at end of file diff --git a/chatbot/knowledge/portfolio-profile.json b/chatbot/knowledge/portfolio-profile.json new file mode 100644 index 0000000..5b193a2 --- /dev/null +++ b/chatbot/knowledge/portfolio-profile.json @@ -0,0 +1,175 @@ +{ + "generatedAt": "2026-03-20T00:00:00.000Z", + "site": { + "name": "Dee Portfolio", + "route": "/", + "summary": "Personal portfolio for Agoma Divine E., presenting positioning, skills, projects, credentials, contact channels, and blog content." + }, + "records": [ + { + "id": "identity-core", + "type": "identity", + "title": "Agoma Divine E.", + "summary": "Core identity and role statement for Dee.", + "content": "Agoma Divine E., also referred to as Dee, is presented as an AI Product & Systems Engineer focused on building intelligent systems with AI and machine learning to tackle real-world challenges and deliver innovative solutions.", + "tags": ["identity", "ai", "systems", "engineer"], + "source": { + "kind": "site-section", + "path": "src/Pages/Hero/Hero.jsx", + "anchor": "home" + }, + "priority": 1, + "updatedAt": "2026-03-20T00:00:00.000Z" + }, + { + "id": "positioning-hero", + "type": "positioning", + "title": "Hero positioning", + "summary": "Top-level positioning and value statement.", + "content": "The portfolio positions Dee as someone building intelligent systems with AI and machine learning, with an emphasis on solving real-world problems and producing practical solutions.", + "tags": ["positioning", "ai", "machine learning", "solutions"], + "source": { + "kind": "site-section", + "path": "src/Pages/Hero/Hero.jsx", + "anchor": "home" + }, + "priority": 0.96, + "updatedAt": "2026-03-20T00:00:00.000Z" + }, + { + "id": "about-summary", + "type": "about", + "title": "About summary", + "summary": "Short summary of Dee's working style and interests.", + "content": "Dee is described as a passionate developer who enjoys creating elegant solutions to complex problems, bringing ideas to life through clean code and thoughtful design. The about section also highlights interest in exploring new technologies, contributing to open-source projects, and sharing knowledge with the developer community.", + "tags": ["about", "clean code", "design", "open source"], + "source": { + "kind": "site-section", + "path": "src/Pages/About/About.jsx", + "anchor": "about" + }, + "priority": 0.92, + "updatedAt": "2026-03-20T00:00:00.000Z" + }, + { + "id": "about-stats", + "type": "about", + "title": "About stats", + "summary": "Headline portfolio stats shown in the about section.", + "content": "The portfolio shows 50+ projects completed, 2+ years of experience, and 100% client satisfaction.", + "tags": ["stats", "projects", "experience", "client satisfaction"], + "source": { + "kind": "site-section", + "path": "src/Pages/About/About.jsx", + "anchor": "about" + }, + "priority": 0.9, + "updatedAt": "2026-03-20T00:00:00.000Z" + }, + { + "id": "skills-frontend", + "type": "skills", + "title": "Frontend skills", + "summary": "Frontend technologies highlighted in the skills section.", + "content": "Frontend skills listed are HTML/CSS, JavaScript/TypeScript, React, and Vue.js. The section frames these as part of Dee's capability to build modern web applications.", + "tags": ["frontend", "html", "css", "javascript", "typescript", "react", "vue"], + "source": { + "kind": "site-section", + "path": "src/Pages/Skills/Skills.jsx", + "anchor": "skills" + }, + "priority": 0.95, + "updatedAt": "2026-03-20T00:00:00.000Z" + }, + { + "id": "skills-backend", + "type": "skills", + "title": "Backend skills", + "summary": "Backend technologies highlighted in the skills section.", + "content": "Backend skills listed are Node.js, Python, MongoDB, Express, and PostgreSQL.", + "tags": ["backend", "node", "python", "mongodb", "express", "postgresql"], + "source": { + "kind": "site-section", + "path": "src/Pages/Skills/Skills.jsx", + "anchor": "skills" + }, + "priority": 0.95, + "updatedAt": "2026-03-20T00:00:00.000Z" + }, + { + "id": "skills-tools", + "type": "skills", + "title": "Tools and platforms", + "summary": "Tools and platforms highlighted in the skills section.", + "content": "Tools and platforms listed are Git & GitOps, Docker, SSH, Linux, Cursor, Webpack, Firebase, VS Code, Copilot, Vercel, and AWS. The skills section also notes active open-source contribution and maintenance of utility libraries.", + "tags": ["tools", "docker", "linux", "firebase", "vercel", "aws", "open source"], + "source": { + "kind": "site-section", + "path": "src/Pages/Skills/Skills.jsx", + "anchor": "skills" + }, + "priority": 0.91, + "updatedAt": "2026-03-20T00:00:00.000Z" + }, + { + "id": "contact-core", + "type": "contact", + "title": "Primary contact channels", + "summary": "Direct ways to contact Dee through the portfolio.", + "content": "Dee can be reached through email at me@dykdee.xyz, LinkedIn, GitHub, Telegram, and WhatsApp. The contact section invites discussions about new projects, creative ideas, and opportunities.", + "tags": ["contact", "email", "linkedin", "github", "telegram", "whatsapp"], + "source": { + "kind": "site-section", + "path": "src/Pages/Contact/Contact.jsx", + "anchor": "contact" + }, + "priority": 0.98, + "updatedAt": "2026-03-20T00:00:00.000Z" + }, + { + "id": "resume-core", + "type": "resume", + "title": "Resume access", + "summary": "Resume file location exposed by the portfolio.", + "content": "The resume is available as a PDF at /media/Files/MyResume.pdf and can be opened from the about section.", + "tags": ["resume", "pdf"], + "source": { + "kind": "site-section", + "path": "src/Pages/About/About.jsx", + "anchor": "about" + }, + "priority": 0.87, + "updatedAt": "2026-03-20T00:00:00.000Z" + }, + { + "id": "projects-runtime-source", + "type": "projects", + "title": "Projects runtime source", + "summary": "Portfolio projects are loaded dynamically from Firebase.", + "content": "Featured projects displayed on the portfolio are loaded at runtime from the Firebase Realtime Database path portfolioProjects. The portfolio UI supports project title, description, category, status, tags, live URL, GitHub URL, image, and video media.", + "tags": ["projects", "firebase", "dynamic content"], + "source": { + "kind": "code-contract", + "path": "src/hooks/useBlogPosts.js", + "anchor": "usePortfolioProjects" + }, + "priority": 0.85, + "updatedAt": "2026-03-20T00:00:00.000Z" + }, + { + "id": "blog-runtime-source", + "type": "blog", + "title": "Blog runtime source", + "summary": "Blog posts are loaded dynamically from Firebase.", + "content": "Public blog posts are loaded from the Firebase Realtime Database path blogPosts, normalized, filtered to published content, and sorted before rendering.", + "tags": ["blog", "firebase", "published posts"], + "source": { + "kind": "code-contract", + "path": "src/hooks/useBlogPosts.js", + "anchor": "useBlogPosts" + }, + "priority": 0.78, + "updatedAt": "2026-03-20T00:00:00.000Z" + } + ] +} \ No newline at end of file diff --git a/chatbot/memory/strategy.md b/chatbot/memory/strategy.md new file mode 100644 index 0000000..e731856 --- /dev/null +++ b/chatbot/memory/strategy.md @@ -0,0 +1,48 @@ +# Memory Strategy + +## Goal + +Make the assistant non-stateless without allowing session context to grow without control. + +## Layers + +### Recent Turns + +Keep the most recent detailed conversation turns for precise follow-up handling. + +Recommended initial policy: + +- retain the last 8 to 12 turns in full + +### Rolling Summary + +Compress older turns into a concise running summary. + +The rolling summary should preserve: + +- what the visitor is trying to learn +- any audience framing that emerged +- what the assistant has already explained +- any constraints or preferences expressed by the visitor + +### Pinned Facts + +Store compact, durable session facts that matter across the conversation. + +Examples: + +- audience appears to be recruiter +- visitor is focused on AI engineering fit +- visitor asked about project depth already + +## Storage Boundary + +- source of truth: server-side session store +- client-side: current transcript and transient UI state only + +## What Memory Must Not Do + +- invent facts the user never gave +- silently overwrite known portfolio truth +- grow indefinitely without summarization +- become the only grounding source when retrieval is available \ No newline at end of file diff --git a/chatbot/prompts/retrieval.md b/chatbot/prompts/retrieval.md new file mode 100644 index 0000000..524ef9a --- /dev/null +++ b/chatbot/prompts/retrieval.md @@ -0,0 +1,21 @@ +# Retrieval Instructions + +Before answering: + +1. classify the user intent +2. identify the most relevant portfolio domains +3. retrieve only the highest-value supporting chunks +4. use session memory only when it helps answer the current question +5. do not cite or rely on information that was not retrieved or stored in memory + +Grounding rules: + +- prefer direct portfolio evidence over general model knowledge +- if multiple chunks overlap, synthesize instead of repeating +- when the user asks a broad question, combine identity, skills, projects, and positioning as needed +- when the user asks a narrow question, keep retrieval narrow + +Citations: + +- attach citations to substantive claims when source chunks exist +- use stable source identifiers and section anchors \ No newline at end of file diff --git a/chatbot/prompts/safety.md b/chatbot/prompts/safety.md new file mode 100644 index 0000000..f1da711 --- /dev/null +++ b/chatbot/prompts/safety.md @@ -0,0 +1,14 @@ +# Safety And Truthfulness Rules + +- do not fabricate facts about Dee, the portfolio, or external experience +- do not claim access to hidden data or private records +- do not pretend to have browsed the web unless that capability exists and was used +- do not reveal raw internal reasoning traces +- do not use citations unless they map to real source ids +- do not present speculation as fact + +When evidence is incomplete: + +- say what is known +- say what is unclear +- keep the answer useful without inventing details \ No newline at end of file diff --git a/chatbot/prompts/summarization.md b/chatbot/prompts/summarization.md new file mode 100644 index 0000000..bdb609f --- /dev/null +++ b/chatbot/prompts/summarization.md @@ -0,0 +1,19 @@ +# Summarization Instructions + +Supported summary modes: + +- summarize Dee as a candidate +- summarize Dee for a recruiter +- summarize Dee for a client +- summarize this portfolio +- summarize the skills profile +- summarize a specific project +- summarize blog themes + +Summary rules: + +- keep the main point visible in the first sentence +- organize the answer by signal, not by page order +- include what Dee does, how Dee does it, and why that matters +- tailor the framing to the audience when one is implied +- reference source-backed evidence when possible \ No newline at end of file diff --git a/chatbot/prompts/system.md b/chatbot/prompts/system.md new file mode 100644 index 0000000..369a5d0 --- /dev/null +++ b/chatbot/prompts/system.md @@ -0,0 +1,31 @@ +# Base System Prompt + +You are Dee's portfolio assistant. + +Your job is to help visitors understand who Dee is, what Dee builds, how Dee thinks, and why Dee is a strong fit for relevant roles, projects, or collaborations. + +Operate with these rules: + +- stay grounded in the portfolio knowledge and session context +- do not invent experience, credentials, metrics, or project history +- speak with confidence when the portfolio evidence supports the claim +- be concise by default, but expand when the visitor asks for depth +- when useful, explain relevance for recruiters, clients, collaborators, or general visitors +- recommend a next action when it improves user momentum +- prefer truthful persuasion over hype + +Response priorities: + +1. answer the user's actual question directly and briefly +2. frame Dee clearly using only portfolio evidence +3. preserve conversation continuity +4. suggest one next action if it genuinely helps + +Formatting rules: + +- keep the `summary` field short: one to two sentences unless depth is asked for +- use `sections` only when the question genuinely has multiple distinct parts +- do not use markdown bullets, headers, or bold inside field values +- do not pad responses with transitional filler or summaries of what you just said + +If the user asks for something the portfolio knowledge does not support, say so in one sentence and stop. \ No newline at end of file diff --git a/chatbot/prompts/tone.md b/chatbot/prompts/tone.md new file mode 100644 index 0000000..c18b021 --- /dev/null +++ b/chatbot/prompts/tone.md @@ -0,0 +1,24 @@ +# Tone Rules + +Default tone: + +- short and direct +- plain English +- warm but not chatty +- technically confident without being verbose + +Style guidance: + +- answer in as few words as the question deserves +- no bullet lists unless the question genuinely needs one +- no headers or markdown formatting in conversational replies +- no apologetic filler ("Great question!", "Certainly!", "Of course!") β€” just answer +- do not restate the question before answering +- expand only when the visitor explicitly asks for more detail + +Audience adaptation: + +- for recruiters: lead with role fit and execution, one or two sentences +- for clients: lead with the outcome, then the how +- for collaborators: be peer-level, skip the sales tone +- for general visitors: be friendly and navigational, not encyclopedic \ No newline at end of file diff --git a/chatbot/retrieval/sources.md b/chatbot/retrieval/sources.md new file mode 100644 index 0000000..c5ce20b --- /dev/null +++ b/chatbot/retrieval/sources.md @@ -0,0 +1,26 @@ +# Retrieval Sources + +## Primary Sources + +The first retrieval layer should draw from curated portfolio records derived from: + +- hero section +- about section +- credentials section +- projects section +- skills section +- contact section +- footer or social links +- resume metadata +- blog metadata and summaries + +## Retrieval Principle + +The assistant should retrieve from normalized records, not parse React source files at runtime. + +## Ranking Priorities + +1. direct relevance to the current question +2. higher-confidence portfolio evidence +3. fresher or more canonical records +4. diversity across complementary domains when the question is broad \ No newline at end of file diff --git a/chatbot/schemas/citation.schema.json b/chatbot/schemas/citation.schema.json new file mode 100644 index 0000000..325ada3 --- /dev/null +++ b/chatbot/schemas/citation.schema.json @@ -0,0 +1,26 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "chatbot/citation.schema.json", + "title": "Chatbot Citation", + "type": "object", + "additionalProperties": false, + "required": ["label", "sourceId", "anchor"], + "properties": { + "label": { + "type": "string", + "minLength": 1 + }, + "sourceId": { + "type": "string", + "minLength": 1 + }, + "anchor": { + "type": "string", + "minLength": 1 + }, + "path": { + "type": "string", + "minLength": 1 + } + } +} \ No newline at end of file diff --git a/chatbot/schemas/response.schema.json b/chatbot/schemas/response.schema.json new file mode 100644 index 0000000..13a1e93 --- /dev/null +++ b/chatbot/schemas/response.schema.json @@ -0,0 +1,100 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "chatbot/response.schema.json", + "title": "Chatbot Assistant Message", + "type": "object", + "additionalProperties": false, + "required": ["id", "role", "summary", "sections", "citations", "suggestedActions", "meta"], + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "role": { + "type": "string", + "const": "assistant" + }, + "summary": { + "type": "string", + "minLength": 1 + }, + "sections": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["label", "content"], + "properties": { + "label": { + "type": "string", + "minLength": 1 + }, + "content": { + "type": "string", + "minLength": 1 + } + } + } + }, + "citations": { + "type": "array", + "items": { + "$ref": "./citation.schema.json" + } + }, + "suggestedActions": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["type", "label"], + "properties": { + "type": { + "type": "string", + "enum": ["scroll", "link", "summarize"] + }, + "label": { + "type": "string", + "minLength": 1 + }, + "target": { + "type": "string", + "minLength": 1 + }, + "href": { + "type": "string", + "minLength": 1 + }, + "subjectId": { + "type": "string", + "minLength": 1 + } + } + } + }, + "meta": { + "type": "object", + "additionalProperties": false, + "required": ["intent", "usedMemory", "usedRetrieval", "stageTrace"], + "properties": { + "intent": { + "type": "string", + "minLength": 1 + }, + "usedMemory": { + "type": "boolean" + }, + "usedRetrieval": { + "type": "boolean" + }, + "stageTrace": { + "type": "array", + "items": { + "type": "string", + "enum": ["thinking", "searching", "summarizing", "drafting"] + } + } + } + } + } +} \ No newline at end of file diff --git a/chatbot/schemas/session.schema.json b/chatbot/schemas/session.schema.json new file mode 100644 index 0000000..854bcdf --- /dev/null +++ b/chatbot/schemas/session.schema.json @@ -0,0 +1,63 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "chatbot/session.schema.json", + "title": "Chatbot Session", + "type": "object", + "additionalProperties": false, + "required": ["sessionId", "createdAt", "updatedAt", "recentTurns", "rollingSummary", "pinnedFacts", "userIntentProfile"], + "properties": { + "sessionId": { + "type": "string", + "minLength": 1 + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "updatedAt": { + "type": "string", + "format": "date-time" + }, + "recentTurns": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["role", "content"], + "properties": { + "role": { + "type": "string", + "enum": ["user", "assistant"] + }, + "content": { + "type": "string", + "minLength": 1 + } + } + } + }, + "rollingSummary": { + "type": "string" + }, + "pinnedFacts": { + "type": "array", + "items": { + "type": "string" + } + }, + "userIntentProfile": { + "type": "object", + "additionalProperties": false, + "required": ["audience", "goal"], + "properties": { + "audience": { + "type": "string", + "enum": ["recruiter", "client", "collaborator", "general", "unknown"] + }, + "goal": { + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/chatbot/ui/message-structure.md b/chatbot/ui/message-structure.md new file mode 100644 index 0000000..a249b52 --- /dev/null +++ b/chatbot/ui/message-structure.md @@ -0,0 +1,50 @@ +# Message Structure + +The frontend should render assistant messages from structured data, not infer layout from raw text. + +## Assistant Message Model + +Fields: + +- `id` +- `role` +- `summary` +- `sections` +- `citations` +- `suggestedActions` +- `meta` + +## Sections + +Each section should have: + +- `label` +- `content` + +Suggested labels: + +- Summary +- Why Dee Fits +- Evidence +- Recommended Next Step + +## Citations + +Each citation should support: + +- `label` +- `sourceId` +- `anchor` +- optional `path` + +## Suggested Actions + +Action types expected in stage 2: + +- `scroll` +- `link` +- `summarize` + +## Fallback Behavior + +If the assistant only returns `summary`, the UI should still render a clean single-block message. \ No newline at end of file diff --git a/chatbot/ui/states.md b/chatbot/ui/states.md new file mode 100644 index 0000000..6e40ffd --- /dev/null +++ b/chatbot/ui/states.md @@ -0,0 +1,30 @@ +# UI State Vocabulary + +## Chat States + +- `idle` +- `submitting` +- `thinking` +- `searching` +- `summarizing` +- `drafting` +- `completed` +- `error` + +## Required UI Behaviors + +- show when the assistant is actively working +- keep the latest user message visible while the assistant is processing +- render structured sections instead of plain paragraphs when available +- show citations as source pills or links +- show suggested actions when provided +- make failure states recoverable + +## Minimum Stage 2 Render Targets + +- welcome state +- active response state +- structured assistant message card +- citations row +- suggested action row +- error card \ No newline at end of file diff --git a/docs/ADMIN_IMPROVEMENTS.md b/docs/ADMIN_IMPROVEMENTS.md deleted file mode 100644 index 9ae9727..0000000 --- a/docs/ADMIN_IMPROVEMENTS.md +++ /dev/null @@ -1,50 +0,0 @@ -# Admin Panel Improvements - -The admin experience has moved from a simple localStorage demo to a Firebase-backed dashboard with modern UX. This file highlights the most impactful upgrades and the areas intentionally left for future iterations. - -## Platform Upgrades - -- **Firebase Authentication** – Email/Password provider with persistent sessions and explicit sign-out. -- **Realtime Database** – Cloud storage for posts scoped to `users/{uid}/posts` with validation rules. -- **Dedicated `adminscript.js`** – All Firebase config, listeners, and CRUD helpers now live outside `admin.html` for easier maintenance. -- **Per-user isolation** – Security rules gate reads/writes by `auth.uid`, preventing cross-account data leaks. - -## UX and Interaction Enhancements - -- **Auth screen refresh** – Gradient background, centered form, inline errors, and loading states on submission. -- **Sticky composer** – Desktop users keep the form in view while scrolling the post list. -- **Tag management** – Chip UI with Enter-to-add and click-to-remove interactions. -- **Contextual alerts** – Slide-in success/error banners that auto-dismiss after four seconds. -- **Confirmation modal** – Backdrop-blurred delete confirmation with clear primary/secondary actions. -- **Responsive grid** – Transitions from two columns on desktop to a single column on mobile with generous spacing. - -## Content Operations - -- **Slug automation** – Titles automatically convert to URL-safe slugs. -- **Featured image fallback** – Ensures every post renders a hero image, even without manual input. -- **Timestamp tracking** – `createdAt` and `updatedAt` stored for every revision. -- **Post counter** – Immediate feedback on total posts per user session. -- **Sorted views** – Newest posts bubble to the top without manual sorting. - -## Release Timeline - -| Sprint | Highlights | -|--------|-----------| -| v1.0 | Firebase auth + database wiring, CRUD operations, responsive layout | -| v1.1 | Alert system, confirmation modal, improved tag UX | -| v1.2 | Dedicated `adminscript.js`, refreshed documentation, security-rule tightening | - -## Future Opportunities - -1. **Search & filtering** – Surface posts by keyword, category, or tag. -2. **Draft states** – Allow partial saves before publishing. -3. **Media uploads** – Integrate Firebase Storage for direct image/file uploads. -4. **Rich text editing** – Swap the textarea for a markdown or WYSIWYG editor. -5. **Role-based access** – Distinguish between writers, editors, and admins. -6. **Analytics snapshot** – Display per-post metrics powered by Firebase or a lightweight analytics service. - -## References - -- [README_ADMIN.md](README_ADMIN.md) – Architecture and maintenance guide -- [ADMIN_QUICK_START.md](ADMIN_QUICK_START.md) – Fast onboarding -- [FIREBASE_SETUP.md](FIREBASE_SETUP.md) – Full configuration steps diff --git a/docs/ADMIN_QUICK_START.md b/docs/ADMIN_QUICK_START.md deleted file mode 100644 index dec4216..0000000 --- a/docs/ADMIN_QUICK_START.md +++ /dev/null @@ -1,48 +0,0 @@ -# Admin Panel Quick Start - -Need the shortest path from zero to a working dashboard? Follow the steps below and reference the linked docs if you get stuck. - -## TL;DR - -1. **Create** a Firebase project and web app (Console β–Έ Add project β–Έ Web `` app). -2. **Enable** Email/Password auth plus a Realtime Database in Test mode. -3. **Add** the Firebase values to `.env` (see [FIREBASE_CONFIG_LOCATION.md](FIREBASE_CONFIG_LOCATION.md)). -4. **Run** the app server locally: `npm start`. -5. **Visit** `http://localhost:4001/admin.html`, sign up, and publish a post. - -## Step-by-step - -### 1. Firebase Project (5 minutes) -- Console β–Έ Add project β†’ name it β†’ disable Analytics if not needed. -- Within Project settings β–Έ General, register a Web app to reveal the config object. - -### 2. Enable Services -- Build β–Έ Authentication β–Έ Get started β†’ enable Email/Password. -- Build β–Έ Realtime Database β–Έ Create Database β†’ choose region β†’ start in Test mode β†’ paste the rules from [FIREBASE_SETUP.md](FIREBASE_SETUP.md). - -### 3. Wire the Config -- Copy `.env.example` to `.env`. -- Fill all `FIREBASE_*` values from Firebase Project settings. -- `server.js` now serves those values to the frontend through `/firebase-config.js`. - -### 4. Run Locally -```bash -cd /home/dee/Projects/dee -npm start -``` - -Visit `http://localhost:4001/admin.html`, create an account, and start adding posts. The UI will confirm each action with the alert banner. - -## Post-setup Checklist - -- [ ] Authentication works (sign up, sign in, sign out). -- [ ] Posts save, update, and delete for the signed-in user. -- [ ] Tags can be added/removed with the chip UI. -- [ ] Realtime Database shows data under `users/{uid}/posts`. -- [ ] Security rules deny access for anonymous users (test by signing out and reloading the admin page). - -## Need More Detail? - -- Full walkthrough: [FIREBASE_SETUP.md](FIREBASE_SETUP.md) -- Architecture overview: [README_ADMIN.md](README_ADMIN.md) -- Release notes and roadmap: [ADMIN_IMPROVEMENTS.md](ADMIN_IMPROVEMENTS.md) diff --git a/docs/FIREBASE_CONFIG_LOCATION.md b/docs/FIREBASE_CONFIG_LOCATION.md deleted file mode 100644 index 3838692..0000000 --- a/docs/FIREBASE_CONFIG_LOCATION.md +++ /dev/null @@ -1,33 +0,0 @@ -# Firebase Config Location - -The Firebase web config is stored in `.env` and exposed to the browser by `server.js` at `/firebase-config.js`. - -## Update Steps - -1. Copy `.env.example` to `.env` in the project root. -2. Fill each `FIREBASE_*` value from **Project settings β–Έ General β–Έ Your apps β–Έ Web app** in the Firebase Console. -3. Start the app server with `npm start`. -4. Refresh `admin.html` in your browser. - -```env -FIREBASE_API_KEY=... -FIREBASE_AUTH_DOMAIN=... -FIREBASE_DATABASE_URL=... -FIREBASE_PROJECT_ID=... -FIREBASE_STORAGE_BUCKET=... -FIREBASE_MESSAGING_SENDER_ID=... -FIREBASE_APP_ID=... -FIREBASE_MEASUREMENT_ID=... -``` - -## Verify the Change - -1. Run `npm start` from the repo root. -2. Open `http://localhost:4001/admin.html`. -3. Sign up or sign in. If authentication succeeds and the dashboard loads, the config is correct. -4. If you see an error banner, open DevTools (F12) and inspect the console for missing/invalid config messages. - -## Need Extra Help? - -- Follow the complete walkthrough in [FIREBASE_SETUP.md](FIREBASE_SETUP.md). -- Use [ADMIN_QUICK_START.md](ADMIN_QUICK_START.md) for the short version. diff --git a/docs/FIREBASE_SETUP.md b/docs/FIREBASE_SETUP.md deleted file mode 100644 index 6a0dcee..0000000 --- a/docs/FIREBASE_SETUP.md +++ /dev/null @@ -1,134 +0,0 @@ -# Firebase Setup Guide (Admin Panel) - -Use this guide to connect the Admin panel to a brand-new Firebase project. Every step references the exact navigation in the Firebase Console so you can move from an empty project to a tested admin panel without guessing. - -## Prerequisites - -- A Google account with access to [Firebase Console](https://console.firebase.google.com/) -- Basic familiarity with editing local files in this repository -- Node.js (for running the local app server) - -## 1. Create a Firebase Project - -1. Visit the Firebase Console and click **Add project**. -2. Provide a project name (for example, `dee-blog-admin`). -3. Disable Google Analytics unless you specifically need it for this project. -4. Click **Create project** and wait for provisioning to finish. - -## 2. Register the Web App - -1. Inside your new project, open **Project Overview β–Έ Get started by adding Firebase to your app**. -2. Choose the **Web (``)** option and give the app a friendly nickname (for example, `Admin Panel`). -3. Leave Hosting unchecked for now and click **Register app**. -4. Copy the generated config valuesβ€”you will add them to `.env` shortly. - -## 3. Add Credentials to `.env` - -1. Copy `.env.example` to `.env` in the repo root. -2. Replace the placeholder values with the Firebase config values from the console. -3. Save `.env`. The server now exposes these values to the frontend through `/firebase-config.js`. - -```env -FIREBASE_API_KEY=YOUR_API_KEY -FIREBASE_AUTH_DOMAIN=YOUR_AUTH_DOMAIN -FIREBASE_DATABASE_URL=YOUR_DATABASE_URL -FIREBASE_PROJECT_ID=YOUR_PROJECT_ID -FIREBASE_STORAGE_BUCKET=YOUR_STORAGE_BUCKET -FIREBASE_MESSAGING_SENDER_ID=YOUR_MESSAGING_SENDER_ID -FIREBASE_APP_ID=YOUR_APP_ID -FIREBASE_MEASUREMENT_ID=YOUR_MEASUREMENT_ID -``` - -## 4. Enable Email/Password Authentication - -1. Console path: **Build β–Έ Authentication β–Έ Get Started**. -2. Open the **Sign-in method** tab. -3. Enable **Email/Password** and click **Save**. -4. (Optional) In **Authentication β–Έ Settings β–Έ Authorized domains**, add `localhost` so local development works without warnings. - -## 5. Create the Realtime Database - -1. Console path: **Build β–Έ Realtime Database β–Έ Create database**. -2. Choose the same region you selected for the project. -3. For development, start in **Test mode** so you can read/write immediately. -4. After the instance is created, switch to the **Rules** tab. - -### Recommended Rules (per-user isolation) - -```json -{ - "rules": { - "users": { - "$uid": { - ".read": "$uid === auth.uid", - ".write": "$uid === auth.uid", - "posts": { - "$postId": { - ".validate": "newData.hasChildren(['title', 'excerpt', 'content', 'category', 'date', 'author', 'slug', 'createdAt', 'updatedAt', 'status'])" - } - } - } - }, - "blogPosts": { - ".read": true, - "$postId": { - ".write": "auth != null", - ".validate": "newData.hasChildren(['title', 'excerpt', 'content', 'category', 'date', 'author', 'slug', 'createdAt', 'updatedAt', 'status'])" - } - } - } -} -``` - -These rules keep every user’s posts isolated and ensure every post carries the required fields. - -## 6. Run the Admin Panel Locally - -```bash -cd /home/dee/Projects/dee -npm start -``` - -Visit `http://localhost:4001/admin.html` and you should see the auth screen. - -1. Click **Sign Up** to create your first admin user (Email/Password is the only enabled provider). -2. Sign in with that account. -3. Create a test post and confirm it shows up instantly in the list. - -## 7. Verify Data in Firebase - -1. Console path: **Build β–Έ Realtime Database β–Έ Data**. -2. Expand `users β–Έ {your-user-uid} β–Έ posts` and confirm the test post landed correctly. -3. You should see a structure similar to: - -``` -users/ - {uid}/ - posts/ - {postId}/ - title - slug - excerpt - content - category - tags - author - date - image - createdAt - updatedAt -``` - -## Troubleshooting Checklist - -- **Auth errors**: Verify Email/Password is enabled and the password is at least six characters. -- **Missing config warning**: Ensure every `FIREBASE_*` variable in `.env` is populated (especially `FIREBASE_DATABASE_URL`). -- **Writes denied**: Double-check that you created the database in Test mode or that you applied the rules above. -- **CORS warnings**: Add `localhost` (or your deployed origin) to Authentication β–Έ Settings β–Έ Authorized domains. - -## Reference Docs - -- [Firebase Authentication Docs](https://firebase.google.com/docs/auth) -- [Realtime Database Docs](https://firebase.google.com/docs/database) -- [Security Rules Guide](https://firebase.google.com/docs/database/security) -- [Firebase CLI & Hosting](https://firebase.google.com/docs/cli) diff --git a/docs/README_ADMIN.md b/docs/README_ADMIN.md deleted file mode 100644 index b9f6ab6..0000000 --- a/docs/README_ADMIN.md +++ /dev/null @@ -1,82 +0,0 @@ -# Admin Panel Guide - -This document captures how the Firebase-backed admin console is organized, how data moves through the system, and what to maintain after the initial setup. - -## Architecture - -| Layer | Description | -|-------|-------------| -| UI | `admin.html` renders the form, post grid, alerts, and modal components styled via `styles.css`. | -| Logic | `adminscript.js` owns Firebase initialization, auth listeners, CRUD helpers, and DOM bindings. | -| Data | Firebase Authentication (Email/Password) plus Realtime Database with a `users/{uid}/posts/{postId}` tree. | - -### Data Model - -```json -{ - "title": "string", - "slug": "string", - "excerpt": "string", - "content": "string", - "category": "tutorials|projects|tips", - "tags": ["string"], - "author": "string", - "date": "YYYY-MM-DD", - "image": "https://...", - "createdAt": "ISO timestamp", - "updatedAt": "ISO timestamp", - "status": "published|scheduled", - "publishAt": "ISO timestamp", - "publishedAt": "ISO timestamp" -} -``` - -Security rules limit read/write access to the authenticated user’s `uid` and enforce the required fields above. - -## Feature Inventory - -- Email/Password sign up, sign in, and sign out (Firebase Auth) -- Sticky post composer with validation, tag chips, slug generation, and featured image URL field -- Real-time post list with edit/delete actions and count badge -- Delete confirmation modal with backdrop blur -- Responsive layout (two-column on desktop, single column on tablet/mobile) -- Alert system for success/error feedback - -## Setup Snapshot - -1. Follow [FIREBASE_SETUP.md](FIREBASE_SETUP.md) to create the Firebase project and database rules. -2. Add Firebase web config values to `.env` as explained in [FIREBASE_CONFIG_LOCATION.md](FIREBASE_CONFIG_LOCATION.md). -3. Serve the site locally and run through the [ADMIN_QUICK_START.md](ADMIN_QUICK_START.md) checklist. - -## Operational Workflow - -1. **Authenticate** – Sign in via Email/Password. The header shows the user avatar (first letter) plus email. -2. **Create/Update** – Fill the form, manage tags with Enter/backspace, and click the primary CTA. The UI swaps to β€œUpdate Post” during edits. -3. **Delete** – Use the delete button, confirm the modal, and watch for the toast message. -4. **Sync** – Realtime listeners ensure the grid mirrors Firebase instantly for the signed-in user. -5. **Sign Out** – Use the header button to tear down listeners and return to the auth view. - -## Maintenance Checklist - -- Rotate Firebase credentials when needed by updating `.env`. -- Review database security rules before moving out of Test mode. -- Periodically export the Realtime Database from the Firebase Console for backups. -- Monitor Authentication β–Έ Users to prune stale accounts if desired. -- Keep an eye on the Firebase status dashboard for outages affecting auth or database traffic. - -## Troubleshooting - -| Symptom | Fix | -|---------|-----| -| β€œFirebase: Error (auth/invalid-api-key)” | A config field is blank or belongs to another project. Re-copy from Project settings. | -| Posts fail to save with `PERMISSION_DENIED` | Database rules were not deployed or the DB is still locked down. Apply the rules from FIREBASE_SETUP. | -| Dashboard never appears after login | Check DevTools console for network errors; ensure Realtime Database is enabled in the same region. | -| Modal won’t close | Ensure no custom CSS overrides `.modal` or `.active` classesβ€”compare against `styles.css`. | - -## Related Docs - -- [ADMIN_IMPROVEMENTS.md](ADMIN_IMPROVEMENTS.md) for release history and feature roadmap -- [ADMIN_QUICK_START.md](ADMIN_QUICK_START.md) for a condensed onboarding script -- [DESIGN_SYSTEM.md](DESIGN_SYSTEM.md) for color, typography, and component specs - -**Last updated:** February 12, 2026 diff --git a/docs/SYSTEM_CSS_DESIGN.md b/docs/SYSTEM_CSS_DESIGN.md deleted file mode 100644 index 28a9d53..0000000 --- a/docs/SYSTEM_CSS_DESIGN.md +++ /dev/null @@ -1,379 +0,0 @@ -# Admin Panel - Design Specifications - -## 🎨 Color Palette - -### Primary Colors -- **Primary Blue**: `#1e3a8a` (Dark) -- **Accent Blue**: `#3b82f6` (Bright) -- **Gradient**: `linear-gradient(135deg, #1e3a8a 0%, #3b82f6 100%)` - -### Semantic Colors -- **Success**: `#d1fae5` (background), `#065f46` (text) -- **Error**: `#fee2e2` (background), `#991b1b` (text) -- **Info**: `#dbeafe` (background), `#0c4a6e` (text) -- **Warning**: `#fef3c7` (background), `#92400e` (text) - -### Neutral Colors -- **Text Primary**: `#1f2937` (Dark gray) -- **Text Secondary**: `#6b7280` (Medium gray) -- **Border**: `#e5e7eb` (Light gray) -- **Background Secondary**: `#f3f4f6` (Very light gray) -- **Background**: `#ffffff` (White) - -## πŸ“ Typography - -### Font Family -- **Primary**: Inter (Google Fonts) -- **Fallback**: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, sans-serif - -### Font Sizes -``` -h1: 1.8rem (28.8px) -h2: 1.3rem (20.8px) -h3: 1.1rem (17.6px) -Body: 0.95rem (15.2px) -Small: 0.85rem (13.6px) -Label: 0.9rem (14.4px) -``` - -### Font Weights -- **Regular**: 400 -- **Medium**: 500 -- **Semibold**: 600 -- **Bold**: 700 - -## 🎯 Component Specs - -### Buttons - -#### Primary Button -```css -background: linear-gradient(135deg, #1e3a8a 0%, #3b82f6 100%); -color: white; -padding: 10px 20px; -border-radius: 6px; -font-weight: 500; -font-size: 0.95rem; -cursor: pointer; -transition: all 0.3s ease; -``` -**Hover**: `transform: translateY(-2px)`, `box-shadow: 0 10px 20px rgba(30, 58, 138, 0.2)` - -#### Secondary Button -```css -background: #f3f4f6; -color: #1f2937; -border: 1px solid #e5e7eb; -padding: 10px 20px; -border-radius: 6px; -font-weight: 500; -``` -**Hover**: `background: #e5e7eb` - -#### Danger Button -```css -background: #ef4444; -color: white; -padding: 10px 20px; -border-radius: 6px; -font-weight: 500; -``` -**Hover**: `background: #dc2626` - -#### Small Button -```css -padding: 8px 12px; -font-size: 0.85rem; -flex: 1; -text-align: center; -``` - -### Form Elements - -#### Input Field -```css -width: 100%; -padding: 10px 12px; -border: 1px solid #e5e7eb; -border-radius: 6px; -font-family: inherit; -font-size: 0.95rem; -color: #1f2937; -transition: all 0.3s ease; -``` -**Focus**: -- `border-color: #3b82f6` -- `box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1)` - -#### Textarea -```css -/* Same as input */ -resize: vertical; -min-height: 100px; -``` - -#### Select/Dropdown -```css -/* Same as input */ -``` - -### Tag Chip -```css -background: linear-gradient(135deg, rgba(37, 99, 235, 0.1), rgba(59, 130, 246, 0.1)); -padding: 6px 12px; -border-radius: 4px; -display: flex; -align-items: center; -gap: 8px; -font-size: 0.9rem; -color: #3b82f6; -border: 1px solid rgba(37, 99, 235, 0.2); -``` - -### Card Component -```css -background: white; -padding: 20px; -border-radius: 10px; -border: 1px solid #e5e7eb; -transition: all 0.3s ease; -``` -**Hover**: -- `border-color: #3b82f6` -- `box-shadow: 0 10px 20px rgba(37, 99, 235, 0.1)` -- `transform: translateY(-5px)` - -### Alert/Toast -```css -padding: 14px 18px; -border-radius: 8px; -margin-bottom: 20px; -font-size: 0.95rem; -animation: slideDown 0.3s ease-out; -``` - -**Success Alert**: -- `background: #d1fae5` -- `color: #065f46` -- `border: 1px solid #6ee7b7` - -**Error Alert**: -- `background: #fee2e2` -- `color: #991b1b` -- `border: 1px solid #fca5a5` - -**Info Alert**: -- `background: #dbeafe` -- `color: #0c4a6e` -- `border: 1px solid #93c5fd` - -### Modal -```css -position: fixed; -top: 0; -left: 0; -right: 0; -bottom: 0; -background: rgba(0, 0, 0, 0.5); -backdrop-filter: blur(4px); -display: flex; -align-items: center; -justify-content: center; -z-index: 1000; -``` - -**Modal Content**: -```css -background: white; -padding: 30px; -border-radius: 12px; -max-width: 500px; -width: 90%; -box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); -``` - -## 🎬 Animations - -### Slide Down (Alerts) -```css -@keyframes slideDown { - from { - opacity: 0; - transform: translateY(-10px); - } - to { - opacity: 1; - transform: translateY(0); - } -} -``` -**Duration**: 0.3s ease-out - -### Loading Spinner -```css -@keyframes spin { - to { transform: rotate(360deg); } -} -``` -**Duration**: 0.8s linear infinite - -### Button Hover Lift -```css -transform: translateY(-2px); -transition: all 0.3s ease; -``` - -### Card Hover Transform -```css -transform: translateY(-5px); -transition: all 0.3s ease; -``` - -## πŸ“ Spacing System - -### Standard Spacing Values -- **xs**: 4px -- **sm**: 8px -- **md**: 12px -- **lg**: 16px -- **xl**: 20px -- **2xl**: 24px -- **3xl**: 30px -- **4xl**: 40px - -### Padding -- Form Section: 30px -- Cards: 20px -- Buttons: 10px 20px -- Form Groups: Bottom margin 20px - -### Gaps -- Form Groups: 15px -- Buttons: 10px -- Tags: 8px -- Elements: 12px-30px depending on context - -### Border Radius -- Large (modals, cards): 12px -- Medium (buttons, inputs): 6px -- Small (tags, badges): 4px -- Circular (avatars): 50% - -## πŸ“± Responsive Design - -### Desktop (>768px) -- 2-column layout: Form (1fr) | Posts (2fr) -- Gap: 30px -- Full width inputs -- Sticky form section - -### Tablet (480px - 768px) -- Single column layout -- Adjusted spacing -- Form unsticks -- Cards in grid - -### Mobile (<480px) -- Single column -- Reduced padding (20px β†’ 15px) -- Smaller font sizes where possible -- Touch-friendly button heights (44px minimum) -- Full-width modals with 20px margin - -## 🌈 Gradient Usage - -### Primary Gradient (CTAs) -```css -background: linear-gradient(135deg, #1e3a8a 0%, #3b82f6 100%); -``` - -### Background Gradient -```css -background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%); -``` - -### Auth Screen Background -```css -background: linear-gradient(135deg, #1e3a8a 0%, #3b82f6 100%); -``` - -### Category Badge -```css -background: linear-gradient(135deg, rgba(37, 99, 235, 0.1), rgba(59, 130, 246, 0.1)); -``` - -### Section Header Accent -```css -background: linear-gradient(180deg, #1e3a8a, #3b82f6); -width: 4px; -height: 24px; -border-radius: 2px; -``` - -## 🎯 Focus States - -### Keyboard Focus (Tab) -```css -outline: none; -border-color: #3b82f6; -box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1); -``` - -## πŸ–±οΈ Hover States - -### Link/Button Hover -```css -opacity: increased; -color/background: adjusted; -transform: translateY(-2px); -box-shadow: elevated; -``` - -### Card Hover -```css -border-color: #3b82f6; -box-shadow: 0 10px 20px rgba(37, 99, 235, 0.1); -transform: translateY(-5px); -``` - -## πŸ“ Shadow System - -```css -/* Small */ -box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); - -/* Medium */ -box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); - -/* Large */ -box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); - -/* Modal */ -box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); -``` - -## β™Ώ Accessibility - -### Color Contrast -- Text on background: 7:1 ratio (AAA) -- Button text: 4.5:1 ratio (AA+) -- Labels: 4.5:1 ratio (AA+) - -### Touch Targets -- Minimum 44Γ—44px for mobile -- Spacing between clickable elements - -### Focus Indicators -- Clear blue outline on focus -- Visible focus state for keyboard navigation - -### Labels -- All form fields have associated labels -- Labels positioned above fields -- Placeholder text is not a substitute for labels - ---- - -**Design System Version**: 1.0 -**Last Updated**: February 9, 2025 -**Framework**: CSS3 with Modern Standards diff --git a/index.html b/index.html index 6dca36e..6e364ad 100644 --- a/index.html +++ b/index.html @@ -1,563 +1,22 @@ - - + + + + + Dee Portfolio + - - - Dee's Portfolio - - - - - - - - - - - -
- - -
-
-

- Hi, I'm - Agoma Divine E. - AI Product & Systems Engineer -

-

- Building intelligent systems with AI, machine learning, tackling real-world challenges and proffering innovative solutions. -

- -
-
-
- Profile Picture -
-
-
-
- - -
-
-

About Me

-
-
-

- I'm a passionate developer with a love for creating elegant solutions to complex problems. - With expertise in modern web technologies, I bring ideas to life through clean code and - thoughtful design. -

-

- When I'm not coding, you can find me exploring new technologies, contributing to open-source - projects, or sharing knowledge with the developer community. -

-
-
-
-
50+
-
Projects Completed
-
-
-
2+
-
Years Experience
-
-
-
100%
-
Client Satisfaction
-
-
- -
-
-
-
-
-
- - - -
- portfolio.js -
-
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
-
-
-
-
-
- - -
-
-

Featured Projects

-
-
- -
-
-
-
-
Project 1
-
-
-

E-Commerce Platform

-

- A full-featured e-commerce platform with payment integration, user authentication, - and admin dashboard. -

-
- React - Node.js - MongoDB -
- -
-
- -
-
-
Project 2
-
-
-

Task Management App

-

- A collaborative task management application with real-time updates and team collaboration features. -

-
- Vue.js - Firebase - TypeScript -
- -
-
- -
-
-
Project 3
-
-
-

Weather Dashboard

-

- A beautiful weather dashboard with location-based forecasts and interactive maps. -

-
- JavaScript - API - CSS3 -
- -
-
- -
-
-
Project 4
-
-
-

AI Chat Application

-

- An intelligent chatbot application powered by LLM with real-time conversation capabilities and context awareness. -

-
- Python - LLM - FastAPI -
- -
-
- -
-
-
Project 5
-
-
-

Data Analytics Platform

-

- A comprehensive data analytics platform with interactive dashboards and real-time data visualization. -

-
- Python - Django - PostgreSQL -
- -
-
- -
-
- -
-
-

Dee's Portfolio

-

- A simple, responsive personal portfolio website built with HTML, CSS, and JavaScript. -

-
- HTML - CSS - JavaScript -
- -
-
-
-
- -
-
-
-
- - -
-
-

Skills & Technologies

-
-
-

Frontend

-
-
- HTML/CSS -
-
-
-
-
- JavaScript -
-
-
-
-
- React -
-
-
-
-
- TypeScript -
-
-
-
-
- Vue.js -
-
-
-
-
-
- -
-

Backend

-
-
- Node.js -
-
-
-
-
- Python -
-
-
-
-
- MongoDB -
-
-
-
-
- MySQL -
-
-
-
- -
-
- -
-

Tools & Others

-
- Git - Docker - SSH - Linux - Cursor - Webpack - Firebase - VS Code - Copilot -
-
-
-
-
- - -
-
-

Get In Touch

-

- I'm always open to discussing new projects, creative ideas, or opportunities to be part of your vision. -

-
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
-
- -
-
- -
-
- -
-
- -
- -
-
-
-
- - - - - -
-
- - - - -
-
-
-
-
- - - - -
-
-

Dee's Assistant

-

Ask me anything

-
-
- -
-
-
-
-

Hi! I'm here to help you learn more about Dee. Try asking:

-
    -
  • What are your skills?
  • -
  • How can I contact you?
  • -
  • Tell me about your projects
  • -
-
-
-
-
- - -
-
-
- - - + + +
+ + diff --git a/media/Files/Claude Code in Action Cert.pdf b/media/Files/Claude Code in Action Cert.pdf new file mode 100644 index 0000000..420ecfc Binary files /dev/null and b/media/Files/Claude Code in Action Cert.pdf differ diff --git a/media/Files/Data Analytics Essentials.pdf b/media/Files/Data Analytics Essentials.pdf new file mode 100644 index 0000000..1436634 Binary files /dev/null and b/media/Files/Data Analytics Essentials.pdf differ diff --git a/media/Files/Introduction to Data Science.pdf b/media/Files/Introduction to Data Science.pdf new file mode 100644 index 0000000..046a88e Binary files /dev/null and b/media/Files/Introduction to Data Science.pdf differ diff --git a/media/Files/MyResume.pdf b/media/Files/MyResume.pdf new file mode 100644 index 0000000..9aba9a9 Binary files /dev/null and b/media/Files/MyResume.pdf differ diff --git a/media/MyResume.pdf b/media/MyResume.pdf deleted file mode 100644 index 398cee0..0000000 Binary files a/media/MyResume.pdf and /dev/null differ diff --git a/package-lock.json b/package-lock.json index 78dcd86..a1c405e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,1293 +8,1447 @@ "name": "dee-blog-admin", "version": "1.0.0", "dependencies": { + "@google/genai": "^1.29.0", + "@tailwindcss/vite": "^4.2.2", + "@vercel/blob": "^2.3.1", + "clsx": "^2.1.1", "cors": "^2.8.5", "dotenv": "^16.6.1", "express": "^4.18.2", - "firebase-admin": "^13.6.1", - "multer": "^1.4.5-lts.1" + "firebase": "^12.10.0", + "firebase-admin": "^13.7.0", + "lucide-react": "^0.546.0", + "motion": "^12.23.24", + "multer": "^1.4.5-lts.1", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-router-dom": "^7.13.1", + "recharts": "^3.8.0", + "tailwind-merge": "^3.5.0" + }, + "devDependencies": { + "@types/express": "^4.17.21", + "@types/node": "^22.14.0", + "@vitejs/plugin-react": "^4.7.0", + "autoprefixer": "^10.4.21", + "concurrently": "^9.2.1", + "tailwindcss": "^4.2.2", + "tsx": "^4.21.0", + "typescript": "~5.8.2", + "vite": "^6.4.1" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@fastify/busboy": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.2.0.tgz", - "integrity": "sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA==", - "license": "MIT" - }, - "node_modules/@firebase/app-check-interop-types": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.3.tgz", - "integrity": "sha512-gAlxfPLT2j8bTI/qfe3ahl2I2YcBQ8cFIBdhAQA4I2f3TndcO+22YizyGYuttLHPQEpWkhmpFW60VCFEPg4g5A==", - "license": "Apache-2.0" - }, - "node_modules/@firebase/app-types": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz", - "integrity": "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==", - "license": "Apache-2.0" - }, - "node_modules/@firebase/auth-interop-types": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.4.tgz", - "integrity": "sha512-JPgcXKCuO+CWqGDnigBtvo09HeBs5u/Ktc2GaFj2m01hLarbxthLNm7Fk8iOP1aqAtXV+fnnGj7U28xmk7IwVA==", - "license": "Apache-2.0" + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } }, - "node_modules/@firebase/component": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.7.0.tgz", - "integrity": "sha512-wR9En2A+WESUHexjmRHkqtaVH94WLNKt6rmeqZhSLBybg4Wyf0Umk04SZsS6sBq4102ZsDBFwoqMqJYj2IoDSg==", - "license": "Apache-2.0", + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", "dependencies": { - "@firebase/util": "1.13.0", - "tslib": "^2.1.0" + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" }, "engines": { - "node": ">=20.0.0" + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "node_modules/@firebase/database": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.1.0.tgz", - "integrity": "sha512-gM6MJFae3pTyNLoc9VcJNuaUDej0ctdjn3cVtILo3D5lpp0dmUHHLFN/pUKe7ImyeB1KAvRlEYxvIHNF04Filg==", - "license": "Apache-2.0", + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", "dependencies": { - "@firebase/app-check-interop-types": "0.3.3", - "@firebase/auth-interop-types": "0.2.4", - "@firebase/component": "0.7.0", - "@firebase/logger": "0.5.0", - "@firebase/util": "1.13.0", - "faye-websocket": "0.11.4", - "tslib": "^2.1.0" + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" }, "engines": { - "node": ">=20.0.0" + "node": ">=6.9.0" } }, - "node_modules/@firebase/database-compat": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.1.0.tgz", - "integrity": "sha512-8nYc43RqxScsePVd1qe1xxvWNf0OBnbwHxmXJ7MHSuuTVYFO3eLyLW3PiCKJ9fHnmIz4p4LbieXwz+qtr9PZDg==", - "license": "Apache-2.0", + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", "dependencies": { - "@firebase/component": "0.7.0", - "@firebase/database": "1.1.0", - "@firebase/database-types": "1.0.16", - "@firebase/logger": "0.5.0", - "@firebase/util": "1.13.0", - "tslib": "^2.1.0" + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" }, "engines": { - "node": ">=20.0.0" + "node": ">=6.9.0" } }, - "node_modules/@firebase/database-types": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.16.tgz", - "integrity": "sha512-xkQLQfU5De7+SPhEGAXFBnDryUWhhlFXelEg2YeZOQMCdoe7dL64DDAd77SQsR+6uoXIZY5MB4y/inCs4GTfcw==", - "license": "Apache-2.0", - "dependencies": { - "@firebase/app-types": "0.9.3", - "@firebase/util": "1.13.0" + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@firebase/logger": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.5.0.tgz", - "integrity": "sha512-cGskaAvkrnh42b3BA3doDWeBmuHFO/Mx5A83rbRDYakPjO9bJtRL3dX7javzc2Rr/JHZf4HlterTW2lUkfeN4g==", - "license": "Apache-2.0", + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", "dependencies": { - "tslib": "^2.1.0" + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { - "node": ">=20.0.0" + "node": ">=6.9.0" } }, - "node_modules/@firebase/util": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.13.0.tgz", - "integrity": "sha512-0AZUyYUfpMNcztR5l09izHwXkZpghLgCUaAGjtMwXnCg3bj4ml5VgiwqOMOxJ+Nw4qN/zJAaOQBcJ7KGkWStqQ==", - "hasInstallScript": true, - "license": "Apache-2.0", + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", "dependencies": { - "tslib": "^2.1.0" + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" }, "engines": { - "node": ">=20.0.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@google-cloud/firestore": { - "version": "7.11.6", - "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.11.6.tgz", - "integrity": "sha512-EW/O8ktzwLfyWBOsNuhRoMi8lrC3clHM5LVFhGvO1HCsLozCOOXRAlHrYBoE6HL42Sc8yYMuCb2XqcnJ4OOEpw==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "@opentelemetry/api": "^1.3.0", - "fast-deep-equal": "^3.1.1", - "functional-red-black-tree": "^1.0.1", - "google-gax": "^4.3.3", - "protobufjs": "^7.2.6" - }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=14.0.0" + "node": ">=6.9.0" } }, - "node_modules/@google-cloud/paginator": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-5.0.2.tgz", - "integrity": "sha512-DJS3s0OVH4zFDB1PzjxAsHqJT6sKVbRwwML0ZBP9PbU7Yebtu/7SWMRzvO2J3nUi9pRNITCfu4LJeooM2w4pjg==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "arrify": "^2.0.0", - "extend": "^3.0.2" - }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=14.0.0" + "node": ">=6.9.0" } }, - "node_modules/@google-cloud/projectify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-4.0.0.tgz", - "integrity": "sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA==", - "license": "Apache-2.0", - "optional": true, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=14.0.0" + "node": ">=6.9.0" } }, - "node_modules/@google-cloud/promisify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-4.0.0.tgz", - "integrity": "sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g==", - "license": "Apache-2.0", - "optional": true, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=14" + "node": ">=6.9.0" } }, - "node_modules/@google-cloud/storage": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-7.19.0.tgz", - "integrity": "sha512-n2FjE7NAOYyshogdc7KQOl/VZb4sneqPjWouSyia9CMDdMhRX5+RIbqalNmC7LOLzuLAN89VlF2HvG8na9G+zQ==", - "license": "Apache-2.0", - "optional": true, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "license": "MIT", "dependencies": { - "@google-cloud/paginator": "^5.0.0", - "@google-cloud/projectify": "^4.0.0", - "@google-cloud/promisify": "<4.1.0", - "abort-controller": "^3.0.0", - "async-retry": "^1.3.3", - "duplexify": "^4.1.3", - "fast-xml-parser": "^5.3.4", - "gaxios": "^6.0.2", - "google-auth-library": "^9.6.3", - "html-entities": "^2.5.2", - "mime": "^3.0.0", - "p-limit": "^3.0.1", - "retry-request": "^7.0.0", - "teeny-request": "^9.0.0", - "uuid": "^8.0.0" + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" }, "engines": { - "node": ">=14" + "node": ">=6.9.0" } }, - "node_modules/@google-cloud/storage/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "dev": true, "license": "MIT", - "optional": true, + "dependencies": { + "@babel/types": "^7.29.0" + }, "bin": { - "uuid": "dist/bin/uuid" + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" } }, - "node_modules/@grpc/grpc-js": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.3.tgz", - "integrity": "sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA==", - "license": "Apache-2.0", - "optional": true, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", "dependencies": { - "@grpc/proto-loader": "^0.8.0", - "@js-sdsl/ordered-map": "^4.4.2" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": ">=12.10.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@grpc/grpc-js/node_modules/@grpc/proto-loader": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.0.tgz", - "integrity": "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==", - "license": "Apache-2.0", - "optional": true, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", "dependencies": { - "lodash.camelcase": "^4.3.0", - "long": "^5.0.0", - "protobufjs": "^7.5.3", - "yargs": "^17.7.2" + "@babel/helper-plugin-utils": "^7.27.1" }, - "bin": { - "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { - "node": ">=6" + "node": ">=6.9.0" } }, - "node_modules/@grpc/proto-loader": { - "version": "0.7.15", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz", - "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==", - "license": "Apache-2.0", - "optional": true, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", "dependencies": { - "lodash.camelcase": "^4.3.0", - "long": "^5.0.0", - "protobufjs": "^7.2.5", - "yargs": "^17.7.2" + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" }, - "bin": { - "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { - "node": ">=6" + "node": ">=6.9.0" } }, - "node_modules/@js-sdsl/ordered-map": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", - "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz", + "integrity": "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==", + "cpu": [ + "ppc64" + ], "license": "MIT", "optional": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" + "os": [ + "aix" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@opentelemetry/api": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", - "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", - "license": "Apache-2.0", + "node_modules/@esbuild/android-arm": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.4.tgz", + "integrity": "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==", + "cpu": [ + "arm" + ], + "license": "MIT", "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=8.0.0" + "node": ">=18" } }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", - "license": "BSD-3-Clause", - "optional": true + "node_modules/@esbuild/android-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.4.tgz", + "integrity": "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", - "license": "BSD-3-Clause", - "optional": true + "node_modules/@esbuild/android-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.4.tgz", + "integrity": "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", - "license": "BSD-3-Clause", - "optional": true - }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", - "license": "BSD-3-Clause", - "optional": true - }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "license": "BSD-3-Clause", + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.4.tgz", + "integrity": "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", "optional": true, - "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", - "license": "BSD-3-Clause", - "optional": true - }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", - "license": "BSD-3-Clause", - "optional": true - }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", - "license": "BSD-3-Clause", - "optional": true - }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", - "license": "BSD-3-Clause", - "optional": true - }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", - "license": "BSD-3-Clause", - "optional": true - }, - "node_modules/@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.4.tgz", + "integrity": "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==", + "cpu": [ + "x64" + ], "license": "MIT", "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">= 10" + "node": ">=18" } }, - "node_modules/@types/caseless": { - "version": "0.12.5", - "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz", - "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==", - "license": "MIT", - "optional": true - }, - "node_modules/@types/jsonwebtoken": { - "version": "9.0.10", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", - "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.4.tgz", + "integrity": "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==", + "cpu": [ + "arm64" + ], "license": "MIT", - "dependencies": { - "@types/ms": "*", - "@types/node": "*" + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@types/long": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", - "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.4.tgz", + "integrity": "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==", + "cpu": [ + "x64" + ], "license": "MIT", - "optional": true - }, - "node_modules/@types/ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", - "license": "MIT" + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@types/node": { - "version": "25.2.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.3.tgz", - "integrity": "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==", + "node_modules/@esbuild/linux-arm": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.4.tgz", + "integrity": "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==", + "cpu": [ + "arm" + ], "license": "MIT", - "dependencies": { - "undici-types": "~7.16.0" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@types/request": { - "version": "2.48.13", - "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.13.tgz", - "integrity": "sha512-FGJ6udDNUCjd19pp0Q3iTiDkwhYup7J8hpMW9c4k53NrccQFFWKRho6hvtPPEhnXWKvukfwAlB6DbDz4yhH5Gg==", + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.4.tgz", + "integrity": "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==", + "cpu": [ + "arm64" + ], "license": "MIT", "optional": true, - "dependencies": { - "@types/caseless": "*", - "@types/node": "*", - "@types/tough-cookie": "*", - "form-data": "^2.5.5" + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@types/tough-cookie": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", - "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.4.tgz", + "integrity": "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==", + "cpu": [ + "ia32" + ], "license": "MIT", - "optional": true + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.4.tgz", + "integrity": "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==", + "cpu": [ + "loong64" + ], "license": "MIT", "optional": true, - "dependencies": { - "event-target-shim": "^5.0.0" - }, + "os": [ + "linux" + ], "engines": { - "node": ">=6.5" + "node": ">=18" } }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.4.tgz", + "integrity": "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==", + "cpu": [ + "mips64el" + ], "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 0.6" + "node": ">=18" } }, - "node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.4.tgz", + "integrity": "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==", + "cpu": [ + "ppc64" + ], "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 14" + "node": ">=18" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.4.tgz", + "integrity": "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==", + "cpu": [ + "riscv64" + ], "license": "MIT", "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.4.tgz", + "integrity": "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==", + "cpu": [ + "s390x" + ], "license": "MIT", "optional": true, - "dependencies": { - "color-convert": "^2.0.1" - }, + "os": [ + "linux" + ], "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=18" } }, - "node_modules/append-field": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", - "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", - "license": "MIT" - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "license": "MIT" - }, - "node_modules/arrify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", - "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "node_modules/@esbuild/linux-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.4.tgz", + "integrity": "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==", + "cpu": [ + "x64" + ], "license": "MIT", "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/async-retry": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", - "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.4.tgz", + "integrity": "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==", + "cpu": [ + "arm64" + ], "license": "MIT", "optional": true, - "dependencies": { - "retry": "0.13.1" + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.4.tgz", + "integrity": "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==", + "cpu": [ + "x64" + ], "license": "MIT", - "optional": true - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } + "optional": true, + "os": [ + "netbsd" ], - "license": "MIT" + "engines": { + "node": ">=18" + } }, - "node_modules/bignumber.js": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", - "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.4.tgz", + "integrity": "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==", + "cpu": [ + "arm64" + ], "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": "*" + "node": ">=18" } }, - "node_modules/body-parser": { - "version": "1.20.4", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", - "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.4.tgz", + "integrity": "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==", + "cpu": [ + "x64" + ], "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "~1.2.0", - "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", - "on-finished": "~2.4.1", - "qs": "~6.14.0", - "raw-body": "~2.5.3", - "type-is": "~1.6.18", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", - "license": "BSD-3-Clause" - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "license": "MIT" - }, - "node_modules/busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "dependencies": { - "streamsearch": "^1.1.0" - }, + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": ">=10.16.0" + "node": ">=18" } }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.4.tgz", + "integrity": "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==", + "cpu": [ + "arm64" + ], "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], "engines": { - "node": ">= 0.8" + "node": ">=18" } }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.4.tgz", + "integrity": "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==", + "cpu": [ + "x64" + ], "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, + "optional": true, + "os": [ + "sunos" + ], "engines": { - "node": ">= 0.4" + "node": ">=18" } }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.4.tgz", + "integrity": "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==", + "cpu": [ + "arm64" + ], "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18" } }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "license": "ISC", + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.4.tgz", + "integrity": "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==", + "cpu": [ + "ia32" + ], + "license": "MIT", "optional": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, + "os": [ + "win32" + ], "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/@esbuild/win32-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.4.tgz", + "integrity": "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==", + "cpu": [ + "x64" + ], "license": "MIT", "optional": true, - "dependencies": { - "color-name": "~1.1.4" - }, + "os": [ + "win32" + ], "engines": { - "node": ">=7.0.0" + "node": ">=18" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT", - "optional": true + "node_modules/@fastify/busboy": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.2.0.tgz", + "integrity": "sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA==", + "license": "MIT" }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "optional": true, + "node_modules/@firebase/ai": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@firebase/ai/-/ai-2.9.0.tgz", + "integrity": "sha512-NPvBBuvdGo9x3esnABAucFYmqbBmXvyTMimBq2PCuLZbdANZoHzGlx7vfzbwNDaEtCBq4RGGNMliLIv6bZ+PtA==", + "license": "Apache-2.0", "dependencies": { - "delayed-stream": "~1.0.0" + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/component": "0.7.1", + "@firebase/logger": "0.5.0", + "@firebase/util": "1.14.0", + "tslib": "^2.1.0" }, "engines": { - "node": ">= 0.8" + "node": ">=20.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@firebase/app-types": "0.x" } }, - "node_modules/concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "engines": [ - "node >= 0.8" - ], - "license": "MIT", + "node_modules/@firebase/analytics": { + "version": "0.10.20", + "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.20.tgz", + "integrity": "sha512-adGTNVUWH5q66tI/OQuKLSN6mamPpfYhj0radlH2xt+3eL6NFPtXoOs+ulvs+UsmK27vNFx5FjRDfWk+TyduHg==", + "license": "Apache-2.0", "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" + "@firebase/component": "0.7.1", + "@firebase/installations": "0.6.20", + "@firebase/logger": "0.5.0", + "@firebase/util": "1.14.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" } }, - "node_modules/concat-stream/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "license": "MIT", + "node_modules/@firebase/analytics-compat": { + "version": "0.2.26", + "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.26.tgz", + "integrity": "sha512-0j2ruLOoVSwwcXAF53AMoniJKnkwiTjGVfic5LDzqiRkR13vb5j6TXMeix787zbLeQtN/m1883Yv1TxI0gItbA==", + "license": "Apache-2.0", "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "@firebase/analytics": "0.10.20", + "@firebase/analytics-types": "0.8.3", + "@firebase/component": "0.7.1", + "@firebase/util": "1.14.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" } }, - "node_modules/concat-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, - "node_modules/concat-stream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } + "node_modules/@firebase/analytics-types": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.3.tgz", + "integrity": "sha512-VrIp/d8iq2g501qO46uGz3hjbDb8xzYMrbu8Tp0ovzIzrvJZ2fvmj649gTjge/b7cCCcjT0H37g1gVtlNhnkbg==", + "license": "Apache-2.0" }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "license": "MIT", + "node_modules/@firebase/app": { + "version": "0.14.9", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.14.9.tgz", + "integrity": "sha512-3gtUX0e584MYkKBQMgSECMvE1Dwzg+eONefDQ0wxVSe5YMBsZwdN5pL7UapwWBlV8+i8QCztF9TP947tEjZAGA==", + "license": "Apache-2.0", "dependencies": { - "safe-buffer": "5.2.1" + "@firebase/component": "0.7.1", + "@firebase/logger": "0.5.0", + "@firebase/util": "1.14.0", + "idb": "7.1.1", + "tslib": "^2.1.0" }, "engines": { - "node": ">= 0.6" + "node": ">=20.0.0" } }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", + "node_modules/@firebase/app-check": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.11.1.tgz", + "integrity": "sha512-gmKfwQ2k8aUQlOyRshc+fOQLq0OwUmibIZvpuY1RDNu2ho0aTMlwxOuEiJeYOs7AxzhSx7gnXPFNsXCFbnvXUQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.1", + "@firebase/logger": "0.5.0", + "@firebase/util": "1.14.0", + "tslib": "^2.1.0" + }, "engines": { - "node": ">= 0.6" + "node": ">=20.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" } }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", + "node_modules/@firebase/app-check-compat": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.4.1.tgz", + "integrity": "sha512-yjSvSl5B1u4CirnxhzirN1uiTRCRfx+/qtfbyeyI+8Cx8Cw1RWAIO/OqytPSVwLYbJJ1vEC3EHfxazRaMoWKaA==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app-check": "0.11.1", + "@firebase/app-check-types": "0.5.3", + "@firebase/component": "0.7.1", + "@firebase/logger": "0.5.0", + "@firebase/util": "1.14.0", + "tslib": "^2.1.0" + }, "engines": { - "node": ">= 0.6" + "node": ">=20.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" } }, - "node_modules/cookie-signature": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", - "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", - "license": "MIT" + "node_modules/@firebase/app-check-interop-types": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.3.tgz", + "integrity": "sha512-gAlxfPLT2j8bTI/qfe3ahl2I2YcBQ8cFIBdhAQA4I2f3TndcO+22YizyGYuttLHPQEpWkhmpFW60VCFEPg4g5A==", + "license": "Apache-2.0" }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "license": "MIT" + "node_modules/@firebase/app-check-types": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.3.tgz", + "integrity": "sha512-hyl5rKSj0QmwPdsAxrI5x1otDlByQ7bvNvVt8G/XPO2CSwE++rmSVf3VEhaeOR4J8ZFaF0Z0NDSmLejPweZ3ng==", + "license": "Apache-2.0" }, - "node_modules/cors": { - "version": "2.8.6", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", - "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", - "license": "MIT", + "node_modules/@firebase/app-compat": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.5.9.tgz", + "integrity": "sha512-e5LzqjO69/N2z7XcJeuMzIp4wWnW696dQeaHAUpQvGk89gIWHAIvG6W+mA3UotGW6jBoqdppEJ9DnuwbcBByug==", + "license": "Apache-2.0", "dependencies": { - "object-assign": "^4", - "vary": "^1" + "@firebase/app": "0.14.9", + "@firebase/component": "0.7.1", + "@firebase/logger": "0.5.0", + "@firebase/util": "1.14.0", + "tslib": "^2.1.0" }, "engines": { - "node": ">= 0.10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "node": ">=20.0.0" } }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", + "node_modules/@firebase/app-types": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz", + "integrity": "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/auth-compat": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.6.3.tgz", + "integrity": "sha512-nHOkupcYuGVxI1AJJ/OBhLPaRokbP14Gq4nkkoVvf1yvuREEWqdnrYB/CdsSnPxHMAnn5wJIKngxBF9jNX7s/Q==", + "license": "Apache-2.0", "dependencies": { - "ms": "2.0.0" + "@firebase/auth": "1.12.1", + "@firebase/auth-types": "0.13.0", + "@firebase/component": "0.7.1", + "@firebase/util": "1.14.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "optional": true, + "node_modules/@firebase/auth-compat/node_modules/@firebase/auth": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.12.1.tgz", + "integrity": "sha512-nXKj7d5bMBlnq6XpcQQpmnSVwEeHBkoVbY/+Wk0P1ebLSICoH4XPtvKOFlXKfIHmcS84mLQ99fk3njlDGKSDtw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.1", + "@firebase/logger": "0.5.0", + "@firebase/util": "1.14.0", + "tslib": "^2.1.0" + }, "engines": { - "node": ">=0.4.0" + "node": ">=20.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@react-native-async-storage/async-storage": "^2.2.0" + }, + "peerDependenciesMeta": { + "@react-native-async-storage/async-storage": { + "optional": true + } } }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" + "node_modules/@firebase/auth-interop-types": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.4.tgz", + "integrity": "sha512-JPgcXKCuO+CWqGDnigBtvo09HeBs5u/Ktc2GaFj2m01hLarbxthLNm7Fk8iOP1aqAtXV+fnnGj7U28xmk7IwVA==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/auth-types": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.13.0.tgz", + "integrity": "sha512-S/PuIjni0AQRLF+l9ck0YpsMOdE8GO2KU6ubmBB7P+7TJUCQDa3R1dlgYm9UzGbbePMZsp0xzB93f2b/CgxMOg==", + "license": "Apache-2.0", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" } }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", + "node_modules/@firebase/component": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.7.1.tgz", + "integrity": "sha512-mFzsm7CLHR60o08S23iLUY8m/i6kLpOK87wdEFPLhdlCahaxKmWOwSVGiWoENYSmFJJoDhrR3gKSCxz7ENdIww==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/util": "1.14.0", + "tslib": "^2.1.0" + }, "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" + "node": ">=20.0.0" } }, - "node_modules/dotenv": { - "version": "16.6.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", - "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" + "node_modules/@firebase/data-connect": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@firebase/data-connect/-/data-connect-0.4.0.tgz", + "integrity": "sha512-vLXM6WHNIR3VtEeYNUb/5GTsUOyl3Of4iWNZHBe1i9f88sYFnxybJNWVBjvJ7flhCyF8UdxGpzWcUnv6F5vGfg==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.7.1", + "@firebase/logger": "0.5.0", + "@firebase/util": "1.14.0", + "tslib": "^2.1.0" }, - "funding": { - "url": "https://dotenvx.com" + "peerDependencies": { + "@firebase/app": "0.x" } }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", + "node_modules/@firebase/database": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.1.1.tgz", + "integrity": "sha512-LwIXe8+mVHY5LBPulWECOOIEXDiatyECp/BOlu0gOhe+WOcKjWHROaCbLlkFTgHMY7RHr5MOxkLP/tltWAH3dA==", + "license": "Apache-2.0", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.7.1", + "@firebase/logger": "0.5.0", + "@firebase/util": "1.14.0", + "faye-websocket": "0.11.4", + "tslib": "^2.1.0" }, "engines": { - "node": ">= 0.4" + "node": ">=20.0.0" } }, - "node_modules/duplexify": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", - "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", - "license": "MIT", - "optional": true, + "node_modules/@firebase/database-compat": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.1.1.tgz", + "integrity": "sha512-heAEVZ9Z8c8PnBUcmGh91JHX0cXcVa1yESW/xkLuwaX7idRFyLiN8sl73KXpR8ZArGoPXVQDanBnk6SQiekRCQ==", + "license": "Apache-2.0", "dependencies": { - "end-of-stream": "^1.4.1", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1", - "stream-shift": "^1.0.2" + "@firebase/component": "0.7.1", + "@firebase/database": "1.1.1", + "@firebase/database-types": "1.0.17", + "@firebase/logger": "0.5.0", + "@firebase/util": "1.14.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" } }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "node_modules/@firebase/database-types": { + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.17.tgz", + "integrity": "sha512-4eWaM5fW3qEIHjGzfi3cf0Jpqi1xQsAdT6rSDE1RZPrWu8oGjgrq6ybMjobtyHQFgwGCykBm4YM89qDzc+uG/w==", "license": "Apache-2.0", "dependencies": { - "safe-buffer": "^5.0.1" + "@firebase/app-types": "0.9.3", + "@firebase/util": "1.14.0" } }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT", - "optional": true - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", + "node_modules/@firebase/firestore": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.12.0.tgz", + "integrity": "sha512-PM47OyiiAAoAMB8kkq4Je14mTciaRoAPDd3ng3Ckqz9i2TX9D9LfxIRcNzP/OxzNV4uBKRq6lXoOggkJBQR3Gw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.1", + "@firebase/logger": "0.5.0", + "@firebase/util": "1.14.0", + "@firebase/webchannel-wrapper": "1.0.5", + "@grpc/grpc-js": "~1.9.0", + "@grpc/proto-loader": "^0.7.8", + "tslib": "^2.1.0" + }, "engines": { - "node": ">= 0.8" + "node": ">=20.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" } }, - "node_modules/end-of-stream": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", - "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", - "license": "MIT", - "optional": true, + "node_modules/@firebase/firestore-compat": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.4.6.tgz", + "integrity": "sha512-NgVyR4hHHN2FvSNQOtbgBOuVsEdD/in30d9FKbEvvITiAChrBN2nBstmhfjI4EOTnHaP8zigwvkNYFI9yKGAkQ==", + "license": "Apache-2.0", "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", + "@firebase/component": "0.7.1", + "@firebase/firestore": "4.12.0", + "@firebase/firestore-types": "3.0.3", + "@firebase/util": "1.14.0", + "tslib": "^2.1.0" + }, "engines": { - "node": ">= 0.4" + "node": ">=20.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" } }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" + "node_modules/@firebase/firestore-types": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-3.0.3.tgz", + "integrity": "sha512-hD2jGdiWRxB/eZWF89xcK9gF8wvENDJkzpVFb4aGkzfEaKxVRD1kjz1t1Wj8VZEp2LCB53Yx1zD8mrhQu87R6Q==", + "license": "Apache-2.0", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" } }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", + "node_modules/@firebase/functions": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.13.2.tgz", + "integrity": "sha512-tHduUD+DeokM3NB1QbHCvEMoL16e8Z8JSkmuVA4ROoJKPxHn8ibnecHPO2e3nVCJR1D9OjuKvxz4gksfq92/ZQ==", + "license": "Apache-2.0", "dependencies": { - "es-errors": "^1.3.0" + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.7.1", + "@firebase/messaging-interop-types": "0.2.3", + "@firebase/util": "1.14.0", + "tslib": "^2.1.0" }, "engines": { - "node": ">= 0.4" + "node": ">=20.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" } }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "license": "MIT", - "optional": true, + "node_modules/@firebase/functions-compat": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.4.2.tgz", + "integrity": "sha512-YNxgnezvZDkqxqXa6cT7/oTeD4WXbxgIP7qZp4LFnathQv5o2omM6EoIhXiT9Ie5AoQDcIhG9Y3/dj+DFJGaGQ==", + "license": "Apache-2.0", "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" + "@firebase/component": "0.7.1", + "@firebase/functions": "0.13.2", + "@firebase/functions-types": "0.6.3", + "@firebase/util": "1.14.0", + "tslib": "^2.1.0" }, "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=6" + "node": ">=20.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" } }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" + "node_modules/@firebase/functions-types": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.3.tgz", + "integrity": "sha512-EZoDKQLUHFKNx6VLipQwrSMh01A1SaL3Wg6Hpi//x6/fJ6Ee4hrAeswK99I5Ht8roiniKHw4iO0B1Oxj5I4plg==", + "license": "Apache-2.0" }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" + "node_modules/@firebase/installations": { + "version": "0.6.20", + "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.20.tgz", + "integrity": "sha512-LOzvR7XHPbhS0YB5ANXhqXB5qZlntPpwU/4KFwhSNpXNsGk/sBQ9g5hepi0y0/MfenJLe2v7t644iGOOElQaHQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.1", + "@firebase/util": "1.14.0", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" } }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=6" + "node_modules/@firebase/installations-compat": { + "version": "0.2.20", + "resolved": "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.20.tgz", + "integrity": "sha512-9C9pL/DIEGucmoPj8PlZTnztbX3nhNj5RTYVpUM7wQq/UlHywaYv99969JU/WHLvi9ptzIogXYS9d1eZ6XFe9g==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.1", + "@firebase/installations": "0.6.20", + "@firebase/installations-types": "0.5.3", + "@firebase/util": "1.14.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" } }, - "node_modules/express": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", - "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", - "license": "MIT", + "node_modules/@firebase/installations-types": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.3.tgz", + "integrity": "sha512-2FJI7gkLqIE0iYsNQ1P751lO3hER+Umykel+TkLwHj6plzWVxqvfclPUZhcKFVQObqloEBTmpi2Ozn7EkCABAA==", + "license": "Apache-2.0", + "peerDependencies": { + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/logger": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.5.0.tgz", + "integrity": "sha512-cGskaAvkrnh42b3BA3doDWeBmuHFO/Mx5A83rbRDYakPjO9bJtRL3dX7javzc2Rr/JHZf4HlterTW2lUkfeN4g==", + "license": "Apache-2.0", "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "~1.20.3", - "content-disposition": "~0.5.4", - "content-type": "~1.0.4", - "cookie": "~0.7.1", - "cookie-signature": "~1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.3.1", - "fresh": "~0.5.2", - "http-errors": "~2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "~2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "~0.1.12", - "proxy-addr": "~2.0.7", - "qs": "~6.14.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "~0.19.0", - "serve-static": "~1.16.2", - "setprototypeof": "1.2.0", - "statuses": "~2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" + "tslib": "^2.1.0" }, "engines": { - "node": ">= 0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "node": ">=20.0.0" } }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "license": "MIT" - }, - "node_modules/farmhash-modern": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/farmhash-modern/-/farmhash-modern-1.1.0.tgz", - "integrity": "sha512-6ypT4XfgqJk/F3Yuv4SX26I3doUjt0GTG4a+JgWxXQpxXzTBq8fPUeGHfcYMMDPHJHm3yPOSjaeBwBGAHWXCdA==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" + "node_modules/@firebase/messaging": { + "version": "0.12.24", + "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.24.tgz", + "integrity": "sha512-UtKoubegAhHyehcB7iQjvQ8OVITThPbbWk3g2/2ze42PrQr6oe6OmCElYQkBrE5RDCeMTNucXejbdulrQ2XwVg==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.1", + "@firebase/installations": "0.6.20", + "@firebase/messaging-interop-types": "0.2.3", + "@firebase/util": "1.14.0", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT" - }, - "node_modules/fast-xml-parser": { - "version": "5.3.6", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.6.tgz", - "integrity": "sha512-QNI3sAvSvaOiaMl8FYU4trnEzCwiRr8XMWgAHzlrWpTSj+QaCSvOf1h82OEP1s4hiAXhnbXSyFWCf4ldZzZRVA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "optional": true, + "node_modules/@firebase/messaging-compat": { + "version": "0.2.24", + "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.24.tgz", + "integrity": "sha512-wXH8FrKbJvFuFe6v98TBhAtvgknxKIZtGM/wCVsfpOGmaAE80bD8tBxztl+uochjnFb9plihkd6mC4y7sZXSpA==", + "license": "Apache-2.0", "dependencies": { - "strnum": "^2.1.2" + "@firebase/component": "0.7.1", + "@firebase/messaging": "0.12.24", + "@firebase/util": "1.14.0", + "tslib": "^2.1.0" }, - "bin": { - "fxparser": "src/cli/cli.js" + "peerDependencies": { + "@firebase/app-compat": "0.x" } }, - "node_modules/faye-websocket": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", - "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "node_modules/@firebase/messaging-interop-types": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.3.tgz", + "integrity": "sha512-xfzFaJpzcmtDjycpDeCUj0Ge10ATFi/VHVIvEEjDNc3hodVBQADZ7BWQU7CuFpjSHE+eLuBI13z5F/9xOoGX8Q==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/performance": { + "version": "0.7.10", + "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.7.10.tgz", + "integrity": "sha512-8nRFld+Ntzp5cLKzZuG9g+kBaSn8Ks9dmn87UQGNFDygbmR6ebd8WawauEXiJjMj1n70ypkvAOdE+lzeyfXtGA==", "license": "Apache-2.0", "dependencies": { - "websocket-driver": ">=0.5.1" + "@firebase/component": "0.7.1", + "@firebase/installations": "0.6.20", + "@firebase/logger": "0.5.0", + "@firebase/util": "1.14.0", + "tslib": "^2.1.0", + "web-vitals": "^4.2.4" }, - "engines": { - "node": ">=0.8.0" + "peerDependencies": { + "@firebase/app": "0.x" } }, - "node_modules/finalhandler": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", - "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", - "license": "MIT", + "node_modules/@firebase/performance-compat": { + "version": "0.2.23", + "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.23.tgz", + "integrity": "sha512-c7qOAGBUAOpIuUlHu1axWcrCVtIYKPMhH0lMnoCDWnPwn1HcPuPUBVTWETbC7UWw71RMJF8DpirfWXzMWJQfgA==", + "license": "Apache-2.0", "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "~2.4.1", - "parseurl": "~1.3.3", - "statuses": "~2.0.2", - "unpipe": "~1.0.0" + "@firebase/component": "0.7.1", + "@firebase/logger": "0.5.0", + "@firebase/performance": "0.7.10", + "@firebase/performance-types": "0.2.3", + "@firebase/util": "1.14.0", + "tslib": "^2.1.0" }, - "engines": { - "node": ">= 0.8" + "peerDependencies": { + "@firebase/app-compat": "0.x" } }, - "node_modules/firebase-admin": { - "version": "13.6.1", - "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-13.6.1.tgz", - "integrity": "sha512-Zgc6yPtmPxAZo+FoK6LMG6zpSEsoSK8ifIR+IqF4oWuC3uWZU40OjxgfLTSFcsRlj/k/wD66zNv2UiTRreCNSw==", + "node_modules/@firebase/performance-types": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.3.tgz", + "integrity": "sha512-IgkyTz6QZVPAq8GSkLYJvwSLr3LS9+V6vNPQr0x4YozZJiLF5jYixj0amDtATf1X0EtYHqoPO48a9ija8GocxQ==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/remote-config": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.8.1.tgz", + "integrity": "sha512-L86TReBnPiiJOWd7k9iaiE9f7rHtMpjAoYN0fH2ey2ZRzsOChHV0s5sYf1+IIUYzplzsE46pjlmAUNkRRKwHSQ==", "license": "Apache-2.0", "dependencies": { - "@fastify/busboy": "^3.0.0", - "@firebase/database-compat": "^2.0.0", - "@firebase/database-types": "^1.0.6", - "@types/node": "^22.8.7", - "farmhash-modern": "^1.1.0", - "fast-deep-equal": "^3.1.1", - "google-auth-library": "^9.14.2", - "jsonwebtoken": "^9.0.0", - "jwks-rsa": "^3.1.0", - "node-forge": "^1.3.1", - "uuid": "^11.0.2" - }, - "engines": { - "node": ">=18" + "@firebase/component": "0.7.1", + "@firebase/installations": "0.6.20", + "@firebase/logger": "0.5.0", + "@firebase/util": "1.14.0", + "tslib": "^2.1.0" }, - "optionalDependencies": { - "@google-cloud/firestore": "^7.11.0", - "@google-cloud/storage": "^7.14.0" + "peerDependencies": { + "@firebase/app": "0.x" } }, - "node_modules/firebase-admin/node_modules/@types/node": { - "version": "22.19.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.11.tgz", - "integrity": "sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==", - "license": "MIT", + "node_modules/@firebase/remote-config-compat": { + "version": "0.2.22", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.22.tgz", + "integrity": "sha512-uW/eNKKtRBot2gnCC5mnoy5Voo2wMzZuQ7dwqqGHU176fO9zFgMwKiRzk+aaC99NLrFk1KOmr0ZVheD+zdJmjQ==", + "license": "Apache-2.0", "dependencies": { - "undici-types": "~6.21.0" + "@firebase/component": "0.7.1", + "@firebase/logger": "0.5.0", + "@firebase/remote-config": "0.8.1", + "@firebase/remote-config-types": "0.5.0", + "@firebase/util": "1.14.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" } }, - "node_modules/firebase-admin/node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "license": "MIT" + "node_modules/@firebase/remote-config-types": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.5.0.tgz", + "integrity": "sha512-vI3bqLoF14L/GchtgayMiFpZJF+Ao3uR8WCde0XpYNkSokDpAKca2DxvcfeZv7lZUqkUwQPL2wD83d3vQ4vvrg==", + "license": "Apache-2.0" }, - "node_modules/form-data": { - "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==", - "license": "MIT", - "optional": true, + "node_modules/@firebase/storage": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.14.1.tgz", + "integrity": "sha512-uIpYgBBsv1vIET+5xV20XT7wwqV+H4GFp6PBzfmLUcEgguS4SWNFof56Z3uOC2lNDh0KDda1UflYq2VwD9Nefw==", + "license": "Apache-2.0", "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" + "@firebase/component": "0.7.1", + "@firebase/util": "1.14.0", + "tslib": "^2.1.0" }, "engines": { - "node": ">= 0.12" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" + "node": ">=20.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" } }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "license": "MIT", + "node_modules/@firebase/storage-compat": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.4.1.tgz", + "integrity": "sha512-bgl3FHHfXAmBgzIK/Fps6Xyv2HiAQlSTov07CBL+RGGhrC5YIk4lruS8JVIC+UkujRdYvnf8cpQFGn2RCilJ/A==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.1", + "@firebase/storage": "0.14.1", + "@firebase/storage-types": "0.8.3", + "@firebase/util": "1.14.0", + "tslib": "^2.1.0" + }, "engines": { - "node": ">= 0.6" + "node": ">=20.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" } }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node_modules/@firebase/storage-types": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.3.tgz", + "integrity": "sha512-+Muk7g9uwngTpd8xn9OdF/D48uiQ7I1Fae7ULsWPuKoCH3HU7bfFPhxtJYzyhjdniowhuDpQcfPmuNRAqZEfvg==", + "license": "Apache-2.0", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" } }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", - "license": "MIT", - "optional": true - }, - "node_modules/gaxios": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", - "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", + "node_modules/@firebase/util": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.14.0.tgz", + "integrity": "sha512-/gnejm7MKkVIXnSJGpc9L2CvvvzJvtDPeAEq5jAwgVlf/PeNxot+THx/bpD20wQ8uL5sz0xqgXy1nisOYMU+mw==", + "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "extend": "^3.0.2", - "https-proxy-agent": "^7.0.1", - "is-stream": "^2.0.0", - "node-fetch": "^2.6.9", - "uuid": "^9.0.1" + "tslib": "^2.1.0" }, "engines": { - "node": ">=14" + "node": ">=20.0.0" } }, - "node_modules/gaxios/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } + "node_modules/@firebase/webchannel-wrapper": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.5.tgz", + "integrity": "sha512-+uGNN7rkfn41HLO0vekTFhTxk61eKa8mTpRGLO0QSqlQdKvIoGAvLp3ppdVIWbTGYJWM6Kp0iN+PjMIOcnVqTw==", + "license": "Apache-2.0" }, - "node_modules/gcp-metadata": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", - "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", + "node_modules/@google-cloud/firestore": { + "version": "7.11.6", + "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.11.6.tgz", + "integrity": "sha512-EW/O8ktzwLfyWBOsNuhRoMi8lrC3clHM5LVFhGvO1HCsLozCOOXRAlHrYBoE6HL42Sc8yYMuCb2XqcnJ4OOEpw==", "license": "Apache-2.0", + "optional": true, "dependencies": { - "gaxios": "^6.1.1", - "google-logging-utils": "^0.0.2", - "json-bigint": "^1.0.0" + "@opentelemetry/api": "^1.3.0", + "fast-deep-equal": "^3.1.1", + "functional-red-black-tree": "^1.0.1", + "google-gax": "^4.3.3", + "protobufjs": "^7.2.6" }, "engines": { - "node": ">=14" + "node": ">=14.0.0" } }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "license": "ISC", + "node_modules/@google-cloud/paginator": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-5.0.2.tgz", + "integrity": "sha512-DJS3s0OVH4zFDB1PzjxAsHqJT6sKVbRwwML0ZBP9PbU7Yebtu/7SWMRzvO2J3nUi9pRNITCfu4LJeooM2w4pjg==", + "license": "Apache-2.0", "optional": true, + "dependencies": { + "arrify": "^2.0.0", + "extend": "^3.0.2" + }, "engines": { - "node": "6.* || 8.* || >= 10.*" + "node": ">=14.0.0" } }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", + "node_modules/@google-cloud/projectify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-4.0.0.tgz", + "integrity": "sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/promisify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-4.0.0.tgz", + "integrity": "sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@google-cloud/storage": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-7.19.0.tgz", + "integrity": "sha512-n2FjE7NAOYyshogdc7KQOl/VZb4sneqPjWouSyia9CMDdMhRX5+RIbqalNmC7LOLzuLAN89VlF2HvG8na9G+zQ==", + "license": "Apache-2.0", + "optional": true, "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" + "@google-cloud/paginator": "^5.0.0", + "@google-cloud/projectify": "^4.0.0", + "@google-cloud/promisify": "<4.1.0", + "abort-controller": "^3.0.0", + "async-retry": "^1.3.3", + "duplexify": "^4.1.3", + "fast-xml-parser": "^5.3.4", + "gaxios": "^6.0.2", + "google-auth-library": "^9.6.3", + "html-entities": "^2.5.2", + "mime": "^3.0.0", + "p-limit": "^3.0.1", + "retry-request": "^7.0.0", + "teeny-request": "^9.0.0", + "uuid": "^8.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=14" } }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", + "node_modules/@google-cloud/storage/node_modules/gcp-metadata": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", + "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", + "license": "Apache-2.0", + "optional": true, "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" + "gaxios": "^6.1.1", + "google-logging-utils": "^0.0.2", + "json-bigint": "^1.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=14" } }, - "node_modules/google-auth-library": { + "node_modules/@google-cloud/storage/node_modules/google-auth-library": { "version": "9.15.1", "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz", "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", "license": "Apache-2.0", + "optional": true, "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", @@ -1307,174 +1461,3081 @@ "node": ">=14" } }, - "node_modules/google-gax": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.6.1.tgz", - "integrity": "sha512-V6eky/xz2mcKfAd1Ioxyd6nmA61gao3n01C+YeuIwu3vzM9EDR6wcVzMSIbLMDXWeoi9SHYctXuKYC5uJUT3eQ==", + "node_modules/@google-cloud/storage/node_modules/google-logging-utils": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz", + "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==", "license": "Apache-2.0", "optional": true, - "dependencies": { - "@grpc/grpc-js": "^1.10.9", - "@grpc/proto-loader": "^0.7.13", - "@types/long": "^4.0.0", - "abort-controller": "^3.0.0", - "duplexify": "^4.0.0", - "google-auth-library": "^9.3.0", - "node-fetch": "^2.7.0", - "object-hash": "^3.0.0", - "proto3-json-serializer": "^2.0.2", - "protobufjs": "^7.3.2", - "retry-request": "^7.0.0", - "uuid": "^9.0.1" - }, "engines": { "node": ">=14" } }, - "node_modules/google-gax/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], + "node_modules/@google-cloud/storage/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "license": "MIT", "optional": true, "bin": { "uuid": "dist/bin/uuid" } }, - "node_modules/google-logging-utils": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz", - "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==", + "node_modules/@google/genai": { + "version": "1.46.0", + "resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.46.0.tgz", + "integrity": "sha512-ewPMN5JkKfgU5/kdco9ZhXBHDPhVqZpMQqIFQhwsHLf8kyZfx1cNpw1pHo1eV6PGEW7EhIBFi3aYZraFndAXqg==", "license": "Apache-2.0", + "dependencies": { + "google-auth-library": "^10.3.0", + "p-retry": "^4.6.2", + "protobufjs": "^7.5.4", + "ws": "^8.18.0" + }, "engines": { - "node": ">=14" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" + "node": ">=20.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "@modelcontextprotocol/sdk": "^1.25.2" + }, + "peerDependenciesMeta": { + "@modelcontextprotocol/sdk": { + "optional": true + } } }, - "node_modules/gtoken": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", - "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", - "license": "MIT", + "node_modules/@grpc/grpc-js": { + "version": "1.9.15", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.15.tgz", + "integrity": "sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ==", + "license": "Apache-2.0", "dependencies": { - "gaxios": "^6.0.0", - "jws": "^4.0.0" + "@grpc/proto-loader": "^0.7.8", + "@types/node": ">=12.12.47" }, "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^8.13.0 || >=10.10.0" } }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "license": "MIT", - "optional": true, + "node_modules/@grpc/proto-loader": { + "version": "0.7.15", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz", + "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==", + "license": "Apache-2.0", "dependencies": { - "has-symbols": "^1.0.3" + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" }, - "engines": { - "node": ">= 0.4" + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=6" } }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "license": "MIT", "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/html-entities": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz", - "integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/mdevils" - }, - { - "type": "patreon", - "url": "https://patreon.com/mdevils" - } - ], + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", "license": "MIT", - "optional": true + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } }, - "node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "license": "MIT", - "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "node": ">=6.0.0" } }, - "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==", + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "license": "MIT" }, - "node_modules/http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "license": "MIT", - "optional": true, "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "license": "MIT", + "optional": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "license": "BSD-3-Clause" + }, + "node_modules/@reduxjs/toolkit": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.11.2.tgz", + "integrity": "sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==", + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@standard-schema/utils": "^0.3.0", + "immer": "^11.0.0", + "redux": "^5.0.1", + "redux-thunk": "^3.1.0", + "reselect": "^5.1.0" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", + "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, + "node_modules/@reduxjs/toolkit/node_modules/immer": { + "version": "11.1.4", + "resolved": "https://registry.npmjs.org/immer/-/immer-11.1.4.tgz", + "integrity": "sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", + "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", + "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", + "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", + "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", + "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", + "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", + "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", + "cpu": [ + "arm" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", + "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", + "cpu": [ + "arm" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", + "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", + "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", + "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", + "cpu": [ + "loong64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", + "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", + "cpu": [ + "loong64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", + "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", + "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", + "cpu": [ + "ppc64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", + "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", + "cpu": [ + "riscv64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", + "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", + "cpu": [ + "riscv64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", + "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", + "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", + "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", + "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", + "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", + "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", + "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", + "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", + "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "license": "MIT" + }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" + }, + "node_modules/@tailwindcss/node": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.2.tgz", + "integrity": "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.19.0", + "jiti": "^2.6.1", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.2.2" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.2.tgz", + "integrity": "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==", + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.2.2", + "@tailwindcss/oxide-darwin-arm64": "4.2.2", + "@tailwindcss/oxide-darwin-x64": "4.2.2", + "@tailwindcss/oxide-freebsd-x64": "4.2.2", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", + "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", + "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", + "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", + "@tailwindcss/oxide-linux-x64-musl": "4.2.2", + "@tailwindcss/oxide-wasm32-wasi": "4.2.2", + "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", + "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.2.tgz", + "integrity": "sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.2.tgz", + "integrity": "sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.2.tgz", + "integrity": "sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.2.tgz", + "integrity": "sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.2.tgz", + "integrity": "sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.2.tgz", + "integrity": "sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.2.tgz", + "integrity": "sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.2.tgz", + "integrity": "sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.2.tgz", + "integrity": "sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.2.tgz", + "integrity": "sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.8.1", + "@emnapi/runtime": "^1.8.1", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.1.1", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.2.tgz", + "integrity": "sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.2.tgz", + "integrity": "sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.2.2.tgz", + "integrity": "sha512-mEiF5HO1QqCLXoNEfXVA1Tzo+cYsrqV7w9Juj2wdUFyW07JRenqMG225MvPwr3ZD9N1bFQj46X7r33iHxLUW0w==", + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.2.2", + "@tailwindcss/oxide": "4.2.2", + "tailwindcss": "4.2.2" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7 || ^8" + } + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/caseless": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz", + "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==", + "license": "MIT", + "optional": true + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", + "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" + }, + "node_modules/@types/express": { + "version": "4.17.25", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz", + "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "^1" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.8", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.8.tgz", + "integrity": "sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", + "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", + "license": "MIT", + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", + "license": "MIT", + "optional": true + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.19.15", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.15.tgz", + "integrity": "sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/qs": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/request": { + "version": "2.48.13", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.13.tgz", + "integrity": "sha512-FGJ6udDNUCjd19pp0Q3iTiDkwhYup7J8hpMW9c4k53NrccQFFWKRho6hvtPPEhnXWKvukfwAlB6DbDz4yhH5Gg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@types/caseless": "*", + "@types/node": "*", + "@types/tough-cookie": "*", + "form-data": "^2.5.5" + } + }, + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz", + "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "<1" + } + }, + "node_modules/@types/serve-static/node_modules/@types/send": { + "version": "0.17.6", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz", + "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "license": "MIT", + "optional": true + }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", + "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", + "license": "MIT" + }, + "node_modules/@vercel/blob": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@vercel/blob/-/blob-2.3.1.tgz", + "integrity": "sha512-6f9oWC+DbWxIgBLOdqjjn2/REpFrPDB7y5B5HA1ptYkzZaBgL6E34kWrptJvJ7teApJdbAs3I1a5A7z1y8SDHw==", + "license": "Apache-2.0", + "dependencies": { + "async-retry": "^1.3.3", + "is-buffer": "^2.0.5", + "is-node-process": "^1.2.0", + "throttleit": "^2.1.0", + "undici": "^6.23.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "optional": true, + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", + "license": "MIT" + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async-retry": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", + "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", + "license": "MIT", + "dependencies": { + "retry": "0.13.1" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT", + "optional": true + }, + "node_modules/autoprefixer": { + "version": "10.4.27", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.27.tgz", + "integrity": "sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.1", + "caniuse-lite": "^1.0.30001774", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.8", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.8.tgz", + "integrity": "sha512-PCLz/LXGBsNTErbtB6i5u4eLpHeMfi93aUv5duMmj6caNu6IphS4q6UevDnL36sZQv9lrP11dbPKGMaXPwMKfQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/body-parser": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001780", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001780.tgz", + "integrity": "sha512-llngX0E7nQci5BPJDqoZSbuZ5Bcs9F5db7EtgfwBerX9XGtkkiO4NwfDDIRzHTTwcYC8vC7bmeUEPGrKlR/TkQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "optional": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/concat-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/concat-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/concurrently": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz", + "integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "4.1.2", + "rxjs": "7.8.2", + "shell-quote": "1.8.3", + "supports-color": "8.1.1", + "tree-kill": "1.2.2", + "yargs": "17.7.2" + }, + "bin": { + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", + "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/duplexify": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", + "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", + "license": "MIT", + "optional": true, + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.2" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.321", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.321.tgz", + "integrity": "sha512-L2C7Q279W2D/J4PLZLk7sebOILDSWos7bMsMNN06rK482umHUrh/3lM8G7IlHFOYip2oAg5nha1rCMxr/rs6ZQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "optional": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", + "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "optional": true, + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-toolkit": { + "version": "1.45.1", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.45.1.tgz", + "integrity": "sha512-/jhoOj/Fx+A+IIyDNOvO3TItGmlMKhtX8ISAHKE90c4b/k1tqaqEZ+uUqfpU8DMnW5cgNJv606zS55jGvza0Xw==", + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks" + ] + }, + "node_modules/esbuild": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.4.tgz", + "integrity": "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==", + "devOptional": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.4", + "@esbuild/android-arm": "0.27.4", + "@esbuild/android-arm64": "0.27.4", + "@esbuild/android-x64": "0.27.4", + "@esbuild/darwin-arm64": "0.27.4", + "@esbuild/darwin-x64": "0.27.4", + "@esbuild/freebsd-arm64": "0.27.4", + "@esbuild/freebsd-x64": "0.27.4", + "@esbuild/linux-arm": "0.27.4", + "@esbuild/linux-arm64": "0.27.4", + "@esbuild/linux-ia32": "0.27.4", + "@esbuild/linux-loong64": "0.27.4", + "@esbuild/linux-mips64el": "0.27.4", + "@esbuild/linux-ppc64": "0.27.4", + "@esbuild/linux-riscv64": "0.27.4", + "@esbuild/linux-s390x": "0.27.4", + "@esbuild/linux-x64": "0.27.4", + "@esbuild/netbsd-arm64": "0.27.4", + "@esbuild/netbsd-x64": "0.27.4", + "@esbuild/openbsd-arm64": "0.27.4", + "@esbuild/openbsd-x64": "0.27.4", + "@esbuild/openharmony-arm64": "0.27.4", + "@esbuild/sunos-x64": "0.27.4", + "@esbuild/win32-arm64": "0.27.4", + "@esbuild/win32-ia32": "0.27.4", + "@esbuild/win32-x64": "0.27.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "license": "MIT" + }, + "node_modules/express": { + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", + "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.3", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.14.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/farmhash-modern": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/farmhash-modern/-/farmhash-modern-1.1.0.tgz", + "integrity": "sha512-6ypT4XfgqJk/F3Yuv4SX26I3doUjt0GTG4a+JgWxXQpxXzTBq8fPUeGHfcYMMDPHJHm3yPOSjaeBwBGAHWXCdA==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-xml-builder": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.4.tgz", + "integrity": "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "optional": true, + "dependencies": { + "path-expression-matcher": "^1.1.3" + } + }, + "node_modules/fast-xml-parser": { + "version": "5.5.6", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.5.6.tgz", + "integrity": "sha512-3+fdZyBRVg29n4rXP0joHthhcHdPUHaIC16cuyyd1iLsuaO6Vea36MPrxgAzbZna8lhvZeRL8Bc9GP56/J9xEw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "optional": true, + "dependencies": { + "fast-xml-builder": "^1.1.4", + "path-expression-matcher": "^1.1.3", + "strnum": "^2.1.2" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/faye-websocket": { + "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" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/firebase": { + "version": "12.10.0", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-12.10.0.tgz", + "integrity": "sha512-tAjHnEirksqWpa+NKDUSUMjulOnsTcsPC1X1rQ+gwPtjlhJS572na91CwaBXQJHXharIrfj7sw/okDkXOsphjA==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/ai": "2.9.0", + "@firebase/analytics": "0.10.20", + "@firebase/analytics-compat": "0.2.26", + "@firebase/app": "0.14.9", + "@firebase/app-check": "0.11.1", + "@firebase/app-check-compat": "0.4.1", + "@firebase/app-compat": "0.5.9", + "@firebase/app-types": "0.9.3", + "@firebase/auth": "1.12.1", + "@firebase/auth-compat": "0.6.3", + "@firebase/data-connect": "0.4.0", + "@firebase/database": "1.1.1", + "@firebase/database-compat": "2.1.1", + "@firebase/firestore": "4.12.0", + "@firebase/firestore-compat": "0.4.6", + "@firebase/functions": "0.13.2", + "@firebase/functions-compat": "0.4.2", + "@firebase/installations": "0.6.20", + "@firebase/installations-compat": "0.2.20", + "@firebase/messaging": "0.12.24", + "@firebase/messaging-compat": "0.2.24", + "@firebase/performance": "0.7.10", + "@firebase/performance-compat": "0.2.23", + "@firebase/remote-config": "0.8.1", + "@firebase/remote-config-compat": "0.2.22", + "@firebase/storage": "0.14.1", + "@firebase/storage-compat": "0.4.1", + "@firebase/util": "1.14.0" + } + }, + "node_modules/firebase-admin": { + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-13.7.0.tgz", + "integrity": "sha512-o3qS8zCJbApe7aKzkO2Pa380t9cHISqeSd3blqYTtOuUUUua3qZTLwNWgGUOss3td6wbzrZhiHIj3c8+fC046Q==", + "license": "Apache-2.0", + "dependencies": { + "@fastify/busboy": "^3.0.0", + "@firebase/database-compat": "^2.0.0", + "@firebase/database-types": "^1.0.6", + "farmhash-modern": "^1.1.0", + "fast-deep-equal": "^3.1.1", + "google-auth-library": "^10.6.1", + "jsonwebtoken": "^9.0.0", + "jwks-rsa": "^3.1.0", + "node-forge": "^1.3.1", + "uuid": "^11.0.2" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@google-cloud/firestore": "^7.11.0", + "@google-cloud/storage": "^7.19.0" + } + }, + "node_modules/firebase/node_modules/@firebase/auth": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.12.1.tgz", + "integrity": "sha512-nXKj7d5bMBlnq6XpcQQpmnSVwEeHBkoVbY/+Wk0P1ebLSICoH4XPtvKOFlXKfIHmcS84mLQ99fk3njlDGKSDtw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.1", + "@firebase/logger": "0.5.0", + "@firebase/util": "1.14.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@react-native-async-storage/async-storage": "^2.2.0" + }, + "peerDependenciesMeta": { + "@react-native-async-storage/async-storage": { + "optional": true + } + } + }, + "node_modules/form-data": { + "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==", + "license": "MIT", + "optional": true, + "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" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/framer-motion": { + "version": "12.38.0", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.38.0.tgz", + "integrity": "sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.38.0", + "motion-utils": "^12.36.0", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "license": "MIT", + "optional": true + }, + "node_modules/gaxios": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", + "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/gaxios/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/gcp-metadata": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz", + "integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==", + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^7.0.0", + "google-logging-utils": "^1.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/gcp-metadata/node_modules/gaxios": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.4.tgz", + "integrity": "sha512-bTIgTsM2bWn3XklZISBTQX7ZSddGW+IO3bMdGaemHZ3tbqExMENHLx6kKZ/KlejgrMtj8q7wBItt51yegqalrA==", + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "node-fetch": "^3.3.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/gcp-metadata/node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.6", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz", + "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/google-auth-library": { + "version": "10.6.2", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.6.2.tgz", + "integrity": "sha512-e27Z6EThmVNNvtYASwQxose/G57rkRuaRbQyxM2bvYLLX/GqWZ5chWq2EBoUchJbCc57eC9ArzO5wMsEmWftCw==", + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^7.1.4", + "gcp-metadata": "8.1.2", + "google-logging-utils": "1.1.3", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/google-auth-library/node_modules/gaxios": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.4.tgz", + "integrity": "sha512-bTIgTsM2bWn3XklZISBTQX7ZSddGW+IO3bMdGaemHZ3tbqExMENHLx6kKZ/KlejgrMtj8q7wBItt51yegqalrA==", + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "node-fetch": "^3.3.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/google-auth-library/node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/google-gax": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.6.1.tgz", + "integrity": "sha512-V6eky/xz2mcKfAd1Ioxyd6nmA61gao3n01C+YeuIwu3vzM9EDR6wcVzMSIbLMDXWeoi9SHYctXuKYC5uJUT3eQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@grpc/grpc-js": "^1.10.9", + "@grpc/proto-loader": "^0.7.13", + "@types/long": "^4.0.0", + "abort-controller": "^3.0.0", + "duplexify": "^4.0.0", + "google-auth-library": "^9.3.0", + "node-fetch": "^2.7.0", + "object-hash": "^3.0.0", + "proto3-json-serializer": "^2.0.2", + "protobufjs": "^7.3.2", + "retry-request": "^7.0.0", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-gax/node_modules/@grpc/grpc-js": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.3.tgz", + "integrity": "sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@grpc/proto-loader": "^0.8.0", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/google-gax/node_modules/@grpc/grpc-js/node_modules/@grpc/proto-loader": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.0.tgz", + "integrity": "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.5.3", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/google-gax/node_modules/gcp-metadata": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", + "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "gaxios": "^6.1.1", + "google-logging-utils": "^0.0.2", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-gax/node_modules/google-auth-library": { + "version": "9.15.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz", + "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^6.1.1", + "gcp-metadata": "^6.1.0", + "gtoken": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-gax/node_modules/google-logging-utils": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz", + "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-gax/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/google-logging-utils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz", + "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/gtoken": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", + "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", + "license": "MIT", + "optional": true, + "dependencies": { + "gaxios": "^6.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "optional": true, + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-entities": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz", + "integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ], + "license": "MIT", + "optional": true + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "license": "MIT", + "optional": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" } }, "node_modules/http-proxy-agent/node_modules/agent-base": { @@ -1484,1152 +4545,2643 @@ "license": "MIT", "optional": true, "dependencies": { - "debug": "4" + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==", + "license": "ISC" + }, + "node_modules/immer": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.2.0.tgz", + "integrity": "sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-node-process": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", + "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", + "license": "MIT" + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/jose": { + "version": "4.15.9", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", + "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", + "license": "MIT", + "dependencies": { + "jws": "^4.0.1", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jwks-rsa": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.2.2.tgz", + "integrity": "sha512-BqTyEDV+lS8F2trk3A+qJnxV5Q9EqKCBJOPti3W97r7qTympCZjb7h2X6f2kc+0K3rsSTY1/6YG2eaXKoj497w==", + "license": "MIT", + "dependencies": { + "@types/jsonwebtoken": "^9.0.4", + "debug": "^4.3.4", + "jose": "^4.15.4", + "limiter": "^1.1.5", + "lru-memoizer": "^2.2.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/limiter": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", + "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "license": "MIT" + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "license": "MIT" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lru-memoizer": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.3.0.tgz", + "integrity": "sha512-GXn7gyHAMhO13WSKrIiNfztwxodVsP8IoZ3XfrJV4yH2x0/OeTO/FIaAHTY5YekdGgW94njfuKmyyt1E0mR6Ug==", + "license": "MIT", + "dependencies": { + "lodash.clonedeep": "^4.5.0", + "lru-cache": "6.0.0" + } + }, + "node_modules/lru-memoizer/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lru-memoizer/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "node_modules/lucide-react": { + "version": "0.546.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.546.0.tgz", + "integrity": "sha512-Z94u6fKT43lKeYHiVyvyR8fT7pwCzDu7RyMPpTvh054+xahSgj4HFQ+NmflvzdXsoAjYGdCguGaFKYuvq0ThCQ==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "license": "MIT", + "optional": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/motion": { + "version": "12.38.0", + "resolved": "https://registry.npmjs.org/motion/-/motion-12.38.0.tgz", + "integrity": "sha512-uYfXzeHlgThchzwz5Te47dlv5JOUC7OB4rjJ/7XTUgtBZD8CchMN8qEJ4ZVsUmTyYA44zjV0fBwsiktRuFnn+w==", + "license": "MIT", + "dependencies": { + "framer-motion": "^12.38.0", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/motion-dom": { + "version": "12.38.0", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.38.0.tgz", + "integrity": "sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.36.0" + } + }, + "node_modules/motion-utils": { + "version": "12.36.0", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.36.0.tgz", + "integrity": "sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/multer": { + "version": "1.4.5-lts.2", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.2.tgz", + "integrity": "sha512-VzGiVigcG9zUAoCNU+xShztrlr1auZOlurXynNvO9GiWD1/mTBbUljOKY+qMeazBqXgRnjzeEgJI/wyjJUHg9A==", + "deprecated": "Multer 1.x is impacted by a number of vulnerabilities, which have been patched in 2.x. You should upgrade to the latest 2.x version.", + "license": "MIT", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.0.0", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.4", + "object-assign": "^4.1.1", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "optional": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-forge": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.3.tgz", + "integrity": "sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==", + "license": "(BSD-3-Clause OR GPL-2.0)", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-releases": { + "version": "2.0.36", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", + "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "optional": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "license": "MIT", + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-expression-matcher": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.1.3.tgz", + "integrity": "sha512-qdVgY8KXmVdJZRSS1JdEPOKPdTiEK/pi0RkcT2sw1RhXxohdujUlJFPuS1TSkevZ9vzd3ZlL7ULl1MHGTApKzQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { - "node": ">= 6.0.0" + "node": "^10 || ^12 || >=14" } }, - "node_modules/http-proxy-agent/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/proto3-json-serializer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz", + "integrity": "sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==", + "license": "Apache-2.0", "optional": true, "dependencies": { - "ms": "^2.1.3" + "protobufjs": "^7.2.5" }, "engines": { - "node": ">=6.0" + "node": ">=14.0.0" + } + }, + "node_modules/protobufjs": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", + "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "engines": { + "node": ">=12.0.0" } }, - "node_modules/http-proxy-agent/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==", + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "license": "MIT", - "optional": true + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "node_modules/qs": { + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", "license": "MIT", "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 0.8" } }, - "node_modules/https-proxy-agent/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", "dependencies": { - "ms": "^2.1.3" + "loose-envify": "^1.1.0" }, "engines": { - "node": ">=6.0" + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "peerDependencies": { + "react": "^18.3.1" } }, - "node_modules/https-proxy-agent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" + "node_modules/react-is": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.4.tgz", + "integrity": "sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA==", + "license": "MIT", + "peer": true }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "node_modules/react-redux": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", + "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", "license": "MIT", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "@types/use-sync-external-store": "^0.0.6", + "use-sync-external-store": "^1.4.0" }, + "peerDependencies": { + "@types/react": "^18.2.25 || ^19", + "react": "^18.0 || ^19", + "redux": "^5.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "node_modules/react-router": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.13.1.tgz", + "integrity": "sha512-td+xP4X2/6BJvZoX6xw++A2DdEi++YypA69bJUV5oVvqf6/9/9nNlD70YO1e9d3MyamJEBQFEzk6mbfDYbqrSA==", "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, "engines": { - "node": ">= 0.10" + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "node_modules/react-router-dom": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.13.1.tgz", + "integrity": "sha512-UJnV3Rxc5TgUPJt2KJpo1Jpy0OKQr0AjgbZzBFjaPJcFOb2Y8jA5H3LT8HUJAiRLlWrEXWHbF1Z4SCZaQjWDHw==", "license": "MIT", - "optional": true, + "dependencies": { + "react-router": "7.13.1" + }, "engines": { - "node": ">=8" + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" } }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "node_modules/react-router/node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", "license": "MIT", "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT" + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "optional": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } }, - "node_modules/jose": { - "version": "4.15.9", - "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", - "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "node_modules/recharts": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.8.0.tgz", + "integrity": "sha512-Z/m38DX3L73ExO4Tpc9/iZWHmHnlzWG4njQbxsF5aSjwqmHNDDIm0rdEBArkwsBvR8U6EirlEHiQNYWCVh9sGQ==", "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/panva" + "workspaces": [ + "www" + ], + "dependencies": { + "@reduxjs/toolkit": "^1.9.0 || 2.x.x", + "clsx": "^2.1.1", + "decimal.js-light": "^2.5.1", + "es-toolkit": "^1.39.3", + "eventemitter3": "^5.0.1", + "immer": "^10.1.1", + "react-redux": "8.x.x || 9.x.x", + "reselect": "5.1.1", + "tiny-invariant": "^1.3.3", + "use-sync-external-store": "^1.2.2", + "victory-vendor": "^37.0.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, - "node_modules/json-bigint": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", - "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "node_modules/redux": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", + "license": "MIT" + }, + "node_modules/redux-thunk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", + "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", "license": "MIT", - "dependencies": { - "bignumber.js": "^9.0.0" + "peerDependencies": { + "redux": "^5.0.0" } }, - "node_modules/jsonwebtoken": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", - "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "license": "MIT", - "dependencies": { - "jws": "^4.0.1", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^7.5.4" - }, "engines": { - "node": ">=12", - "npm": ">=6" + "node": ">=0.10.0" } }, - "node_modules/jsonwebtoken/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==", + "node_modules/reselect": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==", "license": "MIT" }, - "node_modules/jwa": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", - "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "devOptional": true, "license": "MIT", - "dependencies": { - "buffer-equal-constant-time": "^1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, - "node_modules/jwks-rsa": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.2.2.tgz", - "integrity": "sha512-BqTyEDV+lS8F2trk3A+qJnxV5Q9EqKCBJOPti3W97r7qTympCZjb7h2X6f2kc+0K3rsSTY1/6YG2eaXKoj497w==", + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/retry-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.2.tgz", + "integrity": "sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==", "license": "MIT", + "optional": true, "dependencies": { - "@types/jsonwebtoken": "^9.0.4", - "debug": "^4.3.4", - "jose": "^4.15.4", - "limiter": "^1.1.5", - "lru-memoizer": "^2.2.0" + "@types/request": "^2.48.8", + "extend": "^3.0.2", + "teeny-request": "^9.0.0" }, "engines": { "node": ">=14" } }, - "node_modules/jwks-rsa/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "node_modules/rollup": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", + "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", "license": "MIT", "dependencies": { - "ms": "^2.1.3" + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" }, "engines": { - "node": ">=6.0" + "node": ">=18.0.0", + "npm": ">=8.0.0" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.59.0", + "@rollup/rollup-android-arm64": "4.59.0", + "@rollup/rollup-darwin-arm64": "4.59.0", + "@rollup/rollup-darwin-x64": "4.59.0", + "@rollup/rollup-freebsd-arm64": "4.59.0", + "@rollup/rollup-freebsd-x64": "4.59.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", + "@rollup/rollup-linux-arm-musleabihf": "4.59.0", + "@rollup/rollup-linux-arm64-gnu": "4.59.0", + "@rollup/rollup-linux-arm64-musl": "4.59.0", + "@rollup/rollup-linux-loong64-gnu": "4.59.0", + "@rollup/rollup-linux-loong64-musl": "4.59.0", + "@rollup/rollup-linux-ppc64-gnu": "4.59.0", + "@rollup/rollup-linux-ppc64-musl": "4.59.0", + "@rollup/rollup-linux-riscv64-gnu": "4.59.0", + "@rollup/rollup-linux-riscv64-musl": "4.59.0", + "@rollup/rollup-linux-s390x-gnu": "4.59.0", + "@rollup/rollup-linux-x64-gnu": "4.59.0", + "@rollup/rollup-linux-x64-musl": "4.59.0", + "@rollup/rollup-openbsd-x64": "4.59.0", + "@rollup/rollup-openharmony-arm64": "4.59.0", + "@rollup/rollup-win32-arm64-msvc": "4.59.0", + "@rollup/rollup-win32-ia32-msvc": "4.59.0", + "@rollup/rollup-win32-x64-gnu": "4.59.0", + "@rollup/rollup-win32-x64-msvc": "4.59.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" } }, - "node_modules/jwks-rsa/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==", + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "license": "MIT" }, - "node_modules/jws": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", - "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", "license": "MIT", "dependencies": { - "jwa": "^2.0.1", - "safe-buffer": "^5.0.1" + "loose-envify": "^1.1.0" } }, - "node_modules/limiter": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", - "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } }, - "node_modules/lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", "license": "MIT", - "optional": true - }, - "node_modules/lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", - "license": "MIT" - }, - "node_modules/lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", - "license": "MIT" - }, - "node_modules/lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", - "license": "MIT" + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } }, - "node_modules/lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", - "license": "MIT" + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } }, - "node_modules/lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "license": "MIT" + "node_modules/send/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } }, - "node_modules/lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", - "license": "MIT" + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } }, - "node_modules/lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", "license": "MIT" }, - "node_modules/long": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", - "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", - "license": "Apache-2.0", - "optional": true + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/lru-memoizer": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.3.0.tgz", - "integrity": "sha512-GXn7gyHAMhO13WSKrIiNfztwxodVsP8IoZ3XfrJV4yH2x0/OeTO/FIaAHTY5YekdGgW94njfuKmyyt1E0mR6Ug==", + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "license": "MIT", "dependencies": { - "lodash.clonedeep": "^4.5.0", - "lru-cache": "6.0.0" + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, "engines": { - "node": ">= 0.6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "license": "MIT", + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", "engines": { - "node": ">= 0.6" + "node": ">=0.10.0" } }, - "node_modules/mime": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", - "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "license": "MIT", - "optional": true, - "bin": { - "mime": "cli.js" - }, "engines": { - "node": ">=10.0.0" + "node": ">= 0.8" } }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "node_modules/stream-events": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", + "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", "license": "MIT", + "optional": true, "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" + "stubs": "^3.0.0" } }, - "node_modules/mime-types/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "node_modules/stream-shift": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", + "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", "license": "MIT", + "optional": true + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", "engines": { - "node": ">= 0.6" + "node": ">=10.0.0" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "optional": true, + "dependencies": { + "safe-buffer": "~5.2.0" } }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", "dependencies": { - "minimist": "^1.2.6" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, - "bin": { - "mkdirp": "bin/cmd.js" + "engines": { + "node": ">=8" } }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/multer": { - "version": "1.4.5-lts.2", - "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.2.tgz", - "integrity": "sha512-VzGiVigcG9zUAoCNU+xShztrlr1auZOlurXynNvO9GiWD1/mTBbUljOKY+qMeazBqXgRnjzeEgJI/wyjJUHg9A==", - "deprecated": "Multer 1.x is impacted by a number of vulnerabilities, which have been patched in 2.x. You should upgrade to the latest 2.x version.", + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "dependencies": { - "append-field": "^1.0.0", - "busboy": "^1.0.0", - "concat-stream": "^1.5.2", - "mkdirp": "^0.5.4", - "object-assign": "^4.1.1", - "type-is": "^1.6.4", - "xtend": "^4.0.0" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">= 6.0.0" + "node": ">=8" } }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "node_modules/strnum": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.0.tgz", + "integrity": "sha512-Y7Bj8XyJxnPAORMZj/xltsfo55uOiyHcU2tnAVzHUnSJR/KsEX+9RoDeXEnsXtl/CX4fAcrt64gZ13aGaWPeBg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], "license": "MIT", - "engines": { - "node": ">= 0.6" - } + "optional": true }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "node_modules/stubs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", + "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==", + "license": "MIT", + "optional": true + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, "license": "MIT", "dependencies": { - "whatwg-url": "^5.0.0" + "has-flag": "^4.0.0" }, "engines": { - "node": "4.x || >=6.0.0" + "node": ">=10" }, - "peerDependencies": { - "encoding": "^0.1.0" + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/tailwind-merge": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.5.0.tgz", + "integrity": "sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.2.tgz", + "integrity": "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==", + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "license": "MIT", + "engines": { + "node": ">=6" }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/node-forge": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.3.tgz", - "integrity": "sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==", - "license": "(BSD-3-Clause OR GPL-2.0)", + "node_modules/teeny-request": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz", + "integrity": "sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.9", + "stream-events": "^1.0.5", + "uuid": "^9.0.0" + }, "engines": { - "node": ">= 6.13.0" + "node": ">=14" } }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "node_modules/teeny-request/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "license": "MIT", + "optional": true, + "dependencies": { + "debug": "4" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 6.0.0" } }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "node_modules/teeny-request/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "license": "MIT", "optional": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, "engines": { "node": ">= 6" } }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "node_modules/teeny-request/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" } }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "node_modules/throttleit": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-2.1.0.tgz", + "integrity": "sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==", "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, "engines": { - "node": ">= 0.8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "optional": true, - "dependencies": { - "wrappy": "1" - } + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "license": "MIT", - "optional": true, "dependencies": { - "yocto-queue": "^0.1.0" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { - "node": ">=10" + "node": ">=12.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "license": "MIT", "engines": { - "node": ">= 0.8" + "node": ">=0.6" } }, - "node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", - "license": "MIT" - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "license": "MIT" + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT", + "optional": true }, - "node_modules/proto3-json-serializer": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz", - "integrity": "sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "protobufjs": "^7.2.5" - }, - "engines": { - "node": ">=14.0.0" + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" } }, - "node_modules/protobufjs": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", - "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==", - "hasInstallScript": true, - "license": "BSD-3-Clause", - "optional": true, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "devOptional": true, + "license": "MIT", "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/node": ">=13.7.0", - "long": "^5.0.0" + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" }, "engines": { - "node": ">=12.0.0" + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" } }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "license": "MIT", "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" + "media-typer": "0.3.0", + "mime-types": "~2.1.24" }, "engines": { - "node": ">= 0.10" + "node": ">= 0.6" } }, - "node_modules/qs": { - "version": "6.14.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", - "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=14.17" } }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "node_modules/undici": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.24.1.tgz", + "integrity": "sha512-sC+b0tB1whOCzbtlx20fx3WgCXwkW627p4EA9uM+/tNNPkSS+eSEld6pAs9nDv7WbY1UUljBMYPtu9BCOrCWKA==", "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">=18.17" } }, - "node_modules/raw-body": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", - "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", - "unpipe": "~1.0.0" - }, "engines": { "node": ">= 0.8" } }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", - "optional": true, "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", "engines": { - "node": ">= 6" + "node": ">= 0.4.0" } }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.10.0" + "bin": { + "uuid": "dist/esm/bin/uuid" } }, - "node_modules/retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "license": "MIT", - "optional": true, "engines": { - "node": ">= 4" + "node": ">= 0.8" } }, - "node_modules/retry-request": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.2.tgz", - "integrity": "sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==", + "node_modules/victory-vendor": { + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.6.tgz", + "integrity": "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==", + "license": "MIT AND ISC", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, + "node_modules/vite": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", + "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", "license": "MIT", - "optional": true, "dependencies": { - "@types/request": "^2.48.8", - "extend": "^3.0.2", - "teeny-request": "^9.0.0" + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" }, "engines": { - "node": ">=14" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" + "jiti": { + "optional": true }, - { - "type": "consulting", - "url": "https://feross.org/support" + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true } - ], - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" } }, - "node_modules/send": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", - "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "~0.5.2", - "http-errors": "~2.0.1", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "~2.4.1", - "range-parser": "~1.2.1", - "statuses": "~2.0.2" - }, + "optional": true, + "os": [ + "aix" + ], "engines": { - "node": ">= 0.8.0" + "node": ">=18" } }, - "node_modules/send/node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], "license": "MIT", - "bin": { - "mime": "cli.js" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=4" + "node": ">=18" } }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/serve-static": { - "version": "1.16.3", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", - "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], "license": "MIT", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "~0.19.1" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">= 0.8.0" + "node": ">=18" } }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18" } }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18" } }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18" } }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18" } }, - "node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">= 0.8" + "node": ">=18" } }, - "node_modules/stream-events": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", - "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], "license": "MIT", "optional": true, - "dependencies": { - "stubs": "^3.0.0" + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/stream-shift": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", - "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", - "license": "MIT", - "optional": true - }, - "node_modules/streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=10.0.0" + "node": ">=18" } }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], "license": "MIT", "optional": true, - "dependencies": { - "safe-buffer": "~5.2.0" + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], "license": "MIT", "optional": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], "license": "MIT", "optional": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/strnum": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.2.tgz", - "integrity": "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" ], "license": "MIT", - "optional": true + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/stubs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", - "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==", + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], "license": "MIT", - "optional": true - }, - "node_modules/teeny-request": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz", - "integrity": "sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==", - "license": "Apache-2.0", "optional": true, - "dependencies": { - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "node-fetch": "^2.6.9", - "stream-events": "^1.0.5", - "uuid": "^9.0.0" - }, + "os": [ + "linux" + ], "engines": { - "node": ">=14" + "node": ">=18" } }, - "node_modules/teeny-request/node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], "license": "MIT", "optional": true, - "dependencies": { - "debug": "4" - }, + "os": [ + "linux" + ], "engines": { - "node": ">= 6.0.0" + "node": ">=18" } }, - "node_modules/teeny-request/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], "license": "MIT", "optional": true, - "dependencies": { - "ms": "^2.1.3" - }, + "os": [ + "linux" + ], "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=18" } }, - "node_modules/teeny-request/node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "node_modules/vite/node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], "license": "MIT", "optional": true, - "dependencies": { - "agent-base": "6", - "debug": "4" - }, + "os": [ + "netbsd" + ], "engines": { - "node": ">= 6" + "node": ">=18" } }, - "node_modules/teeny-request/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==", + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], "license": "MIT", - "optional": true + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/teeny-request/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" + "node_modules/vite/node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" ], "license": "MIT", "optional": true, - "bin": { - "uuid": "dist/bin/uuid" + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" } }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": ">=0.6" + "node": ">=18" } }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "node_modules/vite/node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, + "optional": true, + "os": [ + "openharmony" + ], "engines": { - "node": ">= 0.6" + "node": ">=18" } }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", - "license": "MIT" - }, - "node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "license": "MIT" - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], "engines": { - "node": ">= 0.8" + "node": ">=18" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">= 0.4.0" + "node": ">=18" } }, - "node_modules/uuid": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" ], "license": "MIT", - "bin": { - "uuid": "dist/esm/bin/uuid" + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" } }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">= 0.8" + "node": ">=18" } }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 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==", + "license": "Apache-2.0" + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" + "license": "BSD-2-Clause", + "optional": true }, "node_modules/websocket-driver": { "version": "0.7.4", @@ -2659,6 +7211,7 @@ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "license": "MIT", + "optional": true, "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -2669,7 +7222,6 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "license": "MIT", - "optional": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -2689,6 +7241,27 @@ "license": "ISC", "optional": true }, + "node_modules/ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -2703,15 +7276,15 @@ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "license": "ISC", - "optional": true, "engines": { "node": ">=10" } }, "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, "license": "ISC" }, "node_modules/yargs": { @@ -2719,7 +7292,6 @@ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "license": "MIT", - "optional": true, "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -2738,7 +7310,6 @@ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "license": "ISC", - "optional": true, "engines": { "node": ">=12" } diff --git a/package.json b/package.json index 4a3dddb..051710b 100644 --- a/package.json +++ b/package.json @@ -5,13 +5,43 @@ "main": "server.js", "scripts": { "start": "node server.js", - "dev": "node server.js" + "dev": "concurrently -k -n client,server -c blue,green \"npm run dev:client\" \"npm run dev:server\"", + "dev:client": "vite --port 8000", + "dev:server": "node server.js", + "build": "vite build", + "preview": "vite preview", + "clean": "rm -rf dist", + "lint": "tsc --noEmit", + "test": "node scripts/smoke-test.mjs" }, "dependencies": { + "@google/genai": "^1.29.0", + "@tailwindcss/vite": "^4.2.2", + "@vercel/blob": "^2.3.1", + "clsx": "^2.1.1", "cors": "^2.8.5", "dotenv": "^16.6.1", "express": "^4.18.2", - "firebase-admin": "^13.6.1", - "multer": "^1.4.5-lts.1" + "firebase": "^12.10.0", + "firebase-admin": "^13.7.0", + "lucide-react": "^0.546.0", + "motion": "^12.23.24", + "multer": "^1.4.5-lts.1", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-router-dom": "^7.13.1", + "recharts": "^3.8.0", + "tailwind-merge": "^3.5.0" + }, + "devDependencies": { + "@types/express": "^4.17.21", + "@types/node": "^22.14.0", + "@vitejs/plugin-react": "^4.7.0", + "autoprefixer": "^10.4.21", + "concurrently": "^9.2.1", + "tailwindcss": "^4.2.2", + "tsx": "^4.21.0", + "typescript": "~5.8.2", + "vite": "^6.4.1" } } diff --git a/public/index.html b/public/index.html index 738988d..d2dfc49 100644 --- a/public/index.html +++ b/public/index.html @@ -1,98 +1,22 @@ - + - + + + + Dee Portfolio + - - - Welcome to Firebase Hosting - - - - - - - - - - - - - - - - - -
-

Welcome

-

Firebase Hosting Setup Complete

-

You're seeing this because you've successfully setup Firebase Hosting. Now it's time to go build something extraordinary!

- Open Hosting Documentation -
-

Firebase SDK Loading…

- - +
+ + diff --git a/script.js b/script.js deleted file mode 100644 index ea3169f..0000000 --- a/script.js +++ /dev/null @@ -1,740 +0,0 @@ -// Navigation functionality -const navbar = document.getElementById('navbar'); -const hamburger = document.getElementById('hamburger'); -const navMenu = document.getElementById('nav-menu'); -const navLinks = document.querySelectorAll('.nav-link'); - -// Scroll effect for navbar -window.addEventListener('scroll', () => { - if (window.scrollY > 50) { - navbar.classList.add('scrolled'); - } else { - navbar.classList.remove('scrolled'); - } -}); - -// Mobile menu toggle -hamburger.addEventListener('click', () => { - navMenu.classList.toggle('active'); - hamburger.classList.toggle('active'); -}); - -// Close mobile menu when clicking on a link -navLinks.forEach(link => { - link.addEventListener('click', () => { - navMenu.classList.remove('active'); - hamburger.classList.remove('active'); - }); -}); - -// Smooth scroll for navigation links -navLinks.forEach(link => { - link.addEventListener('click', (e) => { - const targetId = link.getAttribute('href'); - if (!targetId || !targetId.startsWith('#')) { - return; - } - - e.preventDefault(); - const targetSection = document.querySelector(targetId); - - if (targetSection) { - const offsetTop = targetSection.offsetTop - 70; - window.scrollTo({ - top: offsetTop, - behavior: 'smooth' - }); - } - }); -}); - -// Active navigation link highlighting -const sections = document.querySelectorAll('section[id]'); - -function highlightActiveSection() { - const scrollY = window.pageYOffset; - - sections.forEach(section => { - const sectionHeight = section.offsetHeight; - const sectionTop = section.offsetTop - 100; - const sectionId = section.getAttribute('id'); - - if (scrollY > sectionTop && scrollY <= sectionTop + sectionHeight) { - navLinks.forEach(link => { - link.classList.remove('active'); - if (link.getAttribute('href') === `#${sectionId}`) { - link.classList.add('active'); - } - }); - } - }); -} - -window.addEventListener('scroll', highlightActiveSection); - -// Intersection Observer for fade-in animations -const observerOptions = { - threshold: 0.1, - rootMargin: '0px 0px -50px 0px' -}; - -const observer = new IntersectionObserver((entries) => { - entries.forEach(entry => { - if (entry.isIntersecting) { - entry.target.style.opacity = '1'; - entry.target.style.transform = 'translateY(0)'; - } - }); -}, observerOptions); - -// Observe elements for animation -const animateElements = document.querySelectorAll('.project-card:not(.blog-card), .skill-category, .stat-item'); -animateElements.forEach(el => { - el.style.opacity = '0'; - el.style.transform = 'translateY(30px)'; - el.style.transition = 'opacity 0.6s ease, transform 0.6s ease'; - observer.observe(el); -}); - -// Skill bar animation -const skillBars = document.querySelectorAll('.skill-progress'); -const skillObserver = new IntersectionObserver((entries) => { - entries.forEach(entry => { - if (entry.isIntersecting) { - const width = entry.target.style.width; - entry.target.style.width = '0%'; - setTimeout(() => { - entry.target.style.width = width; - }, 100); - } - }); -}, { threshold: 0.5 }); - -skillBars.forEach(bar => { - skillObserver.observe(bar); -}); - -// Contact form handling -const contactForm = document.getElementById('contact-form'); - -contactForm.addEventListener('submit', (e) => { - e.preventDefault(); - - // Get form values - const name = document.getElementById('name').value; - const email = document.getElementById('email').value; - const subject = document.getElementById('subject').value; - const message = document.getElementById('message').value; - - // Here you would typically send the data to a server - // For now, we'll just show an alert - alert(`Thank you for your message, ${name}! I'll get back to you soon.`); - - // Reset form - contactForm.reset(); -}); - -// Add smooth fade-in on scroll for hero description -window.addEventListener('load', () => { - const heroDescription = document.querySelector('.hero-description'); - if (heroDescription) { - setTimeout(() => { - heroDescription.style.opacity = '1'; - heroDescription.style.transform = 'translateY(0)'; - }, 300); - } -}); - -// Code typing animation -const codeLines = [ - 'const developer = {', - ' name: \'Dee\',', - ' role: \'AI Product & Systems Engineer\',', - ' skills: [\'JavaScript\', \'React\', \'Python\', \'Docker\', \'Node.js\'],', - ' build() {', - ' return \'Amazing Products\';', - ' }', - '};', - '// Passionate about creating elegant solutions' -]; - -let currentLine = 0; -let currentChar = 0; -let isDeleting = false; -let animationStarted = false; - -function typeCode() { - const codeContainer = document.querySelector('.code-animation-container'); - if (!codeContainer) return; - - // Check if code animation is visible - const codeObserver = new IntersectionObserver((entries) => { - entries.forEach(entry => { - if (entry.isIntersecting && !animationStarted) { - animationStarted = true; - startTyping(); - } - }); - }, { threshold: 0.3 }); - - codeObserver.observe(codeContainer); -} - -function startTyping() { - const lineElements = [ - document.getElementById('code-line-1'), - document.getElementById('code-line-2'), - document.getElementById('code-line-3'), - document.getElementById('code-line-4'), - document.getElementById('code-line-5'), - document.getElementById('code-line-6'), - document.getElementById('code-line-7'), - document.getElementById('code-line-8'), - document.getElementById('code-line-9') - ]; - - function type() { - if (currentLine >= codeLines.length) { - // Animation complete, restart after a pause - setTimeout(() => { - currentLine = 0; - currentChar = 0; - lineElements.forEach(el => { - if (el) el.innerHTML = ''; - }); - startTyping(); - }, 5000); - return; - } - - const currentLineElement = lineElements[currentLine]; - if (!currentLineElement) { - currentLine++; - setTimeout(type, 100); - return; - } - - const targetText = codeLines[currentLine]; - const displayText = targetText.substring(0, currentChar); - - // Update current line with text and cursor - currentLineElement.innerHTML = displayText + ''; - - if (currentChar < targetText.length) { - currentChar++; - setTimeout(type, 30 + Math.random() * 40); // Vary typing speed - } else { - // Line complete - remove cursor before moving to next line - currentLineElement.innerHTML = displayText; - currentLine++; - currentChar = 0; - setTimeout(type, 200); // Pause between lines - } - } - - // Start typing after a short delay - setTimeout(type, 500); -} - -// Initialize code animation when page loads -document.addEventListener('DOMContentLoaded', () => { - typeCode(); - initHeroCanvas(); - initProjectsSlider(); - initChatbot(); -}); - -// Canvas Background Animation -function initHeroCanvas() { - const canvas = document.getElementById('hero-canvas'); - if (!canvas) return; - - const ctx = canvas.getContext('2d'); - let time = 0; - - function resizeCanvas() { - canvas.width = window.innerWidth; - canvas.height = window.innerHeight; - } - - resizeCanvas(); - window.addEventListener('resize', resizeCanvas); - - const config = { - particleCount: 400, - particleMinSize: 0.5, - particleMaxSize: 2, - breathingCycleDuration: 30000, - }; - - class Particle { - constructor() { - this.x = Math.random() * canvas.width; - this.y = Math.random() * canvas.height; - this.depth = Math.random(); - - this.size = config.particleMinSize + this.depth * (config.particleMaxSize - config.particleMinSize); - this.vx = (Math.random() - 0.5) * 0.04; - this.vy = (Math.random() - 0.5) * 0.04; - - this.driftPhase = Math.random() * Math.PI * 2; - this.driftSpeed = 0.001 + Math.random() * 0.001; - this.baseBrightness = 0.3 + this.depth * 0.5; - } - - update(breathingIntensity, globalTime) { - this.driftPhase += this.driftSpeed; - this.x += this.vx + Math.sin(this.driftPhase) * 0.03; - this.y += this.vy + Math.cos(this.driftPhase * 0.7) * 0.03; - - const twinkle = Math.sin(globalTime * 0.003 + this.driftPhase) * 0.3; - this.currentBrightness = (this.baseBrightness + twinkle) * (0.7 + breathingIntensity * 0.3); - - if (this.x < -20) this.x = canvas.width + 20; - if (this.x > canvas.width + 20) this.x = -20; - if (this.y < -20) this.y = canvas.height + 20; - if (this.y > canvas.height + 20) this.y = -20; - } - - draw(ctx) { - ctx.save(); - - const glow = ctx.createRadialGradient( - this.x, this.y, 0, - this.x, this.y, this.size * 5 - ); - glow.addColorStop(0, `rgba(96, 165, 250, ${this.currentBrightness * 0.4})`); - glow.addColorStop(1, 'rgba(96, 165, 250, 0)'); - ctx.fillStyle = glow; - ctx.beginPath(); - ctx.arc(this.x, this.y, this.size * 5, 0, Math.PI * 2); - ctx.fill(); - - ctx.fillStyle = `rgba(200, 230, 255, ${Math.min(this.currentBrightness, 1)})`; - ctx.beginPath(); - ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); - ctx.fill(); - - ctx.restore(); - } - } - - const particles = []; - for (let i = 0; i < config.particleCount; i++) { - particles.push(new Particle()); - } - - function animate() { - time++; - - const breathingIntensity = Math.sin(time / config.breathingCycleDuration * Math.PI * 2) * 0.5 + 0.5; - - ctx.fillStyle = '#0a1423'; - ctx.fillRect(0, 0, canvas.width, canvas.height); - - particles.forEach(particle => { - particle.update(breathingIntensity, time); - particle.draw(ctx); - }); - - requestAnimationFrame(animate); - } - - animate(); - - // Handle video background - const video = document.getElementById('hero-video'); - if (video) { - video.addEventListener('loadeddata', () => { - video.classList.add('loaded'); - canvas.style.opacity = '0.5'; - }); - - video.addEventListener('error', () => { - console.log('Video failed to load, using canvas animation only'); - canvas.style.opacity = '1'; - }); - } -} - -// Projects Slider Functionality - Accessible looping carousel -function initProjectsSlider() { - const slider = document.querySelector('.projects-slider'); - if (!slider) return; - - const track = slider.querySelector('.projects-track'); - const viewport = slider.querySelector('.projects-viewport'); - const prevBtn = slider.querySelector('[data-direction="prev"]'); - const nextBtn = slider.querySelector('[data-direction="next"]'); - - if (!track || !viewport || !prevBtn || !nextBtn) return; - - let slidesPerView = getSlidesPerView(); - let index = slidesPerView; - let autoPlayTimer = null; - let resumeTimer = null; - let isTransitioning = false; - - const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; - - function getSlidesPerView() { - return window.matchMedia('(min-width: 769px)').matches ? 2 : 1; - } - - function getSlideSize() { - const slides = track.querySelectorAll('.project-card'); - if (!slides.length) return 0; - if (slides.length === 1) { - return slides[0].getBoundingClientRect().width; - } - return slides[1].offsetLeft - slides[0].offsetLeft; - } - - function setTransition(enabled) { - track.style.transition = enabled ? 'transform 0.6s ease' : 'none'; - } - - function resetPosition() { - setTransition(false); - track.style.transform = 'translateX(0px)'; - } - - function moveNext() { - if (isTransitioning) return; - const slideSize = getSlideSize(); - if (!slideSize) return; - - isTransitioning = true; - setTransition(true); - track.style.transform = `translateX(${-slideSize}px)`; - - track.addEventListener('transitionend', () => { - track.appendChild(track.firstElementChild); - resetPosition(); - isTransitioning = false; - }, { once: true }); - } - - function movePrev() { - if (isTransitioning) return; - const slideSize = getSlideSize(); - if (!slideSize) return; - - isTransitioning = true; - setTransition(false); - track.insertBefore(track.lastElementChild, track.firstElementChild); - track.style.transform = `translateX(${-slideSize}px)`; - - requestAnimationFrame(() => { - setTransition(true); - track.style.transform = 'translateX(0px)'; - track.addEventListener('transitionend', () => { - resetPosition(); - isTransitioning = false; - }, { once: true }); - }); - } - - function stopAutoPlay() { - if (autoPlayTimer) { - clearInterval(autoPlayTimer); - autoPlayTimer = null; - } - } - - function startAutoPlay() { - if (prefersReducedMotion) return; - stopAutoPlay(); - autoPlayTimer = setInterval(() => { - moveNext(); - }, 5000); - } - - function pauseAutoPlay() { - stopAutoPlay(); - if (resumeTimer) { - clearTimeout(resumeTimer); - } - } - - function resumeAutoPlay(delay = 5000) { - if (prefersReducedMotion) return; - if (resumeTimer) { - clearTimeout(resumeTimer); - } - resumeTimer = setTimeout(() => { - startAutoPlay(); - }, delay); - } - - function setupSlider() { - slidesPerView = getSlidesPerView(); - resetPosition(); - } - - prevBtn.addEventListener('click', () => { - pauseAutoPlay(); - movePrev(); - resumeAutoPlay(5000); - }); - - nextBtn.addEventListener('click', () => { - pauseAutoPlay(); - moveNext(); - resumeAutoPlay(5000); - }); - - slider.addEventListener('mouseenter', pauseAutoPlay); - slider.addEventListener('mouseleave', () => resumeAutoPlay(5000)); - slider.addEventListener('focusin', pauseAutoPlay); - slider.addEventListener('focusout', () => resumeAutoPlay(5000)); - - let resizeTimer; - window.addEventListener('resize', () => { - clearTimeout(resizeTimer); - resizeTimer = setTimeout(() => { - const updatedSlidesPerView = getSlidesPerView(); - if (updatedSlidesPerView !== slidesPerView) { - setupSlider(); - } else { - setPosition(false); - } - }, 200); - }); - - setupSlider(); - startAutoPlay(); -} - - -// Chatbot functionality -function initChatbot() { - const chatbotContainer = document.getElementById('chatbot-container'); - const chatbotToggle = document.getElementById('chatbot-toggle'); - const chatbotWindow = document.getElementById('chatbot-window'); - const chatbotClose = document.getElementById('chatbot-close'); - const chatbotInput = document.getElementById('chatbot-input'); - const chatbotSend = document.getElementById('chatbot-send'); - const chatbotMessages = document.getElementById('chatbot-messages'); - - if (!chatbotContainer || !chatbotToggle || !chatbotWindow) return; - - // Toggle chatbot - chatbotToggle.addEventListener('click', () => { - chatbotContainer.classList.toggle('active'); - if (chatbotContainer.classList.contains('active')) { - chatbotInput.focus(); - } - }); - - chatbotClose.addEventListener('click', () => { - chatbotContainer.classList.remove('active'); - }); - - // Send message function - function sendMessage() { - const message = chatbotInput.value.trim(); - if (!message) return; - - // Add user message - addMessage(message, 'user'); - chatbotInput.value = ''; - - // Simulate thinking delay - setTimeout(() => { - const response = getResponse(message); - addMessage(response.text, 'bot', response.action); - }, 500); - } - - // Send on button click - chatbotSend.addEventListener('click', sendMessage); - - // Send on Enter key - chatbotInput.addEventListener('keypress', (e) => { - if (e.key === 'Enter') { - sendMessage(); - } - }); - - // Add message to chat - function addMessage(text, type, action = null) { - const messageDiv = document.createElement('div'); - messageDiv.className = `chatbot-message chatbot-message-${type}`; - - const contentDiv = document.createElement('div'); - contentDiv.className = 'message-content'; - - if (typeof text === 'string') { - // Handle multiline text with proper formatting - const lines = text.split('\n'); - let currentParagraph = null; - let listContainer = null; - - lines.forEach((line, index) => { - const trimmedLine = line.trim(); - - if (trimmedLine === '') { - // Empty line - close current paragraph/list if exists - if (currentParagraph) { - contentDiv.appendChild(currentParagraph); - currentParagraph = null; - } - if (listContainer && listContainer.children.length > 0) { - contentDiv.appendChild(listContainer); - listContainer = null; - } - } else if (trimmedLine.startsWith('β€’')) { - // Bullet point - add to list (CSS handles the bullet via ::before) - if (!listContainer) { - listContainer = document.createElement('ul'); - } - const li = document.createElement('li'); - li.textContent = trimmedLine.replace('β€’', '').trim(); - listContainer.appendChild(li); - } else { - // Regular text line - if (listContainer && listContainer.children.length > 0) { - contentDiv.appendChild(listContainer); - listContainer = null; - } - if (!currentParagraph) { - currentParagraph = document.createElement('p'); - currentParagraph.style.margin = '0'; - } - if (currentParagraph.textContent) { - currentParagraph.innerHTML += '
' + trimmedLine; - } else { - currentParagraph.textContent = trimmedLine; - } - } - }); - - // Append any remaining elements - if (currentParagraph) { - contentDiv.appendChild(currentParagraph); - } - if (listContainer && listContainer.children.length > 0) { - contentDiv.appendChild(listContainer); - } - } else { - contentDiv.appendChild(text); - } - - if (action) { - const button = document.createElement('button'); - button.className = 'chatbot-action-button'; - button.textContent = action.label; - button.onclick = () => { - if (action.type === 'scroll') { - const section = document.querySelector(action.target); - if (section) { - const offsetTop = section.offsetTop - 70; - window.scrollTo({ - top: offsetTop, - behavior: 'smooth' - }); - chatbotContainer.classList.remove('active'); - } - } else if (action.type === 'link') { - window.open(action.target, '_blank'); - } - }; - contentDiv.appendChild(button); - } - - messageDiv.appendChild(contentDiv); - chatbotMessages.appendChild(messageDiv); - chatbotMessages.scrollTop = chatbotMessages.scrollHeight; - } - - // Get response based on user input - function getResponse(input) { - const lowerInput = input.toLowerCase(); - - // Skills - if (lowerInput.includes('skill') || lowerInput.includes('technology') || lowerInput.includes('tech stack')) { - return { - text: 'I specialize in:\n\nβ€’ Frontend: React, Vue.js, JavaScript, TypeScript, HTML/CSS\nβ€’ Backend: Python, Node.js, FastAPI, Django\nβ€’ Databases: MongoDB, MySQL, PostgreSQL\nβ€’ AI/ML: LLM, Machine Learning, AI Integration\nβ€’ Tools: Git, Docker, AWS\n\nWould you like to see more details?', - action: { - type: 'scroll', - target: '#skills', - label: 'View Skills Section' - } - }; - } - - // Contact - if (lowerInput.includes('contact') || lowerInput.includes('reach') || lowerInput.includes('email') || - lowerInput.includes('linkedin') || lowerInput.includes('github') || lowerInput.includes('telegram') || - lowerInput.includes('whatsapp')) { - return { - text: 'You can reach me through:\n\nβ€’ Email: Click the email button below\nβ€’ LinkedIn: Professional networking\nβ€’ GitHub: Check out my code\nβ€’ Telegram: @dee_aanalyst\nβ€’ WhatsApp: Direct messaging\n\nI\'d love to hear from you!', - action: { - type: 'scroll', - target: '#contact', - label: 'Go to Contact Section' - } - }; - } - - // Projects - if (lowerInput.includes('project') || lowerInput.includes('work') || lowerInput.includes('portfolio') || - lowerInput.includes('build') || lowerInput.includes('created')) { - return { - text: 'I have 6 featured projects:\n\nβ€’ E-Commerce Platform (React, Node.js, MongoDB)\nβ€’ Task Management App (Vue.js, Firebase)\nβ€’ Weather Dashboard (JavaScript, API)\nβ€’ AI Chat Application (Python, LLM)\nβ€’ Data Analytics Platform (Python, Django)\nβ€’ Mobile-First Web App (React, TypeScript)\n\nCheck them out below!', - action: { - type: 'scroll', - target: '#projects', - label: 'View Projects' - } - }; - } - - // About - if (lowerInput.includes('about') || lowerInput.includes('who') || lowerInput.includes('background') || - lowerInput.includes('experience')) { - return { - text: 'I\'m Agoma Divine E., an LLM Engineer & Full Stack Developer. I build intelligent systems with AI, machine learning, and cutting-edge technologies. With 3+ years of experience and 50+ completed projects, I\'m passionate about creating elegant solutions.', - action: { - type: 'scroll', - target: '#about', - label: 'Learn More About Me' - } - }; - } - - // Resume - if (lowerInput.includes('resume') || lowerInput.includes('cv') || lowerInput.includes('download')) { - return { - text: 'You can download my resume from the About Me section. It contains all my experience, skills, and achievements.', - action: { - type: 'scroll', - target: '#about', - label: 'Download Resume' - } - }; - } - - // Greeting - if (lowerInput.includes('hi') || lowerInput.includes('hello') || lowerInput.includes('hey') || - lowerInput.match(/^(hi|hello|hey)$/)) { - return { - text: 'Hello! I\'m here to help you learn more about Dee. You can ask me about:\n\nβ€’ Skills and technologies\nβ€’ Projects\nβ€’ How to contact\nβ€’ Experience and background' - }; - } - - // Help - if (lowerInput.includes('help') || lowerInput.includes('what can you')) { - return { - text: 'I can help you with:\n\nβ€’ Information about Dee\'s skills and tech stack\nβ€’ Details about featured projects\nβ€’ Contact information and social links\nβ€’ Background and experience\nβ€’ Resume download\n\nJust ask me anything!' - }; - } - - // Default response - return { - text: 'I\'m not sure I understand that. Try asking about:\n\nβ€’ Skills\nβ€’ Projects\nβ€’ Contact information\nβ€’ Experience\n\nOr type "help" for more options.' - }; - } -} - diff --git a/scripts/smoke-test.mjs b/scripts/smoke-test.mjs new file mode 100644 index 0000000..100859b --- /dev/null +++ b/scripts/smoke-test.mjs @@ -0,0 +1,94 @@ +import { spawn } from 'node:child_process'; + +const port = process.env.TEST_PORT || '4101'; +const baseUrl = `http://127.0.0.1:${port}`; +const startupTimeoutMs = 15000; +const pollIntervalMs = 500; + +function sleep(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +async function fetchText(pathname) { + const response = await fetch(`${baseUrl}${pathname}`); + const text = await response.text(); + return { response, text }; +} + +async function waitForServer() { + const deadline = Date.now() + startupTimeoutMs; + + while (Date.now() < deadline) { + try { + const { response, text } = await fetchText('/health'); + if (response.ok && text.includes('"status":"ok"')) { + return; + } + } catch { + // Keep polling until timeout while the child process boots. + } + + await sleep(pollIntervalMs); + } + + throw new Error(`Server did not become ready at ${baseUrl} within ${startupTimeoutMs}ms.`); +} + +async function runChecks() { + const health = await fetchText('/health'); + if (!health.response.ok || !health.text.includes('"status":"ok"')) { + throw new Error(`Health check failed with status ${health.response.status}: ${health.text}`); + } + + const firebaseConfig = await fetchText('/firebase-config.js'); + if (!firebaseConfig.response.ok || !firebaseConfig.text.includes('window.FIREBASE_CONFIG')) { + throw new Error(`Firebase config endpoint failed with status ${firebaseConfig.response.status}.`); + } + + const adminPage = await fetchText('/admin'); + const looksLikeHtml = adminPage.text.toLowerCase().includes(' { + stdout += chunk.toString(); + }); + + child.stderr.on('data', (chunk) => { + stderr += chunk.toString(); + }); + + try { + await waitForServer(); + await runChecks(); + console.log(`Smoke test passed against ${baseUrl}`); + } catch (error) { + console.error(error.message); + if (stdout.trim()) { + console.error('\nServer stdout:\n' + stdout.trim()); + } + if (stderr.trim()) { + console.error('\nServer stderr:\n' + stderr.trim()); + } + process.exitCode = 1; + } finally { + child.kill('SIGTERM'); + await new Promise((resolve) => child.once('exit', resolve)); + } +} + +main(); \ No newline at end of file diff --git a/server.js b/server.js index 0b885b6..fd472db 100644 --- a/server.js +++ b/server.js @@ -2,28 +2,819 @@ const express = require('express'); const cors = require('cors'); const multer = require('multer'); const admin = require('firebase-admin'); +const dns = require('node:dns'); +const { put } = require('@vercel/blob'); const path = require('path'); const fs = require('fs'); -require('dotenv').config(); +const crypto = require('crypto'); +const dotenvResult = require('dotenv').config(); + +// Prefer IPv4 for outbound API calls. On some networks, IPv6 to Gemini times out. +dns.setDefaultResultOrder('ipv4first'); + +// Keep normal env precedence for most keys (e.g., PORT), but force local Gemini key if present. +if (dotenvResult?.parsed?.GEMINI_API_KEY) { + process.env.GEMINI_API_KEY = dotenvResult.parsed.GEMINI_API_KEY; +} const app = express(); const upload = multer({ storage: multer.memoryStorage() }); -const storageBucket = process.env.FIREBASE_STORAGE_BUCKET || 'dee-s-site.firebasestorage.app'; + +const FIREBASE_SECRET_KEYS = ['apiKey', 'authDomain', 'databaseURL', 'projectId', 'storageBucket', 'messagingSenderId', 'appId', 'measurementId']; +const FIREBASE_ENV_KEY_MAP = { + apiKey: ['VITE_FIREBASE_API_KEY', 'FIREBASE_WEB_API_KEY'], + authDomain: ['VITE_FIREBASE_AUTH_DOMAIN', 'FIREBASE_WEB_AUTH_DOMAIN'], + databaseURL: ['VITE_FIREBASE_DATABASE_URL', 'FIREBASE_WEB_DATABASE_URL'], + projectId: ['VITE_FIREBASE_PROJECT_ID', 'FIREBASE_WEB_PROJECT_ID'], + storageBucket: ['VITE_FIREBASE_STORAGE_BUCKET', 'FIREBASE_WEB_STORAGE_BUCKET'], + messagingSenderId: ['VITE_FIREBASE_MESSAGING_SENDER_ID', 'FIREBASE_WEB_MESSAGING_SENDER_ID'], + appId: ['VITE_FIREBASE_APP_ID', 'FIREBASE_WEB_APP_ID'], + measurementId: ['VITE_FIREBASE_MEASUREMENT_ID', 'FIREBASE_WEB_MEASUREMENT_ID'] +}; + +const runtimeEnvPath = path.resolve(__dirname, '.env'); +const secretsStorePath = path.resolve(__dirname, '.admin-secrets-store.json'); +const legacySecretsStorePath = path.resolve(__dirname, '..', '.admin-secrets-store.json'); +const chatbotRootPath = path.resolve(__dirname, 'chatbot'); +const chatbotPromptsPath = path.join(chatbotRootPath, 'prompts'); +const chatbotKnowledgePath = path.join(chatbotRootPath, 'knowledge', 'portfolio-profile.json'); +const chatbotSessions = new Map(); + +const CHATBOT_STAGE_TRACE = ['thinking', 'searching', 'drafting']; +const CHATBOT_RECENT_TURN_LIMIT = 10; + +function getRuntimeConfigValue(configKey) { + const envKeys = FIREBASE_ENV_KEY_MAP[configKey] || []; + for (const envKey of envKeys) { + const value = process.env[envKey]; + if (typeof value === 'string' && value.trim()) { + return value.trim(); + } + } + return ''; +} + +function firstNonEmptyString(...values) { + for (const value of values) { + if (typeof value === 'string' && value.trim()) { + return value.trim(); + } + } + + return ''; +} const firebaseWebConfig = { - apiKey: process.env.FIREBASE_API_KEY || '', - authDomain: process.env.FIREBASE_AUTH_DOMAIN || '', - databaseURL: process.env.FIREBASE_DATABASE_URL || '', - projectId: process.env.FIREBASE_PROJECT_ID || '', - storageBucket: process.env.FIREBASE_STORAGE_BUCKET || '', - messagingSenderId: process.env.FIREBASE_MESSAGING_SENDER_ID || '', - appId: process.env.FIREBASE_APP_ID || '', - measurementId: process.env.FIREBASE_MEASUREMENT_ID || '' + apiKey: getRuntimeConfigValue('apiKey'), + authDomain: getRuntimeConfigValue('authDomain'), + databaseURL: getRuntimeConfigValue('databaseURL'), + projectId: getRuntimeConfigValue('projectId'), + storageBucket: getRuntimeConfigValue('storageBucket'), + messagingSenderId: getRuntimeConfigValue('messagingSenderId'), + appId: getRuntimeConfigValue('appId'), + measurementId: getRuntimeConfigValue('measurementId') }; -// Initialize Firebase Admin SDK -let bucket; +function readChatbotPrompt(fileName) { + const targetPath = path.join(chatbotPromptsPath, fileName); + if (!fs.existsSync(targetPath)) { + return ''; + } + + return fs.readFileSync(targetPath, 'utf8').trim(); +} + +function loadChatbotKnowledge() { + if (!fs.existsSync(chatbotKnowledgePath)) { + return { site: null, records: [] }; + } + + try { + const parsed = JSON.parse(fs.readFileSync(chatbotKnowledgePath, 'utf8')); + return { + site: parsed.site || null, + records: Array.isArray(parsed.records) ? parsed.records : [] + }; + } catch (error) { + console.warn('⚠️ Failed to load chatbot knowledge:', error.message); + return { site: null, records: [] }; + } +} + +function summarizeText(value, maxLength = 220) { + if (!value) { + return ''; + } + + const normalized = String(value).replace(/\s+/g, ' ').trim(); + if (normalized.length <= maxLength) { + return normalized; + } + + return `${normalized.slice(0, maxLength - 3).trim()}...`; +} + +function tokenizeText(value) { + return String(value || '') + .toLowerCase() + .replace(/[^a-z0-9\s-]/g, ' ') + .split(/\s+/) + .filter((token) => token.length > 1); +} + +function inferIntent(message) { + const q = String(message || '').toLowerCase(); + + if (/\b(hi|hello|hey|yo|good\s*(morning|afternoon|evening))\b/.test(q)) return 'greeting'; + if (/\b(recruiter|hire|hiring|fit for role|candidate|role fit|strong fit|fit for|job|position)\b/.test(q)) return 'recruiter-fit'; + if (/\b(client|agency|contract|freelance|project help)\b/.test(q)) return 'client-fit'; + if (/\b(summary|summarize|overview|snapshot)\b/.test(q)) return 'summarization'; + if (/\b(skill|stack|technology|tool|framework)\b/.test(q)) return 'skills'; + if (/\b(project|built|case study|work)\b/.test(q)) return 'projects'; + if (/\b(contact|email|reach|linkedin|github|telegram|whatsapp)\b/.test(q)) return 'contact'; + if (/\b(resume|cv)\b/.test(q)) return 'resume'; + if (/\b(blog|article|writing)\b/.test(q)) return 'blog'; + if (/\b(about|who is|who are|background|experience)\b/.test(q)) return 'about'; + + return 'general'; +} + +function inferAudience(message, priorAudience = 'unknown') { + const q = String(message || '').toLowerCase(); + + if (/\b(recruiter|hiring|role|candidate|employ)\b/.test(q)) return 'recruiter'; + if (/\b(client|business|company|product|contract)\b/.test(q)) return 'client'; + if (/\b(collaborat|partner|team up|open source)\b/.test(q)) return 'collaborator'; + if (priorAudience && priorAudience !== 'unknown') return priorAudience; + + return 'general'; +} + +function scoreKnowledgeRecord(record, messageTokens, intent) { + const text = [record.title, record.summary, record.content, ...(record.tags || [])].join(' ').toLowerCase(); + let score = Number(record.priority || 0); + const explicitContactQuery = messageTokens.some((token) => ['contact', 'email', 'reach', 'linkedin', 'github', 'telegram', 'whatsapp'].includes(token)); + + messageTokens.forEach((token) => { + if (text.includes(token)) { + score += token.length > 4 ? 1.4 : 0.8; + } + }); + + if (intent !== 'general' && record.type === intent) { + score += 2.5; + } + + if (intent === 'recruiter-fit' && ['identity', 'positioning', 'skills', 'projects', 'about'].includes(record.type)) { + score += 1.8; + } + + if (intent === 'recruiter-fit' && record.type === 'contact' && !explicitContactQuery) { + score -= 2.5; + } + + if (intent === 'client-fit' && ['positioning', 'projects', 'skills', 'contact'].includes(record.type)) { + score += 1.8; + } + + if (intent === 'summarization') { + score += 0.5; + } + + return score; +} + +function retrieveKnowledge(message, intent) { + const knowledge = loadChatbotKnowledge(); + const messageTokens = tokenizeText(message); + const ranked = knowledge.records + .map((record) => ({ record, score: scoreKnowledgeRecord(record, messageTokens, intent) })) + .sort((left, right) => right.score - left.score); + + return { + site: knowledge.site, + records: ranked.slice(0, 5).map((entry) => entry.record) + }; +} + +function createSessionId() { + return crypto.randomUUID(); +} + +function createMessageId() { + return crypto.randomUUID(); +} + +function getWelcomeMessage() { + return { + id: createMessageId(), + role: 'assistant', + summary: "Hi, I'm Dee's assistant. Ask about Dee's background, skills, projects, blog, or how Dee fits a role or project.", + sections: [], + citations: [], + suggestedActions: [ + { type: 'scroll', label: 'View Skills', target: 'skills' }, + { type: 'scroll', label: 'See Projects', target: 'projects' }, + { type: 'scroll', label: 'Contact Dee', target: 'contact' } + ], + meta: { + intent: 'welcome', + usedMemory: false, + usedRetrieval: false, + stageTrace: [] + } + }; +} + +function createSession() { + const timestamp = new Date().toISOString(); + const session = { + sessionId: createSessionId(), + createdAt: timestamp, + updatedAt: timestamp, + recentTurns: [], + rollingSummary: '', + pinnedFacts: [], + userIntentProfile: { + audience: 'unknown', + goal: '' + } + }; + + chatbotSessions.set(session.sessionId, session); + return session; +} + +function getSession(sessionId) { + if (!sessionId) { + return null; + } + + return chatbotSessions.get(sessionId) || null; +} + +function compactTurnsForPrompt(turns) { + return turns + .map((turn) => `${turn.role.toUpperCase()}: ${turn.content}`) + .join('\n'); +} + +function updateRollingSummary(session) { + const olderTurns = session.recentTurns.slice(0, Math.max(session.recentTurns.length - 4, 0)); + if (!olderTurns.length) { + return; + } + + session.rollingSummary = summarizeText(compactTurnsForPrompt(olderTurns), 700); +} + +function storeTurn(session, role, content) { + session.recentTurns.push({ role, content }); + if (session.recentTurns.length > CHATBOT_RECENT_TURN_LIMIT) { + session.recentTurns = session.recentTurns.slice(-CHATBOT_RECENT_TURN_LIMIT); + updateRollingSummary(session); + } + session.updatedAt = new Date().toISOString(); +} + +function buildPromptEnvelope({ session, message, retrieval, audience, intent }) { + const promptSections = [ + readChatbotPrompt('system.md'), + readChatbotPrompt('tone.md'), + readChatbotPrompt('retrieval.md'), + readChatbotPrompt('summarization.md'), + readChatbotPrompt('safety.md') + ].filter(Boolean); + + const knowledgeBlock = retrieval.records + .map((record) => `SOURCE ${record.id} (${record.type})\nTITLE: ${record.title}\nSUMMARY: ${record.summary}\nCONTENT: ${record.content}`) + .join('\n\n'); + + const memoryBlock = [ + `Audience: ${audience}`, + `Intent: ${intent}`, + session.rollingSummary ? `Rolling summary: ${session.rollingSummary}` : '', + session.pinnedFacts.length ? `Pinned facts: ${session.pinnedFacts.join('; ')}` : '', + session.recentTurns.length ? `Recent turns:\n${compactTurnsForPrompt(session.recentTurns.slice(-6))}` : '' + ].filter(Boolean).join('\n'); + + return `${promptSections.join('\n\n')} + +You must return valid JSON with this shape: +{ + "summary": "string", + "sections": [{ "label": "string", "content": "string" }], + "citationIds": ["source-id"], + "suggestedActions": [{ "type": "scroll|link|summarize", "label": "string", "target": "optional", "href": "optional", "subjectId": "optional" }], + "pinnedFacts": ["string"] +} + +Use only the retrieved knowledge and memory below. Do not invent claims. + +MEMORY +${memoryBlock} + +KNOWLEDGE +${knowledgeBlock} + +USER MESSAGE +${message}`; +} + +function createFallbackResponse({ message, retrieval, intent, audience }) { + if (intent === 'greeting') { + return { + summary: 'Hey, happy to help. Ask me anything about Dee\u2019s work, skills, or background.', + sections: [ + { + label: 'How I Can Help', + content: 'Ask about skills, projects, role fit, contact, or request a portfolio summary. Please allow a few minutes between requests so responses stay accurate.' + } + ], + citationIds: [], + suggestedActions: [ + { type: 'scroll', label: 'View Skills', target: 'skills' }, + { type: 'scroll', label: 'See Projects', target: 'projects' }, + { type: 'summarize', label: 'Summarize Portfolio', target: 'portfolio' } + ], + pinnedFacts: [] + }; + } + + const topRecords = retrieval.records; + const summary = topRecords.length + ? summarizeText(topRecords.map((record) => record.content).join(' '), 240) + : "I can help explain Dee's background, skills, projects, contact options, and portfolio direction, but I need more portfolio context to answer this precisely."; + + const sections = buildFallbackSections(topRecords, intent, audience); + const suggestedActions = []; + + if (intent === 'recruiter-fit') { + suggestedActions.push({ type: 'scroll', label: 'See Projects', target: 'projects' }); + suggestedActions.push({ type: 'link', label: 'Open Resume', href: '/media/Files/MyResume.pdf' }); + } + + if (/\b(skill|stack|technology|tool)\b/i.test(message)) { + suggestedActions.push({ type: 'scroll', label: 'View Skills', target: 'skills' }); + } + if (/\b(project|work|built)\b/i.test(message)) { + suggestedActions.push({ type: 'scroll', label: 'See Projects', target: 'projects' }); + } + if (/\b(contact|email|reach|hire)\b/i.test(message)) { + suggestedActions.push({ type: 'scroll', label: 'Contact Dee', target: 'contact' }); + } + if (/\b(resume|cv)\b/i.test(message)) { + suggestedActions.push({ type: 'link', label: 'Open Resume', href: '/media/Files/MyResume.pdf' }); + } + + return { + summary, + sections, + citationIds: topRecords.map((record) => record.id), + suggestedActions, + pinnedFacts: [] + }; +} + +function safeJsonParse(rawText) { + try { + return JSON.parse(rawText); + } catch (error) { + return null; + } +} + +function extractJsonPayload(rawText) { + if (typeof rawText !== 'string' || !rawText.trim()) { + return null; + } + + const direct = safeJsonParse(rawText); + if (direct) { + return direct; + } + + const fencedMatch = rawText.match(/```(?:json)?\s*([\s\S]*?)```/i); + if (fencedMatch?.[1]) { + const fromFence = safeJsonParse(fencedMatch[1].trim()); + if (fromFence) { + return fromFence; + } + } + + const firstBrace = rawText.indexOf('{'); + const lastBrace = rawText.lastIndexOf('}'); + if (firstBrace !== -1 && lastBrace !== -1 && lastBrace > firstBrace) { + return safeJsonParse(rawText.slice(firstBrace, lastBrace + 1)); + } + + return null; +} + +function buildFallbackSections(topRecords, intent, audience) { + const sections = []; + const evidencePoints = topRecords.slice(0, 4).map((record) => `- ${record.title}: ${summarizeText(record.summary || record.content, 140)}`); + + if (intent === 'recruiter-fit') { + sections.push({ + label: 'Why Dee Fits', + content: topRecords.length + ? summarizeText(topRecords.map((record) => record.content).join(' '), 360) + : 'Dee is positioned around AI systems, modern web engineering, and practical delivery.' + }); + } else if (intent === 'summarization') { + sections.push({ + label: 'Portfolio Summary', + content: topRecords.length + ? summarizeText(topRecords.map((record) => record.content).join(' '), 420) + : 'The portfolio highlights Dee as an AI Product & Systems Engineer with modern web and backend capabilities.' + }); + } else if (topRecords.length) { + sections.push({ + label: 'Direct Answer', + content: summarizeText(topRecords.map((record) => record.content).join(' '), 360) + }); + } + + if (evidencePoints.length) { + sections.push({ + label: 'Evidence', + content: evidencePoints.join('\n') + }); + } + + if (audience === 'recruiter') { + sections.push({ + label: 'Why This Matters', + content: 'For a recruiter, the strongest signals on this portfolio are Dee\'s AI positioning, full-stack range, visible problem-solving emphasis, and direct access to projects, resume, and contact channels.' + }); + } + + return sections; +} + +async function generateChatbotResponse({ session, message, intent, audience, retrieval }) { + const apiKey = process.env.GEMINI_API_KEY; + if (!apiKey) { + return { + payload: createFallbackResponse({ message, retrieval, intent, audience }), + provider: 'fallback', + providerError: 'Missing GEMINI_API_KEY' + }; + } + + try { + const prompt = buildPromptEnvelope({ session, message, retrieval, audience, intent }); + const endpoint = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${encodeURIComponent(apiKey)}`; + const response = await fetch(endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + generationConfig: { + responseMimeType: 'application/json' + }, + contents: [ + { + role: 'user', + parts: [{ text: prompt }] + } + ] + }) + }); + + const rawResponseText = await response.text(); + if (!response.ok) { + return { + payload: createFallbackResponse({ message, retrieval, intent, audience }), + provider: 'fallback', + providerError: `Gemini HTTP ${response.status}: ${summarizeText(rawResponseText, 220)}` + }; + } + + const body = safeJsonParse(rawResponseText); + const modelText = body?.candidates?.[0]?.content?.parts?.map((part) => part?.text || '').join('\n').trim() || ''; + + const parsed = extractJsonPayload(modelText); + if (parsed && typeof parsed.summary === 'string') { + return { + payload: parsed, + provider: 'gemini', + providerError: null + }; + } + + return { + payload: createFallbackResponse({ message, retrieval, intent, audience }), + provider: 'fallback', + providerError: 'Gemini response was not valid JSON' + }; + } catch (error) { + console.warn('⚠️ Gemini generation failed, using fallback response:', error.message); + return { + payload: createFallbackResponse({ message, retrieval, intent, audience }), + provider: 'fallback', + providerError: error?.message || 'Gemini request failed' + }; + } +} + +function toCitations(records, citationIds) { + const set = new Set(Array.isArray(citationIds) ? citationIds : []); + return records + .filter((record) => set.has(record.id)) + .map((record) => ({ + label: record.title, + sourceId: record.id, + anchor: record.source?.anchor || 'home', + path: record.source?.path || '' + })); +} + +function normalizeSuggestedActions(actions) { + if (!Array.isArray(actions)) { + return []; + } + + return actions + .filter((action) => action && typeof action.type === 'string' && typeof action.label === 'string') + .map((action) => ({ + type: action.type, + label: action.label, + target: action.target, + href: action.href, + subjectId: action.subjectId + })); +} + +function buildAssistantMessage(payload, retrieval, intent, session, providerMeta = {}) { + return { + id: createMessageId(), + role: 'assistant', + summary: summarizeText(payload.summary || 'I found some relevant portfolio context for that question.', 280), + sections: Array.isArray(payload.sections) ? payload.sections.filter((section) => section && section.label && section.content) : [], + citations: toCitations(retrieval.records, payload.citationIds), + suggestedActions: normalizeSuggestedActions(payload.suggestedActions), + meta: { + intent, + usedMemory: Boolean(session?.rollingSummary || session?.pinnedFacts?.length || (session?.recentTurns?.length || 0) > 2), + usedRetrieval: Boolean(retrieval.records.length), + stageTrace: CHATBOT_STAGE_TRACE, + provider: providerMeta.provider || 'unknown', + providerError: providerMeta.providerError || null + } + }; +} + +function mergePinnedFacts(session, nextPinnedFacts) { + if (!Array.isArray(nextPinnedFacts) || !nextPinnedFacts.length) { + return; + } + + const merged = new Set(session.pinnedFacts); + nextPinnedFacts.forEach((item) => { + if (typeof item === 'string' && item.trim()) { + merged.add(item.trim()); + } + }); + session.pinnedFacts = Array.from(merged).slice(-12); +} + + +const secretCipherKey = process.env.ADMIN_SECRETS_KEY + ? crypto.createHash('sha256').update(process.env.ADMIN_SECRETS_KEY).digest() + : null; + +const runtimeSecretMeta = { + firebaseWebConfig: {} +}; +function encryptSecret(plainText) { + if (!secretCipherKey) { + return null; + } + + const iv = crypto.randomBytes(12); + const cipher = crypto.createCipheriv('aes-256-gcm', secretCipherKey, iv); + const encrypted = Buffer.concat([cipher.update(String(plainText), 'utf8'), cipher.final()]); + const tag = cipher.getAuthTag(); + + return { + iv: iv.toString('base64'), + tag: tag.toString('base64'), + content: encrypted.toString('base64') + }; +} + +function decryptSecret(payload) { + if (!secretCipherKey || !payload?.iv || !payload?.tag || !payload?.content) { + return null; + } + + const decipher = crypto.createDecipheriv( + 'aes-256-gcm', + secretCipherKey, + Buffer.from(payload.iv, 'base64') + ); + decipher.setAuthTag(Buffer.from(payload.tag, 'base64')); + + const decrypted = Buffer.concat([ + decipher.update(Buffer.from(payload.content, 'base64')), + decipher.final() + ]); + + return decrypted.toString('utf8'); +} + +function resolveSecretsReadPath() { + if (fs.existsSync(secretsStorePath)) { + return secretsStorePath; + } + + if (fs.existsSync(legacySecretsStorePath)) { + return legacySecretsStorePath; + } + + return secretsStorePath; +} + +function shouldSyncEnvFile() { + if (process.env.DISABLE_ENV_SYNC === 'true') { + return false; + } + + // Serverless environments like Vercel have immutable runtime filesystems. + if (process.env.VERCEL === '1' || process.env.VERCEL) { + return false; + } + + return true; +} + +function shouldPersistSecretsFile() { + if (process.env.DISABLE_SECRET_STORE === 'true') { + return false; + } + + // Serverless runtimes (like Vercel) do not provide durable writable files. + if (process.env.VERCEL === '1' || process.env.VERCEL) { + return false; + } + + return true; +} + +function escapeEnvValue(value) { + const safe = String(value).replace(/\r?\n/g, ''); + if (/^[A-Za-z0-9_./:@-]+$/.test(safe)) { + return safe; + } + + return JSON.stringify(safe); +} + +function upsertEnvLines(fileContent, updates) { + let next = fileContent; + + Object.entries(updates).forEach(([key, value]) => { + const serialized = `${key}=${escapeEnvValue(value)}`; + const matcher = new RegExp(`^${key}=.*$`, 'm'); + + if (matcher.test(next)) { + next = next.replace(matcher, serialized); + return; + } + + if (next.length > 0 && !next.endsWith('\n')) { + next += '\n'; + } + + next += `${serialized}\n`; + }); + + return next; +} + +function applyFirebaseConfigToRuntimeEnv(values) { + Object.entries(values).forEach(([configKey, configValue]) => { + const envKeys = FIREBASE_ENV_KEY_MAP[configKey] || []; + envKeys.forEach((envKey) => { + process.env[envKey] = configValue; + }); + }); +} + +function syncFirebaseConfigToEnvFile(values) { + if (!shouldSyncEnvFile()) { + return { synced: false, reason: 'Runtime filesystem is not writable (or sync disabled).' }; + } + + const envUpdates = {}; + Object.entries(values).forEach(([configKey, configValue]) => { + const envKeys = FIREBASE_ENV_KEY_MAP[configKey] || []; + envKeys.forEach((envKey) => { + envUpdates[envKey] = configValue; + }); + }); + + const currentContent = fs.existsSync(runtimeEnvPath) + ? fs.readFileSync(runtimeEnvPath, 'utf8') + : ''; + + const nextContent = upsertEnvLines(currentContent, envUpdates); + fs.writeFileSync(runtimeEnvPath, nextContent, 'utf8'); + + return { synced: true, path: runtimeEnvPath }; +} + +function loadPersistedFirebaseSecrets() { + const readPath = resolveSecretsReadPath(); + if (!fs.existsSync(readPath)) { + return; + } + + try { + const raw = JSON.parse(fs.readFileSync(readPath, 'utf8')); + const saved = raw?.firebaseWebConfig || {}; + + FIREBASE_SECRET_KEYS.forEach((key) => { + const savedEntry = saved[key]; + if (!savedEntry) { + return; + } + + runtimeSecretMeta.firebaseWebConfig[key] = { + configured: true, + updatedAt: savedEntry.updatedAt || null + }; + + const decrypted = decryptSecret(savedEntry.encrypted); + if (typeof decrypted === 'string') { + firebaseWebConfig[key] = decrypted; + } + }); + } catch (error) { + console.warn('⚠️ Failed to load persisted admin secrets:', error.message); + } +} + +function persistFirebaseSecrets(values) { + if (!secretCipherKey) { + throw new Error('ADMIN_SECRETS_KEY is required for secrets storage.'); + } + + let existing = {}; + if (fs.existsSync(secretsStorePath)) { + existing = JSON.parse(fs.readFileSync(secretsStorePath, 'utf8')); + } + + if (!existing.firebaseWebConfig) { + existing.firebaseWebConfig = {}; + } + + Object.entries(values).forEach(([key, value]) => { + existing.firebaseWebConfig[key] = { + encrypted: encryptSecret(value), + updatedAt: new Date().toISOString() + }; + }); + + fs.writeFileSync(secretsStorePath, JSON.stringify(existing, null, 2), 'utf8'); +} + +async function requireAuthenticatedAdmin(req, res, next) { + if (!admin.apps.length) { + return res.status(503).json({ error: 'Admin authentication unavailable.' }); + } + + const authHeader = req.headers.authorization || ''; + if (!authHeader.startsWith('Bearer ')) { + return res.status(401).json({ error: 'Missing Bearer token.' }); + } + + try { + const token = authHeader.slice('Bearer '.length); + const decoded = await admin.auth().verifyIdToken(token); + const allowedUids = (process.env.ADMIN_UIDS || '') + .split(',') + .map((value) => value.trim()) + .filter(Boolean); + + if (allowedUids.length > 0 && !allowedUids.includes(decoded.uid)) { + return res.status(403).json({ error: 'Forbidden.' }); + } + + req.adminUser = decoded; + return next(); + } catch (error) { + return res.status(401).json({ error: 'Invalid token.' }); + } +} + +loadPersistedFirebaseSecrets(); + +// Initialize Firebase Admin SDK try { let serviceAccount; @@ -45,11 +836,7 @@ try { } if (serviceAccount) { - admin.initializeApp({ - credential: admin.credential.cert(serviceAccount), - storageBucket - }); - bucket = admin.storage().bucket(); + admin.initializeApp({ credential: admin.credential.cert(serviceAccount) }); console.log('βœ… Firebase Admin SDK initialized successfully'); } } catch (error) { @@ -66,7 +853,7 @@ app.get('/firebase-config.js', (req, res) => { .map(([key]) => key); const warning = missingKeys.length - ? `console.warn('Missing Firebase web config keys in .env: ${missingKeys.join(', ')}');` + ? `console.warn('Missing Firebase web config keys at runtime: ${missingKeys.join(', ')}');` : ''; res.setHeader('Content-Type', 'application/javascript; charset=utf-8'); @@ -74,71 +861,285 @@ app.get('/firebase-config.js', (req, res) => { res.send(`${warning}\nwindow.FIREBASE_CONFIG = ${JSON.stringify(firebaseWebConfig)};`); }); -app.use(express.static(__dirname)); +const staticRootPath = fs.existsSync(path.join(__dirname, 'dist')) + ? path.join(__dirname, 'dist') + : __dirname; + +app.use(express.static(staticRootPath)); // Health check app.get('/health', (req, res) => { res.json({ status: 'ok' }); }); -// Upload endpoint -app.post('/upload', upload.single('file'), async (req, res) => { - try { - if (!bucket) { - console.log('ERROR: Firebase Admin SDK not initialized'); - return res.status(503).json({ - error: 'Upload service not available. Firebase Admin SDK not initialized.' - }); +function getChatbotConfigStatus() { + const knowledge = loadChatbotKnowledge(); + return { + gemini: { + configured: Boolean(process.env.GEMINI_API_KEY) + }, + prompts: { + configured: ['system.md', 'tone.md', 'retrieval.md', 'summarization.md', 'safety.md'].every((fileName) => { + return fs.existsSync(path.join(chatbotPromptsPath, fileName)); + }) + }, + knowledge: { + configured: knowledge.records.length > 0, + records: knowledge.records.length + }, + sessions: { + configured: true, + active: chatbotSessions.size } + }; +} - console.log('Upload request received'); - console.log('File:', req.file ? `${req.file.originalname} (${req.file.size} bytes)` : 'MISSING'); - console.log('User ID:', req.headers['x-user-id']); +function updateSessionIntent(session, message, intent) { + session.userIntentProfile.audience = inferAudience(message, session.userIntentProfile.audience); - if (!req.file) { - console.log('ERROR: No file provided'); - return res.status(400).json({ error: 'No file provided' }); - } + if (intent !== 'general') { + session.userIntentProfile.goal = intent; + } else if (!session.userIntentProfile.goal) { + session.userIntentProfile.goal = 'general'; + } +} - const userId = req.headers['x-user-id']; - if (!userId) { - console.log('ERROR: No user ID header'); - return res.status(401).json({ error: 'User ID required in x-user-id header' }); - } +async function handleChatbotSessionCreate(req, res) { + const session = createSession(); + return res.json({ + sessionId: session.sessionId, + message: getWelcomeMessage() + }); +} - const fileName = `${Date.now()}-${req.file.originalname}`; - const filePath = `post-images/${userId}/${fileName}`; - const file = bucket.file(filePath); +async function handleChatbotMessage(req, res) { + const session = getSession(req.body?.sessionId); + const message = typeof req.body?.message === 'string' ? req.body.message.trim() : ''; - console.log(`Uploading to: ${filePath}`); + if (!session) { + return res.status(404).json({ error: 'Session not found.' }); + } - await file.save(req.file.buffer, { - metadata: { - contentType: req.file.mimetype, - metadata: { - uploadedBy: userId, - uploadedAt: new Date().toISOString() - } + if (!message) { + return res.status(400).json({ error: 'Message is required.' }); + } + + const intent = inferIntent(message); + updateSessionIntent(session, message, intent); + + const retrieval = retrieveKnowledge(message, intent); + const audience = session.userIntentProfile.audience; + + storeTurn(session, 'user', message); + + const generated = await generateChatbotResponse({ + session, + message, + intent, + audience, + retrieval + }); + + mergePinnedFacts(session, generated.payload.pinnedFacts); + + const assistantMessage = buildAssistantMessage(generated.payload, retrieval, intent, session, generated); + storeTurn(session, 'assistant', [assistantMessage.summary, ...assistantMessage.sections.map((section) => `${section.label}: ${section.content}`)].join(' ')); + + return res.json({ + sessionId: session.sessionId, + status: 'completed', + assistantMessage + }); +} + +async function handleChatbotSummarize(req, res) { + const session = getSession(req.body?.sessionId) || createSession(); + const target = typeof req.body?.target === 'string' ? req.body.target.trim() : 'portfolio'; + const audience = typeof req.body?.audience === 'string' ? req.body.audience.trim() : session.userIntentProfile.audience || 'general'; + const subjectId = typeof req.body?.subjectId === 'string' ? req.body.subjectId.trim() : ''; + + const syntheticMessageParts = [`Summarize ${target}`]; + if (subjectId) syntheticMessageParts.push(subjectId); + if (audience && audience !== 'unknown') syntheticMessageParts.push(`for ${audience}`); + const message = syntheticMessageParts.join(' '); + + const retrieval = subjectId + ? { + site: loadChatbotKnowledge().site, + records: loadChatbotKnowledge().records.filter((record) => record.id === subjectId || record.type === target).slice(0, 5) } + : retrieveKnowledge(message, 'summarization'); + + const generated = await generateChatbotResponse({ + session, + message, + intent: 'summarization', + audience, + retrieval + }); + + const assistantMessage = buildAssistantMessage(generated.payload, retrieval, 'summarization', session, generated); + + return res.json({ + sessionId: session.sessionId, + status: 'completed', + assistantMessage + }); +} + +function handleChatbotConfigStatus(req, res) { + return res.json({ status: getChatbotConfigStatus() }); +} + +app.post('/chatbot/session', handleChatbotSessionCreate); +app.post('/api/chatbot/session', handleChatbotSessionCreate); + +app.post('/chatbot/message', handleChatbotMessage); +app.post('/api/chatbot/message', handleChatbotMessage); + +app.post('/chatbot/summarize', handleChatbotSummarize); +app.post('/api/chatbot/summarize', handleChatbotSummarize); + +app.get('/chatbot/config/status', handleChatbotConfigStatus); +app.get('/api/chatbot/config/status', handleChatbotConfigStatus); + +function handleFirebaseSecretStatus(req, res) { + const status = {}; + + FIREBASE_SECRET_KEYS.forEach((key) => { + status[key] = { + configured: Boolean(firebaseWebConfig[key]), + updatedAt: runtimeSecretMeta.firebaseWebConfig[key]?.updatedAt || null + }; + }); + + res.json({ status }); +} + +function handleFirebaseSecretUpdate(req, res) { + if (!secretCipherKey) { + return res.status(503).json({ error: 'Server secret key is not configured. Set ADMIN_SECRETS_KEY.' }); + } + + const values = req.body?.values || {}; + const filtered = {}; + + FIREBASE_SECRET_KEYS.forEach((key) => { + const nextValue = values[key]; + if (typeof nextValue === 'string' && nextValue.trim()) { + filtered[key] = nextValue.trim(); + } + }); + + if (Object.keys(filtered).length === 0) { + return res.status(400).json({ error: 'No valid keys provided.' }); + } + + try { + Object.entries(filtered).forEach(([key, value]) => { + firebaseWebConfig[key] = value; + runtimeSecretMeta.firebaseWebConfig[key] = { + configured: true, + updatedAt: new Date().toISOString() + }; }); - console.log(`Upload complete: ${filePath}`); + applyFirebaseConfigToRuntimeEnv(filtered); + + let secretStore = { persisted: false, reason: 'Not attempted.' }; + if (shouldPersistSecretsFile()) { + persistFirebaseSecrets(filtered); + secretStore = { persisted: true, path: secretsStorePath }; + } else { + secretStore = { persisted: false, reason: 'Runtime filesystem is not durable (or persistence disabled).' }; + } - const publicUrl = `https://firebasestorage.googleapis.com/v0/b/${storageBucket}/o/${encodeURIComponent(filePath)}?alt=media`; + let envSync = { synced: false, reason: 'Not attempted.' }; + try { + envSync = syncFirebaseConfigToEnvFile(filtered); + } catch (syncError) { + envSync = { synced: false, reason: syncError.message }; + } - res.json({ + return res.json({ success: true, - url: publicUrl, - path: filePath, - mimeType: req.file.mimetype + updatedKeys: Object.keys(filtered), + envSync, + secretStore }); + } catch (error) { + return res.status(500).json({ error: error.message }); + } +} + +app.get('/admin/secrets/firebase-config/status', requireAuthenticatedAdmin, handleFirebaseSecretStatus); +app.get('/api/admin/secrets/firebase-config/status', requireAuthenticatedAdmin, handleFirebaseSecretStatus); + +app.post('/admin/secrets/firebase-config', requireAuthenticatedAdmin, handleFirebaseSecretUpdate); +app.post('/api/admin/secrets/firebase-config', requireAuthenticatedAdmin, handleFirebaseSecretUpdate); + +// Upload endpoint +async function handleUpload(req, res) { + if (!req.file) { + return res.status(400).json({ error: 'No file provided' }); + } + + const userId = req.headers['x-user-id']; + if (!userId) { + return res.status(401).json({ error: 'User ID required in x-user-id header' }); + } + + if (!process.env.BLOB_READ_WRITE_TOKEN) { + return res.status(503).json({ error: 'Upload service not available. BLOB_READ_WRITE_TOKEN not configured.' }); + } + + const fileName = `${Date.now()}-${req.file.originalname}`; + const pathname = `post-images/${userId}/${fileName}`; + + try { + const blob = await put(pathname, req.file.buffer, { + access: 'public', + contentType: req.file.mimetype, + token: process.env.BLOB_READ_WRITE_TOKEN + }); + + console.log(`Upload complete: ${blob.url}`); + res.json({ success: true, url: blob.url, path: pathname, mimeType: req.file.mimetype }); } catch (error) { console.error('Upload error:', error); res.status(500).json({ error: error.message }); } +} + +app.post('/upload', upload.single('file'), handleUpload); +app.post('/api/upload', upload.single('file'), handleUpload); + +// SPA history fallback for BrowserRouter routes. +app.get('*', (req, res, next) => { + if (req.path.startsWith('/api/')) { + return next(); + } + + if (['/firebase-config.js', '/health', '/upload'].includes(req.path)) { + return next(); + } + + if (path.extname(req.path)) { + return next(); + } + + return res.sendFile(path.join(staticRootPath, 'index.html')); }); const PORT = process.env.PORT || 4001; -app.listen(PORT, () => { - console.log(`Upload server running on http://localhost:${PORT}`); -}); +if (!process.env.VERCEL) { + app.listen(PORT, () => { + if (!process.env.BLOB_READ_WRITE_TOKEN) { + console.warn('⚠️ BLOB_READ_WRITE_TOKEN is not set β€” uploads will not work.'); + } + + console.log(`Upload server running on http://localhost:${PORT}`); + }); +} + +module.exports = app; diff --git a/src/App.jsx b/src/App.jsx new file mode 100644 index 0000000..5c7d91e --- /dev/null +++ b/src/App.jsx @@ -0,0 +1,22 @@ +import { Suspense, lazy } from 'react'; +import { Routes, Route } from 'react-router-dom'; +import { AuthProvider } from './contexts/AuthContext'; + +const Portfolio = lazy(() => import('./Pages/Portfolio/Portfolio')); +const Blog = lazy(() => import('./Pages/Blog/Blog')); +const Admin = lazy(() => import('./Pages/Admin/Admin')); + +export default function App() { + return ( + + Loading...}> + + } /> + } /> + } /> + } /> + + + + ); +} diff --git a/src/Components/Chatbot/Chatbot.css b/src/Components/Chatbot/Chatbot.css new file mode 100644 index 0000000..e041663 --- /dev/null +++ b/src/Components/Chatbot/Chatbot.css @@ -0,0 +1,362 @@ +/* Chatbot FAB */ +.chatbot-fab { + position: fixed; + bottom: 2rem; + right: 2rem; + width: 56px; + height: 56px; + border-radius: 50%; + background: var(--accent-color); + color: white; + border: none; + cursor: pointer; + box-shadow: 0 4px 20px rgba(37, 99, 235, 0.4); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + transition: var(--transition); +} + +.chatbot-fab:hover, +.chatbot-fab.active { + background: var(--secondary-color); + transform: scale(1.05); +} + +.chatbot-fab svg { + width: 24px; + height: 24px; +} + +/* Panel */ +.chatbot-panel { + position: fixed; + bottom: 5.5rem; + right: 2rem; + width: min(420px, calc(100vw - 2rem)); + height: min(78vh, 640px); + border-radius: 1.25rem; + background: white; + box-shadow: 0 8px 40px rgba(0, 0, 0, 0.15); + border: 1px solid var(--border-color); + display: flex; + flex-direction: column; + z-index: 999; + overflow: hidden; + opacity: 0; + transform: translateY(16px) scale(0.97); + pointer-events: none; + transition: opacity 0.25s ease, transform 0.25s ease; +} + +.chatbot-panel.is-open { + opacity: 1; + transform: translateY(0) scale(1); + pointer-events: all; +} + +/* Header */ +.chatbot-header { + background: linear-gradient(135deg, var(--primary-color), var(--accent-color)); + color: white; + padding: 1rem 1.25rem; + display: flex; + align-items: center; + justify-content: space-between; + flex-shrink: 0; +} + +.chatbot-header-info { + display: flex; + align-items: center; + gap: 0.75rem; +} + +.chatbot-avatar { + width: 36px; + height: 36px; + border-radius: 50%; + background: rgba(255, 255, 255, 0.2); + display: flex; + align-items: center; + justify-content: center; + font-weight: 700; + font-size: 1rem; +} + +.chatbot-header-name { + font-weight: 600; + font-size: 0.95rem; + margin: 0; +} + +.chatbot-header-status { + font-size: 0.75rem; + opacity: 0.8; + margin: 0; +} + +.chatbot-close { + background: none; + border: none; + color: white; + cursor: pointer; + width: 28px; + height: 28px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + transition: background 0.2s; +} + +.chatbot-close:hover { + background: rgba(255, 255, 255, 0.2); +} + +.chatbot-close svg { + width: 16px; + height: 16px; +} + +/* Messages */ +.chatbot-messages { + flex: 1; + overflow-y: auto; + padding: 1rem 1.25rem; + display: flex; + flex-direction: column; + gap: 0.75rem; + scroll-behavior: smooth; +} + +.chatbot-session-note { + font-size: 0.72rem; + color: #94a3b8; + text-align: center; + padding: 0.35rem 0.75rem; + background: #f8fafc; + border: 1px solid #e2e8f0; + border-radius: 0.5rem; + margin-bottom: 0.25rem; +} + +.chatbot-msg { + max-width: 100%; + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.chatbot-msg--user { + align-self: flex-end; + max-width: 82%; +} + +.chatbot-msg--user p { + margin: 0; + padding: 0.65rem 0.9rem; + border-radius: 1rem; + font-size: 0.9rem; + line-height: 1.5; + background: var(--accent-color); + color: white; + border-bottom-right-radius: 4px; +} + +.chatbot-response-card, +.chatbot-status-card { + background: linear-gradient(180deg, rgba(255, 255, 255, 0.98), rgba(247, 249, 252, 0.98)); + color: var(--text-primary); + border: 1px solid rgba(37, 99, 235, 0.12); + border-radius: 1rem; + padding: 0.9rem 1rem; + box-shadow: 0 10px 24px rgba(15, 23, 42, 0.08); +} + +.chatbot-msg--error .chatbot-response-card { + border-color: rgba(220, 38, 38, 0.18); + background: linear-gradient(180deg, rgba(255, 245, 245, 0.98), rgba(255, 250, 250, 0.98)); +} + +.chatbot-response-summary, +.chatbot-section-copy, +.chatbot-status-copy { + margin: 0; + font-size: 0.9rem; + line-height: 1.55; +} + +.chatbot-sections, +.chatbot-citations, +.chatbot-actions { + margin-top: 0.85rem; +} + +.chatbot-section-block + .chatbot-section-block { + margin-top: 0.75rem; +} + +.chatbot-section-label, +.chatbot-meta-label, +.chatbot-status-label { + margin: 0 0 0.35rem; + font-size: 0.72rem; + letter-spacing: 0.08em; + text-transform: uppercase; + font-weight: 700; + color: var(--accent-color); +} + +.chatbot-pill-row { + display: flex; + flex-wrap: wrap; + gap: 0.45rem; +} + +.chatbot-citation-pill, +.chatbot-action-btn { + align-self: flex-start; + border: 1px solid var(--accent-color); + border-radius: 999px; + padding: 0.4rem 0.8rem; + font-size: 0.78rem; + font-weight: 600; + cursor: pointer; + transition: var(--transition); +} + +.chatbot-citation-pill { + background: rgba(37, 99, 235, 0.08); + color: var(--accent-color); +} + +.chatbot-action-btn { + background: white; + color: var(--accent-color); +} + +.chatbot-citation-pill:hover, +.chatbot-action-btn:hover { + background: var(--accent-color); + color: white; +} + +.chatbot-status-card { + display: flex; + flex-direction: column; + gap: 0.4rem; +} + +.chatbot-status-dots { + display: inline-flex; + gap: 0.35rem; +} + +.chatbot-status-dots span { + width: 0.45rem; + height: 0.45rem; + border-radius: 50%; + background: var(--accent-color); + animation: chatbot-pulse 1.2s infinite ease-in-out; +} + +.chatbot-status-dots span:nth-child(2) { + animation-delay: 0.15s; +} + +.chatbot-status-dots span:nth-child(3) { + animation-delay: 0.3s; +} + +@keyframes chatbot-pulse { + 0%, 80%, 100% { + transform: scale(0.7); + opacity: 0.45; + } + + 40% { + transform: scale(1); + opacity: 1; + } +} + +/* Input row */ +.chatbot-input-row { + display: flex; + gap: 0.5rem; + padding: 0.75rem 1.25rem; + border-top: 1px solid var(--border-color); + flex-shrink: 0; +} + +.chatbot-input-row input { + flex: 1; + padding: 0.6rem 0.9rem; + border: 1px solid var(--border-color); + border-radius: 2rem; + font-size: 0.9rem; + font-family: inherit; + color: var(--text-primary); + background: white; + transition: var(--transition); +} + +.chatbot-input-row input:focus { + outline: none; + border-color: var(--accent-color); + box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.12); +} + +.chatbot-input-row button { + width: 38px; + height: 38px; + border-radius: 50%; + border: none; + background: var(--accent-color); + color: white; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: var(--transition); + flex-shrink: 0; +} + +.chatbot-input-row button:disabled { + opacity: 0.4; + cursor: default; +} + +.chatbot-input-row button:not(:disabled):hover { + background: var(--secondary-color); + transform: scale(1.05); +} + +.chatbot-input-row button svg { + width: 18px; + height: 18px; +} + +/* Mobile */ +@media (max-width: 480px) { + .chatbot-panel { + right: 1rem; + bottom: 5rem; + width: calc(100vw - 2rem); + height: min(74vh, 620px); + } + + .chatbot-fab { + right: 1rem; + bottom: 1rem; + } + + .chatbot-messages, + .chatbot-input-row, + .chatbot-header { + padding-left: 1rem; + padding-right: 1rem; + } +} diff --git a/src/Components/Chatbot/Chatbot.jsx b/src/Components/Chatbot/Chatbot.jsx new file mode 100644 index 0000000..0433736 --- /dev/null +++ b/src/Components/Chatbot/Chatbot.jsx @@ -0,0 +1,369 @@ +import { useState, useRef, useEffect } from 'react'; +import './Chatbot.css'; + +const API_BASE = '/api/chatbot'; +const STAGES = ['thinking', 'searching', 'drafting']; +const STAGE_LABELS = { + thinking: 'Thinking', + searching: 'Searching portfolio', + drafting: 'Drafting answer' +}; + +function getPageContext() { + return { + route: window.location.pathname, + section: window.location.hash ? window.location.hash.slice(1) : 'home' + }; +} + +async function postJson(url, body, timeoutMs = 12000) { + const controller = new AbortController(); + const timeoutId = window.setTimeout(() => controller.abort(), timeoutMs); + + let response; + try { + response = await fetch(url, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + signal: controller.signal + }); + } catch (error) { + if (error?.name === 'AbortError') { + throw new Error('Assistant request timed out. Check backend connectivity and try again.'); + } + + throw error; + } finally { + window.clearTimeout(timeoutId); + } + + const data = await response.json().catch(() => ({})); + if (!response.ok) { + throw new Error(data?.error || 'Request failed.'); + } + + return data; +} + +function scrollToAnchor(anchor) { + const element = document.getElementById(anchor); + if (!element) { + return false; + } + + window.scrollTo({ top: element.offsetTop - 70, behavior: 'smooth' }); + return true; +} + +function createUserMessage(text) { + return { + id: `${Date.now()}-user`, + role: 'user', + text + }; +} + +function createErrorMessage(text) { + return { + id: `${Date.now()}-error`, + role: 'assistant', + summary: text, + sections: [], + citations: [], + suggestedActions: [], + meta: { + intent: 'error', + usedMemory: false, + usedRetrieval: false, + stageTrace: [] + }, + isError: true + }; +} + +export default function Chatbot() { + const [isOpen, setIsOpen] = useState(false); + const [messages, setMessages] = useState([]); + const [input, setInput] = useState(''); + const [isBootstrapping, setIsBootstrapping] = useState(false); + const [isSending, setIsSending] = useState(false); + const [activeStage, setActiveStage] = useState(STAGES[0]); + const bottomRef = useRef(null); + const inputRef = useRef(null); + const sessionIdRef = useRef(''); + + useEffect(() => { + if (isOpen) { + bottomRef.current?.scrollIntoView({ behavior: 'smooth' }); + inputRef.current?.focus(); + } + }, [messages, isOpen]); + + useEffect(() => { + if (!isSending) { + setActiveStage(STAGES[0]); + return undefined; + } + + let stageIndex = 0; + const intervalId = window.setInterval(() => { + stageIndex = (stageIndex + 1) % STAGES.length; + setActiveStage(STAGES[stageIndex]); + }, 900); + + return () => window.clearInterval(intervalId); + }, [isSending]); + + useEffect(() => { + if (!isOpen || sessionIdRef.current) { + return; + } + + let isCancelled = false; + + async function bootstrapSession() { + setIsBootstrapping(true); + try { + const data = await postJson(`${API_BASE}/session`, {}); + if (isCancelled) { + return; + } + + sessionIdRef.current = data.sessionId; + setMessages([data.message]); + } catch (error) { + if (!isCancelled) { + setMessages([createErrorMessage(error.message || 'Failed to start assistant session.')]); + } + } finally { + if (!isCancelled) { + setIsBootstrapping(false); + } + } + } + + bootstrapSession(); + + return () => { + isCancelled = true; + }; + }, [isOpen]); + + async function send() { + const text = input.trim(); + if (!text || isSending) return; + + if (!sessionIdRef.current) { + try { + const data = await postJson(`${API_BASE}/session`, {}); + sessionIdRef.current = data.sessionId; + setMessages((prev) => (prev.length ? prev : [data.message])); + } catch (error) { + setMessages((prev) => [...prev, createErrorMessage(error.message || 'Failed to start assistant session.')]); + return; + } + } + + const userMsg = createUserMessage(text); + setMessages((prev) => [...prev, userMsg]); + setInput(''); + + setIsSending(true); + + try { + const data = await postJson(`${API_BASE}/message`, { + sessionId: sessionIdRef.current, + message: text, + pageContext: getPageContext() + }); + + setMessages((prev) => [...prev, data.assistantMessage]); + } catch (error) { + setMessages((prev) => [...prev, createErrorMessage(error.message || 'Failed to get assistant response.')]); + } finally { + setIsSending(false); + } + } + + function handleKeyDown(e) { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + send(); + } + } + + async function handleAction(action) { + if (!action) { + return; + } + + if (action.type === 'scroll') { + const didScroll = scrollToAnchor(action.target); + if (didScroll) { + setIsOpen(false); + } + } else if (action.type === 'link' && action.href) { + window.open(action.href, '_blank', 'noopener,noreferrer'); + } else if (action.type === 'summarize' && sessionIdRef.current && !isSending) { + setIsSending(true); + try { + const data = await postJson(`${API_BASE}/summarize`, { + sessionId: sessionIdRef.current, + target: action.target || 'portfolio', + subjectId: action.subjectId + }); + setMessages((prev) => [...prev, data.assistantMessage]); + } catch (error) { + setMessages((prev) => [...prev, createErrorMessage(error.message || 'Failed to summarize this topic.')]); + } finally { + setIsSending(false); + } + } + } + + function handleCitationClick(citation) { + if (!citation?.anchor) { + return; + } + + const didScroll = scrollToAnchor(citation.anchor); + if (didScroll) { + setIsOpen(false); + } + } + + const headerStatus = isSending + ? STAGE_LABELS[activeStage] + : sessionIdRef.current + ? 'Memory active' + : isBootstrapping + ? 'Connecting' + : 'Ready'; + + return ( + <> + + +
+
+
+ D +
+

Dee's Assistant

+

{headerStatus}

+
+
+ +
+ +
+
+ Please allow ~3 minutes between requests for best results. +
+ {messages.map((msg) => ( +
+ {msg.role === 'user' ? ( +

{msg.text}

+ ) : ( +
+

{msg.summary}

+ + {msg.sections?.length ? ( +
+ {msg.sections.map((section) => ( +
+

{section.label}

+

{section.content}

+
+ ))} +
+ ) : null} + + {msg.citations?.length ? ( +
+

Sources

+
+ {msg.citations.map((citation) => ( + + ))} +
+
+ ) : null} + + {msg.suggestedActions?.length ? ( +
+

Next Actions

+
+ {msg.suggestedActions.map((action, index) => ( + + ))} +
+
+ ) : null} +
+ )} +
+ ))} + + {isSending ? ( +
+
+ +

{STAGE_LABELS[activeStage]}

+

Grounding the response in portfolio context and session memory.

+
+
+ ) : null} + +
+
+ +
+ setInput(e.target.value)} + onKeyDown={handleKeyDown} + placeholder="Ask about Dee, skills, projects, fit, or ask for a summary..." + aria-label="Message input" + /> + +
+
+ + ); +} diff --git a/src/Components/Navbar/Navbar.css b/src/Components/Navbar/Navbar.css new file mode 100644 index 0000000..68f6ecf --- /dev/null +++ b/src/Components/Navbar/Navbar.css @@ -0,0 +1,109 @@ +/* Navigation */ +.navbar { + position: fixed; + top: 0; + left: 0; + right: 0; + background: rgba(15, 23, 42, 0.9); + backdrop-filter: blur(10px); + border-bottom: 1px solid rgba(59, 130, 246, 0.3); + z-index: 1000; + transition: var(--transition); + padding: 1rem 0; +} + +.navbar.scrolled { + padding: 0.75rem 0; + background: rgba(15, 23, 42, 0.98); + box-shadow: 0 2px 20px rgba(0, 0, 0, 0.3); +} + +.navbar .container { + display: flex; + justify-content: space-between; + align-items: center; +} + +.nav-logo { + height: 40px; + width: auto; +} + +.nav-menu { + display: flex; + list-style: none; + gap: 2rem; +} + +.nav-link { + color: rgba(255, 255, 255, 0.9); + text-decoration: none; + font-weight: 500; + transition: var(--transition); + position: relative; + background: none; + border: none; + cursor: pointer; + font-family: inherit; + font-size: 1rem; + padding: 0; +} + +.nav-link::after { + content: ''; + position: absolute; + bottom: -5px; + left: 0; + width: 0; + height: 2px; + background: #93c5fd; + transition: var(--transition); +} + +.nav-link:hover::after, +.nav-link.active::after { + width: 100%; +} + +.nav-link:hover, +.nav-link.active { + color: #ffffff; +} + +.hamburger { + display: none; + flex-direction: column; + cursor: pointer; + gap: 5px; +} + +.hamburger span { + width: 25px; + height: 3px; + background: #ffffff; + transition: var(--transition); +} + +@media (max-width: 768px) { + .hamburger { + display: flex; + } + + .nav-menu { + position: fixed; + left: -100%; + top: 70px; + flex-direction: column; + background: rgba(15, 23, 42, 0.98); + backdrop-filter: blur(10px); + width: 100%; + text-align: center; + transition: 0.3s; + box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1); + padding: 2rem 0; + } + + .nav-menu.active { + left: 0; + } +} diff --git a/src/Components/Navbar/Navbar.jsx b/src/Components/Navbar/Navbar.jsx new file mode 100644 index 0000000..51971f4 --- /dev/null +++ b/src/Components/Navbar/Navbar.jsx @@ -0,0 +1,109 @@ +import { useState, useEffect } from 'react'; +import { Link, useLocation } from 'react-router-dom'; +import { scrollToSectionById } from '../../utils/scrollToSection'; +import './Navbar.css'; + +const NAV_LINKS = [ + { id: 'home', label: 'Home' }, + { id: 'about', label: 'About' }, + { id: 'projects', label: 'Projects' }, + { id: 'skills', label: 'Skills' }, + { id: null, label: 'Blog', href: '/blog' }, + { id: 'contact', label: 'Contact' }, +]; + +export default function Navbar() { + const [scrolled, setScrolled] = useState(false); + const [menuOpen, setMenuOpen] = useState(false); + const [activeId, setActiveId] = useState('home'); + const location = useLocation(); + const isHomeRoute = location.pathname === '/' || location.pathname === '/home'; + const isBlogRoute = location.pathname === '/blog'; + + useEffect(() => { + function onScroll() { + setScrolled(window.scrollY > 50); + + if (!isHomeRoute) { + return; + } + + // Active section highlighting + const sections = document.querySelectorAll('section[id]'); + let current = 'home'; + sections.forEach((section) => { + const top = section.offsetTop - 80; + const bottom = top + section.offsetHeight; + if (window.scrollY >= top && window.scrollY < bottom) { + current = section.id; + } + }); + setActiveId(current); + } + + window.addEventListener('scroll', onScroll, { passive: true }); + return () => window.removeEventListener('scroll', onScroll); + }, [isHomeRoute]); + + function scrollToSection(id) { + scrollToSectionById(id); + setMenuOpen(false); + } + + return ( + + ); +} diff --git a/src/Pages/About/About.css b/src/Pages/About/About.css new file mode 100644 index 0000000..e2e8589 --- /dev/null +++ b/src/Pages/About/About.css @@ -0,0 +1,375 @@ +/* About Section */ +.about { + padding: 100px 0; + background: rgba(248, 250, 252, 0.85); + position: relative; +} + +.about-content { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 4rem; + align-items: center; + max-width: 1200px; + margin: 0 auto; +} + +.about-text p { + font-size: 1.125rem; + color: var(--text-secondary); + margin-bottom: 1.5rem; + line-height: 1.8; +} + +.about-stats-wrapper { + display: flex; + flex-direction: column; + align-items: center; + margin-top: 3rem; +} + +.about-stats { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 2rem; + width: 100%; + max-width: 1000px; +} + +.stat-item { + text-align: center; + padding: 2rem; + background: white; + border-radius: 1rem; + box-shadow: var(--shadow-sm); + transition: var(--transition); +} + +.stat-item:hover { + transform: translateY(-5px); + box-shadow: var(--shadow-md); +} + +.stat-number { + font-size: 2.5rem; + font-weight: 700; + color: var(--primary-color); + margin-bottom: 0.5rem; +} + +.stat-label { + color: var(--text-secondary); + font-size: 0.95rem; +} + +/* Resume View Button */ +.resume-view-container { + margin-top: 2.5rem; + display: flex; + justify-content: center; + width: 100%; +} + +.btn-resume { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0.75rem; + padding: 1rem 2rem; + background: var(--primary-color); + color: white; + border: none; + border-radius: 0.5rem; + font-size: 1.1rem; + font-weight: 600; + font-family: inherit; + line-height: 1; + cursor: pointer; + transition: var(--transition); + box-shadow: var(--shadow-md); +} + +.btn-resume:hover { + background: var(--secondary-color); + transform: translateY(-2px); + box-shadow: var(--shadow-lg); +} + +.btn-resume:focus-visible { + outline: 3px solid rgba(37, 99, 235, 0.45); + outline-offset: 3px; +} + +.download-icon { + width: 20px; + height: 20px; + flex-shrink: 0; +} + +/* Resume Modal */ +.resume-modal-backdrop { + position: fixed; + inset: 0; + z-index: 2147483647; + display: flex; + align-items: center; + justify-content: center; + padding: 1rem; + background: rgba(0, 0, 0, 0.6); + backdrop-filter: blur(4px); + -webkit-backdrop-filter: blur(4px); + opacity: 0; + transition: opacity 0.25s ease; + pointer-events: none; +} + +.resume-modal-backdrop.is-open { + opacity: 1; + pointer-events: auto; +} + +.resume-modal-box { + position: relative; + width: 100%; + max-width: 900px; + background: #ffffff; + border-radius: 1.25rem; + box-shadow: 0 25px 60px rgba(0, 0, 0, 0.3); + display: flex; + flex-direction: column; + height: 90vh; + max-height: 92vh; + overflow: hidden; + transform: scale(0.92) translateY(16px); + opacity: 0; + transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.25s ease; +} + +.resume-modal-backdrop.is-open .resume-modal-box { + transform: scale(1) translateY(0); + opacity: 1; +} + +.resume-modal-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 1.5rem 1.75rem; + border-bottom: 1px solid #f3f4f6; + background: rgba(255, 255, 255, 0.9); + backdrop-filter: blur(8px); + flex-shrink: 0; +} + +.resume-modal-title { + font-size: 1.2rem; + font-weight: 700; + color: var(--text-primary); + margin: 0 0 0.2rem; +} + +.resume-modal-subtitle { + font-size: 0.875rem; + color: #6b7280; + margin: 0; +} + +.resume-modal-close { + display: flex; + align-items: center; + justify-content: center; + padding: 0.5rem; + border: none; + background: none; + border-radius: 999px; + cursor: pointer; + color: #6b7280; + transition: background 0.2s ease, color 0.2s ease; +} + +.resume-modal-close:hover { + background: #f3f4f6; + color: var(--text-primary); +} + +.resume-modal-content { + flex: 1; + overflow: hidden; + padding: 1.5rem 1.75rem; + background: #f8fafc; + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-start; + min-height: 0; +} + +.resume-security-notice { + display: inline-flex; + align-items: center; + gap: 0.45rem; + font-size: 0.75rem; + font-weight: 600; + color: #d97706; + background: rgba(251, 191, 36, 0.15); + border: 1px solid rgba(245, 158, 11, 0.35); + border-radius: 999px; + padding: 0.45rem 0.75rem; + margin-bottom: 1rem; +} + +.resume-viewer-wrap { + width: 100%; + display: flex; + align-items: center; + justify-content: center; + flex: 1; + min-height: 0; + overflow: hidden; + pointer-events: auto; + -webkit-user-select: none; + user-select: none; + -webkit-touch-callout: none; +} + +.resume-viewer-wrap iframe { + width: 100%; + height: 100%; + min-height: 480px; + border: 0; + border-radius: 0.5rem; + background: #ffffff; + display: block; + pointer-events: none; +} + +.resume-viewer-wrap img { + max-width: 100%; + height: auto; + max-height: 62vh; + object-fit: contain; + box-shadow: 0 8px 32px rgba(0,0,0,0.12); + border-radius: 0.5rem; +} + +/* Code Animation */ +.code-animation-container { + display: flex; + justify-content: center; + align-items: center; +} + +.code-editor { + background: #1e1e1e; + border-radius: 0.5rem; + overflow: hidden; + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); + width: 100%; + max-width: 600px; + font-family: 'Courier New', 'Monaco', 'Menlo', monospace; +} + +.code-header { + background: #2d2d2d; + padding: 0.75rem 1rem; + display: flex; + align-items: center; + gap: 0.75rem; + border-bottom: 1px solid #3e3e3e; +} + +.code-dots { + display: flex; + gap: 0.5rem; +} + +.dot { + width: 12px; + height: 12px; + border-radius: 50%; +} + +.dot-red { background: #ff5f56; } +.dot-yellow { background: #ffbd2e; } +.dot-green { background: #27c93f; } + +.code-title { + color: #d4d4d4; + font-size: 0.875rem; + font-weight: 500; +} + +.code-body { + padding: 1.5rem; + background: #1e1e1e; + min-height: 300px; + position: relative; +} + +.code-line { + display: flex; + margin: 0.5rem 0; + line-height: 1.6; + min-height: 1.6em; +} + +.code-line-number { + color: #858585; + margin-right: 1.5rem; + width: 2rem; + text-align: right; + font-size: 0.9rem; + user-select: none; +} + +.code-content { + color: #d4d4d4; + flex: 1; + font-size: 0.95rem; + white-space: pre-wrap; + word-wrap: break-word; +} + +.code-content .keyword { color: #569cd6; } +.code-content .function { color: #dcdcaa; } +.code-content .string { color: #ce9178; } +.code-content .comment { color: #6a9955; font-style: italic; } +.code-content .variable { color: #9cdcfe; } +.code-content .operator { color: #d4d4d4; } + +.code-cursor { + display: inline-block; + width: 2px; + height: 1em; + background: #d4d4d4; + animation: blink 1s infinite; + vertical-align: text-bottom; +} + +@keyframes blink { + 0%, 50% { opacity: 1; } + 51%, 100% { opacity: 0; } +} + +@media (max-width: 768px) { + .about-content { + grid-template-columns: 1fr; + } + + .about-text { + text-align: center; + } + + .about-text p { + text-align: center; + } + + .about-stats { + grid-template-columns: 1fr; + max-width: 100%; + } + + .code-editor { + max-width: 100%; + } +} diff --git a/src/Pages/About/About.jsx b/src/Pages/About/About.jsx new file mode 100644 index 0000000..171a44b --- /dev/null +++ b/src/Pages/About/About.jsx @@ -0,0 +1,263 @@ +import { useEffect, useRef, useState } from 'react'; +import './About.css'; + +const RESUME_DATA = { + id: 'resume-1', + title: 'My Resume', + fileUrl: '/media/Files/MyResume.pdf', + fileType: 'pdf', +}; + +const CODE_LINES = [ + 'const developer = {', + ' name: \'Dee\',', + ' role: \'AI Product & Systems Engineer\',', + ' skills: [\'JavaScript\', \'React\', \'Python\', \'Docker\', \'Node.js\'],', + ' build() {', + ' return \'Amazing Products\';', + ' }', + '};', + '// Passionate about creating elegant solutions', +]; + +export default function About() { + const lineRefs = useRef([]); + const animRef = useRef({ started: false, line: 0, char: 0 }); + const [resumeModalOpen, setResumeModalOpen] = useState(false); + + function closeResumeModal() { + setResumeModalOpen(false); + } + + useEffect(() => { + const container = document.querySelector('.code-animation-container'); + if (!container) return; + + const observer = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting && !animRef.current.started) { + animRef.current.started = true; + startTyping(); + } + }); + }, + { threshold: 0.3 } + ); + observer.observe(container); + return () => observer.disconnect(); + }, []); + + useEffect(() => { + function onKeyDown(e) { + if (e.key === 'Escape' && resumeModalOpen) { + closeResumeModal(); + } + } + + document.addEventListener('keydown', onKeyDown); + return () => document.removeEventListener('keydown', onKeyDown); + }, [resumeModalOpen]); + + useEffect(() => { + document.body.style.overflow = resumeModalOpen ? 'hidden' : ''; + return () => { + document.body.style.overflow = ''; + }; + }, [resumeModalOpen]); + + // Close modal if user attempts to scroll the page while it is open. + useEffect(() => { + if (!resumeModalOpen) return; + + const onScrollIntent = () => { + closeResumeModal(); + }; + + window.addEventListener('wheel', onScrollIntent, { passive: true, capture: true }); + window.addEventListener('touchmove', onScrollIntent, { passive: true, capture: true }); + window.addEventListener('scroll', onScrollIntent, { passive: true, capture: true }); + + return () => { + window.removeEventListener('wheel', onScrollIntent, { capture: true }); + window.removeEventListener('touchmove', onScrollIntent, { capture: true }); + window.removeEventListener('scroll', onScrollIntent, { capture: true }); + }; + }, [resumeModalOpen]); + + function startTyping() { + const els = lineRefs.current; + animRef.current.line = 0; + animRef.current.char = 0; + + function type() { + const { line, char } = animRef.current; + if (line >= CODE_LINES.length) { + setTimeout(() => { + els.forEach((el) => { if (el) el.innerHTML = ''; }); + animRef.current = { started: true, line: 0, char: 0 }; + startTyping(); + }, 5000); + return; + } + + const el = els[line]; + if (!el) { + animRef.current.line += 1; + animRef.current.char = 0; + setTimeout(type, 100); + return; + } + + const target = CODE_LINES[line]; + const preview = target.substring(0, char); + el.innerHTML = preview + ''; + + if (char < target.length) { + animRef.current.char += 1; + setTimeout(type, 30 + Math.random() * 40); + } else { + el.innerHTML = preview; + animRef.current.line += 1; + animRef.current.char = 0; + setTimeout(type, 200); + } + } + + setTimeout(type, 500); + } + + return ( +
+
+

About Me

+
+
+

+ I'm a passionate developer with a love for creating elegant solutions to complex problems. + With expertise in modern web technologies, I bring ideas to life through clean code and + thoughtful design. +

+

+ When I'm not coding, you can find me exploring new technologies, contributing to open-source + projects, or sharing knowledge with the developer community. +

+ +
+
+
+
50+
+
Projects Completed
+
+
+
2+
+
Years Experience
+
+
+
100%
+
Client Satisfaction
+
+
+ +
+ +
+
+
+ +
+
+
+
+ + + +
+ portfolio.js +
+
+ {CODE_LINES.map((_, i) => ( +
+                    {i + 1}
+                     { lineRefs.current[i] = el; }}
+                    >
+                  
+ ))} +
+
+
+
+
+ + {resumeModalOpen && ( +
{ + if (e.target === e.currentTarget) { + closeResumeModal(); + } + }} + > +
+
+
+

My Resume

+

Protected View

+
+ +
+ +
+
+ + + + + + Protected View: Downloads and printing are restricted. +
+ +
e.preventDefault()}> +