diff --git a/.clinerules b/.clinerules index f966e91a..939a5d5b 100644 --- a/.clinerules +++ b/.clinerules @@ -3,10 +3,13 @@ ## Development Server IMPORTANT: Always follow these instructions exactly when starting the development server: -```bash -lsof -ti :8000 | xargs kill -9; find . -type d -name "__pycache__" -exec rm -r {} +; ./scripts/dev_server.sh +FIRST, assume the server is running. Always. Assume the changes have taken effect. -Note: These steps must be executed in this exact order as a single command to ensure consistent behavior. If server does not start correctly, do not attempt to modify the dev_server.sh script. +IF THERE IS AN ISSUE WITH THE SERVER, run the following command exactly: +```bash +lsof -ti :8000 | xargs kill -9; find . -type d -name "__pycache__" -exec rm -r {} +; cd backend && uv run manage.py runserver_plus && cd ../frontend && pnpm run dev + +Note: These steps must be executed in this exact order to ensure consistent behavior. If server does not start correctly, fix the error in accordance with the error details as best you can. ## Package Management IMPORTANT: When a Python package is needed, only use UV to add it: @@ -47,8 +50,30 @@ IMPORTANT: Follow these entity relationship patterns consistently: - PropertyOwners: Companies that own park property (new concept, optional) - Manufacturers: Companies that manufacture rides (replaces Company for rides) - Designers: Companies/individuals that design rides (existing concept) +- IMPORTANT: All entities can have locations. # Relationship Constraints - Operator and PropertyOwner are usually the same entity but CAN be different - Manufacturers and Designers are distinct concepts and should not be conflated -- All entity relationships should use proper foreign keys with appropriate null/blank settings \ No newline at end of file +- All entity relationships should use proper foreign keys with appropriate null/blank settings + +- You are to NEVER assume that blank output means your fixes were correct. That assumption can lead to further issues down the line. +- ALWAYS verify your changes by testing the affected functionality thoroughly. +- ALWAYS use context7 to check documentation when troubleshooting. It contains VITAL documentation for any and all frameworks, modules, and packages. +- ALWAYS document your code changes with conport and the reasoning behind them. +- ALWAYS include relevant context and information when making changes to the codebase. +- ALWAYS ensure that your code changes are properly tested and validated before deployment. +- ALWAYS communicate clearly and effectively with your team about any changes you make. +- ALWAYS be open to feedback and willing to make adjustments as necessary. +- ALWAYS strive for continuous improvement in your work and processes. +- ALWAYS prioritize code readability and maintainability. +- ALWAYS keep security best practices in mind when developing and reviewing code. +- ALWAYS consider performance implications when making changes to the codebase. +- ALWAYS be mindful of the impact of your changes on the overall system architecture. +- ALWAYS keep scalability in mind when designing new features or modifying existing ones. +- ALWAYS consider the potential for code reuse and modularity in your designs. +- ALWAYS document your code with clear and concise comments. +- ALWAYS keep your code DRY (Don't Repeat Yourself) by abstracting common functionality into reusable components. +- ALWAYS use meaningful variable and function names to improve code readability. +- ALWAYS handle errors and exceptions gracefully to improve the user experience. +- ALWAYS log important events and errors for troubleshooting purposes. \ No newline at end of file diff --git a/README.md b/README.md index 462c7ddd..de7679e3 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,29 @@ # ThrillWiki Django + Vue.js Monorepo -A modern monorepo architecture for ThrillWiki, combining a Django REST API backend with a Vue.js frontend. +A comprehensive theme park and roller coaster information system built with a modern monorepo architecture combining Django REST API backend with Vue.js frontend. -## ๐Ÿ—๏ธ Architecture +## ๐Ÿ—๏ธ Architecture Overview -This project uses a monorepo structure that cleanly separates backend and frontend concerns: +This project uses a monorepo structure that cleanly separates backend and frontend concerns while maintaining shared resources and documentation: ``` thrillwiki-monorepo/ -โ”œโ”€โ”€ backend/ # Django REST API -โ”œโ”€โ”€ frontend/ # Vue.js SPA -โ””โ”€โ”€ shared/ # Shared resources and documentation +โ”œโ”€โ”€ backend/ # Django REST API (Port 8000) +โ”‚ โ”œโ”€โ”€ apps/ # Modular Django applications +โ”‚ โ”œโ”€โ”€ config/ # Django settings and configuration +โ”‚ โ”œโ”€โ”€ templates/ # Django templates +โ”‚ โ””โ”€โ”€ static/ # Static assets +โ”œโ”€โ”€ frontend/ # Vue.js SPA (Port 5174) +โ”‚ โ”œโ”€โ”€ src/ # Vue.js source code +โ”‚ โ”œโ”€โ”€ public/ # Static assets +โ”‚ โ””โ”€โ”€ dist/ # Build output +โ”œโ”€โ”€ shared/ # Shared resources and documentation +โ”‚ โ”œโ”€โ”€ docs/ # Comprehensive documentation +โ”‚ โ”œโ”€โ”€ scripts/ # Development and deployment scripts +โ”‚ โ”œโ”€โ”€ config/ # Shared configuration +โ”‚ โ””โ”€โ”€ media/ # Shared media files +โ”œโ”€โ”€ architecture/ # Architecture documentation +โ””โ”€โ”€ profiles/ # Development profiles ``` ## ๐Ÿš€ Quick Start @@ -19,6 +32,8 @@ thrillwiki-monorepo/ - **Python 3.11+** with [uv](https://docs.astral.sh/uv/) for backend dependencies - **Node.js 18+** with [pnpm](https://pnpm.io/) for frontend dependencies +- **PostgreSQL 14+** (optional, defaults to SQLite for development) +- **Redis 6+** (optional, for caching and sessions) ### Development Setup @@ -32,41 +47,63 @@ thrillwiki-monorepo/ ```bash # Install frontend dependencies pnpm install - + # Install backend dependencies - cd backend && uv sync + cd backend && uv sync && cd .. ``` -3. **Start development servers** +3. **Environment configuration** ```bash - # Start both frontend and backend - pnpm run dev - - # Or start individually - pnpm run dev:frontend # Vue.js on :3000 - pnpm run dev:backend # Django on :8000 + # Copy environment files + cp .env.example .env + cp backend/.env.example backend/.env + cp frontend/.env.development frontend/.env.local + + # Edit .env files with your settings ``` -## ๐Ÿ“ Project Structure +4. **Database setup** + ```bash + cd backend + uv run manage.py migrate + uv run manage.py createsuperuser + cd .. + ``` + +5. **Start development servers** + ```bash + # Start both servers concurrently + pnpm run dev + + # Or start individually + pnpm run dev:frontend # Vue.js on :5174 + pnpm run dev:backend # Django on :8000 + ``` + +## ๐Ÿ“ Project Structure Details ### Backend (`/backend`) -- **Django REST API** with modular app architecture -- **UV package management** for Python dependencies -- **PostgreSQL** database (configurable) -- **Redis** for caching and sessions +- **Django 5.0+** with REST Framework for API development +- **Modular app architecture** with separate apps for parks, rides, accounts, etc. +- **UV package management** for fast, reliable Python dependency management +- **PostgreSQL/SQLite** database with comprehensive entity relationships +- **Redis** for caching, sessions, and background tasks +- **Comprehensive API** with frontend serializers for camelCase conversion ### Frontend (`/frontend`) -- **Vue 3** with Composition API -- **TypeScript** for type safety -- **Vite** for fast development and building -- **Tailwind CSS** for styling -- **Pinia** for state management +- **Vue 3** with Composition API and ` - - -``` - -### Shared Components - -- **AppHeader** - Navigation and user menu -- **AppSidebar** - Main navigation sidebar -- **LoadingSpinner** - Loading indicator -- **ErrorMessage** - Error display component -- **SearchBox** - Global search functionality - -## ๐Ÿ—‚๏ธ State Management - -Using **Pinia** for state management: - -```typescript -// stores/auth.ts -export const useAuthStore = defineStore('auth', () => { - const user = ref(null) - const isAuthenticated = computed(() => !!user.value) - - const login = async (credentials: LoginData) => { - // Login logic - } - - return { user, isAuthenticated, login } -}) -``` - -### Available Stores - -- **authStore** - User authentication -- **parksStore** - Park data and operations -- **ridesStore** - Ride information -- **uiStore** - UI state (modals, notifications) -- **themeStore** - Dark/light mode toggle - -## ๐Ÿ›ฃ๏ธ Routing - -Vue Router setup with: - -- **Route guards** for authentication -- **Lazy loading** for code splitting -- **Meta fields** for page titles and permissions - -```typescript -// router/index.ts -const routes = [ - { - path: '/', - name: 'Home', - component: () => import('@/views/Home.vue') - }, - { - path: '/parks', - name: 'Parks', - component: () => import('@/views/parks/ParksIndex.vue') - } -] -``` - -## ๐ŸŽจ Styling +- **Vue 3** plugin with JSX support +- **Path aliases** for clean imports +- **CSS preprocessing** with PostCSS and Tailwind +- **Development server** with proxy to backend API +- **Build optimizations** for production ### Tailwind CSS -- **Dark mode** support with class strategy -- **Custom colors** for brand consistency -- **Responsive design** utilities -- **Component classes** for reusable styles +Custom design system configured in `tailwind.config.js`: -### Theme System +- **Custom color palette** with CSS variables +- **Dark mode support** with `class` strategy +- **Component classes** for consistent styling +- **Material Design** inspired design tokens -```typescript -// composables/useTheme.ts -export const useTheme = () => { - const isDark = ref(false) - - const toggleTheme = () => { - isDark.value = !isDark.value - document.documentElement.classList.toggle('dark') - } - - return { isDark, toggleTheme } -} -``` +## ๐Ÿ“ Project Structure Details -## ๐Ÿ”Œ API Integration +### Components Architecture -### Service Layer +#### UI Components (`src/components/ui/`) +Base component library following shadcn-vue patterns: -```typescript -// services/api.ts -class ApiService { - private client = axios.create({ - baseURL: import.meta.env.VITE_API_BASE_URL, - withCredentials: true, - }) - - // API methods - getParks(params?: ParkFilters) { - return this.client.get('/parks/', { params }) - } -} -``` +- **Button** - Multiple variants and sizes +- **Card** - Flexible content containers +- **Badge** - Status indicators and labels +- **SearchInput** - Search functionality with debouncing +- **Input, Textarea, Select** - Form components +- **Dialog, Sheet, Dropdown** - Overlay components -### Error Handling +#### Layout Components (`src/components/layout/`) +Application layout and navigation: -- Global error interceptors -- User-friendly error messages -- Retry mechanisms for failed requests -- Offline support indicators +- **Navbar** - Main navigation with responsive design +- **ThemeController** - Dark/light mode toggle +- **Footer** - Site footer with links -## ๐Ÿงช Testing +#### Specialized Components +- **State Layer** - Material Design ripple effects +- **Icon** - Lucide React icon wrapper +- **Button variants** - Different button styles -### Test Structure +### Views Structure -```bash -tests/ -โ”œโ”€โ”€ unit/ # Unit tests -โ”œโ”€โ”€ e2e/ # End-to-end tests -โ””โ”€โ”€ __mocks__/ # Mock files -``` +#### Page Components (`src/views/`) +- **Home.vue** - Landing page with featured content +- **SearchResults.vue** - Global search results display +- **parks/ParkList.vue** - List of all parks +- **parks/ParkDetail.vue** - Individual park information +- **rides/RideList.vue** - List of rides with filtering +- **rides/RideDetail.vue** - Detailed ride information -### Running Tests +### State Management -```bash -# Unit tests -pnpm run test +#### Pinia Stores (`src/stores/`) +- **Theme Store** - Dark/light mode state +- **Search Store** - Search functionality and results +- **Park Store** - Park data management +- **Ride Store** - Ride data management +- **UI Store** - General UI state -# E2E tests -pnpm run test:e2e +### API Integration -# Watch mode -pnpm run test:watch -``` +#### Services (`src/services/`) +- **API client** with Axios configuration +- **Authentication** service +- **Park service** - CRUD operations for parks +- **Ride service** - CRUD operations for rides +- **Search service** - Global search functionality -### Testing Tools +### Type Definitions -- **Vitest** - Unit testing framework -- **Vue Test Utils** - Vue component testing -- **Playwright** - End-to-end testing +#### TypeScript Types (`src/types/`) +- **API response types** matching backend serializers +- **Component prop types** for better type safety +- **Store state types** for Pinia stores +- **Utility types** for common patterns -## ๐Ÿ“ฑ Progressive Web App +## ๐ŸŽจ Design System -PWA features: +### Color Palette +- **Primary colors** - Brand identity +- **Semantic colors** - Success, warning, error states +- **Neutral colors** - Grays for text and backgrounds +- **Dark mode variants** - Automatic color adjustments -- **Service Worker** for offline functionality -- **App Manifest** for installation -- **Push Notifications** for updates -- **Background Sync** for data synchronization +### Typography +- **Inter font family** for modern appearance +- **Responsive text scales** for all screen sizes +- **Consistent line heights** for readability -## ๐Ÿ”ง Build & Deployment +### Component Variants +- **Button variants** - Primary, secondary, outline, ghost +- **Card variants** - Default, elevated, outlined +- **Input variants** - Default, error, success -### Development Build +### Dark Mode +- **Automatic detection** of system preference +- **Manual toggle** in theme controller +- **Smooth transitions** between themes +- **CSS custom properties** for dynamic theming -```bash -pnpm run dev -``` +## ๐Ÿงช Testing Strategy -### Production Build +### Unit Tests (Vitest) +- **Component testing** with Vue Test Utils +- **Composable testing** for custom hooks +- **Service testing** for API calls +- **Store testing** for Pinia state management -```bash -pnpm run build -``` +### End-to-End Tests (Playwright) +- **User journey testing** - Complete user flows +- **Cross-browser testing** - Chrome, Firefox, Safari +- **Mobile testing** - Responsive behavior +- **Accessibility testing** - WCAG compliance -### Preview Production Build - -```bash -pnpm run preview -``` - -### Build Output - -- Static assets in `dist/` -- Optimized and minified code -- Source maps for debugging -- Chunk splitting for performance - -## ๐ŸŽฏ Performance - -### Optimization Strategies - -- **Code splitting** with dynamic imports -- **Image optimization** with responsive loading -- **Bundle analysis** with rollup-plugin-visualizer -- **Lazy loading** for routes and components - -### Core Web Vitals - -- First Contentful Paint (FCP) -- Largest Contentful Paint (LCP) -- Cumulative Layout Shift (CLS) - -## โ™ฟ Accessibility - -- **ARIA labels** and roles -- **Keyboard navigation** support -- **Screen reader** compatibility -- **Color contrast** compliance -- **Focus management** - -## ๐Ÿ› Debugging - -### Development Tools - -- Vue DevTools browser extension -- Vite's built-in debugging features -- TypeScript error reporting -- Hot module replacement (HMR) - -### Logging - -```typescript -// utils/logger.ts -export const logger = { - info: (message: string, data?: any) => { - if (import.meta.env.DEV) { - console.log(message, data) - } - } -} -``` +### Test Configuration +- **Vitest config** in `vitest.config.ts` +- **Playwright config** in `playwright.config.ts` +- **Test utilities** in `src/__tests__/` +- **Mock data** for consistent testing ## ๐Ÿš€ Deployment -See the [Deployment Guide](../shared/docs/deployment/) for production setup. +### Build Process +```bash +# Production build +pnpm build + +# Preview build locally +pnpm preview + +# Type checking before build +pnpm type-check +``` + +### Build Output +- **Optimized bundles** with code splitting +- **Asset optimization** (images, fonts, CSS) +- **Source maps** for debugging (development only) +- **Service worker** for PWA features + +### Environment Configurations +- **Development** - `.env.development` +- **Staging** - `.env.staging` +- **Production** - `.env.production` + +## ๐Ÿ”ง Development Tools + +### IDE Setup +- **VSCode** with Volar extension +- **Vue Language Features** for better Vue support +- **TypeScript Importer** for auto-imports +- **Tailwind CSS IntelliSense** for styling + +### Browser Extensions +- **Vue DevTools** for debugging +- **Tailwind CSS DevTools** for styling +- **Playwright Inspector** for E2E testing + +### Performance Monitoring +- **Vite's built-in analyzer** for bundle analysis +- **Vue DevTools performance tab** +- **Lighthouse** for performance metrics + +## ๐Ÿ“– API Integration + +### Backend Communication +- **RESTful API** integration with Django backend +- **Automatic field conversion** (snake_case โ†” camelCase) +- **Error handling** with user-friendly messages +- **Loading states** for better UX + +### Authentication Flow +- **JWT token management** +- **Automatic token refresh** +- **Protected routes** with guards +- **User session management** ## ๐Ÿค Contributing -1. Follow Vue.js style guide -2. Use TypeScript for type safety -3. Write tests for components -4. Follow Prettier formatting -5. Use conventional commits \ No newline at end of file +### Code Standards +1. **Vue 3 Composition API** with ` - \ No newline at end of file + diff --git a/frontend/package.json b/frontend/package.json index 7f2f4362..661d8188 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,47 +1,66 @@ { - "name": "thrillwiki-frontend", - "version": "0.1.0", - "description": "ThrillWiki Vue.js Frontend", + "name": "frontend", + "version": "0.0.0", "private": true, "type": "module", + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, "scripts": { "dev": "vite", - "build": "vue-tsc && vite build", + "build": "run-p type-check \"build-only {@}\" --", "preview": "vite preview", - "test": "vitest", + "test:unit": "vitest", "test:e2e": "playwright test", - "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix", + "build-only": "vite build", + "type-check": "vue-tsc --build", + "lint:oxlint": "oxlint . --fix -D correctness --ignore-path .gitignore", + "lint:eslint": "eslint . --fix", + "lint": "run-s lint:*", "format": "prettier --write src/", - "type-check": "vue-tsc --noEmit" + "add": "liftkit add" }, "dependencies": { - "vue": "^3.4.0", - "vue-router": "^4.3.0", - "pinia": "^2.1.0", - "axios": "^1.6.0", - "@headlessui/vue": "^1.7.0", - "@heroicons/vue": "^2.0.0" + "@csstools/normalize.css": "^12.1.1", + "@heroicons/vue": "^2.2.0", + "@material/material-color-utilities": "^0.3.0", + "lucide-react": "^0.541.0", + "pinia": "^3.0.3", + "vue": "^3.5.19", + "vue-router": "^4.5.1" }, "devDependencies": { - "@vitejs/plugin-vue": "^5.0.0", - "vite": "^5.0.0", - "vue-tsc": "^2.0.0", - "typescript": "^5.3.0", - "tailwindcss": "^3.4.0", - "autoprefixer": "^10.4.0", - "postcss": "^8.4.0", - "eslint": "^8.57.0", - "@typescript-eslint/eslint-plugin": "^7.0.0", - "@typescript-eslint/parser": "^7.0.0", - "eslint-plugin-vue": "^9.20.0", - "prettier": "^3.2.0", - "vitest": "^1.3.0", - "@playwright/test": "^1.42.0", - "@vue/test-utils": "^2.4.0", - "jsdom": "^24.0.0" - }, - "engines": { - "node": ">=18.0.0", - "pnpm": ">=8.0.0" + "@chainlift/liftkit": "^0.2.0", + "@playwright/test": "^1.55.0", + "@prettier/plugin-oxc": "^0.0.4", + "@tailwindcss/postcss": "^4.1.12", + "@tailwindcss/vite": "^4.1.12", + "@tsconfig/node22": "^22.0.2", + "@types/jsdom": "^21.1.7", + "@types/node": "^24.3.0", + "@vitejs/plugin-vue": "^6.0.1", + "@vitejs/plugin-vue-jsx": "^5.0.1", + "@vitest/eslint-plugin": "^1.3.4", + "@vue/eslint-config-prettier": "^10.2.0", + "@vue/eslint-config-typescript": "^14.6.0", + "@vue/test-utils": "^2.4.6", + "@vue/tsconfig": "^0.8.1", + "autoprefixer": "^10.4.21", + "eslint": "^9.34.0", + "eslint-plugin-oxlint": "~1.12.0", + "eslint-plugin-playwright": "^2.2.2", + "eslint-plugin-vue": "~10.4.0", + "jiti": "^2.5.1", + "jsdom": "^26.1.0", + "npm-run-all2": "^8.0.4", + "oxlint": "~1.12.0", + "postcss": "^8.5.6", + "prettier": "3.6.2", + "tailwindcss": "^4.1.12", + "typescript": "~5.9.2", + "vite": "npm:rolldown-vite@^7.1.4", + "vite-plugin-vue-devtools": "^8.0.1", + "vitest": "^3.2.4", + "vue-tsc": "^3.0.6" } } \ No newline at end of file diff --git a/frontend/playwright.config.ts b/frontend/playwright.config.ts new file mode 100644 index 00000000..5ece9567 --- /dev/null +++ b/frontend/playwright.config.ts @@ -0,0 +1,110 @@ +import process from 'node:process' +import { defineConfig, devices } from '@playwright/test' + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// require('dotenv').config(); + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: './e2e', + /* Maximum time one test can run for. */ + timeout: 30 * 1000, + expect: { + /** + * Maximum time expect() should wait for the condition to be met. + * For example in `await expect(locator).toHaveText();` + */ + timeout: 5000, + }, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: 'html', + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ + actionTimeout: 0, + /* Base URL to use in actions like `await page.goto('/')`. */ + baseURL: process.env.CI ? 'http://localhost:4173' : 'http://localhost:5173', + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + + /* Only on CI systems run the tests headless */ + headless: !!process.env.CI, + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { + ...devices['Desktop Chrome'], + }, + }, + { + name: 'firefox', + use: { + ...devices['Desktop Firefox'], + }, + }, + { + name: 'webkit', + use: { + ...devices['Desktop Safari'], + }, + }, + + /* Test against mobile viewports. */ + // { + // name: 'Mobile Chrome', + // use: { + // ...devices['Pixel 5'], + // }, + // }, + // { + // name: 'Mobile Safari', + // use: { + // ...devices['iPhone 12'], + // }, + // }, + + /* Test against branded browsers. */ + // { + // name: 'Microsoft Edge', + // use: { + // channel: 'msedge', + // }, + // }, + // { + // name: 'Google Chrome', + // use: { + // channel: 'chrome', + // }, + // }, + ], + + /* Folder for test artifacts such as screenshots, videos, traces, etc. */ + // outputDir: 'test-results/', + + /* Run your local dev server before starting the tests */ + webServer: { + /** + * Use the dev server by default for faster feedback loop. + * Use the preview server on CI for more realistic testing. + * Playwright will re-use the local server if there is already a dev-server running. + */ + command: process.env.CI ? 'npm run preview' : 'npm run dev', + port: process.env.CI ? 4173 : 5173, + reuseExistingServer: !process.env.CI, + }, +}) diff --git a/frontend/postcss.config.js b/frontend/postcss.config.js new file mode 100644 index 00000000..af9d8dc3 --- /dev/null +++ b/frontend/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + '@tailwindcss/postcss': {}, + autoprefixer: {}, + }, +} \ No newline at end of file diff --git a/frontend/public/favicon.ico b/frontend/public/favicon.ico index e69de29b..df36fcfb 100644 Binary files a/frontend/public/favicon.ico and b/frontend/public/favicon.ico differ diff --git a/frontend/src/App.vue b/frontend/src/App.vue new file mode 100644 index 00000000..24d1abfd --- /dev/null +++ b/frontend/src/App.vue @@ -0,0 +1,459 @@ + + + + + diff --git a/frontend/src/__tests__/App.spec.ts b/frontend/src/__tests__/App.spec.ts new file mode 100644 index 00000000..5b17801e --- /dev/null +++ b/frontend/src/__tests__/App.spec.ts @@ -0,0 +1,11 @@ +import { describe, it, expect } from 'vitest' + +import { mount } from '@vue/test-utils' +import App from '../App.vue' + +describe('App', () => { + it('mounts renders properly', () => { + const wrapper = mount(App) + expect(wrapper.text()).toContain('You did it!') + }) +}) diff --git a/frontend/src/assets/styles/globals.css b/frontend/src/assets/styles/globals.css deleted file mode 100644 index 2a918a88..00000000 --- a/frontend/src/assets/styles/globals.css +++ /dev/null @@ -1,23 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -@layer base { - html { - font-family: 'Inter', system-ui, sans-serif; - } -} - -@layer components { - .btn { - @apply px-4 py-2 rounded-md font-medium focus:outline-none focus:ring-2 focus:ring-offset-2; - } - - .btn-primary { - @apply btn bg-primary-600 text-white hover:bg-primary-700 focus:ring-primary-500; - } - - .btn-secondary { - @apply btn bg-gray-200 text-gray-900 hover:bg-gray-300 focus:ring-gray-500; - } -} \ No newline at end of file diff --git a/frontend/src/components/auth/AuthManager.vue b/frontend/src/components/auth/AuthManager.vue new file mode 100644 index 00000000..cc8cdcf6 --- /dev/null +++ b/frontend/src/components/auth/AuthManager.vue @@ -0,0 +1,81 @@ + + + diff --git a/frontend/src/components/auth/AuthModal.vue b/frontend/src/components/auth/AuthModal.vue new file mode 100644 index 00000000..ba20fb35 --- /dev/null +++ b/frontend/src/components/auth/AuthModal.vue @@ -0,0 +1,103 @@ + + + diff --git a/frontend/src/components/auth/ForgotPasswordModal.vue b/frontend/src/components/auth/ForgotPasswordModal.vue new file mode 100644 index 00000000..61d361fd --- /dev/null +++ b/frontend/src/components/auth/ForgotPasswordModal.vue @@ -0,0 +1,175 @@ + + + diff --git a/frontend/src/components/auth/LoginModal.vue b/frontend/src/components/auth/LoginModal.vue new file mode 100644 index 00000000..fb87666d --- /dev/null +++ b/frontend/src/components/auth/LoginModal.vue @@ -0,0 +1,237 @@ + + + diff --git a/frontend/src/components/auth/SignupModal.vue b/frontend/src/components/auth/SignupModal.vue new file mode 100644 index 00000000..dfdba1eb --- /dev/null +++ b/frontend/src/components/auth/SignupModal.vue @@ -0,0 +1,335 @@ + + + diff --git a/frontend/src/components/button/button.css b/frontend/src/components/button/button.css new file mode 100644 index 00000000..5d9a7fce --- /dev/null +++ b/frontend/src/components/button/button.css @@ -0,0 +1,87 @@ +[data-lk-component='button'] { + /* DEFAULTS */ + --button-font-size: var(--body-font-size); + --button-line-height: var(--lk-halfstep) !important; + --button-padX: var(--button-font-size); + --button-padY: calc( + var(--button-font-size) * calc(var(--lk-halfstep) / var(--lk-size-xl-unitless)) + ); + --button-padX-sideWithIcon: calc(var(--button-font-size) / var(--lk-wholestep)); + --button-gap: calc(var(--button-padY) / var(--lk-eighthstep)); + cursor: pointer; + + display: inline-flex; + vertical-align: middle; + border: 1px solid rgba(0, 0, 0, 0); + border-radius: 100em; + position: relative; + text-decoration: none; + white-space: pre; + word-break: keep-all; + overflow: hidden; + padding: var(--button-padY) 1em; + font-weight: 500; + font-size: var(--button-font-size); + line-height: var(--button-line-height); + font-family: inherit; +} + +/* SIZE VARIANTS */ +[data-lk-button-size='sm'] { + --button-font-size: var(--subheading-font-size); +} + +[data-lk-button-size='lg'] { + --button-font-size: var(--title3-font-size); +} + +/* ICON-BASED PADDING ADJUSTMENTS */ +[data-lk-component='button']:has([data-lk-icon-position='start']) { + padding-left: var(--button-padX-sideWithIcon); + padding-right: var(--button-padX); +} + +[data-lk-component='button']:has([data-lk-icon-position='end']) { + padding-left: 1em; + padding-right: var(--button-padX-sideWithIcon); +} + +[data-lk-component='button']:has([data-lk-icon-position='start']):has( + [data-lk-icon-position='end'] + ) { + padding-left: var(--button-padX-sideWithIcon); + padding-right: var(--button-padX-sideWithIcon); +} + +/* CONTENT WRAPPER */ +[data-lk-button-content-wrap='true'] { + display: inline-flex; + align-items: center; + gap: var(--button-gap); +} + +/* TODO: Remove entirely */ + +/* [data-lk-component="button"] div:has(> [data-lk-component="icon"]) { + width: calc(1em * var(--lk-halfstep)); + aspect-ratio: 1; + display: flex; + align-items: center; + justify-content: center; +} */ + +/* ICON VERTICAL OPTICAL ALIGNMENTS */ + +[data-lk-button-optic-icon-shift='true'] div:has(> [data-lk-component='icon']) { + margin-top: calc(-1 * calc(1em * var(--lk-quarterstep-dec))); +} + +/* STYLE VARIANTS */ + +[data-lk-button-variant='text'] { + background: transparent !important; +} +[data-lk-button-variant='outline'] { + background: transparent !important; + border: 1px solid var(--lk-outlinevariant); +} diff --git a/frontend/src/components/button/index.tsx b/frontend/src/components/button/index.tsx new file mode 100644 index 00000000..311eec36 --- /dev/null +++ b/frontend/src/components/button/index.tsx @@ -0,0 +1,137 @@ +'use client' + +import { useMemo } from 'react' +import { propsToDataAttrs } from '@/lib/utilities' +import { getOnToken } from '@/lib/colorUtils' +import { IconName } from 'lucide-react/dynamic' +import '@/components/button/button.css' +import StateLayer from '@/components/state-layer' +import { LkStateLayerProps } from '@/components/state-layer' +import Icon from '@/components/icon' + +export interface LkButtonProps extends React.ButtonHTMLAttributes { + label?: string + variant?: 'fill' | 'outline' | 'text' + color?: LkColorWithOnToken + size?: 'sm' | 'md' | 'lg' + material?: string + startIcon?: IconName + endIcon?: IconName + opticIconShift?: boolean + modifiers?: string + stateLayerOverride?: LkStateLayerProps // Optional override for state layer properties +} + +/** + * A customizable button component with support for various visual styles, sizes, and icons. + * + * @param props - The button component props + * @param props.label - The text content displayed inside the button. Defaults to "Button" + * @param props.variant - The visual style variant of the button. Defaults to "fill" + * @param props.color - The color theme of the button. Defaults to "primary" + * @param props.size - The size of the button (sm, md, lg). Defaults to "md" + * @param props.startIcon - Optional icon element to display at the start of the button + * @param props.endIcon - Optional icon element to display at the end of the button + * @param props.restProps - Additional props to be spread to the underlying button element + * @param props.opticIconShift - Boolean to control optical icon alignment on the y-axis. Defaults to true. Pulls icons up slightly. + * @param props.modifiers - Additional class names to concatenate onto the button's default class list + * @param props.stateLayerOverride - Optional override for state layer properties, allowing customization of the state layer's appearance + * + * @returns A styled button element with optional start/end icons and a state layer overlay + * + * @example + * ```tsx + * + ) +} diff --git a/frontend/src/components/icon/icon.css b/frontend/src/components/icon/icon.css new file mode 100644 index 00000000..9353c737 --- /dev/null +++ b/frontend/src/components/icon/icon.css @@ -0,0 +1,238 @@ +[data-lk-component='icon'] { + width: calc(1em * var(--lk-halfstep)); + aspect-ratio: 1; + display: flex; + align-items: center; + justify-content: center; +} + +/* Regular families */ +[data-lk-icon-font-class='display1'] { + --lineHeightInEms: calc(1em * var(--lk-quarterstep)); + --md: var(--lineHeightInEms); + + --sm: calc(var(--lineHeightInEms) * var(--lk-wholestep-dec)); + --xs: calc(var(--lineHeightInEms) * var(--lk-halfstep-dec)); + --2xs: calc(var(--lineHeightInEms) * var(--lk-quarterstep-dec)); + + --sm: calc(var(--lineHeightInEms) * calc(1 / var(--lk-halfstep))); + --lg: calc(var(--lineHeightInEms) * var(--lk-wholestep)); + --xl: calc(var(--lk-size-lg) * var(--lk-wholestep)); + --2xl: calc(var(--lk-size-xl) * var(--lk-wholestep)); + + letter-spacing: -0.022em; + font-size: var(--display1-font-size); + font-weight: 400; + line-height: var(--lk-quarterstep); +} + +[data-lk-icon-font-class='display2'] { + --lineHeightInEms: calc(1em * var(--lk-halfstep)); + --md: var(--lineHeightInEms); + + --sm: calc(var(--lineHeightInEms) * var(--lk-wholestep-dec)); + --xs: calc(var(--lineHeightInEms) * var(--lk-halfstep-dec)); + --2xs: calc(var(--lineHeightInEms) * var(--lk-quarterstep-dec)); + + --lg: calc(var(--lineHeightInEms) * var(--lk-wholestep)); + --xl: calc(var(--lk-size-lg) * var(--lk-wholestep)); + --2xl: calc(var(--lk-size-xl) * var(--lk-wholestep)); + + letter-spacing: -0.022em; + font-size: var(--display2-font-size); + font-weight: 400; + line-height: var(--lk-halfstep); +} + +[data-lk-icon-font-class='title1'] { + --lineHeightInEms: calc(1em * var(--lk-halfstep)); + --md: var(--lineHeightInEms); + + --sm: calc(var(--lineHeightInEms) * var(--lk-wholestep-dec)); + --xs: calc(var(--lineHeightInEms) * var(--lk-halfstep-dec)); + --2xs: calc(var(--lineHeightInEms) * var(--lk-quarterstep-dec)); + + --lg: calc(var(--lineHeightInEms) * var(--lk-wholestep)); + --xl: calc(var(--lk-size-lg) * var(--lk-wholestep)); + --2xl: calc(var(--lk-size-xl) * var(--lk-wholestep)); + + letter-spacing: -0.022em; + font-size: var(--title1-font-size); + font-weight: 400; + line-height: var(--lk-halfstep); +} + +[data-lk-icon-font-class='title2'] { + --lineHeightInEms: calc(1em * var(--lk-halfstep)); + --md: var(--lineHeightInEms); + + --sm: calc(var(--lineHeightInEms) * var(--lk-wholestep-dec)); + --xs: calc(var(--lineHeightInEms) * var(--lk-halfstep-dec)); + --2xs: calc(var(--lineHeightInEms) * var(--lk-quarterstep-dec)); + + --lg: calc(var(--lineHeightInEms) * var(--lk-wholestep)); + --xl: calc(var(--lk-size-lg) * var(--lk-wholestep)); + --2xl: calc(var(--lk-size-xl) * var(--lk-wholestep)); + + letter-spacing: -0.02em; + font-size: var(--title2-font-size); + font-weight: 400; + line-height: var(--lk-halfstep); +} + +[data-lk-icon-font-class='title3'] { + --lineHeightInEms: calc(1em * var(--lk-halfstep)); + --md: var(--lineHeightInEms); + + --sm: calc(var(--lineHeightInEms) * var(--lk-wholestep-dec)); + --xs: calc(var(--lineHeightInEms) * var(--lk-halfstep-dec)); + --2xs: calc(var(--lineHeightInEms) * var(--lk-quarterstep-dec)); + + --lg: calc(var(--lineHeightInEms) * var(--lk-wholestep)); + --xl: calc(var(--lk-size-lg) * var(--lk-wholestep)); + --2xl: calc(var(--lk-size-xl) * var(--lk-wholestep)); + + letter-spacing: -0.017em; + font-size: var(--title3-font-size); + font-weight: 400; + line-height: var(--lk-halfstep); +} + +[data-lk-icon-font-class='heading'] { + --lineHeightInEms: calc(1em * var(--lk-halfstep)); + --md: var(--lineHeightInEms); + + --sm: calc(var(--lineHeightInEms) * var(--lk-wholestep-dec)); + --xs: calc(var(--lineHeightInEms) * var(--lk-halfstep-dec)); + --2xs: calc(var(--lineHeightInEms) * var(--lk-quarterstep-dec)); + + --lg: calc(var(--lineHeightInEms) * var(--lk-wholestep)); + --xl: calc(var(--lk-size-lg) * var(--lk-wholestep)); + --2xl: calc(var(--lk-size-xl) * var(--lk-wholestep)); + + letter-spacing: -0.014em; + font-size: var(--heading-font-size); + font-weight: 600; + line-height: var(--lk-halfstep); +} + +[data-lk-icon-font-class='subheading'] { + --lineHeightInEms: calc(1em * var(--lk-halfstep)); + --md: var(--lineHeightInEms); + + --sm: calc(var(--lineHeightInEms) * var(--lk-wholestep-dec)); + --xs: calc(var(--lineHeightInEms) * var(--lk-halfstep-dec)); + --2xs: calc(var(--lineHeightInEms) * var(--lk-quarterstep-dec)); + + --lg: calc(var(--lineHeightInEms) * var(--lk-wholestep)); + --xl: calc(var(--lk-size-lg) * var(--lk-wholestep)); + --2xl: calc(var(--lk-size-xl) * var(--lk-wholestep)); + + letter-spacing: -0.007em; + font-size: var(--subheading-font-size); + font-weight: 400; + line-height: var(--lk-halfstep); +} + +[data-lk-icon-font-class='body'] { + --lineHeightInEms: var(--title2-font-size); + --md: 1em; + --sm: calc(var(--lineHeightInEms) * var(--lk-wholestep-dec)); + --xs: calc(var(--lineHeightInEms) * var(--lk-halfstep-dec)); + --2xs: calc(var(--lineHeightInEms) * var(--lk-quarterstep-dec)); + + --lg: calc(1em * var(--lk-wholestep)); + --xl: calc(var(--lk-size-lg) * var(--lk-wholestep)); + --2xl: calc(var(--lk-size-xl) * var(--lk-wholestep)); + + letter-spacing: -0.011em; + cursor: default; + font-size: 1em; + font-weight: 400; + line-height: var(--lk-wholestep); +} + +[data-lk-icon-font-class='callout'] { + --lineHeightInEms: calc(1em * var(--lk-halfstep)); + --md: var(--lineHeightInEms); + + --sm: calc(var(--lineHeightInEms) * var(--lk-wholestep-dec)); + --xs: calc(var(--lineHeightInEms) * var(--lk-halfstep-dec)); + --2xs: calc(var(--lineHeightInEms) * var(--lk-quarterstep-dec)); + + --lg: calc(var(--lineHeightInEms) * var(--lk-wholestep)); + --xl: calc(var(--lk-size-lg) * var(--lk-wholestep)); + --2xl: calc(var(--lk-size-xl) * var(--lk-wholestep)); + + letter-spacing: -0.009em; + font-size: var(--callout-font-size); + font-weight: 400; + line-height: var(--lk-halfstep); +} + +[data-lk-icon-font-class='label'] { + --lineHeightInEms: calc(1em * var(--lk-halfstep)); + --md: var(--lineHeightInEms); + + --sm: calc(var(--lineHeightInEms) * var(--lk-wholestep-dec)); + --xs: calc(var(--lineHeightInEms) * var(--lk-halfstep-dec)); + --2xs: calc(var(--lineHeightInEms) * var(--lk-quarterstep-dec)); + + --lg: calc(var(--lineHeightInEms) * var(--lk-wholestep)); + --xl: calc(var(--lk-size-lg) * var(--lk-wholestep)); + --2xl: calc(var(--lk-size-xl) * var(--lk-wholestep)); + + letter-spacing: -0.004em; + font-size: var(--label-font-size); + font-weight: 600; + line-height: var(--lk-halfstep); + position: static; +} + +[data-lk-icon-font-class='caption'] { + --lineHeightInEms: calc(1em * var(--lk-halfstep)); + --md: var(--lineHeightInEms); + + --sm: calc(var(--lineHeightInEms) * var(--lk-wholestep-dec)); + --xs: calc(var(--lineHeightInEms) * var(--lk-halfstep-dec)); + --2xs: calc(var(--lineHeightInEms) * var(--lk-quarterstep-dec)); + + --lg: calc(var(--lineHeightInEms) * var(--lk-wholestep)); + --xl: calc(var(--lk-size-lg) * var(--lk-wholestep)); + --2xl: calc(var(--lk-size-xl) * var(--lk-wholestep)); + + letter-spacing: -0.007em; + font-size: var(--caption-font-size); + font-weight: 400; + line-height: var(--lk-halfstep); +} + +[data-lk-icon-font-class='capline'] { + --lineHeightInEms: calc(1em * var(--lk-halfstep)); + --md: var(--lineHeightInEms); + + --sm: calc(var(--lineHeightInEms) * var(--lk-wholestep-dec)); + --xs: calc(var(--lineHeightInEms) * var(--lk-halfstep-dec)); + --2xs: calc(var(--lineHeightInEms) * var(--lk-quarterstep-dec)); + + --lg: calc(var(--lineHeightInEms) * var(--lk-wholestep)); + --xl: calc(var(--lk-size-lg) * var(--lk-wholestep)); + --2xl: calc(var(--lk-size-xl) * var(--lk-wholestep)); + + letter-spacing: 0.0618em; + text-transform: uppercase; + font-size: var(--capline-font-size); + font-weight: 400; + line-height: var(--lk-halfstep); +} + +/* Ignore the width and aspect ratio rules when inside an icon-button component */ + +[data-lk-component='icon-button'] [data-lk-component='icon'] { + width: unset; + aspect-ratio: unset; +} + +[data-lk-icon-offset='true'] { + margin-top: calc(-1 * calc(1em * var(--lk-quarterstep-dec))); +} diff --git a/frontend/src/components/icon/index.tsx b/frontend/src/components/icon/index.tsx new file mode 100644 index 00000000..523f81f4 --- /dev/null +++ b/frontend/src/components/icon/index.tsx @@ -0,0 +1,38 @@ +import { DynamicIcon } from 'lucide-react/dynamic' +import type { IconName } from 'lucide-react/dynamic' +import '@/components/icon/icon.css' + +export interface LkIconProps extends React.HTMLAttributes { + name?: IconName + fontClass?: Exclude + color?: LkColor | 'currentColor' + display?: 'block' | 'inline-block' | 'inline' + strokeWidth?: number + opticShift?: boolean //if true, pulls icon slightly upward +} + +export default function Icon({ + name = 'roller-coaster', + fontClass, + color = 'onsurface', + strokeWidth = 2, + opticShift = false, + ...restProps +}: LkIconProps) { + return ( +
+ +
+ ) +} diff --git a/frontend/src/components/icons/DiscordIcon.vue b/frontend/src/components/icons/DiscordIcon.vue new file mode 100644 index 00000000..65f82a0f --- /dev/null +++ b/frontend/src/components/icons/DiscordIcon.vue @@ -0,0 +1,7 @@ + diff --git a/frontend/src/components/icons/GoogleIcon.vue b/frontend/src/components/icons/GoogleIcon.vue new file mode 100644 index 00000000..5af6a62b --- /dev/null +++ b/frontend/src/components/icons/GoogleIcon.vue @@ -0,0 +1,20 @@ + diff --git a/frontend/src/components/layout/Navbar.vue b/frontend/src/components/layout/Navbar.vue new file mode 100644 index 00000000..2b75c040 --- /dev/null +++ b/frontend/src/components/layout/Navbar.vue @@ -0,0 +1,259 @@ + + + diff --git a/frontend/src/components/layout/ThemeController.vue b/frontend/src/components/layout/ThemeController.vue new file mode 100644 index 00000000..8c5f466e --- /dev/null +++ b/frontend/src/components/layout/ThemeController.vue @@ -0,0 +1,363 @@ + + + diff --git a/frontend/src/components/state-layer/index.tsx b/frontend/src/components/state-layer/index.tsx new file mode 100644 index 00000000..056a2ed9 --- /dev/null +++ b/frontend/src/components/state-layer/index.tsx @@ -0,0 +1,19 @@ +import '@/components/state-layer/state-layer.css' + +export interface LkStateLayerProps { + bgColor?: LkColor | 'currentColor' + forcedState?: 'hover' | 'active' | 'focus' // Used when you need a static state controlled by something higher, like a select field that keeps actively-selected options grayed out +} + +export default function StateLayer({ bgColor = 'currentColor', forcedState }: LkStateLayerProps) { + return ( + <> +
+ + ) +} diff --git a/frontend/src/components/state-layer/state-layer.css b/frontend/src/components/state-layer/state-layer.css new file mode 100644 index 00000000..70d21fe6 --- /dev/null +++ b/frontend/src/components/state-layer/state-layer.css @@ -0,0 +1,46 @@ +[data-lk-component='state-layer'] { + z-index: 1; + opacity: 0; + + pointer-events: none; + position: absolute; + top: 0%; + bottom: 0%; + left: 0%; + right: 0%; + transition: opacity 0.1s ease-out; +} + +/* Only apply styles to the [data-lk-component="state-layer"] when its direct parent is hovered, active, or focused */ +div:hover > [data-lk-component='state-layer'], +a:hover > [data-lk-component='state-layer'], +button:hover > [data-lk-component='state-layer'], +li:hover > [data-lk-component='state-layer'] { + opacity: 0.16 !important; +} + +div:active > [data-lk-component='state-layer'], +a:active > [data-lk-component='state-layer'], +button:active > [data-lk-component='state-layer'], +li:active > [data-lk-component='state-layer'] { + opacity: 0.5 !important; +} + +div:focus > [data-lk-component='state-layer'], +a:focus > [data-lk-component='state-layer'], +button:focus > [data-lk-component='state-layer'], +li:focus > [data-lk-component='state-layer'] { + opacity: 0.35 !important; +} + +.is-active [data-lk-component='state-layer'] { + opacity: 0.16 !important; +} + +.list-item.active [data-lk-component='state-layer'] { + opacity: 0.24 !important; +} + +[data-lk-forced-state='active'] { + opacity: 0.12 !important; +} diff --git a/frontend/src/components/ui/Badge.vue b/frontend/src/components/ui/Badge.vue new file mode 100644 index 00000000..52c6ce4e --- /dev/null +++ b/frontend/src/components/ui/Badge.vue @@ -0,0 +1,123 @@ + + + diff --git a/frontend/src/components/ui/Button.vue b/frontend/src/components/ui/Button.vue new file mode 100644 index 00000000..7bc28762 --- /dev/null +++ b/frontend/src/components/ui/Button.vue @@ -0,0 +1,172 @@ + + + diff --git a/frontend/src/components/ui/Card.vue b/frontend/src/components/ui/Card.vue new file mode 100644 index 00000000..769c9cd9 --- /dev/null +++ b/frontend/src/components/ui/Card.vue @@ -0,0 +1,206 @@ + + + diff --git a/frontend/src/components/ui/SearchInput.vue b/frontend/src/components/ui/SearchInput.vue new file mode 100644 index 00000000..f3d157c7 --- /dev/null +++ b/frontend/src/components/ui/SearchInput.vue @@ -0,0 +1,297 @@ + + + diff --git a/frontend/src/components/ui/index.ts b/frontend/src/components/ui/index.ts new file mode 100644 index 00000000..3839874b --- /dev/null +++ b/frontend/src/components/ui/index.ts @@ -0,0 +1,82 @@ +// UI Components +export { default as Badge } from './Badge.vue' +export { default as Button } from './Button.vue' +export { default as Card } from './Card.vue' +export { default as SearchInput } from './SearchInput.vue' + +// Layout Components +export { default as ThemeController } from '../layout/ThemeController.vue' +export { default as Navbar } from '../layout/Navbar.vue' + +// Type definitions for component props +export interface BadgeProps { + variant?: 'default' | 'primary' | 'secondary' | 'success' | 'warning' | 'error' | 'info' + size?: 'sm' | 'md' | 'lg' + rounded?: boolean + outline?: boolean + removable?: boolean +} + +export interface ButtonProps { + variant?: 'primary' | 'secondary' | 'outline' | 'ghost' | 'link' | 'destructive' + size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' + disabled?: boolean + loading?: boolean + block?: boolean + rounded?: 'none' | 'sm' | 'md' | 'lg' | 'full' + iconStart?: any + iconEnd?: any + iconOnly?: boolean + href?: string + to?: string | object + target?: string + type?: 'button' | 'submit' | 'reset' +} + +export interface CardProps { + variant?: 'default' | 'outline' | 'ghost' | 'elevated' + size?: 'sm' | 'md' | 'lg' + title?: string + padding?: 'none' | 'sm' | 'md' | 'lg' + rounded?: 'none' | 'sm' | 'md' | 'lg' | 'xl' + shadow?: 'none' | 'sm' | 'md' | 'lg' | 'xl' + hover?: boolean + interactive?: boolean +} + +export interface SearchInputProps { + modelValue?: string + type?: 'search' | 'text' + placeholder?: string + label?: string + disabled?: boolean + readonly?: boolean + clearable?: boolean + searchButton?: boolean | 'icon' | 'text' + allowEmptySearch?: boolean + size?: 'sm' | 'md' | 'lg' + variant?: 'default' | 'outline' + error?: string + helpText?: string + ariaLabel?: string + clearButtonLabel?: string + searchButtonLabel?: string + debounceMs?: number +} + +export interface ThemeControllerProps { + variant?: 'button' | 'dropdown' + size?: 'sm' | 'md' | 'lg' + showText?: boolean + showDropdown?: boolean + position?: 'fixed' | 'relative' +} + +export interface NavbarProps { + sticky?: boolean + shadow?: boolean + height?: 'compact' | 'default' | 'comfortable' + showMobileSearch?: boolean + showThemeToggle?: boolean + showMobileMenu?: boolean +} diff --git a/frontend/src/composables/useAuth.ts b/frontend/src/composables/useAuth.ts new file mode 100644 index 00000000..79dd0ecd --- /dev/null +++ b/frontend/src/composables/useAuth.ts @@ -0,0 +1,201 @@ +import { ref, computed } from 'vue' +import { authApi, type AuthResponse, type User } from '@/services/api' +import type { LoginCredentials, SignupCredentials } from '@/types' + +// Global authentication state +const currentUser = ref(null) +const authToken = ref(localStorage.getItem('auth_token')) +const isLoading = ref(false) +const authError = ref(null) + +// Computed properties +const isAuthenticated = computed(() => !!currentUser.value && !!authToken.value) + +// Authentication composable +export function useAuth() { + /** + * Initialize authentication state + */ + const initAuth = async () => { + const token = localStorage.getItem('auth_token') + if (token) { + authToken.value = token + authApi.setAuthToken(token) + await getCurrentUser() + } + } + + /** + * Get current user information + */ + const getCurrentUser = async () => { + if (!authToken.value) return null + + try { + const user = await authApi.getCurrentUser() + currentUser.value = user + return user + } catch (error) { + console.error('Failed to get current user:', error) + await logout() + return null + } + } + + /** + * Login with username/email and password + */ + const login = async (credentials: LoginCredentials): Promise => { + try { + isLoading.value = true + authError.value = null + + const response = await authApi.login(credentials) + + // Store authentication data + authToken.value = response.token + currentUser.value = response.user + localStorage.setItem('auth_token', response.token) + + return response + } catch (error) { + const errorMessage = error instanceof Error ? error.message : 'Login failed' + authError.value = errorMessage + throw error + } finally { + isLoading.value = false + } + } + + /** + * Register new user + */ + const signup = async (credentials: SignupCredentials): Promise => { + try { + isLoading.value = true + authError.value = null + + const response = await authApi.signup(credentials) + + // Store authentication data + authToken.value = response.token + currentUser.value = response.user + localStorage.setItem('auth_token', response.token) + + return response + } catch (error) { + const errorMessage = error instanceof Error ? error.message : 'Registration failed' + authError.value = errorMessage + throw error + } finally { + isLoading.value = false + } + } + + /** + * Logout user + */ + const logout = async () => { + try { + if (authToken.value) { + await authApi.logout() + } + } catch (error) { + console.error('Logout error:', error) + } finally { + // Clear local state regardless of API call success + currentUser.value = null + authToken.value = null + localStorage.removeItem('auth_token') + authApi.setAuthToken(null) + } + } + + /** + * Request password reset + */ + const requestPasswordReset = async (email: string) => { + try { + isLoading.value = true + authError.value = null + + const response = await authApi.requestPasswordReset({ email }) + return response + } catch (error) { + const errorMessage = error instanceof Error ? error.message : 'Password reset request failed' + authError.value = errorMessage + throw error + } finally { + isLoading.value = false + } + } + + /** + * Change password + */ + const changePassword = async (oldPassword: string, newPassword: string) => { + try { + isLoading.value = true + authError.value = null + + const response = await authApi.changePassword({ + old_password: oldPassword, + new_password: newPassword, + new_password_confirm: newPassword, + }) + + return response + } catch (error) { + const errorMessage = error instanceof Error ? error.message : 'Password change failed' + authError.value = errorMessage + throw error + } finally { + isLoading.value = false + } + } + + /** + * Get available social providers + */ + const getSocialProviders = async () => { + try { + return await authApi.getSocialProviders() + } catch (error) { + console.error('Failed to get social providers:', error) + return [] + } + } + + /** + * Clear authentication error + */ + const clearError = () => { + authError.value = null + } + + return { + // State + currentUser: computed(() => currentUser.value), + authToken: computed(() => authToken.value), + isLoading: computed(() => isLoading.value), + authError: computed(() => authError.value), + isAuthenticated, + + // Methods + initAuth, + getCurrentUser, + login, + signup, + logout, + requestPasswordReset, + changePassword, + getSocialProviders, + clearError, + } +} + +// Initialize auth state on app startup +const auth = useAuth() +auth.initAuth() + +export default auth diff --git a/frontend/src/main.ts b/frontend/src/main.ts index ec82fe68..59f6067d 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -1,13 +1,15 @@ import { createApp } from 'vue' import { createPinia } from 'pinia' -import router from './router' -import App from './App.vue' -import './assets/styles/globals.css' +import App from './App.vue' +import router from './router' + +// Import Tailwind CSS +import './style.css' const app = createApp(App) app.use(createPinia()) app.use(router) -app.mount('#app') \ No newline at end of file +app.mount('#app') diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts new file mode 100644 index 00000000..e97bebd7 --- /dev/null +++ b/frontend/src/router/index.ts @@ -0,0 +1,104 @@ +import { createRouter, createWebHistory } from 'vue-router' +import Home from '@/views/Home.vue' +import ParkList from '@/views/parks/ParkList.vue' +import ParkDetail from '@/views/parks/ParkDetail.vue' +import RideList from '@/views/rides/RideList.vue' +import RideDetail from '@/views/rides/RideDetail.vue' +import SearchResults from '@/views/SearchResults.vue' +import Login from '@/views/accounts/Login.vue' +import Signup from '@/views/accounts/Signup.vue' +import ForgotPassword from '@/views/accounts/ForgotPassword.vue' +import NotFound from '@/views/NotFound.vue' +import Error from '@/views/Error.vue' + +const router = createRouter({ + history: createWebHistory(import.meta.env.BASE_URL), + routes: [ + { + path: '/', + name: 'home', + component: Home, + }, + { + path: '/parks/', + name: 'park-list', + component: ParkList, + }, + { + path: '/parks/:slug/', + name: 'park-detail', + component: ParkDetail, + props: true, + }, + { + path: '/parks/:parkSlug/rides/', + name: 'park-ride-list', + component: RideList, + props: true, + }, + { + path: '/parks/:parkSlug/rides/:rideSlug/', + name: 'ride-detail', + component: RideDetail, + props: true, + }, + { + path: '/rides/', + name: 'global-ride-list', + component: RideList, + }, + { + path: '/rides/:rideSlug/', + name: 'global-ride-detail', + component: RideDetail, + props: true, + }, + { + path: '/search/', + name: 'search-results', + component: SearchResults, + }, + { + path: '/search/parks/', + name: 'search-parks', + component: SearchResults, + props: { searchType: 'parks' }, + }, + { + path: '/search/rides/', + name: 'search-rides', + component: SearchResults, + props: { searchType: 'rides' }, + }, + // Authentication routes + { + path: '/auth/login/', + name: 'login', + component: Login, + }, + { + path: '/auth/signup/', + name: 'signup', + component: Signup, + }, + { + path: '/auth/forgot-password/', + name: 'forgot-password', + component: ForgotPassword, + }, + // Error routes + { + path: '/error/', + name: 'error', + component: Error, + }, + // 404 catch-all route (must be last) + { + path: '/:pathMatch(.*)*', + name: 'not-found', + component: NotFound, + }, + ], +}) + +export default router diff --git a/frontend/src/services/api.ts b/frontend/src/services/api.ts new file mode 100644 index 00000000..02f8dd54 --- /dev/null +++ b/frontend/src/services/api.ts @@ -0,0 +1,800 @@ +/** + * API service for communicating with Django backend + */ + +import type { + Park, + Ride, + User, + LoginCredentials, + SignupCredentials, + AuthResponse, + PasswordResetRequest, + PasswordChangeRequest, + SocialAuthProvider, +} from '@/types' + +// History-specific types +export interface HistoryEvent { + id: string + pgh_created_at: string + pgh_label: 'created' | 'updated' | 'deleted' + pgh_model: string + pgh_obj_id: number + pgh_context?: { + user_id?: number + request_id?: string + ip_address?: string + } + changed_fields?: string[] + field_changes?: Record +} + +export interface UnifiedHistoryEvent { + id: string + pgh_created_at: string + pgh_label: 'created' | 'updated' | 'deleted' + pgh_model: string + pgh_obj_id: number + entity_name: string + entity_slug: string + change_significance: 'major' | 'minor' | 'routine' + change_summary: string +} + +export interface HistorySummary { + total_events: number + first_recorded: string | null + last_modified: string | null + significant_changes?: Array<{ + date: string + event_type: string + description: string + }> +} + +export interface ParkHistoryResponse { + park: Park + current_state: Park + summary: HistorySummary + events: HistoryEvent[] +} + +export interface RideHistoryResponse { + ride: Ride + current_state: Ride + summary: HistorySummary + events: HistoryEvent[] +} + +export interface UnifiedHistoryTimeline { + summary: { + total_events: number + events_returned: number + event_type_breakdown: Record + model_type_breakdown: Record + time_range: { + earliest: string | null + latest: string | null + } + } + events: UnifiedHistoryEvent[] +} + +export interface HistoryParams { + limit?: number + offset?: number + event_type?: 'created' | 'updated' | 'deleted' + start_date?: string + end_date?: string + model_type?: 'park' | 'ride' | 'company' | 'user' + significance?: 'major' | 'minor' | 'routine' +} + +// API configuration +const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || '' + +// API response types +interface ApiResponse { + count: number + next: string | null + previous: string | null + results: T[] +} + +interface SearchResponse { + results: T[] + count: number + query: string +} + +/** + * Base API client with common functionality + */ +class ApiClient { + private baseUrl: string + private authToken: string | null = null + + constructor(baseUrl: string = API_BASE_URL) { + this.baseUrl = baseUrl + // Check for existing auth token + this.authToken = localStorage.getItem('auth_token') + } + + /** + * Set authentication token + */ + setAuthToken(token: string | null) { + this.authToken = token + if (token) { + localStorage.setItem('auth_token', token) + } else { + localStorage.removeItem('auth_token') + } + } + + /** + * Get authentication token + */ + getAuthToken(): string | null { + return this.authToken + } + + /** + * Make HTTP request with error handling + */ + private async request(endpoint: string, options: RequestInit = {}): Promise { + const url = `${this.baseUrl}${endpoint}` + + const headers: Record = { + 'Content-Type': 'application/json', + Accept: 'application/json', + } + + // Add auth token if available + if (this.authToken) { + headers['Authorization'] = `Token ${this.authToken}` + } + + // Add CSRF token for state-changing requests + if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(options.method || 'GET')) { + const csrfToken = this.getCSRFToken() + if (csrfToken) { + headers['X-CSRFToken'] = csrfToken + } + } + + const defaultOptions: RequestInit = { + headers, + credentials: 'include', // Include cookies for session auth + ...options, + } + + try { + const response = await fetch(url, defaultOptions) + + if (!response.ok) { + const errorData = await response.json().catch(() => ({})) + throw new Error( + errorData.detail || errorData.message || `HTTP error! status: ${response.status}`, + ) + } + + return await response.json() + } catch (error) { + console.error(`API request failed for ${endpoint}:`, error) + throw error + } + } + + /** + * Get CSRF token from cookies + */ + private getCSRFToken(): string | null { + const name = 'csrftoken' + if (document.cookie) { + const cookies = document.cookie.split(';') + for (let cookie of cookies) { + cookie = cookie.trim() + if (cookie.substring(0, name.length + 1) === name + '=') { + return decodeURIComponent(cookie.substring(name.length + 1)) + } + } + } + return null + } + + /** + * GET request + */ + async get(endpoint: string, params?: Record): Promise { + let url = endpoint + + if (params) { + const searchParams = new URLSearchParams(params) + url += `?${searchParams.toString()}` + } + + return this.request(url, { method: 'GET' }) + } + + /** + * POST request + */ + async post(endpoint: string, data?: any): Promise { + return this.request(endpoint, { + method: 'POST', + body: data ? JSON.stringify(data) : undefined, + }) + } + + /** + * PUT request + */ + async put(endpoint: string, data?: any): Promise { + return this.request(endpoint, { + method: 'PUT', + body: data ? JSON.stringify(data) : undefined, + }) + } + + /** + * DELETE request + */ + async delete(endpoint: string): Promise { + return this.request(endpoint, { method: 'DELETE' }) + } +} + +/** + * Parks API service + */ +export class ParksApi { + private client: ApiClient + + constructor(client: ApiClient = new ApiClient()) { + this.client = client + } + + /** + * Get all parks with pagination + */ + async getParks(params?: { + page?: number + search?: string + ordering?: string + }): Promise> { + const queryParams: Record = {} + + if (params?.page) queryParams.page = params.page.toString() + if (params?.search) queryParams.search = params.search + if (params?.ordering) queryParams.ordering = params.ordering + + return this.client.get>('/api/parks/', queryParams) + } + + /** + * Get a single park by slug + */ + async getPark(slug: string): Promise { + return this.client.get(`/api/parks/${slug}/`) + } + + /** + * Search parks + */ + async searchParks(query: string): Promise> { + return this.client.get>('/api/parks/search/', { q: query }) + } + + /** + * Get rides for a specific park + */ + async getParkRides(parkSlug: string): Promise> { + return this.client.get>(`/api/parks/${parkSlug}/rides/`) + } + + /** + * Get recently changed parks + */ + async getRecentChanges(days?: number): Promise<{ + count: number + days: number + parks: Park[] + }> { + const params: Record = {} + if (days) params.days = days.toString() + return this.client.get<{ + count: number + days: number + parks: Park[] + }>('/api/v1/parks/recent_changes/', params) + } + + /** + * Get recently opened parks + */ + async getRecentOpenings(days?: number): Promise<{ + count: number + days: number + parks: Park[] + }> { + const params: Record = {} + if (days) params.days = days.toString() + return this.client.get<{ + count: number + days: number + parks: Park[] + }>('/api/v1/parks/recent_openings/', params) + } + + /** + * Get recently closed parks + */ + async getRecentClosures(days?: number): Promise<{ + count: number + days: number + parks: Park[] + }> { + const params: Record = {} + if (days) params.days = days.toString() + return this.client.get<{ + count: number + days: number + parks: Park[] + }>('/api/v1/parks/recent_closures/', params) + } + + /** + * Get parks with recent name changes + */ + async getRecentNameChanges(days?: number): Promise<{ + count: number + days: number + parks: Park[] + }> { + const params: Record = {} + if (days) params.days = days.toString() + return this.client.get<{ + count: number + days: number + parks: Park[] + }>('/api/v1/parks/recent_name_changes/', params) + } +} + +/** + * Rides API service + */ +export class RidesApi { + private client: ApiClient + + constructor(client: ApiClient = new ApiClient()) { + this.client = client + } + + /** + * Get all rides with pagination + */ + async getRides(params?: { + page?: number + search?: string + ordering?: string + }): Promise> { + const queryParams: Record = {} + + if (params?.page) queryParams.page = params.page.toString() + if (params?.search) queryParams.search = params.search + if (params?.ordering) queryParams.ordering = params.ordering + + return this.client.get>('/api/rides/', queryParams) + } + + /** + * Get a single ride by park and ride slug + */ + async getRide(parkSlug: string, rideSlug: string): Promise { + return this.client.get(`/api/rides/${parkSlug}/${rideSlug}/`) + } + + /** + * Search rides + */ + async searchRides(query: string): Promise> { + return this.client.get>('/api/rides/search/', { q: query }) + } + + /** + * Get rides by park + */ + async getRidesByPark(parkSlug: string): Promise> { + return this.client.get>(`/api/rides/by-park/${parkSlug}/`) + } + + /** + * Get history for a specific ride (convenience method) + */ + async getRideHistory( + parkSlug: string, + rideSlug: string, + params?: HistoryParams + ): Promise { + const historyApi = new HistoryApi(this.client) + return historyApi.getRideHistory(parkSlug, rideSlug, params) + } + + /** + * Get complete ride history with current state (convenience method) + */ + async getRideHistoryDetail(parkSlug: string, rideSlug: string): Promise { + const historyApi = new HistoryApi(this.client) + return historyApi.getRideHistoryDetail(parkSlug, rideSlug) + } + + /** + * Get recently changed rides + */ + async getRecentChanges(days?: number): Promise<{ + count: number + days: number + rides: Ride[] + }> { + const params: Record = {} + if (days) params.days = days.toString() + return this.client.get<{ + count: number + days: number + rides: Ride[] + }>('/api/v1/rides/recent_changes/', params) + } + + /** + * Get recently opened rides + */ + async getRecentOpenings(days?: number): Promise<{ + count: number + days: number + rides: Ride[] + }> { + const params: Record = {} + if (days) params.days = days.toString() + return this.client.get<{ + count: number + days: number + rides: Ride[] + }>('/api/v1/rides/recent_openings/', params) + } + + /** + * Get recently closed rides + */ + async getRecentClosures(days?: number): Promise<{ + count: number + days: number + rides: Ride[] + }> { + const params: Record = {} + if (days) params.days = days.toString() + return this.client.get<{ + count: number + days: number + rides: Ride[] + }>('/api/v1/rides/recent_closures/', params) + } + + /** + * Get rides with recent name changes + */ + async getRecentNameChanges(days?: number): Promise<{ + count: number + days: number + rides: Ride[] + }> { + const params: Record = {} + if (days) params.days = days.toString() + return this.client.get<{ + count: number + days: number + rides: Ride[] + }>('/api/v1/rides/recent_name_changes/', params) + } + + /** + * Get rides that have been relocated recently + */ + async getRecentRelocations(days?: number): Promise<{ + count: number + days: number + rides: Ride[] + }> { + const params: Record = {} + if (days) params.days = days.toString() + return this.client.get<{ + count: number + days: number + rides: Ride[] + }>('/api/v1/rides/recent_relocations/', params) + } +} + +/** + * Authentication API service + */ +export class AuthApi { + private client: ApiClient + + constructor(client: ApiClient = new ApiClient()) { + this.client = client + } + + /** + * Login with username/email and password + */ + async login(credentials: LoginCredentials): Promise { + const response = await this.client.post('/api/accounts/login/', credentials) + if (response.token) { + this.client.setAuthToken(response.token) + } + return response + } + + /** + * Logout user + */ + async logout(): Promise { + await this.client.post('/api/auth/logout/') + this.client.setAuthToken(null) + } + + /** + * Register new user + */ + async signup(credentials: SignupCredentials): Promise { + const response = await this.client.post('/api/auth/signup/', credentials) + if (response.token) { + this.client.setAuthToken(response.token) + } + return response + } + + /** + * Get current user info + */ + async getCurrentUser(): Promise { + return this.client.get('/api/auth/user/') + } + + /** + * Request password reset + */ + async requestPasswordReset(data: PasswordResetRequest): Promise<{ detail: string }> { + return this.client.post<{ detail: string }>('/api/auth/password/reset/', data) + } + + /** + * Change password + */ + async changePassword(data: PasswordChangeRequest): Promise<{ detail: string }> { + return this.client.post<{ detail: string }>('/api/auth/password/change/', data) + } + + /** + * Get available social auth providers + */ + async getSocialProviders(): Promise { + return this.client.get('/api/auth/providers/') + } + + /** + * Check if user is authenticated + */ + isAuthenticated(): boolean { + return !!this.client.getAuthToken() + } +} + +/** + * History API service for tracking changes across all models + */ +export class HistoryApi { + private client: ApiClient + + constructor(client: ApiClient = new ApiClient()) { + this.client = client + } + + /** + * Get unified history timeline across all models + */ + async getUnifiedTimeline(params?: HistoryParams): Promise { + const queryParams: Record = {} + + if (params?.limit) queryParams.limit = params.limit.toString() + if (params?.offset) queryParams.offset = params.offset.toString() + if (params?.event_type) queryParams.event_type = params.event_type + if (params?.start_date) queryParams.start_date = params.start_date + if (params?.end_date) queryParams.end_date = params.end_date + if (params?.model_type) queryParams.model_type = params.model_type + if (params?.significance) queryParams.significance = params.significance + + return this.client.get('/api/v1/history/timeline/', queryParams) + } + + /** + * Get history events for a specific park + */ + async getParkHistory(parkSlug: string, params?: HistoryParams): Promise { + const queryParams: Record = {} + + if (params?.limit) queryParams.limit = params.limit.toString() + if (params?.offset) queryParams.offset = params.offset.toString() + if (params?.event_type) queryParams.event_type = params.event_type + if (params?.start_date) queryParams.start_date = params.start_date + if (params?.end_date) queryParams.end_date = params.end_date + + return this.client.get(`/api/v1/parks/${parkSlug}/history/`, queryParams) + } + + /** + * Get complete park history with current state and summary + */ + async getParkHistoryDetail(parkSlug: string): Promise { + return this.client.get(`/api/v1/parks/${parkSlug}/history/detail/`) + } + + /** + * Get history events for a specific ride + */ + async getRideHistory( + parkSlug: string, + rideSlug: string, + params?: HistoryParams + ): Promise { + const queryParams: Record = {} + + if (params?.limit) queryParams.limit = params.limit.toString() + if (params?.offset) queryParams.offset = params.offset.toString() + if (params?.event_type) queryParams.event_type = params.event_type + if (params?.start_date) queryParams.start_date = params.start_date + if (params?.end_date) queryParams.end_date = params.end_date + + return this.client.get( + `/api/v1/parks/${parkSlug}/rides/${rideSlug}/history/`, + queryParams + ) + } + + /** + * Get complete ride history with current state and summary + */ + async getRideHistoryDetail(parkSlug: string, rideSlug: string): Promise { + return this.client.get( + `/api/v1/parks/${parkSlug}/rides/${rideSlug}/history/detail/` + ) + } + + /** + * Get recent changes across all models (convenience method) + */ + async getRecentChanges(limit: number = 50): Promise { + const timeline = await this.getUnifiedTimeline({ limit }) + return timeline.events + } + + /** + * Get history for a specific model type + */ + async getModelHistory( + modelType: 'park' | 'ride' | 'company' | 'user', + params?: HistoryParams + ): Promise { + const timeline = await this.getUnifiedTimeline({ + ...params, + model_type: modelType, + }) + return timeline.events + } + + /** + * Get significant changes only (major events) + */ + async getSignificantChanges(params?: HistoryParams): Promise { + const timeline = await this.getUnifiedTimeline({ + ...params, + significance: 'major', + }) + return timeline.events + } + + /** + * Search history events by date range + */ + async getHistoryByDateRange( + startDate: string, + endDate: string, + params?: Omit + ): Promise { + const timeline = await this.getUnifiedTimeline({ + ...params, + start_date: startDate, + end_date: endDate, + }) + return timeline.events + } +} + +/** + * Main API service that combines all endpoints + */ +export class ThrillWikiApi { + public parks: ParksApi + public rides: RidesApi + public auth: AuthApi + public history: HistoryApi + private client: ApiClient + + constructor() { + this.client = new ApiClient() + this.parks = new ParksApi(this.client) + this.rides = new RidesApi(this.client) + this.auth = new AuthApi(this.client) + this.history = new HistoryApi(this.client) + } + + /** + * Global search across parks and rides + */ + async globalSearch(query: string): Promise<{ + parks: SearchResponse + rides: SearchResponse + }> { + const [parksResult, ridesResult] = await Promise.all([ + this.parks.searchParks(query), + this.rides.searchRides(query), + ]) + + return { + parks: parksResult, + rides: ridesResult, + } + } + + /** + * Get API base URL for use in other parts of the app + */ + getBaseUrl(): string { + return this.client['baseUrl'] + } + + /** + * Health check endpoint + */ + async healthCheck(): Promise<{ status: string; timestamp: string }> { + return this.client.get<{ status: string; timestamp: string }>('/health/') + } +} + +// Create and export singleton instance +export const api = new ThrillWikiApi() + +// Export individual services for direct use +export const parksApi = api.parks +export const ridesApi = api.rides +export const authApi = api.auth +export const historyApi = api.history + +// Export types for use in components +export type { + ApiResponse, + SearchResponse, + HistoryEvent, + UnifiedHistoryEvent, + HistorySummary, + ParkHistoryResponse, + RideHistoryResponse, + UnifiedHistoryTimeline, + HistoryParams +} diff --git a/frontend/src/stores/counter.ts b/frontend/src/stores/counter.ts new file mode 100644 index 00000000..b6757ba5 --- /dev/null +++ b/frontend/src/stores/counter.ts @@ -0,0 +1,12 @@ +import { ref, computed } from 'vue' +import { defineStore } from 'pinia' + +export const useCounterStore = defineStore('counter', () => { + const count = ref(0) + const doubleCount = computed(() => count.value * 2) + function increment() { + count.value++ + } + + return { count, doubleCount, increment } +}) diff --git a/frontend/src/stores/parks.ts b/frontend/src/stores/parks.ts new file mode 100644 index 00000000..1b2292e9 --- /dev/null +++ b/frontend/src/stores/parks.ts @@ -0,0 +1,72 @@ +import { defineStore } from 'pinia' +import { ref, computed } from 'vue' +import { api } from '@/services/api' +import type { Park } from '@/types' + +export const useParksStore = defineStore('parks', () => { + const parks = ref([]) + + const isLoading = ref(false) + const error = ref(null) + + // Computed getters + const openParks = computed(() => parks.value.filter((park) => park.status === 'open')) + + const seasonalParks = computed(() => parks.value.filter((park) => park.status === 'seasonal')) + + const totalParks = computed(() => parks.value.length) + + // Actions + const fetchParks = async () => { + isLoading.value = true + error.value = null + + try { + const response = await api.parks.getParks() + parks.value = response.results + } catch (err) { + error.value = 'Failed to fetch parks' + console.error('Error fetching parks:', err) + } finally { + isLoading.value = false + } + } + + const getParkBySlug = async (slug: string): Promise => { + try { + return await api.parks.getPark(slug) + } catch (err) { + console.error('Error fetching park by slug:', err) + return null + } + } + + const searchParks = async (query: string): Promise => { + if (!query.trim()) return parks.value + + try { + const response = await api.parks.searchParks(query) + return response.results + } catch (err) { + console.error('Error searching parks:', err) + return [] + } + } + + return { + // State + parks, + isLoading, + error, + + // Getters + openParks, + seasonalParks, + totalParks, + + // Actions + fetchParks, + getParkBySlug, + searchParks, + } +}) diff --git a/frontend/src/stores/rides.ts b/frontend/src/stores/rides.ts new file mode 100644 index 00000000..83fba198 --- /dev/null +++ b/frontend/src/stores/rides.ts @@ -0,0 +1,115 @@ +import { defineStore } from 'pinia' +import { ref, computed } from 'vue' +import { api } from '@/services/api' +import type { Ride } from '@/types' + +export const useRidesStore = defineStore('rides', () => { + const rides = ref([]) + + const isLoading = ref(false) + const error = ref(null) + + // Computed getters + const operatingRides = computed(() => rides.value.filter((ride) => ride.status === 'operating')) + + const ridesByCategory = computed(() => { + const categories: Record = {} + rides.value.forEach((ride) => { + if (!categories[ride.category]) { + categories[ride.category] = [] + } + categories[ride.category].push(ride) + }) + return categories + }) + + const ridesByStatus = computed(() => { + const statuses: Record = {} + rides.value.forEach((ride) => { + if (!statuses[ride.status]) { + statuses[ride.status] = [] + } + statuses[ride.status].push(ride) + }) + return statuses + }) + + const totalRides = computed(() => rides.value.length) + + // Actions + const fetchRides = async () => { + isLoading.value = true + error.value = null + + try { + const response = await api.rides.getRides() + rides.value = response.results + } catch (err) { + error.value = 'Failed to fetch rides' + console.error('Error fetching rides:', err) + } finally { + isLoading.value = false + } + } + + const fetchRidesByPark = async (parkSlug: string): Promise => { + isLoading.value = true + error.value = null + + try { + const response = await api.parks.getParkRides(parkSlug) + return response.results + } catch (err) { + error.value = 'Failed to fetch park rides' + console.error('Error fetching park rides:', err) + return [] + } finally { + isLoading.value = false + } + } + + const getRideBySlug = async (parkSlug: string, rideSlug: string): Promise => { + try { + return await api.rides.getRide(parkSlug, rideSlug) + } catch (err) { + console.error('Error fetching ride by slug:', err) + return null + } + } + + const searchRides = async (query: string): Promise => { + if (!query.trim()) return rides.value + + try { + const response = await api.rides.searchRides(query) + return response.results + } catch (err) { + console.error('Error searching rides:', err) + return [] + } + } + + const getRidesByParkSlug = (parkSlug: string): Ride[] => { + return rides.value.filter((ride) => ride.parkSlug === parkSlug) + } + + return { + // State + rides, + isLoading, + error, + + // Getters + operatingRides, + ridesByCategory, + ridesByStatus, + totalRides, + + // Actions + fetchRides, + fetchRidesByPark, + getRideBySlug, + searchRides, + getRidesByParkSlug, + } +}) diff --git a/frontend/src/style.css b/frontend/src/style.css new file mode 100644 index 00000000..d5a82d33 --- /dev/null +++ b/frontend/src/style.css @@ -0,0 +1,38 @@ +@import 'tailwindcss'; + +/* Custom base styles */ +@layer base { + html { + @apply scroll-smooth; + } + + body { + @apply bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 transition-colors duration-200; + } +} + +/* Custom component styles */ +@layer components { + .btn-primary { + @apply bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded-lg transition-colors duration-200; + } + + .btn-secondary { + @apply bg-gray-200 hover:bg-gray-300 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-900 dark:text-gray-100 font-medium py-2 px-4 rounded-lg transition-colors duration-200; + } + + .card { + @apply bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-sm transition-colors duration-200; + } + + .input { + @apply w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 placeholder-gray-500 dark:placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-colors duration-200; + } +} + +/* Custom utilities */ +@layer utilities { + .text-balance { + text-wrap: balance; + } +} diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts new file mode 100644 index 00000000..44b343aa --- /dev/null +++ b/frontend/src/types/index.ts @@ -0,0 +1,181 @@ +/** + * Type definitions for ThrillWiki application + */ + +// Park related types +export interface Park { + id: number + slug: string + name: string + description: string + location: string + country: string + openingYear: number | null + status: 'open' | 'closed' | 'seasonal' | 'construction' + operator: string + propertyOwner?: string + website?: string + area?: number + rideCount: number + created: string + updated: string +} + +// Ride related types +export interface Ride { + id: number + slug: string + name: string + description: string + category: + | 'roller_coaster' + | 'water_ride' + | 'dark_ride' + | 'thrill_ride' + | 'family_ride' + | 'kiddie_ride' + | 'transport' + status: 'operating' | 'closed' | 'under_construction' | 'seasonal' | 'sbno' + openingDate: string | null + closingDate: string | null + manufacturer?: string + designer?: string + height?: number + length?: number + speed?: number + inversions?: number + duration?: number + capacity?: number + parkId: number + parkName: string + parkSlug: string + created: string + updated: string +} + +// Search and filter types +export interface SearchFilters { + query?: string + category?: string + status?: string + country?: string + manufacturer?: string + designer?: string + minHeight?: number + maxHeight?: number + minSpeed?: number + maxSpeed?: number +} + +export interface ParkFilters { + query?: string + status?: string + country?: string + operator?: string + minYear?: number + maxYear?: number +} + +// API response types +export interface ApiResponse { + count: number + next: string | null + previous: string | null + results: T[] +} + +export interface SearchResponse { + results: T[] + count: number + query: string +} + +// Component prop types +export interface PaginationInfo { + currentPage: number + totalPages: number + totalItems: number + itemsPerPage: number +} + +// Form types +export interface ContactForm { + name: string + email: string + subject: string + message: string +} + +// Theme types +export interface ThemeConfig { + isDark: boolean + primaryColor: string + accentColor: string +} + +// Navigation types +export interface NavItem { + label: string + to: string + icon?: string + children?: NavItem[] +} + +// Error types +export interface ApiError { + message: string + status: number + details?: Record +} + +// Authentication types +export interface User { + id: number + username: string + email: string + firstName?: string + lastName?: string + displayName?: string + avatar?: string + role?: string + isStaff: boolean + isActive: boolean + dateJoined: string + lastLogin?: string +} + +export interface LoginCredentials { + username: string + password: string + remember?: boolean +} + +export interface SignupCredentials { + username: string + email: string + password1: string + password2: string +} + +export interface AuthResponse { + user: User + token?: string + sessionId?: string +} + +export interface PasswordResetRequest { + email: string +} + +export interface PasswordChangeRequest { + oldPassword: string + newPassword1: string + newPassword2: string +} + +export interface SocialAuthProvider { + id: string + name: string + iconUrl?: string + authUrl: string +} diff --git a/frontend/src/views/Error.vue b/frontend/src/views/Error.vue new file mode 100644 index 00000000..ccd9cd75 --- /dev/null +++ b/frontend/src/views/Error.vue @@ -0,0 +1,127 @@ + + + diff --git a/frontend/src/views/Home.vue b/frontend/src/views/Home.vue new file mode 100644 index 00000000..09ca88ae --- /dev/null +++ b/frontend/src/views/Home.vue @@ -0,0 +1,553 @@ + + + diff --git a/frontend/src/views/NotFound.vue b/frontend/src/views/NotFound.vue new file mode 100644 index 00000000..b342195a --- /dev/null +++ b/frontend/src/views/NotFound.vue @@ -0,0 +1,116 @@ + + + diff --git a/frontend/src/views/SearchResults.vue b/frontend/src/views/SearchResults.vue new file mode 100644 index 00000000..526e8336 --- /dev/null +++ b/frontend/src/views/SearchResults.vue @@ -0,0 +1,378 @@ + + + + + diff --git a/frontend/src/views/accounts/ForgotPassword.vue b/frontend/src/views/accounts/ForgotPassword.vue new file mode 100644 index 00000000..df98dfc6 --- /dev/null +++ b/frontend/src/views/accounts/ForgotPassword.vue @@ -0,0 +1,64 @@ + + + diff --git a/frontend/src/views/accounts/Login.vue b/frontend/src/views/accounts/Login.vue new file mode 100644 index 00000000..a2802adf --- /dev/null +++ b/frontend/src/views/accounts/Login.vue @@ -0,0 +1,78 @@ + + + diff --git a/frontend/src/views/accounts/Signup.vue b/frontend/src/views/accounts/Signup.vue new file mode 100644 index 00000000..47e4a82f --- /dev/null +++ b/frontend/src/views/accounts/Signup.vue @@ -0,0 +1,78 @@ + + + diff --git a/frontend/src/views/parks/ParkDetail.vue b/frontend/src/views/parks/ParkDetail.vue new file mode 100644 index 00000000..291ff92f --- /dev/null +++ b/frontend/src/views/parks/ParkDetail.vue @@ -0,0 +1,233 @@ + + + + + diff --git a/frontend/src/views/parks/ParkList.vue b/frontend/src/views/parks/ParkList.vue new file mode 100644 index 00000000..0357cd7e --- /dev/null +++ b/frontend/src/views/parks/ParkList.vue @@ -0,0 +1,215 @@ + + + + + diff --git a/frontend/src/views/rides/RideDetail.vue b/frontend/src/views/rides/RideDetail.vue new file mode 100644 index 00000000..c5416ec7 --- /dev/null +++ b/frontend/src/views/rides/RideDetail.vue @@ -0,0 +1,267 @@ + + + diff --git a/frontend/src/views/rides/RideList.vue b/frontend/src/views/rides/RideList.vue new file mode 100644 index 00000000..969f4152 --- /dev/null +++ b/frontend/src/views/rides/RideList.vue @@ -0,0 +1,276 @@ + + + + + diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index f42b8686..f5cb55ea 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -8,27 +8,27 @@ export default { theme: { extend: { colors: { - primary: { - 50: '#eff6ff', - 100: '#dbeafe', - 200: '#bfdbfe', - 300: '#93c5fd', - 400: '#60a5fa', - 500: '#3b82f6', - 600: '#2563eb', - 700: '#1d4ed8', - 800: '#1e40af', - 900: '#1e3a8a', - 950: '#172554', + purple: { + 50: '#faf5ff', + 100: '#f3e8ff', + 200: '#e9d5ff', + 300: '#d8b4fe', + 400: '#c084fc', + 500: '#a855f7', + 600: '#9333ea', + 700: '#7c3aed', + 800: '#6b21a8', + 900: '#581c87', }, }, fontFamily: { - sans: ['Inter', 'system-ui', 'sans-serif'], + sans: ['Inter', 'ui-sans-serif', 'system-ui', 'sans-serif'], + }, + boxShadow: { + 'lg': '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)', + 'xl': '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)', }, }, }, - plugins: [ - require('@tailwindcss/forms'), - require('@tailwindcss/typography'), - ], + plugins: [], } \ No newline at end of file diff --git a/frontend/tsconfig.app.json b/frontend/tsconfig.app.json new file mode 100644 index 00000000..913b8f27 --- /dev/null +++ b/frontend/tsconfig.app.json @@ -0,0 +1,12 @@ +{ + "extends": "@vue/tsconfig/tsconfig.dom.json", + "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], + "exclude": ["src/**/__tests__/*"], + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + + "paths": { + "@/*": ["./src/*"] + } + } +} diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 5645ab08..e831b97a 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -1,42 +1,21 @@ { - "compilerOptions": { - "target": "ES2020", - "useDefineForClassFields": true, - "lib": [ - "ES2020", - "DOM", - "DOM.Iterable" - ], - "module": "ESNext", - "skipLibCheck": true, - /* Bundler mode */ - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, - "jsx": "preserve", - /* Linting */ - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true, - /* Path mapping */ - "baseUrl": ".", - "paths": { - "@/*": [ - "src/*" - ] - } + "files": [], + "references": [ + { + "path": "./tsconfig.node.json" }, - "include": [ - "src/**/*.ts", - "src/**/*.tsx", - "src/**/*.vue" - ], - "references": [ - { - "path": "./tsconfig.node.json" - } - ] + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.vitest.json" + } + ], + "compilerOptions": { + "paths": { + "@/*": [ + "./src/*" + ] + } + } } \ No newline at end of file diff --git a/frontend/tsconfig.node.json b/frontend/tsconfig.node.json index b85dd47b..a83dfc9d 100644 --- a/frontend/tsconfig.node.json +++ b/frontend/tsconfig.node.json @@ -1,12 +1,19 @@ { - "compilerOptions": { - "composite": true, - "skipLibCheck": true, - "module": "ESNext", - "moduleResolution": "bundler", - "allowSyntheticDefaultImports": true - }, - "include": [ - "vite.config.ts" - ] -} \ No newline at end of file + "extends": "@tsconfig/node22/tsconfig.json", + "include": [ + "vite.config.*", + "vitest.config.*", + "cypress.config.*", + "nightwatch.conf.*", + "playwright.config.*", + "eslint.config.*" + ], + "compilerOptions": { + "noEmit": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + + "module": "ESNext", + "moduleResolution": "Bundler", + "types": ["node"] + } +} diff --git a/frontend/tsconfig.vitest.json b/frontend/tsconfig.vitest.json new file mode 100644 index 00000000..7d1d8cef --- /dev/null +++ b/frontend/tsconfig.vitest.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.app.json", + "include": ["src/**/__tests__/*", "env.d.ts"], + "exclude": [], + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.vitest.tsbuildinfo", + + "lib": [], + "types": ["node", "jsdom"] + } +} diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index fcbb7814..fa355745 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -1,33 +1,53 @@ +import { fileURLToPath, URL } from 'node:url' + import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' -import { resolve } from 'path' +import vueJsx from '@vitejs/plugin-vue-jsx' +import vueDevTools from 'vite-plugin-vue-devtools' +import tailwindcss from '@tailwindcss/vite' -// https://vitejs.dev/config/ -export default defineConfig({ - plugins: [vue()], +// https://vite.dev/config/ +export default defineConfig(({ mode }) => ({ + plugins: [ + vue(), + vueJsx(), + vueDevTools(), + tailwindcss(), + ], resolve: { alias: { - '@': resolve(__dirname, 'src'), + '@': fileURLToPath(new URL('./src', import.meta.url)) }, }, server: { - port: 3000, proxy: { '/api': { - target: 'http://localhost:8000', + target: 'http://127.0.0.1:8000', changeOrigin: true, secure: false, - }, - '/media': { - target: 'http://localhost:8000', - changeOrigin: true, - secure: false, - }, - }, + ws: true, + rewrite: (path) => path.replace(/^\/api/, '/api/v1') + } + } }, build: { outDir: 'dist', - assetsDir: 'assets', - sourcemap: true, - }, -}) \ No newline at end of file + sourcemap: mode === 'development', + minify: mode === 'production', + rollupOptions: { + output: { + manualChunks(id) { + if (id.includes('node_modules')) { + if (id.includes('vue') || id.includes('pinia') || id.includes('vue-router')) { + return 'vue-vendor'; + } + if (id.includes('lucide-vue-next')) { + return 'ui-vendor'; + } + return 'vendor'; + } + } + } + } + } +})) diff --git a/frontend/vitest.config.ts b/frontend/vitest.config.ts new file mode 100644 index 00000000..c3287171 --- /dev/null +++ b/frontend/vitest.config.ts @@ -0,0 +1,14 @@ +import { fileURLToPath } from 'node:url' +import { mergeConfig, defineConfig, configDefaults } from 'vitest/config' +import viteConfig from './vite.config' + +export default mergeConfig( + viteConfig, + defineConfig({ + test: { + environment: 'jsdom', + exclude: [...configDefaults.exclude, 'e2e/**'], + root: fileURLToPath(new URL('./', import.meta.url)), + }, + }), +) diff --git a/package.json b/package.json index d52a6bb4..21154b15 100644 --- a/package.json +++ b/package.json @@ -1,29 +1,51 @@ { - "name": "thrillwiki-monorepo", + "name": "thrillwiki-fullstack", "version": "1.0.0", - "description": "ThrillWiki Django + Vue.js Monorepo", + "description": "ThrillWiki theme park and ride information system", "private": true, - "packageManager": "pnpm@9.0.0", - "workspaces": [ - "frontend" - ], "scripts": { - "dev": "concurrently \"pnpm run dev:backend\" \"pnpm run dev:frontend\"", - "dev:backend": "cd backend && uv run manage.py runserver", + "dev": "concurrently \"npm run dev:backend\" \"npm run dev:frontend\"", + "dev:backend": "cd backend && lsof -ti :8000 | xargs kill -9; find . -type d -name \"__pycache__\" -exec rm -r {} +; uv run manage.py runserver", "dev:frontend": "cd frontend && pnpm run dev", - "build": "pnpm run build:frontend && cd backend && uv run manage.py collectstatic --noinput", - "build:frontend": "cd frontend && pnpm run build", - "test": "pnpm run test:backend && pnpm run test:frontend", - "test:backend": "cd backend && uv run manage.py test", + "dev:full": "./shared/scripts/dev/start-all.sh", + "dev:setup": "./shared/scripts/dev/setup-dev.sh", + "build": "cd frontend && pnpm run build", + "build:staging": "cd frontend && pnpm run build --mode staging", + "build:production": "cd frontend && pnpm run build --mode production", + "build:frontend": "./shared/scripts/build/build-frontend.sh", + "build:all": "./shared/scripts/build/build-all.sh", + "build:clean": "./shared/scripts/build/build-all.sh --clean", + "preview": "cd frontend && pnpm run preview", + "install:all": "cd backend && uv sync && cd ../frontend && pnpm install", + "setup": "./shared/scripts/dev/setup-dev.sh", + "type-check": "cd frontend && pnpm run type-check", + "lint": "cd frontend && pnpm run lint", + "lint:fix": "cd frontend && pnpm run lint", + "format": "cd frontend && pnpm run format", + "test": "concurrently \"npm run test:backend\" \"npm run test:frontend\"", + "test:backend": "cd backend && uv run python -m pytest", "test:frontend": "cd frontend && pnpm run test", - "lint": "cd frontend && pnpm run lint && cd ../backend && uv run flake8 .", - "format": "cd frontend && pnpm run format && cd ../backend && uv run black ." + "test:full": "./shared/scripts/build/build-all.sh --no-checks", + "deploy": "./shared/scripts/deploy/deploy.sh", + "deploy:dev": "./shared/scripts/deploy/deploy.sh dev", + "deploy:staging": "./shared/scripts/deploy/deploy.sh staging", + "deploy:production": "./shared/scripts/deploy/deploy.sh production", + "docs:api": "echo 'API documentation available at: shared/docs/api/README.md'", + "docs:dev": "echo 'Development workflow available at: shared/docs/development/workflow.md'", + "clean": "rm -rf frontend/dist backend/staticfiles deploy build-report* deployment-report*", + "clean:all": "npm run clean && rm -rf backend/.venv frontend/node_modules" }, "devDependencies": { "concurrently": "^8.2.2" }, - "engines": { - "node": ">=18.0.0", - "pnpm": ">=8.0.0" - } + "keywords": [ + "theme-park", + "roller-coaster", + "django", + "vue", + "typescript", + "pinia" + ], + "author": "ThrillWiki Team", + "license": "MIT" } \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 00000000..32861e17 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,5411 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + concurrently: + specifier: ^8.2.2 + version: 8.2.2 + + frontend: + dependencies: + '@csstools/normalize.css': + specifier: ^12.1.1 + version: 12.1.1 + '@heroicons/vue': + specifier: ^2.2.0 + version: 2.2.0(vue@3.5.19(typescript@5.9.2)) + '@material/material-color-utilities': + specifier: ^0.3.0 + version: 0.3.0 + lucide-react: + specifier: ^0.541.0 + version: 0.541.0(react@19.1.1) + pinia: + specifier: ^3.0.3 + version: 3.0.3(typescript@5.9.2)(vue@3.5.19(typescript@5.9.2)) + vue: + specifier: ^3.5.19 + version: 3.5.19(typescript@5.9.2) + vue-router: + specifier: ^4.5.1 + version: 4.5.1(vue@3.5.19(typescript@5.9.2)) + devDependencies: + '@chainlift/liftkit': + specifier: ^0.2.0 + version: 0.2.0 + '@playwright/test': + specifier: ^1.55.0 + version: 1.55.0 + '@prettier/plugin-oxc': + specifier: ^0.0.4 + version: 0.0.4 + '@tailwindcss/postcss': + specifier: ^4.1.12 + version: 4.1.12 + '@tailwindcss/vite': + specifier: ^4.1.12 + version: 4.1.12(rolldown-vite@7.1.4(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(yaml@2.8.1)) + '@tsconfig/node22': + specifier: ^22.0.2 + version: 22.0.2 + '@types/jsdom': + specifier: ^21.1.7 + version: 21.1.7 + '@types/node': + specifier: ^24.3.0 + version: 24.3.0 + '@vitejs/plugin-vue': + specifier: ^6.0.1 + version: 6.0.1(rolldown-vite@7.1.4(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(yaml@2.8.1))(vue@3.5.19(typescript@5.9.2)) + '@vitejs/plugin-vue-jsx': + specifier: ^5.0.1 + version: 5.0.1(rolldown-vite@7.1.4(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(yaml@2.8.1))(vue@3.5.19(typescript@5.9.2)) + '@vitest/eslint-plugin': + specifier: ^1.3.4 + version: 1.3.4(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2)(vitest@3.2.4(@types/node@24.3.0)(jiti@2.5.1)(jsdom@26.1.0)(lightningcss@1.30.1)(yaml@2.8.1)) + '@vue/eslint-config-prettier': + specifier: ^10.2.0 + version: 10.2.0(eslint@9.34.0(jiti@2.5.1))(prettier@3.6.2) + '@vue/eslint-config-typescript': + specifier: ^14.6.0 + version: 14.6.0(eslint-plugin-vue@10.4.0(@typescript-eslint/parser@8.40.0(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.34.0(jiti@2.5.1))(vue-eslint-parser@10.2.0(eslint@9.34.0(jiti@2.5.1))))(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2) + '@vue/test-utils': + specifier: ^2.4.6 + version: 2.4.6 + '@vue/tsconfig': + specifier: ^0.8.1 + version: 0.8.1(typescript@5.9.2)(vue@3.5.19(typescript@5.9.2)) + autoprefixer: + specifier: ^10.4.21 + version: 10.4.21(postcss@8.5.6) + eslint: + specifier: ^9.34.0 + version: 9.34.0(jiti@2.5.1) + eslint-plugin-oxlint: + specifier: ~1.12.0 + version: 1.12.0 + eslint-plugin-playwright: + specifier: ^2.2.2 + version: 2.2.2(eslint@9.34.0(jiti@2.5.1)) + eslint-plugin-vue: + specifier: ~10.4.0 + version: 10.4.0(@typescript-eslint/parser@8.40.0(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.34.0(jiti@2.5.1))(vue-eslint-parser@10.2.0(eslint@9.34.0(jiti@2.5.1))) + jiti: + specifier: ^2.5.1 + version: 2.5.1 + jsdom: + specifier: ^26.1.0 + version: 26.1.0 + npm-run-all2: + specifier: ^8.0.4 + version: 8.0.4 + oxlint: + specifier: ~1.12.0 + version: 1.12.0 + postcss: + specifier: ^8.5.6 + version: 8.5.6 + prettier: + specifier: 3.6.2 + version: 3.6.2 + tailwindcss: + specifier: ^4.1.12 + version: 4.1.12 + typescript: + specifier: ~5.9.2 + version: 5.9.2 + vite: + specifier: npm:rolldown-vite@^7.1.4 + version: rolldown-vite@7.1.4(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(yaml@2.8.1) + vite-plugin-vue-devtools: + specifier: ^8.0.1 + version: 8.0.1(rolldown-vite@7.1.4(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(yaml@2.8.1))(vue@3.5.19(typescript@5.9.2)) + vitest: + specifier: ^3.2.4 + version: 3.2.4(@types/node@24.3.0)(jiti@2.5.1)(jsdom@26.1.0)(lightningcss@1.30.1)(yaml@2.8.1) + vue-tsc: + specifier: ^3.0.6 + version: 3.0.6(typescript@5.9.2) + +packages: + + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@asamuzakjp/css-color@3.2.0': + resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} + + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.28.0': + resolution: {integrity: sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.28.3': + resolution: {integrity: sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.28.3': + resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-annotate-as-pure@7.27.3': + resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-create-class-features-plugin@7.28.3': + resolution: {integrity: sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-member-expression-to-functions@7.27.1': + resolution: {integrity: sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.27.1': + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.3': + resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-optimise-call-expression@7.27.1': + resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-plugin-utils@7.27.1': + resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-replace-supers@7.27.1': + resolution: {integrity: sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.28.3': + resolution: {integrity: sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.3': + resolution: {integrity: sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-proposal-decorators@7.28.0': + resolution: {integrity: sha512-zOiZqvANjWDUaUS9xMxbMcK/Zccztbe/6ikvUXaG9nsPH3w6qh5UaPGAnirI/WhIbZ8m3OHU0ReyPrknG+ZKeg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-decorators@7.27.1': + resolution: {integrity: sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-attributes@7.27.1': + resolution: {integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-meta@7.10.4': + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-jsx@7.27.1': + resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.27.1': + resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typescript@7.28.0': + resolution: {integrity: sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.28.3': + resolution: {integrity: sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.28.3': + resolution: {integrity: sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.28.2': + resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==} + engines: {node: '>=6.9.0'} + + '@chainlift/liftkit@0.2.0': + resolution: {integrity: sha512-9St6tcUE46OHarYOdG2mJQIIbEL2djfKB3YtwnFWoKpC5fwFsK48D395QOxGcqnz4p1xZ3dvQ8ae/8zn+g1BdQ==} + engines: {node: '>=16'} + hasBin: true + + '@csstools/color-helpers@5.1.0': + resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==} + engines: {node: '>=18'} + + '@csstools/css-calc@2.1.4': + resolution: {integrity: sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-color-parser@3.1.0': + resolution: {integrity: sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-parser-algorithms@3.0.5': + resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-tokenizer@3.0.4': + resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} + engines: {node: '>=18'} + + '@csstools/normalize.css@12.1.1': + resolution: {integrity: sha512-YAYeJ+Xqh7fUou1d1j9XHl44BmsuThiTr4iNrgCQ3J27IbhXsxXDGZ1cXv8Qvs99d4rBbLiSKy3+WZiet32PcQ==} + + '@emnapi/core@1.4.5': + resolution: {integrity: sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==} + + '@emnapi/runtime@1.4.5': + resolution: {integrity: sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==} + + '@emnapi/wasi-threads@1.0.4': + resolution: {integrity: sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==} + + '@esbuild/aix-ppc64@0.25.9': + resolution: {integrity: sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.9': + resolution: {integrity: sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.9': + resolution: {integrity: sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.9': + resolution: {integrity: sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.9': + resolution: {integrity: sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.9': + resolution: {integrity: sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.9': + resolution: {integrity: sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.9': + resolution: {integrity: sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.9': + resolution: {integrity: sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.9': + resolution: {integrity: sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.9': + resolution: {integrity: sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.9': + resolution: {integrity: sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.9': + resolution: {integrity: sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.9': + resolution: {integrity: sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.9': + resolution: {integrity: sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.9': + resolution: {integrity: sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.9': + resolution: {integrity: sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.9': + resolution: {integrity: sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.9': + resolution: {integrity: sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.9': + resolution: {integrity: sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.9': + resolution: {integrity: sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.9': + resolution: {integrity: sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.9': + resolution: {integrity: sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.9': + resolution: {integrity: sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.9': + resolution: {integrity: sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.9': + resolution: {integrity: sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.7.0': + resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.1': + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.0': + resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.3.1': + resolution: {integrity: sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.15.2': + resolution: {integrity: sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.1': + resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.34.0': + resolution: {integrity: sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.6': + resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.3.5': + resolution: {integrity: sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@heroicons/vue@2.2.0': + resolution: {integrity: sha512-G3dbSxoeEKqbi/DFalhRxJU4mTXJn7GwZ7ae8NuEQzd1bqdd0jAbdaBZlHPcvPD2xI1iGzNVB4k20Un2AguYPw==} + peerDependencies: + vue: '>= 3' + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.6': + resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.3.1': + resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} + engines: {node: '>=18.18'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.30': + resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==} + + '@material/material-color-utilities@0.3.0': + resolution: {integrity: sha512-ztmtTd6xwnuh2/xu+Vb01btgV8SQWYCaK56CkRK8gEkWe5TuDyBcYJ0wgkMRn+2VcE9KUmhvkz+N9GHrqw/C0g==} + + '@napi-rs/wasm-runtime@0.2.12': + resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} + + '@napi-rs/wasm-runtime@1.0.3': + resolution: {integrity: sha512-rZxtMsLwjdXkMUGC3WwsPwLNVqVqnTJT6MNIB6e+5fhMcSCPP0AOsNWuMQ5mdCq6HNjs/ZeWAEchpqeprqBD2Q==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@one-ini/wasm@0.1.1': + resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} + + '@oxc-parser/binding-android-arm64@0.74.0': + resolution: {integrity: sha512-lgq8TJq22eyfojfa2jBFy2m66ckAo7iNRYDdyn9reXYA3I6Wx7tgGWVx1JAp1lO+aUiqdqP/uPlDaETL9tqRcg==} + engines: {node: '>=20.0.0'} + cpu: [arm64] + os: [android] + + '@oxc-parser/binding-darwin-arm64@0.74.0': + resolution: {integrity: sha512-xbY/io/hkARggbpYEMFX6CwFzb7f4iS6WuBoBeZtdqRWfIEi7sm/uYWXfyVeB8uqOATvJ07WRFC2upI8PSI83g==} + engines: {node: '>=20.0.0'} + cpu: [arm64] + os: [darwin] + + '@oxc-parser/binding-darwin-x64@0.74.0': + resolution: {integrity: sha512-FIj2gAGtFaW0Zk+TnGyenMUoRu1ju+kJ/h71D77xc1owOItbFZFGa+4WSVck1H8rTtceeJlK+kux+vCjGFCl9Q==} + engines: {node: '>=20.0.0'} + cpu: [x64] + os: [darwin] + + '@oxc-parser/binding-freebsd-x64@0.74.0': + resolution: {integrity: sha512-W1I+g5TJg0TRRMHgEWNWsTIfe782V3QuaPgZxnfPNmDMywYdtlzllzclBgaDq6qzvZCCQc/UhvNb37KWTCTj8A==} + engines: {node: '>=20.0.0'} + cpu: [x64] + os: [freebsd] + + '@oxc-parser/binding-linux-arm-gnueabihf@0.74.0': + resolution: {integrity: sha512-gxqkyRGApeVI8dgvJ19SYe59XASW3uVxF1YUgkE7peW/XIg5QRAOVTFKyTjI9acYuK1MF6OJHqx30cmxmZLtiQ==} + engines: {node: '>=20.0.0'} + cpu: [arm] + os: [linux] + + '@oxc-parser/binding-linux-arm-musleabihf@0.74.0': + resolution: {integrity: sha512-jpnAUP4Fa93VdPPDzxxBguJmldj/Gpz7wTXKFzpAueqBMfZsy9KNC+0qT2uZ9HGUDMzNuKw0Se3bPCpL/gfD2Q==} + engines: {node: '>=20.0.0'} + cpu: [arm] + os: [linux] + + '@oxc-parser/binding-linux-arm64-gnu@0.74.0': + resolution: {integrity: sha512-fcWyM7BNfCkHqIf3kll8fJctbR/PseL4RnS2isD9Y3FFBhp4efGAzhDaxIUK5GK7kIcFh1P+puIRig8WJ6IMVQ==} + engines: {node: '>=20.0.0'} + cpu: [arm64] + os: [linux] + + '@oxc-parser/binding-linux-arm64-musl@0.74.0': + resolution: {integrity: sha512-AMY30z/C77HgiRRJX7YtVUaelKq1ex0aaj28XoJu4SCezdS8i0IftUNTtGS1UzGjGZB8zQz5SFwVy4dRu4GLwg==} + engines: {node: '>=20.0.0'} + cpu: [arm64] + os: [linux] + + '@oxc-parser/binding-linux-riscv64-gnu@0.74.0': + resolution: {integrity: sha512-/RZAP24TgZo4vV/01TBlzRqs0R7E6xvatww4LnmZEBBulQBU/SkypDywfriFqWuFoa61WFXPV7sLcTjJGjim/w==} + engines: {node: '>=20.0.0'} + cpu: [riscv64] + os: [linux] + + '@oxc-parser/binding-linux-s390x-gnu@0.74.0': + resolution: {integrity: sha512-620J1beNAlGSPBD+Msb3ptvrwxu04B8iULCH03zlf0JSLy/5sqlD6qBs0XUVkUJv1vbakUw1gfVnUQqv0UTuEg==} + engines: {node: '>=20.0.0'} + cpu: [s390x] + os: [linux] + + '@oxc-parser/binding-linux-x64-gnu@0.74.0': + resolution: {integrity: sha512-WBFgQmGtFnPNzHyLKbC1wkYGaRIBxXGofO0+hz1xrrkPgbxbJS1Ukva1EB8sPaVBBQ52Bdc2GjLSp721NWRvww==} + engines: {node: '>=20.0.0'} + cpu: [x64] + os: [linux] + + '@oxc-parser/binding-linux-x64-musl@0.74.0': + resolution: {integrity: sha512-y4mapxi0RGqlp3t6Sm+knJlAEqdKDYrEue2LlXOka/F2i4sRN0XhEMPiSOB3ppHmvK4I2zY2XBYTsX1Fel0fAg==} + engines: {node: '>=20.0.0'} + cpu: [x64] + os: [linux] + + '@oxc-parser/binding-wasm32-wasi@0.74.0': + resolution: {integrity: sha512-yDS9bRDh5ymobiS2xBmjlrGdUuU61IZoJBaJC5fELdYT5LJNBXlbr3Yc6m2PWfRJwkH6Aq5fRvxAZ4wCbkGa8w==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@oxc-parser/binding-win32-arm64-msvc@0.74.0': + resolution: {integrity: sha512-XFWY52Rfb4N5wEbMCTSBMxRkDLGbAI9CBSL24BIDywwDJMl31gHEVlmHdCDRoXAmanCI6gwbXYTrWe0HvXJ7Aw==} + engines: {node: '>=20.0.0'} + cpu: [arm64] + os: [win32] + + '@oxc-parser/binding-win32-x64-msvc@0.74.0': + resolution: {integrity: sha512-1D3x6iU2apLyfTQHygbdaNbX3nZaHu4yaXpD7ilYpoLo7f0MX0tUuoDrqJyJrVGqvyXgc0uz4yXz9tH9ZZhvvg==} + engines: {node: '>=20.0.0'} + cpu: [x64] + os: [win32] + + '@oxc-project/runtime@0.82.2': + resolution: {integrity: sha512-cYxcj5CPn/vo5QSpCZcYzBiLidU5+GlFSqIeNaMgBDtcVRBsBJHZg3pHw999W6nHamFQ1EHuPPByB26tjaJiJw==} + engines: {node: '>=6.9.0'} + + '@oxc-project/types@0.74.0': + resolution: {integrity: sha512-KOw/RZrVlHGhCXh1RufBFF7Nuo7HdY5w1lRJukM/igIl6x9qtz8QycDvZdzb4qnHO7znrPyo2sJrFJK2eKHgfQ==} + + '@oxc-project/types@0.82.2': + resolution: {integrity: sha512-WMGSwd9FsNBs/WfqIOH0h3k1LBdjZJQGYjGnC+vla/fh6HUsu5HzGPerRljiq1hgMQ6gs031YJR12VyP57b/hQ==} + + '@oxlint-tsgolint/darwin-arm64@0.0.4': + resolution: {integrity: sha512-qL0zqIYdYrXl6ghTIHnhJkvyYy1eKz0P8YIEp59MjY3/zNiyk/gtyp8LkwZdqb9ezbcX9UDQhSuSO1wURJsq8g==} + cpu: [arm64] + os: [darwin] + + '@oxlint-tsgolint/darwin-x64@0.0.4': + resolution: {integrity: sha512-c3nSjqmDSKzemChAEUv/zy2e9cwgkkO/7rz4Y447+8pSbeZNHi3RrNpVHdrKL/Qep4pt6nFZE+6PoczZxHNQjg==} + cpu: [x64] + os: [darwin] + + '@oxlint-tsgolint/linux-arm64@0.0.4': + resolution: {integrity: sha512-P2BA54c/Ej5AGkChH1/7zMd6PwZfa+jnw8juB/JWops+BX+lbhbbBHz0cYduDBgWYjRo4e3OVJOTskqcpuMfNw==} + cpu: [arm64] + os: [linux] + + '@oxlint-tsgolint/linux-x64@0.0.4': + resolution: {integrity: sha512-hbgLpnDNicPrbHOAQ9nNfLOSrUrdWANP/umR7P/cwCc1sv66eEs7bm4G3mrhRU8aXFBJmbhdNqiDSUkYYvHWJQ==} + cpu: [x64] + os: [linux] + + '@oxlint-tsgolint/win32-arm64@0.0.4': + resolution: {integrity: sha512-ozKEppmwZhC5LMedClBEat6cXgBGUvxGOgsKK2ZZNE6zSScX7QbvJAOt3nWMGs8GQshHy/6ndMB33+uRloglQA==} + cpu: [arm64] + os: [win32] + + '@oxlint-tsgolint/win32-x64@0.0.4': + resolution: {integrity: sha512-gLfx+qogW21QcaRKFg6ARgra7tSPqyn+Ems3FgTUyxV4OpJYn7KsQroygxOWElqv6JUobtvHBrxdB6YhlvERbQ==} + cpu: [x64] + os: [win32] + + '@oxlint/darwin-arm64@1.12.0': + resolution: {integrity: sha512-Pv+Ho1uq2ny8g2P6JgQpaIUF1FHPL32DfOlZhKqmzDT3PydtFvZp/7zNyJE3BIXeTOOOG1Eg12hjZHMLsWxyNw==} + cpu: [arm64] + os: [darwin] + + '@oxlint/darwin-x64@1.12.0': + resolution: {integrity: sha512-kNXPH/7jXjX4pawrEWXQHOasOdOsrYKhskA1qYwLYcv/COVSoxOSElkQtQa+KxN5zzt3F02kBdWDndLpgJLbLQ==} + cpu: [x64] + os: [darwin] + + '@oxlint/linux-arm64-gnu@1.12.0': + resolution: {integrity: sha512-U7NETs02K55ZyDlgdhx4lWeFYbkUKcL+YcG+Ak70EyEt/BKIIVt4B84VdV1JzC71FErUipDYAwPJmxMREXr4Sg==} + cpu: [arm64] + os: [linux] + + '@oxlint/linux-arm64-musl@1.12.0': + resolution: {integrity: sha512-e4Pb2eZu3V2BsiX4t4gyv9iJ8+KRT6bkoWM5uC9BLX7edsVchwLwL6LB2vPYusYdPPrxdjlFCg6ni+9wlw7FbQ==} + cpu: [arm64] + os: [linux] + + '@oxlint/linux-x64-gnu@1.12.0': + resolution: {integrity: sha512-qJK98Dj/z7Nbm0xoz0nCCMFGy0W/kLewPzOK5QENxuUoQQ6ymt7/75rXOuTwAZJ6JFTarqfSuMAA0pka6Tmytw==} + cpu: [x64] + os: [linux] + + '@oxlint/linux-x64-musl@1.12.0': + resolution: {integrity: sha512-jNeltpHc1eonSev/bWKipJ7FI6+Rc7EXh6Y7E0pm8e95sc1klFA29FFVs3FjMA6CCa+SRT0u0nnNTTAtf2QOiQ==} + cpu: [x64] + os: [linux] + + '@oxlint/win32-arm64@1.12.0': + resolution: {integrity: sha512-T3fpNZJ3Q9YGgJTKc1YyvGoomSXnrV5mREz0QACE06zUzfS8EWyaYc/GN17FhHvQ4uQk/1xLgnM6FPsuLMeRhw==} + cpu: [arm64] + os: [win32] + + '@oxlint/win32-x64@1.12.0': + resolution: {integrity: sha512-2eC4XQ1SMM2z7bCDG+Ifrn5GrvP6fkL0FGi4ZwDCrx6fwb1byFrXgSUNIPiqiiqBBrFRMKlXzU9zD6IjuFlUOg==} + cpu: [x64] + os: [win32] + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@pkgr/core@0.2.9': + resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + + '@playwright/test@1.55.0': + resolution: {integrity: sha512-04IXzPwHrW69XusN/SIdDdKZBzMfOT9UNT/YiJit/xpy2VuAoB8NHc8Aplb96zsWDddLnbkPL3TsmrS04ZU2xQ==} + engines: {node: '>=18'} + hasBin: true + + '@polka/url@1.0.0-next.29': + resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + + '@prettier/plugin-oxc@0.0.4': + resolution: {integrity: sha512-UGXe+g/rSRbglL0FOJiar+a+nUrst7KaFmsg05wYbKiInGWP6eAj/f8A2Uobgo5KxEtb2X10zeflNH6RK2xeIQ==} + engines: {node: '>=14'} + + '@rolldown/binding-android-arm64@1.0.0-beta.33': + resolution: {integrity: sha512-xhDQXKftRkEULIxCddrKMR8y0YO/Y+6BKk/XrQP2B29YjV2wr8DByoEz+AHX9BfLHb2srfpdN46UquBW2QXWpQ==} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.0.0-beta.33': + resolution: {integrity: sha512-7lhhY08v5ZtRq8JJQaJ49fnJombAPnqllKKCDLU/UvaqNAOEyTGC8J1WVOLC4EA4zbXO5U3CCRgVGyAFNH2VtQ==} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.0.0-beta.33': + resolution: {integrity: sha512-U2iGjcDV7NWyYyhap8YuY0nwrLX6TvX/9i7gBtdEMPm9z3wIUVGNMVdGlA43uqg7xDpRGpEqGnxbeDgiEwYdnA==} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.0.0-beta.33': + resolution: {integrity: sha512-gd6ASromVHFLlzrjJWMG5CXHkS7/36DEZ8HhvGt2NN8eZALCIuyEx8HMMLqvKA7z4EAztVkdToVrdxpGMsKZxw==} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.33': + resolution: {integrity: sha512-xmeLfkfGthuynO1EpCdyTVr0r4G+wqvnKCuyR6rXOet+hLrq5HNAC2XtP/jU2TB4Bc6aiLYxl868B8CGtFDhcw==} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.33': + resolution: {integrity: sha512-cHGp8yfHL4pes6uaLbO5L58ceFkUK4efd8iE86jClD1QPPDLKiqEXJCFYeuK3OfODuF5EBOmf0SlcUZNEYGdmw==} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.33': + resolution: {integrity: sha512-wZ1t7JAvVeFgskH1L9y7c47ITitPytpL0s8FmAT8pVfXcaTmS58ZyoXT+y6cz8uCkQnETjrX3YezTGI18u3ecg==} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.33': + resolution: {integrity: sha512-cDndWo3VEYbm7yeujOV6Ie2XHz0K8YX/R/vbNmMo03m1QwtBKKvbYNSyJb3B9+8igltDjd8zNM9mpiNNrq/ekQ==} + cpu: [x64] + os: [linux] + + '@rolldown/binding-linux-x64-musl@1.0.0-beta.33': + resolution: {integrity: sha512-bl7uzi6es/l6LT++NZcBpiX43ldLyKXCPwEZGY1rZJ99HQ7m1g3KxWwYCcGxtKjlb2ExVvDZicF6k+96vxOJKg==} + cpu: [x64] + os: [linux] + + '@rolldown/binding-openharmony-arm64@1.0.0-beta.33': + resolution: {integrity: sha512-TrgzQanpLgcmmzolCbYA9BPZgF1gYxkIGZhU/HROnJPsq67gcyaYw/JBLioqQLjIwMipETkn25YY799D2OZzJA==} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-wasm32-wasi@1.0.0-beta.33': + resolution: {integrity: sha512-z0LltdUfvoKak9SuaLz/M9AVSg+RTOZjFksbZXzC6Svl1odyW4ai21VHhZy3m2Faeeb/rl/9efVLayj+qYEGxw==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.33': + resolution: {integrity: sha512-CpvOHyqDNOYx9riD4giyXQDIu72bWRU2Dwt1xFSPlBudk6NumK0OJl6Ch+LPnkp5podQHcQg0mMauAXPVKct7g==} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.33': + resolution: {integrity: sha512-/tNTvZTWHz6HiVuwpR3zR0kGIyCNb+/tFhnJmti+Aw2fAXs3l7Aj0DcXd0646eFKMX8L2w5hOW9H08FXTUkN0g==} + cpu: [ia32] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.33': + resolution: {integrity: sha512-Bb2qK3z7g2mf4zaKRvkohHzweaP1lLbaoBmXZFkY6jJWMm0Z8Pfnh8cOoRlH1IVM1Ufbo8ZZ1WXp1LbOpRMtXw==} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.0-beta.29': + resolution: {integrity: sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==} + + '@rolldown/pluginutils@1.0.0-beta.33': + resolution: {integrity: sha512-she25NCG6NoEPC/SEB4pHs5STcnfI4VBFOzjeI63maSPrWME5J2XC8ogrBgp8NaE/xzj28/kbpSaebiMvFRj+w==} + + '@rollup/rollup-android-arm-eabi@4.48.0': + resolution: {integrity: sha512-aVzKH922ogVAWkKiyKXorjYymz2084zrhrZRXtLrA5eEx5SO8Dj0c/4FpCHZyn7MKzhW2pW4tK28vVr+5oQ2xw==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.48.0': + resolution: {integrity: sha512-diOdQuw43xTa1RddAFbhIA8toirSzFMcnIg8kvlzRbK26xqEnKJ/vqQnghTAajy2Dcy42v+GMPMo6jq67od+Dw==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.48.0': + resolution: {integrity: sha512-QhR2KA18fPlJWFefySJPDYZELaVqIUVnYgAOdtJ+B/uH96CFg2l1TQpX19XpUMWUqMyIiyY45wje8K6F4w4/CA==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.48.0': + resolution: {integrity: sha512-Q9RMXnQVJ5S1SYpNSTwXDpoQLgJ/fbInWOyjbCnnqTElEyeNvLAB3QvG5xmMQMhFN74bB5ZZJYkKaFPcOG8sGg==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.48.0': + resolution: {integrity: sha512-3jzOhHWM8O8PSfyft+ghXZfBkZawQA0PUGtadKYxFqpcYlOYjTi06WsnYBsbMHLawr+4uWirLlbhcYLHDXR16w==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.48.0': + resolution: {integrity: sha512-NcD5uVUmE73C/TPJqf78hInZmiSBsDpz3iD5MF/BuB+qzm4ooF2S1HfeTChj5K4AV3y19FFPgxonsxiEpy8v/A==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.48.0': + resolution: {integrity: sha512-JWnrj8qZgLWRNHr7NbpdnrQ8kcg09EBBq8jVOjmtlB3c8C6IrynAJSMhMVGME4YfTJzIkJqvSUSVJRqkDnu/aA==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.48.0': + resolution: {integrity: sha512-9xu92F0TxuMH0tD6tG3+GtngwdgSf8Bnz+YcsPG91/r5Vgh5LNofO48jV55priA95p3c92FLmPM7CvsVlnSbGQ==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.48.0': + resolution: {integrity: sha512-NLtvJB5YpWn7jlp1rJiY0s+G1Z1IVmkDuiywiqUhh96MIraC0n7XQc2SZ1CZz14shqkM+XN2UrfIo7JB6UufOA==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.48.0': + resolution: {integrity: sha512-QJ4hCOnz2SXgCh+HmpvZkM+0NSGcZACyYS8DGbWn2PbmA0e5xUk4bIP8eqJyNXLtyB4gZ3/XyvKtQ1IFH671vQ==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.48.0': + resolution: {integrity: sha512-Pk0qlGJnhILdIC5zSKQnprFjrGmjfDM7TPZ0FKJxRkoo+kgMRAg4ps1VlTZf8u2vohSicLg7NP+cA5qE96PaFg==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.48.0': + resolution: {integrity: sha512-/dNFc6rTpoOzgp5GKoYjT6uLo8okR/Chi2ECOmCZiS4oqh3mc95pThWma7Bgyk6/WTEvjDINpiBCuecPLOgBLQ==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.48.0': + resolution: {integrity: sha512-YBwXsvsFI8CVA4ej+bJF2d9uAeIiSkqKSPQNn0Wyh4eMDY4wxuSp71BauPjQNCKK2tD2/ksJ7uhJ8X/PVY9bHQ==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.48.0': + resolution: {integrity: sha512-FI3Rr2aGAtl1aHzbkBIamsQyuauYtTF9SDUJ8n2wMXuuxwchC3QkumZa1TEXYIv/1AUp1a25Kwy6ONArvnyeVQ==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.48.0': + resolution: {integrity: sha512-Dx7qH0/rvNNFmCcIRe1pyQ9/H0XO4v/f0SDoafwRYwc2J7bJZ5N4CHL/cdjamISZ5Cgnon6iazAVRFlxSoHQnQ==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.48.0': + resolution: {integrity: sha512-GUdZKTeKBq9WmEBzvFYuC88yk26vT66lQV8D5+9TgkfbewhLaTHRNATyzpQwwbHIfJvDJ3N9WJ90wK/uR3cy3Q==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.48.0': + resolution: {integrity: sha512-ao58Adz/v14MWpQgYAb4a4h3fdw73DrDGtaiF7Opds5wNyEQwtO6M9dBh89nke0yoZzzaegq6J/EXs7eBebG8A==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.48.0': + resolution: {integrity: sha512-kpFno46bHtjZVdRIOxqaGeiABiToo2J+st7Yce+aiAoo1H0xPi2keyQIP04n2JjDVuxBN6bSz9R6RdTK5hIppw==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.48.0': + resolution: {integrity: sha512-rFYrk4lLk9YUTIeihnQMiwMr6gDhGGSbWThPEDfBoU/HdAtOzPXeexKi7yU8jO+LWRKnmqPN9NviHQf6GDwBcQ==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.48.0': + resolution: {integrity: sha512-sq0hHLTgdtwOPDB5SJOuaoHyiP1qSwg+71TQWk8iDS04bW1wIE0oQ6otPiRj2ZvLYNASLMaTp8QRGUVZ+5OL5A==} + cpu: [x64] + os: [win32] + + '@sec-ant/readable-stream@0.4.1': + resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} + + '@sindresorhus/merge-streams@4.0.0': + resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} + engines: {node: '>=18'} + + '@tailwindcss/node@4.1.12': + resolution: {integrity: sha512-3hm9brwvQkZFe++SBt+oLjo4OLDtkvlE8q2WalaD/7QWaeM7KEJbAiY/LJZUaCs7Xa8aUu4xy3uoyX4q54UVdQ==} + + '@tailwindcss/oxide-android-arm64@4.1.12': + resolution: {integrity: sha512-oNY5pq+1gc4T6QVTsZKwZaGpBb2N1H1fsc1GD4o7yinFySqIuRZ2E4NvGasWc6PhYJwGK2+5YT1f9Tp80zUQZQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.1.12': + resolution: {integrity: sha512-cq1qmq2HEtDV9HvZlTtrj671mCdGB93bVY6J29mwCyaMYCP/JaUBXxrQQQm7Qn33AXXASPUb2HFZlWiiHWFytw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.1.12': + resolution: {integrity: sha512-6UCsIeFUcBfpangqlXay9Ffty9XhFH1QuUFn0WV83W8lGdX8cD5/+2ONLluALJD5+yJ7k8mVtwy3zMZmzEfbLg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.1.12': + resolution: {integrity: sha512-JOH/f7j6+nYXIrHobRYCtoArJdMJh5zy5lr0FV0Qu47MID/vqJAY3r/OElPzx1C/wdT1uS7cPq+xdYYelny1ww==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.12': + resolution: {integrity: sha512-v4Ghvi9AU1SYgGr3/j38PD8PEe6bRfTnNSUE3YCMIRrrNigCFtHZ2TCm8142X8fcSqHBZBceDx+JlFJEfNg5zQ==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.12': + resolution: {integrity: sha512-YP5s1LmetL9UsvVAKusHSyPlzSRqYyRB0f+Kl/xcYQSPLEw/BvGfxzbH+ihUciePDjiXwHh+p+qbSP3SlJw+6g==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-musl@4.1.12': + resolution: {integrity: sha512-V8pAM3s8gsrXcCv6kCHSuwyb/gPsd863iT+v1PGXC4fSL/OJqsKhfK//v8P+w9ThKIoqNbEnsZqNy+WDnwQqCA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-gnu@4.1.12': + resolution: {integrity: sha512-xYfqYLjvm2UQ3TZggTGrwxjYaLB62b1Wiysw/YE3Yqbh86sOMoTn0feF98PonP7LtjsWOWcXEbGqDL7zv0uW8Q==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.1.12': + resolution: {integrity: sha512-ha0pHPamN+fWZY7GCzz5rKunlv9L5R8kdh+YNvP5awe3LtuXb5nRi/H27GeL2U+TdhDOptU7T6Is7mdwh5Ar3A==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-wasm32-wasi@4.1.12': + resolution: {integrity: sha512-4tSyu3dW+ktzdEpuk6g49KdEangu3eCYoqPhWNsZgUhyegEda3M9rG0/j1GV/JjVVsj+lG7jWAyrTlLzd/WEBg==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.12': + resolution: {integrity: sha512-iGLyD/cVP724+FGtMWslhcFyg4xyYyM+5F4hGvKA7eifPkXHRAUDFaimu53fpNg9X8dfP75pXx/zFt/jlNF+lg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.1.12': + resolution: {integrity: sha512-NKIh5rzw6CpEodv/++r0hGLlfgT/gFN+5WNdZtvh6wpU2BpGNgdjvj6H2oFc8nCM839QM1YOhjpgbAONUb4IxA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.1.12': + resolution: {integrity: sha512-gM5EoKHW/ukmlEtphNwaGx45fGoEmP10v51t9unv55voWh6WrOL19hfuIdo2FjxIaZzw776/BUQg7Pck++cIVw==} + engines: {node: '>= 10'} + + '@tailwindcss/postcss@4.1.12': + resolution: {integrity: sha512-5PpLYhCAwf9SJEeIsSmCDLgyVfdBhdBpzX1OJ87anT9IVR0Z9pjM0FNixCAUAHGnMBGB8K99SwAheXrT0Kh6QQ==} + + '@tailwindcss/vite@4.1.12': + resolution: {integrity: sha512-4pt0AMFDx7gzIrAOIYgYP0KCBuKWqyW8ayrdiLEjoJTT4pKTjrzG/e4uzWtTLDziC+66R9wbUqZBccJalSE5vQ==} + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 + + '@tsconfig/node22@22.0.2': + resolution: {integrity: sha512-Kmwj4u8sDRDrMYRoN9FDEcXD8UpBSaPQQ24Gz+Gamqfm7xxn+GBR7ge/Z7pK8OXNGyUzbSwJj+TH6B+DS/epyA==} + + '@tybys/wasm-util@0.10.0': + resolution: {integrity: sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==} + + '@types/chai@5.2.2': + resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/jsdom@21.1.7': + resolution: {integrity: sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/node@24.3.0': + resolution: {integrity: sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==} + + '@types/tough-cookie@4.0.5': + resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} + + '@typescript-eslint/eslint-plugin@8.40.0': + resolution: {integrity: sha512-w/EboPlBwnmOBtRbiOvzjD+wdiZdgFeo17lkltrtn7X37vagKKWJABvyfsJXTlHe6XBzugmYgd4A4nW+k8Mixw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.40.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/parser@8.40.0': + resolution: {integrity: sha512-jCNyAuXx8dr5KJMkecGmZ8KI61KBUhkCob+SD+C+I5+Y1FWI2Y3QmY4/cxMCC5WAsZqoEtEETVhUiUMIGCf6Bw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/project-service@8.40.0': + resolution: {integrity: sha512-/A89vz7Wf5DEXsGVvcGdYKbVM9F7DyFXj52lNYUDS1L9yJfqjW/fIp5PgMuEJL/KeqVTe2QSbXAGUZljDUpArw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/scope-manager@8.40.0': + resolution: {integrity: sha512-y9ObStCcdCiZKzwqsE8CcpyuVMwRouJbbSrNuThDpv16dFAj429IkM6LNb1dZ2m7hK5fHyzNcErZf7CEeKXR4w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.40.0': + resolution: {integrity: sha512-jtMytmUaG9d/9kqSl/W3E3xaWESo4hFDxAIHGVW/WKKtQhesnRIJSAJO6XckluuJ6KDB5woD1EiqknriCtAmcw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/type-utils@8.40.0': + resolution: {integrity: sha512-eE60cK4KzAc6ZrzlJnflXdrMqOBaugeukWICO2rB0KNvwdIMaEaYiywwHMzA1qFpTxrLhN9Lp4E/00EgWcD3Ow==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/types@8.40.0': + resolution: {integrity: sha512-ETdbFlgbAmXHyFPwqUIYrfc12ArvpBhEVgGAxVYSwli26dn8Ko+lIo4Su9vI9ykTZdJn+vJprs/0eZU0YMAEQg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.40.0': + resolution: {integrity: sha512-k1z9+GJReVVOkc1WfVKs1vBrR5MIKKbdAjDTPvIK3L8De6KbFfPFt6BKpdkdk7rZS2GtC/m6yI5MYX+UsuvVYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/utils@8.40.0': + resolution: {integrity: sha512-Cgzi2MXSZyAUOY+BFwGs17s7ad/7L+gKt6Y8rAVVWS+7o6wrjeFN4nVfTpbE25MNcxyJ+iYUXflbs2xR9h4UBg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/visitor-keys@8.40.0': + resolution: {integrity: sha512-8CZ47QwalyRjsypfwnbI3hKy5gJDPmrkLjkgMxhi0+DZZ2QNx2naS6/hWoVYUHU7LU2zleF68V9miaVZvhFfTA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@vitejs/plugin-vue-jsx@5.0.1': + resolution: {integrity: sha512-X7qmQMXbdDh+sfHUttXokPD0cjPkMFoae7SgbkF9vi3idGUKmxLcnU2Ug49FHwiKXebfzQRIm5yK3sfCJzNBbg==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 + vue: ^3.0.0 + + '@vitejs/plugin-vue@6.0.1': + resolution: {integrity: sha512-+MaE752hU0wfPFJEUAIxqw18+20euHHdxVtMvbFcOEpjEyfqXH/5DCoTHiVJ0J29EhTJdoTkjEv5YBKU9dnoTw==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 + vue: ^3.2.25 + + '@vitest/eslint-plugin@1.3.4': + resolution: {integrity: sha512-EOg8d0jn3BAiKnR55WkFxmxfWA3nmzrbIIuOXyTe6A72duryNgyU+bdBEauA97Aab3ho9kLmAwgPX63Ckj4QEg==} + peerDependencies: + eslint: '>= 8.57.0' + typescript: '>= 5.0.0' + vitest: '*' + peerDependenciesMeta: + typescript: + optional: true + vitest: + optional: true + + '@vitest/expect@3.2.4': + resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} + + '@vitest/mocker@3.2.4': + resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@3.2.4': + resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} + + '@vitest/runner@3.2.4': + resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} + + '@vitest/snapshot@3.2.4': + resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} + + '@vitest/spy@3.2.4': + resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} + + '@vitest/utils@3.2.4': + resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + + '@volar/language-core@2.4.23': + resolution: {integrity: sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ==} + + '@volar/source-map@2.4.23': + resolution: {integrity: sha512-Z1Uc8IB57Lm6k7q6KIDu/p+JWtf3xsXJqAX/5r18hYOTpJyBn0KXUR8oTJ4WFYOcDzWC9n3IflGgHowx6U6z9Q==} + + '@volar/typescript@2.4.23': + resolution: {integrity: sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag==} + + '@vue/babel-helper-vue-transform-on@1.5.0': + resolution: {integrity: sha512-0dAYkerNhhHutHZ34JtTl2czVQHUNWv6xEbkdF5W+Yrv5pCWsqjeORdOgbtW2I9gWlt+wBmVn+ttqN9ZxR5tzA==} + + '@vue/babel-plugin-jsx@1.5.0': + resolution: {integrity: sha512-mneBhw1oOqCd2247O0Yw/mRwC9jIGACAJUlawkmMBiNmL4dGA2eMzuNZVNqOUfYTa6vqmND4CtOPzmEEEqLKFw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + peerDependenciesMeta: + '@babel/core': + optional: true + + '@vue/babel-plugin-resolve-type@1.5.0': + resolution: {integrity: sha512-Wm/60o+53JwJODm4Knz47dxJnLDJ9FnKnGZJbUUf8nQRAtt6P+undLUAVU3Ha33LxOJe6IPoifRQ6F/0RrU31w==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@vue/compiler-core@3.5.19': + resolution: {integrity: sha512-/afpyvlkrSNYbPo94Qu8GtIOWS+g5TRdOvs6XZNw6pWQQmj5pBgSZvEPOIZlqWq0YvoUhDDQaQ2TnzuJdOV4hA==} + + '@vue/compiler-dom@3.5.19': + resolution: {integrity: sha512-Drs6rPHQZx/pN9S6ml3Z3K/TWCIRPvzG2B/o5kFK9X0MNHt8/E+38tiRfojufrYBfA6FQUFB2qBBRXlcSXWtOA==} + + '@vue/compiler-sfc@3.5.19': + resolution: {integrity: sha512-YWCm1CYaJ+2RvNmhCwI7t3I3nU+hOrWGWMsn+Z/kmm1jy5iinnVtlmkiZwbLlbV1SRizX7vHsc0/bG5dj0zRTg==} + + '@vue/compiler-ssr@3.5.19': + resolution: {integrity: sha512-/wx0VZtkWOPdiQLWPeQeqpHWR/LuNC7bHfSX7OayBTtUy8wur6vT6EQIX6Et86aED6J+y8tTw43qo2uoqGg5sw==} + + '@vue/compiler-vue2@2.7.16': + resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} + + '@vue/devtools-api@6.6.4': + resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} + + '@vue/devtools-api@7.7.7': + resolution: {integrity: sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg==} + + '@vue/devtools-core@8.0.1': + resolution: {integrity: sha512-Lf/+ambV3utWJ18r5TnpePbJ60IcIcqeZSQYLyNcFw2sFel0tGMnMyCdDtR1JNIdVZGAVaksTLhGh0FlrNu+sw==} + peerDependencies: + vue: ^3.0.0 + + '@vue/devtools-kit@7.7.7': + resolution: {integrity: sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==} + + '@vue/devtools-kit@8.0.1': + resolution: {integrity: sha512-7kiPhgTKNtNeXltEHnJJjIDlndlJP4P+UJvCw54uVHNDlI6JzwrSiRmW4cxKTug2wDbc/dkGaMnlZghcwV+aWA==} + + '@vue/devtools-shared@7.7.7': + resolution: {integrity: sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==} + + '@vue/devtools-shared@8.0.1': + resolution: {integrity: sha512-PqtWqPPRpMwZ9FjTzyugb5KeV9kmg2C3hjxZHwjl0lijT4QIJDd0z6AWcnbM9w2nayjDymyTt0+sbdTv3pVeNg==} + + '@vue/eslint-config-prettier@10.2.0': + resolution: {integrity: sha512-GL3YBLwv/+b86yHcNNfPJxOTtVFJ4Mbc9UU3zR+KVoG7SwGTjPT+32fXamscNumElhcpXW3mT0DgzS9w32S7Bw==} + peerDependencies: + eslint: '>= 8.21.0' + prettier: '>= 3.0.0' + + '@vue/eslint-config-typescript@14.6.0': + resolution: {integrity: sha512-UpiRY/7go4Yps4mYCjkvlIbVWmn9YvPGQDxTAlcKLphyaD77LjIu3plH4Y9zNT0GB4f3K5tMmhhtRhPOgrQ/bQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^9.10.0 + eslint-plugin-vue: ^9.28.0 || ^10.0.0 + typescript: '>=4.8.4' + peerDependenciesMeta: + typescript: + optional: true + + '@vue/language-core@3.0.6': + resolution: {integrity: sha512-e2RRzYWm+qGm8apUHW1wA5RQxzNhkqbbKdbKhiDUcmMrNAZGyM8aTiL3UrTqkaFI5s7wJRGGrp4u3jgusuBp2A==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@vue/reactivity@3.5.19': + resolution: {integrity: sha512-4bueZg2qs5MSsK2dQk3sssV0cfvxb/QZntTC8v7J448GLgmfPkQ+27aDjlt40+XFqOwUq5yRxK5uQh14Fc9eVA==} + + '@vue/runtime-core@3.5.19': + resolution: {integrity: sha512-TaooCr8Hge1sWjLSyhdubnuofs3shhzZGfyD11gFolZrny76drPwBVQj28/z/4+msSFb18tOIg6VVVgf9/IbIA==} + + '@vue/runtime-dom@3.5.19': + resolution: {integrity: sha512-qmahqeok6ztuUTmV8lqd7N9ymbBzctNF885n8gL3xdCC1u2RnM/coX16Via0AiONQXUoYpxPojL3U1IsDgSWUQ==} + + '@vue/server-renderer@3.5.19': + resolution: {integrity: sha512-ZJ/zV9SQuaIO+BEEVq/2a6fipyrSYfjKMU3267bPUk+oTx/hZq3RzV7VCh0Unlppt39Bvh6+NzxeopIFv4HJNg==} + peerDependencies: + vue: 3.5.19 + + '@vue/shared@3.5.19': + resolution: {integrity: sha512-IhXCOn08wgKrLQxRFKKlSacWg4Goi1BolrdEeLYn6tgHjJNXVrWJ5nzoxZqNwl5p88aLlQ8LOaoMa3AYvaKJ/Q==} + + '@vue/test-utils@2.4.6': + resolution: {integrity: sha512-FMxEjOpYNYiFe0GkaHsnJPXFHxQ6m4t8vI/ElPGpMWxZKpmRvQ33OIrvRXemy6yha03RxhOlQuy+gZMC3CQSow==} + + '@vue/tsconfig@0.8.1': + resolution: {integrity: sha512-aK7feIWPXFSUhsCP9PFqPyFOcz4ENkb8hZ2pneL6m2UjCkccvaOhC/5KCKluuBufvp2KzkbdA2W2pk20vLzu3g==} + peerDependencies: + typescript: 5.x + vue: ^3.4.0 + peerDependenciesMeta: + typescript: + optional: true + vue: + optional: true + + abbrev@2.0.0: + resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + alien-signals@2.0.7: + resolution: {integrity: sha512-wE7y3jmYeb0+h6mr5BOovuqhFv22O/MV9j5p0ndJsa7z1zJNPGQ4ph5pQk/kTTCWRC3xsA4SmtwmkzQO+7NCNg==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.2.0: + resolution: {integrity: sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + ansis@4.1.0: + resolution: {integrity: sha512-BGcItUBWSMRgOCe+SVZJ+S7yTRG0eGt9cXAHev72yuGcY23hnLA7Bky5L/xLyPINoSN95geovfBkqoTlNZYa7w==} + engines: {node: '>=14'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + autoprefixer@10.4.21: + resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + birpc@2.5.0: + resolution: {integrity: sha512-VSWO/W6nNQdyP520F1mhf+Lc2f8pjGQOtoHHm7Ze8Go1kX7akpVIrtTa0fn+HB0QJEDVacl6aO08YE0PgXfdnQ==} + + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.25.3: + resolution: {integrity: sha512-cDGv1kkDI4/0e5yON9yM5G/0A5u8sf5TnmdX5C9qHzI9PPu++sQ9zjm1k9NiOrf3riY4OkK0zSGqfvJyJsgCBQ==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + bundle-name@4.1.0: + resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} + engines: {node: '>=18'} + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001737: + resolution: {integrity: sha512-BiloLiXtQNrY5UyF0+1nSJLXUENuhka2pzy2Fx5pGxqavdrxSCW4U6Pn/PoG3Efspi2frRbHpBV2XsrPE6EDlw==} + + chai@5.3.3: + resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} + engines: {node: '>=18'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + check-error@2.1.1: + resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} + engines: {node: '>= 16'} + + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + concurrently@8.2.2: + resolution: {integrity: sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==} + engines: {node: ^14.13.0 || >=16.0.0} + hasBin: true + + config-chain@1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + copy-anything@3.0.5: + resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==} + engines: {node: '>=12.13'} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + cssstyle@4.6.0: + resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==} + engines: {node: '>=18'} + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + data-urls@5.0.0: + resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} + engines: {node: '>=18'} + + date-fns@2.30.0: + resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} + engines: {node: '>=0.11'} + + de-indent@1.0.2: + resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} + + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + default-browser-id@5.0.0: + resolution: {integrity: sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==} + engines: {node: '>=18'} + + default-browser@5.2.1: + resolution: {integrity: sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==} + engines: {node: '>=18'} + + define-lazy-prop@3.0.0: + resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} + engines: {node: '>=12'} + + detect-libc@2.0.4: + resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} + engines: {node: '>=8'} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + editorconfig@1.0.4: + resolution: {integrity: sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==} + engines: {node: '>=14'} + hasBin: true + + electron-to-chromium@1.5.208: + resolution: {integrity: sha512-ozZyibehoe7tOhNaf16lKmljVf+3npZcJIEbJRVftVsmAg5TeA1mGS9dVCZzOwr2xT7xK15V0p7+GZqSPgkuPg==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + enhanced-resolve@5.18.3: + resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} + engines: {node: '>=10.13.0'} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + + error-stack-parser-es@1.0.5: + resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==} + + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + + esbuild@0.25.9: + resolution: {integrity: sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-config-prettier@10.1.8: + resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-plugin-oxlint@1.12.0: + resolution: {integrity: sha512-4rVg1CgiiA3bKkjVSh4nhZE46K0ZznkTbDqVCAhKSnM2PPu8I1lBXy1k5APg68QBXzOIVlZiNsNCPTh2Rl/lZA==} + + eslint-plugin-playwright@2.2.2: + resolution: {integrity: sha512-j0jKpndIPOXRRP9uMkwb9l/nSmModOU3452nrFdgFJoEv/435J1onk8+aITzjDW8DfypxgmVaDMdmVIa6F7I0w==} + engines: {node: '>=16.6.0'} + peerDependencies: + eslint: '>=8.40.0' + + eslint-plugin-prettier@5.5.4: + resolution: {integrity: sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + '@types/eslint': '>=8.0.0' + eslint: '>=8.0.0' + eslint-config-prettier: '>= 7.0.0 <10.0.0 || >=10.1.0' + prettier: '>=3.0.0' + peerDependenciesMeta: + '@types/eslint': + optional: true + eslint-config-prettier: + optional: true + + eslint-plugin-vue@10.4.0: + resolution: {integrity: sha512-K6tP0dW8FJVZLQxa2S7LcE1lLw3X8VvB3t887Q6CLrFVxHYBXGANbXvwNzYIu6Ughx1bSJ5BDT0YB3ybPT39lw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^7.0.0 || ^8.0.0 + eslint: ^8.57.0 || ^9.0.0 + vue-eslint-parser: ^10.0.0 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.34.0: + resolution: {integrity: sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + execa@9.6.0: + resolution: {integrity: sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==} + engines: {node: ^18.19.0 || >=20.5.0} + + expect-type@1.2.2: + resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} + engines: {node: '>=12.0.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + figures@6.1.0: + resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} + engines: {node: '>=18'} + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + + fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-stream@9.0.1: + resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} + engines: {node: '>=18'} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + + globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + + hookable@5.5.3: + resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} + + html-encoding-sniffer@4.0.0: + resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} + engines: {node: '>=18'} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + human-signals@8.0.1: + resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==} + engines: {node: '>=18.18.0'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + + is-stream@4.0.1: + resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} + engines: {node: '>=18'} + + is-unicode-supported@2.1.0: + resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} + engines: {node: '>=18'} + + is-what@4.1.16: + resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==} + engines: {node: '>=12.13'} + + is-wsl@3.1.0: + resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} + engines: {node: '>=16'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + isexe@3.1.1: + resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==} + engines: {node: '>=16'} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jiti@2.5.1: + resolution: {integrity: sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==} + hasBin: true + + js-beautify@1.15.4: + resolution: {integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==} + engines: {node: '>=14'} + hasBin: true + + js-cookie@3.0.5: + resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} + engines: {node: '>=14'} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + jsdom@26.1.0: + resolution: {integrity: sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==} + engines: {node: '>=18'} + peerDependencies: + canvas: ^3.0.0 + peerDependenciesMeta: + canvas: + optional: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-parse-even-better-errors@4.0.0: + resolution: {integrity: sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==} + engines: {node: ^18.17.0 || >=20.5.0} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonc-parser@3.3.1: + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kolorist@1.8.0: + resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lightningcss-darwin-arm64@1.30.1: + resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.30.1: + resolution: {integrity: sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.30.1: + resolution: {integrity: sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.30.1: + resolution: {integrity: sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.30.1: + resolution: {integrity: sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.30.1: + resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.30.1: + resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.30.1: + resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.30.1: + resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.30.1: + resolution: {integrity: sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.30.1: + resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==} + engines: {node: '>= 12.0.0'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + loupe@3.2.1: + resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lucide-react@0.541.0: + resolution: {integrity: sha512-s0Vircsu5WaGv2KoJZ5+SoxiAJ3UXV5KqEM3eIFDHaHkcLIFdIWgXtZ412+Gh02UsdS7Was+jvEpBvPCWQISlg==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + magic-string@0.30.18: + resolution: {integrity: sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==} + + memorystream@0.3.1: + resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} + engines: {node: '>= 0.10.0'} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@9.0.1: + resolution: {integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==} + engines: {node: '>=16 || 14 >=14.17'} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + minizlib@3.0.2: + resolution: {integrity: sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==} + engines: {node: '>= 18'} + + mitt@3.0.1: + resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} + + mkdirp@3.0.1: + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} + engines: {node: '>=10'} + hasBin: true + + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + muggle-string@0.4.1: + resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + nanoid@5.1.5: + resolution: {integrity: sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==} + engines: {node: ^18 || >=20} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + node-releases@2.0.19: + resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + + nopt@7.2.1: + resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + + normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + + npm-normalize-package-bin@4.0.0: + resolution: {integrity: sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==} + engines: {node: ^18.17.0 || >=20.5.0} + + npm-run-all2@8.0.4: + resolution: {integrity: sha512-wdbB5My48XKp2ZfJUlhnLVihzeuA1hgBnqB2J9ahV77wLS+/YAJAlN8I+X3DIFIPZ3m5L7nplmlbhNiFDmXRDA==} + engines: {node: ^20.5.0 || >=22.0.0, npm: '>= 10'} + hasBin: true + + npm-run-path@6.0.0: + resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} + engines: {node: '>=18'} + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + nwsapi@2.2.21: + resolution: {integrity: sha512-o6nIY3qwiSXl7/LuOU0Dmuctd34Yay0yeuZRLFmDPrrdHpXKFndPj3hM+YEPVHYC5fx2otBx4Ilc/gyYSAUaIA==} + + ohash@2.0.11: + resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} + + open@10.2.0: + resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==} + engines: {node: '>=18'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + oxc-parser@0.74.0: + resolution: {integrity: sha512-2tDN/ttU8WE6oFh8EzKNam7KE7ZXSG5uXmvX85iNzxdJfMssDWcj3gpYzZi1E04XuE7m3v1dVWl/8BE886vPGw==} + engines: {node: '>=20.0.0'} + + oxlint-tsgolint@0.0.4: + resolution: {integrity: sha512-KFWVP+VU3ymgK/Dtuf6iRkqjo+aN42lS1YThY6JWlNi1GQqm7wtio/kAwssqDhm8kP+CVXbgZAtu1wgsK4XeTg==} + hasBin: true + + oxlint@1.12.0: + resolution: {integrity: sha512-tBQ9aB00aYLlGXE21WJHnKQAI8xoi2V6Eiz/WvGV7FwU9YLYuNOurEEVbfoS5u0ODX8GLvGWj1fdHh5Rb74Kkw==} + engines: {node: '>=8.*'} + hasBin: true + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-ms@4.0.0: + resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} + engines: {node: '>=18'} + + parse5@7.3.0: + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + pathval@2.0.1: + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} + engines: {node: '>= 14.16'} + + perfect-debounce@1.0.0: + resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + pidtree@0.6.0: + resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} + engines: {node: '>=0.10'} + hasBin: true + + pinia@3.0.3: + resolution: {integrity: sha512-ttXO/InUULUXkMHpTdp9Fj4hLpD/2AoJdmAbAeW2yu1iy1k+pkFekQXw5VpC0/5p51IOR/jDaDRfRWRnMMsGOA==} + peerDependencies: + typescript: '>=4.4.4' + vue: ^2.7.0 || ^3.5.11 + peerDependenciesMeta: + typescript: + optional: true + + playwright-core@1.55.0: + resolution: {integrity: sha512-GvZs4vU3U5ro2nZpeiwyb0zuFaqb9sUiAJuyrWpcGouD8y9/HLgGbNRjIph7zU9D3hnPaisMl9zG9CgFi/biIg==} + engines: {node: '>=18'} + hasBin: true + + playwright@1.55.0: + resolution: {integrity: sha512-sdCWStblvV1YU909Xqx0DhOjPZE4/5lJsIS84IfN9dAZfcl/CIZ5O8l3o0j7hPMjDvqoTF8ZUcc+i/GL5erstA==} + engines: {node: '>=18'} + hasBin: true + + postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier-linter-helpers@1.0.0: + resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + engines: {node: '>=6.0.0'} + + prettier@3.6.2: + resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} + engines: {node: '>=14'} + hasBin: true + + pretty-ms@9.2.0: + resolution: {integrity: sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==} + engines: {node: '>=18'} + + proto-list@1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + react@19.1.1: + resolution: {integrity: sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==} + engines: {node: '>=0.10.0'} + + read-package-json-fast@4.0.0: + resolution: {integrity: sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg==} + engines: {node: ^18.17.0 || >=20.5.0} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + + rolldown-vite@7.1.4: + resolution: {integrity: sha512-VE0cXhJfTypUhm71w4pR62dMyqw8JKHWMdbUBSDVqZTGGpZz5Zkw+cT47rvBR/SQ9E9F2GtlW02rWIY2T9HdLg==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + esbuild: ^0.25.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + rolldown@1.0.0-beta.33: + resolution: {integrity: sha512-mgu118ZuRguC8unhPCbdZbyRbjQfEMiWqlojBA5aRIncBelRaBomnHNpGKYkYWeK7twRz5Cql30xgqqrA3Xelw==} + hasBin: true + + rollup@4.48.0: + resolution: {integrity: sha512-BXHRqK1vyt9XVSEHZ9y7xdYtuYbwVod2mLwOMFP7t/Eqoc1pHRlG/WdV2qNeNvZHRQdLedaFycljaYYM96RqJQ==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + rrweb-cssom@0.8.0: + resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} + + run-applescript@7.0.0: + resolution: {integrity: sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==} + engines: {node: '>=18'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + rxjs@7.8.2: + resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + engines: {node: '>=10'} + hasBin: true + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shell-quote@1.8.3: + resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} + engines: {node: '>= 0.4'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + sirv@3.0.1: + resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==} + engines: {node: '>=18'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + spawn-command@0.0.2: + resolution: {integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==} + + speakingurl@14.0.1: + resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==} + engines: {node: '>=0.10.0'} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@3.9.0: + resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-final-newline@4.0.0: + resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} + engines: {node: '>=18'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + strip-literal@3.0.0: + resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==} + + superjson@2.2.2: + resolution: {integrity: sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==} + engines: {node: '>=16'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + + synckit@0.11.11: + resolution: {integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==} + engines: {node: ^14.18.0 || >=16.0.0} + + tailwindcss@4.1.12: + resolution: {integrity: sha512-DzFtxOi+7NsFf7DBtI3BJsynR+0Yp6etH+nRPTbpWnS2pZBaSksv/JGctNwSWzbFjp0vxSqknaUylseZqMDGrA==} + + tapable@2.2.3: + resolution: {integrity: sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==} + engines: {node: '>=6'} + + tar@7.4.3: + resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==} + engines: {node: '>=18'} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + tinyglobby@0.2.14: + resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} + engines: {node: '>=12.0.0'} + + tinypool@1.1.1: + resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + engines: {node: '>=14.0.0'} + + tinyspy@4.0.3: + resolution: {integrity: sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==} + engines: {node: '>=14.0.0'} + + tldts-core@6.1.86: + resolution: {integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==} + + tldts@6.1.86: + resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==} + hasBin: true + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + + tough-cookie@5.1.2: + resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==} + engines: {node: '>=16'} + + tr46@5.1.1: + resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} + engines: {node: '>=18'} + + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + + ts-api-utils@2.1.0: + resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + + typescript-eslint@8.40.0: + resolution: {integrity: sha512-Xvd2l+ZmFDPEt4oj1QEXzA4A2uUK6opvKu3eGN9aGjB8au02lIVcLyi375w94hHyejTOmzIU77L8ol2sRg9n7Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + typescript@5.9.2: + resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@7.10.0: + resolution: {integrity: sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==} + + unicorn-magic@0.3.0: + resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} + engines: {node: '>=18'} + + unplugin-utils@0.2.5: + resolution: {integrity: sha512-gwXJnPRewT4rT7sBi/IvxKTjsms7jX7QIDLOClApuZwR49SXbrB1z2NLUZ+vDHyqCj/n58OzRRqaW+B8OZi8vg==} + engines: {node: '>=18.12.0'} + + update-browserslist-db@1.1.3: + resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + vite-dev-rpc@1.1.0: + resolution: {integrity: sha512-pKXZlgoXGoE8sEKiKJSng4hI1sQ4wi5YT24FCrwrLt6opmkjlqPPVmiPWWJn8M8byMxRGzp1CrFuqQs4M/Z39A==} + peerDependencies: + vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.1 || ^7.0.0-0 + + vite-hot-client@2.1.0: + resolution: {integrity: sha512-7SpgZmU7R+dDnSmvXE1mfDtnHLHQSisdySVR7lO8ceAXvM0otZeuQQ6C8LrS5d/aYyP/QZ0hI0L+dIPrm4YlFQ==} + peerDependencies: + vite: ^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 + + vite-node@3.2.4: + resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + + vite-plugin-inspect@11.3.2: + resolution: {integrity: sha512-nzwvyFQg58XSMAmKVLr2uekAxNYvAbz1lyPmCAFVIBncCgN9S/HPM+2UM9Q9cvc4JEbC5ZBgwLAdaE2onmQuKg==} + engines: {node: '>=14'} + peerDependencies: + '@nuxt/kit': '*' + vite: ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + '@nuxt/kit': + optional: true + + vite-plugin-vue-devtools@8.0.1: + resolution: {integrity: sha512-ecm/Xvtg5xsFPfY7SJ38Zb6NfmVrHxBhLMk/3nm5ZDAd7n8Dk2BV8JBuq1L5wRMVfvCth01vtzJViZC9TAC6qg==} + engines: {node: '>=v14.21.3'} + peerDependencies: + vite: ^6.0.0 || ^7.0.0-0 + + vite-plugin-vue-inspector@5.3.2: + resolution: {integrity: sha512-YvEKooQcSiBTAs0DoYLfefNja9bLgkFM7NI2b07bE2SruuvX0MEa9cMaxjKVMkeCp5Nz9FRIdcN1rOdFVBeL6Q==} + peerDependencies: + vite: ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 + + vite@7.1.3: + resolution: {integrity: sha512-OOUi5zjkDxYrKhTV3V7iKsoS37VUM7v40+HuwEmcrsf11Cdx9y3DIr2Px6liIcZFwt3XSRpQvFpL3WVy7ApkGw==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + 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 + + vitest@3.2.4: + resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/debug': ^4.1.12 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@vitest/browser': 3.2.4 + '@vitest/ui': 3.2.4 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/debug': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + vscode-uri@3.1.0: + resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} + + vue-component-type-helpers@2.2.12: + resolution: {integrity: sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==} + + vue-eslint-parser@10.2.0: + resolution: {integrity: sha512-CydUvFOQKD928UzZhTp4pr2vWz1L+H99t7Pkln2QSPdvmURT0MoC4wUccfCnuEaihNsu9aYYyk+bep8rlfkUXw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + + vue-router@4.5.1: + resolution: {integrity: sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==} + peerDependencies: + vue: ^3.2.0 + + vue-tsc@3.0.6: + resolution: {integrity: sha512-Tbs8Whd43R2e2nxez4WXPvvdjGbW24rOSgRhLOHXzWiT4pcP4G7KeWh0YCn18rF4bVwv7tggLLZ6MJnO6jXPBg==} + hasBin: true + peerDependencies: + typescript: '>=5.0.0' + + vue@3.5.19: + resolution: {integrity: sha512-ZRh0HTmw6KChRYWgN8Ox/wi7VhpuGlvMPrHjIsdRbzKNgECFLzy+dKL5z9yGaBSjCpmcfJCbh3I1tNSRmBz2tg==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + + whatwg-url@14.2.0: + resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==} + engines: {node: '>=18'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + which@5.0.0: + resolution: {integrity: sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==} + engines: {node: ^18.17.0 || >=20.5.0} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + ws@8.18.3: + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} + 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 + + wsl-utils@0.1.0: + resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==} + engines: {node: '>=18'} + + xml-name-validator@4.0.0: + resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} + engines: {node: '>=12'} + + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + + yaml@2.8.1: + resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==} + engines: {node: '>= 14.6'} + hasBin: true + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + yoctocolors@2.1.2: + resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} + engines: {node: '>=18'} + +snapshots: + + '@alloc/quick-lru@5.2.0': {} + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.30 + + '@asamuzakjp/css-color@3.2.0': + dependencies: + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + lru-cache: 10.4.3 + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.27.1 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.28.0': {} + + '@babel/core@7.28.3': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.3 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.3) + '@babel/helpers': 7.28.3 + '@babel/parser': 7.28.3 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.3 + '@babel/types': 7.28.2 + convert-source-map: 2.0.0 + debug: 4.4.1 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.28.3': + dependencies: + '@babel/parser': 7.28.3 + '@babel/types': 7.28.2 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.30 + jsesc: 3.1.0 + + '@babel/helper-annotate-as-pure@7.27.3': + dependencies: + '@babel/types': 7.28.2 + + '@babel/helper-compilation-targets@7.27.2': + dependencies: + '@babel/compat-data': 7.28.0 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.25.3 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.28.3(@babel/core@7.28.3)': + dependencies: + '@babel/core': 7.28.3 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-member-expression-to-functions': 7.27.1 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.3) + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/traverse': 7.28.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-member-expression-to-functions@7.27.1': + dependencies: + '@babel/traverse': 7.28.3 + '@babel/types': 7.28.2 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.27.1': + dependencies: + '@babel/traverse': 7.28.3 + '@babel/types': 7.28.2 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.3)': + dependencies: + '@babel/core': 7.28.3 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.28.3 + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.27.1': + dependencies: + '@babel/types': 7.28.2 + + '@babel/helper-plugin-utils@7.27.1': {} + + '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.3)': + dependencies: + '@babel/core': 7.28.3 + '@babel/helper-member-expression-to-functions': 7.27.1 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/traverse': 7.28.3 + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + dependencies: + '@babel/traverse': 7.28.3 + '@babel/types': 7.28.2 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.27.1': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.28.3': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.2 + + '@babel/parser@7.28.3': + dependencies: + '@babel/types': 7.28.2 + + '@babel/plugin-proposal-decorators@7.28.0(@babel/core@7.28.3)': + dependencies: + '@babel/core': 7.28.3 + '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.3) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-decorators': 7.27.1(@babel/core@7.28.3) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-syntax-decorators@7.27.1(@babel/core@7.28.3)': + dependencies: + '@babel/core': 7.28.3 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.3)': + dependencies: + '@babel/core': 7.28.3 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.3)': + dependencies: + '@babel/core': 7.28.3 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.3)': + dependencies: + '@babel/core': 7.28.3 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.3)': + dependencies: + '@babel/core': 7.28.3 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-typescript@7.28.0(@babel/core@7.28.3)': + dependencies: + '@babel/core': 7.28.3 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.3) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.3) + transitivePeerDependencies: + - supports-color + + '@babel/runtime@7.28.3': {} + + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.3 + '@babel/types': 7.28.2 + + '@babel/traverse@7.28.3': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.3 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.3 + '@babel/template': 7.27.2 + '@babel/types': 7.28.2 + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.28.2': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + + '@chainlift/liftkit@0.2.0': {} + + '@csstools/color-helpers@5.1.0': {} + + '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-color-parser@3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/color-helpers': 5.1.0 + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-tokenizer@3.0.4': {} + + '@csstools/normalize.css@12.1.1': {} + + '@emnapi/core@1.4.5': + dependencies: + '@emnapi/wasi-threads': 1.0.4 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.4.5': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.0.4': + dependencies: + tslib: 2.8.1 + optional: true + + '@esbuild/aix-ppc64@0.25.9': + optional: true + + '@esbuild/android-arm64@0.25.9': + optional: true + + '@esbuild/android-arm@0.25.9': + optional: true + + '@esbuild/android-x64@0.25.9': + optional: true + + '@esbuild/darwin-arm64@0.25.9': + optional: true + + '@esbuild/darwin-x64@0.25.9': + optional: true + + '@esbuild/freebsd-arm64@0.25.9': + optional: true + + '@esbuild/freebsd-x64@0.25.9': + optional: true + + '@esbuild/linux-arm64@0.25.9': + optional: true + + '@esbuild/linux-arm@0.25.9': + optional: true + + '@esbuild/linux-ia32@0.25.9': + optional: true + + '@esbuild/linux-loong64@0.25.9': + optional: true + + '@esbuild/linux-mips64el@0.25.9': + optional: true + + '@esbuild/linux-ppc64@0.25.9': + optional: true + + '@esbuild/linux-riscv64@0.25.9': + optional: true + + '@esbuild/linux-s390x@0.25.9': + optional: true + + '@esbuild/linux-x64@0.25.9': + optional: true + + '@esbuild/netbsd-arm64@0.25.9': + optional: true + + '@esbuild/netbsd-x64@0.25.9': + optional: true + + '@esbuild/openbsd-arm64@0.25.9': + optional: true + + '@esbuild/openbsd-x64@0.25.9': + optional: true + + '@esbuild/openharmony-arm64@0.25.9': + optional: true + + '@esbuild/sunos-x64@0.25.9': + optional: true + + '@esbuild/win32-arm64@0.25.9': + optional: true + + '@esbuild/win32-ia32@0.25.9': + optional: true + + '@esbuild/win32-x64@0.25.9': + optional: true + + '@eslint-community/eslint-utils@4.7.0(eslint@9.34.0(jiti@2.5.1))': + dependencies: + eslint: 9.34.0(jiti@2.5.1) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.1': {} + + '@eslint/config-array@0.21.0': + dependencies: + '@eslint/object-schema': 2.1.6 + debug: 4.4.1 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.3.1': {} + + '@eslint/core@0.15.2': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.1': + dependencies: + ajv: 6.12.6 + debug: 4.4.1 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.34.0': {} + + '@eslint/object-schema@2.1.6': {} + + '@eslint/plugin-kit@0.3.5': + dependencies: + '@eslint/core': 0.15.2 + levn: 0.4.1 + + '@heroicons/vue@2.2.0(vue@3.5.19(typescript@5.9.2))': + dependencies: + vue: 3.5.19(typescript@5.9.2) + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.6': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.3.1 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.3.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.2 + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.30 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.30 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.30': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@material/material-color-utilities@0.3.0': {} + + '@napi-rs/wasm-runtime@0.2.12': + dependencies: + '@emnapi/core': 1.4.5 + '@emnapi/runtime': 1.4.5 + '@tybys/wasm-util': 0.10.0 + optional: true + + '@napi-rs/wasm-runtime@1.0.3': + dependencies: + '@emnapi/core': 1.4.5 + '@emnapi/runtime': 1.4.5 + '@tybys/wasm-util': 0.10.0 + optional: true + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.19.1 + + '@one-ini/wasm@0.1.1': {} + + '@oxc-parser/binding-android-arm64@0.74.0': + optional: true + + '@oxc-parser/binding-darwin-arm64@0.74.0': + optional: true + + '@oxc-parser/binding-darwin-x64@0.74.0': + optional: true + + '@oxc-parser/binding-freebsd-x64@0.74.0': + optional: true + + '@oxc-parser/binding-linux-arm-gnueabihf@0.74.0': + optional: true + + '@oxc-parser/binding-linux-arm-musleabihf@0.74.0': + optional: true + + '@oxc-parser/binding-linux-arm64-gnu@0.74.0': + optional: true + + '@oxc-parser/binding-linux-arm64-musl@0.74.0': + optional: true + + '@oxc-parser/binding-linux-riscv64-gnu@0.74.0': + optional: true + + '@oxc-parser/binding-linux-s390x-gnu@0.74.0': + optional: true + + '@oxc-parser/binding-linux-x64-gnu@0.74.0': + optional: true + + '@oxc-parser/binding-linux-x64-musl@0.74.0': + optional: true + + '@oxc-parser/binding-wasm32-wasi@0.74.0': + dependencies: + '@napi-rs/wasm-runtime': 0.2.12 + optional: true + + '@oxc-parser/binding-win32-arm64-msvc@0.74.0': + optional: true + + '@oxc-parser/binding-win32-x64-msvc@0.74.0': + optional: true + + '@oxc-project/runtime@0.82.2': {} + + '@oxc-project/types@0.74.0': {} + + '@oxc-project/types@0.82.2': {} + + '@oxlint-tsgolint/darwin-arm64@0.0.4': + optional: true + + '@oxlint-tsgolint/darwin-x64@0.0.4': + optional: true + + '@oxlint-tsgolint/linux-arm64@0.0.4': + optional: true + + '@oxlint-tsgolint/linux-x64@0.0.4': + optional: true + + '@oxlint-tsgolint/win32-arm64@0.0.4': + optional: true + + '@oxlint-tsgolint/win32-x64@0.0.4': + optional: true + + '@oxlint/darwin-arm64@1.12.0': + optional: true + + '@oxlint/darwin-x64@1.12.0': + optional: true + + '@oxlint/linux-arm64-gnu@1.12.0': + optional: true + + '@oxlint/linux-arm64-musl@1.12.0': + optional: true + + '@oxlint/linux-x64-gnu@1.12.0': + optional: true + + '@oxlint/linux-x64-musl@1.12.0': + optional: true + + '@oxlint/win32-arm64@1.12.0': + optional: true + + '@oxlint/win32-x64@1.12.0': + optional: true + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@pkgr/core@0.2.9': {} + + '@playwright/test@1.55.0': + dependencies: + playwright: 1.55.0 + + '@polka/url@1.0.0-next.29': {} + + '@prettier/plugin-oxc@0.0.4': + dependencies: + oxc-parser: 0.74.0 + + '@rolldown/binding-android-arm64@1.0.0-beta.33': + optional: true + + '@rolldown/binding-darwin-arm64@1.0.0-beta.33': + optional: true + + '@rolldown/binding-darwin-x64@1.0.0-beta.33': + optional: true + + '@rolldown/binding-freebsd-x64@1.0.0-beta.33': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.33': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.33': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.33': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.33': + optional: true + + '@rolldown/binding-linux-x64-musl@1.0.0-beta.33': + optional: true + + '@rolldown/binding-openharmony-arm64@1.0.0-beta.33': + optional: true + + '@rolldown/binding-wasm32-wasi@1.0.0-beta.33': + dependencies: + '@napi-rs/wasm-runtime': 1.0.3 + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.33': + optional: true + + '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.33': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.33': + optional: true + + '@rolldown/pluginutils@1.0.0-beta.29': {} + + '@rolldown/pluginutils@1.0.0-beta.33': {} + + '@rollup/rollup-android-arm-eabi@4.48.0': + optional: true + + '@rollup/rollup-android-arm64@4.48.0': + optional: true + + '@rollup/rollup-darwin-arm64@4.48.0': + optional: true + + '@rollup/rollup-darwin-x64@4.48.0': + optional: true + + '@rollup/rollup-freebsd-arm64@4.48.0': + optional: true + + '@rollup/rollup-freebsd-x64@4.48.0': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.48.0': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.48.0': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.48.0': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.48.0': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.48.0': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.48.0': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.48.0': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.48.0': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.48.0': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.48.0': + optional: true + + '@rollup/rollup-linux-x64-musl@4.48.0': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.48.0': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.48.0': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.48.0': + optional: true + + '@sec-ant/readable-stream@0.4.1': {} + + '@sindresorhus/merge-streams@4.0.0': {} + + '@tailwindcss/node@4.1.12': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.18.3 + jiti: 2.5.1 + lightningcss: 1.30.1 + magic-string: 0.30.18 + source-map-js: 1.2.1 + tailwindcss: 4.1.12 + + '@tailwindcss/oxide-android-arm64@4.1.12': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.1.12': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.1.12': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.1.12': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.12': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.12': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.1.12': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.1.12': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.1.12': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.1.12': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.12': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.1.12': + optional: true + + '@tailwindcss/oxide@4.1.12': + dependencies: + detect-libc: 2.0.4 + tar: 7.4.3 + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.1.12 + '@tailwindcss/oxide-darwin-arm64': 4.1.12 + '@tailwindcss/oxide-darwin-x64': 4.1.12 + '@tailwindcss/oxide-freebsd-x64': 4.1.12 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.12 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.12 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.12 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.12 + '@tailwindcss/oxide-linux-x64-musl': 4.1.12 + '@tailwindcss/oxide-wasm32-wasi': 4.1.12 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.12 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.12 + + '@tailwindcss/postcss@4.1.12': + dependencies: + '@alloc/quick-lru': 5.2.0 + '@tailwindcss/node': 4.1.12 + '@tailwindcss/oxide': 4.1.12 + postcss: 8.5.6 + tailwindcss: 4.1.12 + + '@tailwindcss/vite@4.1.12(rolldown-vite@7.1.4(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(yaml@2.8.1))': + dependencies: + '@tailwindcss/node': 4.1.12 + '@tailwindcss/oxide': 4.1.12 + tailwindcss: 4.1.12 + vite: rolldown-vite@7.1.4(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(yaml@2.8.1) + + '@tsconfig/node22@22.0.2': {} + + '@tybys/wasm-util@0.10.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/chai@5.2.2': + dependencies: + '@types/deep-eql': 4.0.2 + + '@types/deep-eql@4.0.2': {} + + '@types/estree@1.0.8': {} + + '@types/jsdom@21.1.7': + dependencies: + '@types/node': 24.3.0 + '@types/tough-cookie': 4.0.5 + parse5: 7.3.0 + + '@types/json-schema@7.0.15': {} + + '@types/node@24.3.0': + dependencies: + undici-types: 7.10.0 + + '@types/tough-cookie@4.0.5': {} + + '@typescript-eslint/eslint-plugin@8.40.0(@typescript-eslint/parser@8.40.0(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2)': + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 8.40.0(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2) + '@typescript-eslint/scope-manager': 8.40.0 + '@typescript-eslint/type-utils': 8.40.0(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2) + '@typescript-eslint/utils': 8.40.0(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2) + '@typescript-eslint/visitor-keys': 8.40.0 + eslint: 9.34.0(jiti@2.5.1) + graphemer: 1.4.0 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.1.0(typescript@5.9.2) + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.40.0(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2)': + dependencies: + '@typescript-eslint/scope-manager': 8.40.0 + '@typescript-eslint/types': 8.40.0 + '@typescript-eslint/typescript-estree': 8.40.0(typescript@5.9.2) + '@typescript-eslint/visitor-keys': 8.40.0 + debug: 4.4.1 + eslint: 9.34.0(jiti@2.5.1) + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.40.0(typescript@5.9.2)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.40.0(typescript@5.9.2) + '@typescript-eslint/types': 8.40.0 + debug: 4.4.1 + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.40.0': + dependencies: + '@typescript-eslint/types': 8.40.0 + '@typescript-eslint/visitor-keys': 8.40.0 + + '@typescript-eslint/tsconfig-utils@8.40.0(typescript@5.9.2)': + dependencies: + typescript: 5.9.2 + + '@typescript-eslint/type-utils@8.40.0(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2)': + dependencies: + '@typescript-eslint/types': 8.40.0 + '@typescript-eslint/typescript-estree': 8.40.0(typescript@5.9.2) + '@typescript-eslint/utils': 8.40.0(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2) + debug: 4.4.1 + eslint: 9.34.0(jiti@2.5.1) + ts-api-utils: 2.1.0(typescript@5.9.2) + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.40.0': {} + + '@typescript-eslint/typescript-estree@8.40.0(typescript@5.9.2)': + dependencies: + '@typescript-eslint/project-service': 8.40.0(typescript@5.9.2) + '@typescript-eslint/tsconfig-utils': 8.40.0(typescript@5.9.2) + '@typescript-eslint/types': 8.40.0 + '@typescript-eslint/visitor-keys': 8.40.0 + debug: 4.4.1 + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.2 + ts-api-utils: 2.1.0(typescript@5.9.2) + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.40.0(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2)': + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.34.0(jiti@2.5.1)) + '@typescript-eslint/scope-manager': 8.40.0 + '@typescript-eslint/types': 8.40.0 + '@typescript-eslint/typescript-estree': 8.40.0(typescript@5.9.2) + eslint: 9.34.0(jiti@2.5.1) + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.40.0': + dependencies: + '@typescript-eslint/types': 8.40.0 + eslint-visitor-keys: 4.2.1 + + '@vitejs/plugin-vue-jsx@5.0.1(rolldown-vite@7.1.4(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(yaml@2.8.1))(vue@3.5.19(typescript@5.9.2))': + dependencies: + '@babel/core': 7.28.3 + '@babel/plugin-transform-typescript': 7.28.0(@babel/core@7.28.3) + '@rolldown/pluginutils': 1.0.0-beta.33 + '@vue/babel-plugin-jsx': 1.5.0(@babel/core@7.28.3) + vite: rolldown-vite@7.1.4(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(yaml@2.8.1) + vue: 3.5.19(typescript@5.9.2) + transitivePeerDependencies: + - supports-color + + '@vitejs/plugin-vue@6.0.1(rolldown-vite@7.1.4(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(yaml@2.8.1))(vue@3.5.19(typescript@5.9.2))': + dependencies: + '@rolldown/pluginutils': 1.0.0-beta.29 + vite: rolldown-vite@7.1.4(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(yaml@2.8.1) + vue: 3.5.19(typescript@5.9.2) + + '@vitest/eslint-plugin@1.3.4(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2)(vitest@3.2.4(@types/node@24.3.0)(jiti@2.5.1)(jsdom@26.1.0)(lightningcss@1.30.1)(yaml@2.8.1))': + dependencies: + '@typescript-eslint/utils': 8.40.0(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2) + eslint: 9.34.0(jiti@2.5.1) + optionalDependencies: + typescript: 5.9.2 + vitest: 3.2.4(@types/node@24.3.0)(jiti@2.5.1)(jsdom@26.1.0)(lightningcss@1.30.1)(yaml@2.8.1) + transitivePeerDependencies: + - supports-color + + '@vitest/expect@3.2.4': + dependencies: + '@types/chai': 5.2.2 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + tinyrainbow: 2.0.0 + + '@vitest/mocker@3.2.4(vite@7.1.3(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(yaml@2.8.1))': + dependencies: + '@vitest/spy': 3.2.4 + estree-walker: 3.0.3 + magic-string: 0.30.18 + optionalDependencies: + vite: 7.1.3(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(yaml@2.8.1) + + '@vitest/pretty-format@3.2.4': + dependencies: + tinyrainbow: 2.0.0 + + '@vitest/runner@3.2.4': + dependencies: + '@vitest/utils': 3.2.4 + pathe: 2.0.3 + strip-literal: 3.0.0 + + '@vitest/snapshot@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + magic-string: 0.30.18 + pathe: 2.0.3 + + '@vitest/spy@3.2.4': + dependencies: + tinyspy: 4.0.3 + + '@vitest/utils@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + loupe: 3.2.1 + tinyrainbow: 2.0.0 + + '@volar/language-core@2.4.23': + dependencies: + '@volar/source-map': 2.4.23 + + '@volar/source-map@2.4.23': {} + + '@volar/typescript@2.4.23': + dependencies: + '@volar/language-core': 2.4.23 + path-browserify: 1.0.1 + vscode-uri: 3.1.0 + + '@vue/babel-helper-vue-transform-on@1.5.0': {} + + '@vue/babel-plugin-jsx@1.5.0(@babel/core@7.28.3)': + dependencies: + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.3) + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.3 + '@babel/types': 7.28.2 + '@vue/babel-helper-vue-transform-on': 1.5.0 + '@vue/babel-plugin-resolve-type': 1.5.0(@babel/core@7.28.3) + '@vue/shared': 3.5.19 + optionalDependencies: + '@babel/core': 7.28.3 + transitivePeerDependencies: + - supports-color + + '@vue/babel-plugin-resolve-type@1.5.0(@babel/core@7.28.3)': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/core': 7.28.3 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/parser': 7.28.3 + '@vue/compiler-sfc': 3.5.19 + transitivePeerDependencies: + - supports-color + + '@vue/compiler-core@3.5.19': + dependencies: + '@babel/parser': 7.28.3 + '@vue/shared': 3.5.19 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.19': + dependencies: + '@vue/compiler-core': 3.5.19 + '@vue/shared': 3.5.19 + + '@vue/compiler-sfc@3.5.19': + dependencies: + '@babel/parser': 7.28.3 + '@vue/compiler-core': 3.5.19 + '@vue/compiler-dom': 3.5.19 + '@vue/compiler-ssr': 3.5.19 + '@vue/shared': 3.5.19 + estree-walker: 2.0.2 + magic-string: 0.30.18 + postcss: 8.5.6 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.19': + dependencies: + '@vue/compiler-dom': 3.5.19 + '@vue/shared': 3.5.19 + + '@vue/compiler-vue2@2.7.16': + dependencies: + de-indent: 1.0.2 + he: 1.2.0 + + '@vue/devtools-api@6.6.4': {} + + '@vue/devtools-api@7.7.7': + dependencies: + '@vue/devtools-kit': 7.7.7 + + '@vue/devtools-core@8.0.1(rolldown-vite@7.1.4(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(yaml@2.8.1))(vue@3.5.19(typescript@5.9.2))': + dependencies: + '@vue/devtools-kit': 8.0.1 + '@vue/devtools-shared': 8.0.1 + mitt: 3.0.1 + nanoid: 5.1.5 + pathe: 2.0.3 + vite-hot-client: 2.1.0(rolldown-vite@7.1.4(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(yaml@2.8.1)) + vue: 3.5.19(typescript@5.9.2) + transitivePeerDependencies: + - vite + + '@vue/devtools-kit@7.7.7': + dependencies: + '@vue/devtools-shared': 7.7.7 + birpc: 2.5.0 + hookable: 5.5.3 + mitt: 3.0.1 + perfect-debounce: 1.0.0 + speakingurl: 14.0.1 + superjson: 2.2.2 + + '@vue/devtools-kit@8.0.1': + dependencies: + '@vue/devtools-shared': 8.0.1 + birpc: 2.5.0 + hookable: 5.5.3 + mitt: 3.0.1 + perfect-debounce: 1.0.0 + speakingurl: 14.0.1 + superjson: 2.2.2 + + '@vue/devtools-shared@7.7.7': + dependencies: + rfdc: 1.4.1 + + '@vue/devtools-shared@8.0.1': + dependencies: + rfdc: 1.4.1 + + '@vue/eslint-config-prettier@10.2.0(eslint@9.34.0(jiti@2.5.1))(prettier@3.6.2)': + dependencies: + eslint: 9.34.0(jiti@2.5.1) + eslint-config-prettier: 10.1.8(eslint@9.34.0(jiti@2.5.1)) + eslint-plugin-prettier: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.34.0(jiti@2.5.1)))(eslint@9.34.0(jiti@2.5.1))(prettier@3.6.2) + prettier: 3.6.2 + transitivePeerDependencies: + - '@types/eslint' + + '@vue/eslint-config-typescript@14.6.0(eslint-plugin-vue@10.4.0(@typescript-eslint/parser@8.40.0(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.34.0(jiti@2.5.1))(vue-eslint-parser@10.2.0(eslint@9.34.0(jiti@2.5.1))))(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2)': + dependencies: + '@typescript-eslint/utils': 8.40.0(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2) + eslint: 9.34.0(jiti@2.5.1) + eslint-plugin-vue: 10.4.0(@typescript-eslint/parser@8.40.0(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.34.0(jiti@2.5.1))(vue-eslint-parser@10.2.0(eslint@9.34.0(jiti@2.5.1))) + fast-glob: 3.3.3 + typescript-eslint: 8.40.0(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2) + vue-eslint-parser: 10.2.0(eslint@9.34.0(jiti@2.5.1)) + optionalDependencies: + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + '@vue/language-core@3.0.6(typescript@5.9.2)': + dependencies: + '@volar/language-core': 2.4.23 + '@vue/compiler-dom': 3.5.19 + '@vue/compiler-vue2': 2.7.16 + '@vue/shared': 3.5.19 + alien-signals: 2.0.7 + muggle-string: 0.4.1 + path-browserify: 1.0.1 + picomatch: 4.0.3 + optionalDependencies: + typescript: 5.9.2 + + '@vue/reactivity@3.5.19': + dependencies: + '@vue/shared': 3.5.19 + + '@vue/runtime-core@3.5.19': + dependencies: + '@vue/reactivity': 3.5.19 + '@vue/shared': 3.5.19 + + '@vue/runtime-dom@3.5.19': + dependencies: + '@vue/reactivity': 3.5.19 + '@vue/runtime-core': 3.5.19 + '@vue/shared': 3.5.19 + csstype: 3.1.3 + + '@vue/server-renderer@3.5.19(vue@3.5.19(typescript@5.9.2))': + dependencies: + '@vue/compiler-ssr': 3.5.19 + '@vue/shared': 3.5.19 + vue: 3.5.19(typescript@5.9.2) + + '@vue/shared@3.5.19': {} + + '@vue/test-utils@2.4.6': + dependencies: + js-beautify: 1.15.4 + vue-component-type-helpers: 2.2.12 + + '@vue/tsconfig@0.8.1(typescript@5.9.2)(vue@3.5.19(typescript@5.9.2))': + optionalDependencies: + typescript: 5.9.2 + vue: 3.5.19(typescript@5.9.2) + + abbrev@2.0.0: {} + + acorn-jsx@5.3.2(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn@8.15.0: {} + + agent-base@7.1.4: {} + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + alien-signals@2.0.7: {} + + ansi-regex@5.0.1: {} + + ansi-regex@6.2.0: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.1: {} + + ansis@4.1.0: {} + + argparse@2.0.1: {} + + assertion-error@2.0.1: {} + + autoprefixer@10.4.21(postcss@8.5.6): + dependencies: + browserslist: 4.25.3 + caniuse-lite: 1.0.30001737 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.1.1 + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + balanced-match@1.0.2: {} + + birpc@2.5.0: {} + + boolbase@1.0.0: {} + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.25.3: + dependencies: + caniuse-lite: 1.0.30001737 + electron-to-chromium: 1.5.208 + node-releases: 2.0.19 + update-browserslist-db: 1.1.3(browserslist@4.25.3) + + bundle-name@4.1.0: + dependencies: + run-applescript: 7.0.0 + + cac@6.7.14: {} + + callsites@3.1.0: {} + + caniuse-lite@1.0.30001737: {} + + chai@5.3.3: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.1 + deep-eql: 5.0.2 + loupe: 3.2.1 + pathval: 2.0.1 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + check-error@2.1.1: {} + + chownr@3.0.0: {} + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + commander@10.0.1: {} + + concat-map@0.0.1: {} + + concurrently@8.2.2: + dependencies: + chalk: 4.1.2 + date-fns: 2.30.0 + lodash: 4.17.21 + rxjs: 7.8.2 + shell-quote: 1.8.3 + spawn-command: 0.0.2 + supports-color: 8.1.1 + tree-kill: 1.2.2 + yargs: 17.7.2 + + config-chain@1.1.13: + dependencies: + ini: 1.3.8 + proto-list: 1.2.4 + + convert-source-map@2.0.0: {} + + copy-anything@3.0.5: + dependencies: + is-what: 4.1.16 + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + cssesc@3.0.0: {} + + cssstyle@4.6.0: + dependencies: + '@asamuzakjp/css-color': 3.2.0 + rrweb-cssom: 0.8.0 + + csstype@3.1.3: {} + + data-urls@5.0.0: + dependencies: + whatwg-mimetype: 4.0.0 + whatwg-url: 14.2.0 + + date-fns@2.30.0: + dependencies: + '@babel/runtime': 7.28.3 + + de-indent@1.0.2: {} + + debug@4.4.1: + dependencies: + ms: 2.1.3 + + decimal.js@10.6.0: {} + + deep-eql@5.0.2: {} + + deep-is@0.1.4: {} + + default-browser-id@5.0.0: {} + + default-browser@5.2.1: + dependencies: + bundle-name: 4.1.0 + default-browser-id: 5.0.0 + + define-lazy-prop@3.0.0: {} + + detect-libc@2.0.4: {} + + eastasianwidth@0.2.0: {} + + editorconfig@1.0.4: + dependencies: + '@one-ini/wasm': 0.1.1 + commander: 10.0.1 + minimatch: 9.0.1 + semver: 7.7.2 + + electron-to-chromium@1.5.208: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + enhanced-resolve@5.18.3: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.3 + + entities@4.5.0: {} + + entities@6.0.1: {} + + error-stack-parser-es@1.0.5: {} + + es-module-lexer@1.7.0: {} + + esbuild@0.25.9: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.9 + '@esbuild/android-arm': 0.25.9 + '@esbuild/android-arm64': 0.25.9 + '@esbuild/android-x64': 0.25.9 + '@esbuild/darwin-arm64': 0.25.9 + '@esbuild/darwin-x64': 0.25.9 + '@esbuild/freebsd-arm64': 0.25.9 + '@esbuild/freebsd-x64': 0.25.9 + '@esbuild/linux-arm': 0.25.9 + '@esbuild/linux-arm64': 0.25.9 + '@esbuild/linux-ia32': 0.25.9 + '@esbuild/linux-loong64': 0.25.9 + '@esbuild/linux-mips64el': 0.25.9 + '@esbuild/linux-ppc64': 0.25.9 + '@esbuild/linux-riscv64': 0.25.9 + '@esbuild/linux-s390x': 0.25.9 + '@esbuild/linux-x64': 0.25.9 + '@esbuild/netbsd-arm64': 0.25.9 + '@esbuild/netbsd-x64': 0.25.9 + '@esbuild/openbsd-arm64': 0.25.9 + '@esbuild/openbsd-x64': 0.25.9 + '@esbuild/openharmony-arm64': 0.25.9 + '@esbuild/sunos-x64': 0.25.9 + '@esbuild/win32-arm64': 0.25.9 + '@esbuild/win32-ia32': 0.25.9 + '@esbuild/win32-x64': 0.25.9 + + escalade@3.2.0: {} + + escape-string-regexp@4.0.0: {} + + eslint-config-prettier@10.1.8(eslint@9.34.0(jiti@2.5.1)): + dependencies: + eslint: 9.34.0(jiti@2.5.1) + + eslint-plugin-oxlint@1.12.0: + dependencies: + jsonc-parser: 3.3.1 + + eslint-plugin-playwright@2.2.2(eslint@9.34.0(jiti@2.5.1)): + dependencies: + eslint: 9.34.0(jiti@2.5.1) + globals: 13.24.0 + + eslint-plugin-prettier@5.5.4(eslint-config-prettier@10.1.8(eslint@9.34.0(jiti@2.5.1)))(eslint@9.34.0(jiti@2.5.1))(prettier@3.6.2): + dependencies: + eslint: 9.34.0(jiti@2.5.1) + prettier: 3.6.2 + prettier-linter-helpers: 1.0.0 + synckit: 0.11.11 + optionalDependencies: + eslint-config-prettier: 10.1.8(eslint@9.34.0(jiti@2.5.1)) + + eslint-plugin-vue@10.4.0(@typescript-eslint/parser@8.40.0(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.34.0(jiti@2.5.1))(vue-eslint-parser@10.2.0(eslint@9.34.0(jiti@2.5.1))): + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.34.0(jiti@2.5.1)) + eslint: 9.34.0(jiti@2.5.1) + natural-compare: 1.4.0 + nth-check: 2.1.1 + postcss-selector-parser: 6.1.2 + semver: 7.7.2 + vue-eslint-parser: 10.2.0(eslint@9.34.0(jiti@2.5.1)) + xml-name-validator: 4.0.0 + optionalDependencies: + '@typescript-eslint/parser': 8.40.0(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2) + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint@9.34.0(jiti@2.5.1): + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.34.0(jiti@2.5.1)) + '@eslint-community/regexpp': 4.12.1 + '@eslint/config-array': 0.21.0 + '@eslint/config-helpers': 0.3.1 + '@eslint/core': 0.15.2 + '@eslint/eslintrc': 3.3.1 + '@eslint/js': 9.34.0 + '@eslint/plugin-kit': 0.3.5 + '@humanfs/node': 0.16.6 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.1 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 2.5.1 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 4.2.1 + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + estree-walker@2.0.2: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + esutils@2.0.3: {} + + execa@9.6.0: + dependencies: + '@sindresorhus/merge-streams': 4.0.0 + cross-spawn: 7.0.6 + figures: 6.1.0 + get-stream: 9.0.1 + human-signals: 8.0.1 + is-plain-obj: 4.1.0 + is-stream: 4.0.1 + npm-run-path: 6.0.0 + pretty-ms: 9.2.0 + signal-exit: 4.1.0 + strip-final-newline: 4.0.0 + yoctocolors: 2.1.2 + + expect-type@1.2.2: {} + + fast-deep-equal@3.1.3: {} + + fast-diff@1.3.0: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fastq@1.19.1: + dependencies: + reusify: 1.1.0 + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + figures@6.1.0: + dependencies: + is-unicode-supported: 2.1.0 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + + flatted@3.3.3: {} + + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + fraction.js@4.3.7: {} + + fsevents@2.3.2: + optional: true + + fsevents@2.3.3: + optional: true + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + get-stream@9.0.1: + dependencies: + '@sec-ant/readable-stream': 0.4.1 + is-stream: 4.0.1 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@10.4.5: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + + globals@13.24.0: + dependencies: + type-fest: 0.20.2 + + globals@14.0.0: {} + + graceful-fs@4.2.11: {} + + graphemer@1.4.0: {} + + has-flag@4.0.0: {} + + he@1.2.0: {} + + hookable@5.5.3: {} + + html-encoding-sniffer@4.0.0: + dependencies: + whatwg-encoding: 3.1.1 + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.4 + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + + human-signals@8.0.1: {} + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + ini@1.3.8: {} + + is-docker@3.0.0: {} + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-inside-container@1.0.0: + dependencies: + is-docker: 3.0.0 + + is-number@7.0.0: {} + + is-plain-obj@4.1.0: {} + + is-potential-custom-element-name@1.0.1: {} + + is-stream@4.0.1: {} + + is-unicode-supported@2.1.0: {} + + is-what@4.1.16: {} + + is-wsl@3.1.0: + dependencies: + is-inside-container: 1.0.0 + + isexe@2.0.0: {} + + isexe@3.1.1: {} + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jiti@2.5.1: {} + + js-beautify@1.15.4: + dependencies: + config-chain: 1.1.13 + editorconfig: 1.0.4 + glob: 10.4.5 + js-cookie: 3.0.5 + nopt: 7.2.1 + + js-cookie@3.0.5: {} + + js-tokens@4.0.0: {} + + js-tokens@9.0.1: {} + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + jsdom@26.1.0: + dependencies: + cssstyle: 4.6.0 + data-urls: 5.0.0 + decimal.js: 10.6.0 + html-encoding-sniffer: 4.0.0 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.21 + parse5: 7.3.0 + rrweb-cssom: 0.8.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 5.1.2 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 3.1.1 + whatwg-mimetype: 4.0.0 + whatwg-url: 14.2.0 + ws: 8.18.3 + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + jsesc@3.1.0: {} + + json-buffer@3.0.1: {} + + json-parse-even-better-errors@4.0.0: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@2.2.3: {} + + jsonc-parser@3.3.1: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + kolorist@1.8.0: {} + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lightningcss-darwin-arm64@1.30.1: + optional: true + + lightningcss-darwin-x64@1.30.1: + optional: true + + lightningcss-freebsd-x64@1.30.1: + optional: true + + lightningcss-linux-arm-gnueabihf@1.30.1: + optional: true + + lightningcss-linux-arm64-gnu@1.30.1: + optional: true + + lightningcss-linux-arm64-musl@1.30.1: + optional: true + + lightningcss-linux-x64-gnu@1.30.1: + optional: true + + lightningcss-linux-x64-musl@1.30.1: + optional: true + + lightningcss-win32-arm64-msvc@1.30.1: + optional: true + + lightningcss-win32-x64-msvc@1.30.1: + optional: true + + lightningcss@1.30.1: + dependencies: + detect-libc: 2.0.4 + optionalDependencies: + lightningcss-darwin-arm64: 1.30.1 + lightningcss-darwin-x64: 1.30.1 + lightningcss-freebsd-x64: 1.30.1 + lightningcss-linux-arm-gnueabihf: 1.30.1 + lightningcss-linux-arm64-gnu: 1.30.1 + lightningcss-linux-arm64-musl: 1.30.1 + lightningcss-linux-x64-gnu: 1.30.1 + lightningcss-linux-x64-musl: 1.30.1 + lightningcss-win32-arm64-msvc: 1.30.1 + lightningcss-win32-x64-msvc: 1.30.1 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + lodash@4.17.21: {} + + loupe@3.2.1: {} + + lru-cache@10.4.3: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lucide-react@0.541.0(react@19.1.1): + dependencies: + react: 19.1.1 + + magic-string@0.30.18: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + memorystream@0.3.1: {} + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.12 + + minimatch@9.0.1: + dependencies: + brace-expansion: 2.0.2 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + minipass@7.1.2: {} + + minizlib@3.0.2: + dependencies: + minipass: 7.1.2 + + mitt@3.0.1: {} + + mkdirp@3.0.1: {} + + mrmime@2.0.1: {} + + ms@2.1.3: {} + + muggle-string@0.4.1: {} + + nanoid@3.3.11: {} + + nanoid@5.1.5: {} + + natural-compare@1.4.0: {} + + node-releases@2.0.19: {} + + nopt@7.2.1: + dependencies: + abbrev: 2.0.0 + + normalize-range@0.1.2: {} + + npm-normalize-package-bin@4.0.0: {} + + npm-run-all2@8.0.4: + dependencies: + ansi-styles: 6.2.1 + cross-spawn: 7.0.6 + memorystream: 0.3.1 + picomatch: 4.0.3 + pidtree: 0.6.0 + read-package-json-fast: 4.0.0 + shell-quote: 1.8.3 + which: 5.0.0 + + npm-run-path@6.0.0: + dependencies: + path-key: 4.0.0 + unicorn-magic: 0.3.0 + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + nwsapi@2.2.21: {} + + ohash@2.0.11: {} + + open@10.2.0: + dependencies: + default-browser: 5.2.1 + define-lazy-prop: 3.0.0 + is-inside-container: 1.0.0 + wsl-utils: 0.1.0 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + oxc-parser@0.74.0: + dependencies: + '@oxc-project/types': 0.74.0 + optionalDependencies: + '@oxc-parser/binding-android-arm64': 0.74.0 + '@oxc-parser/binding-darwin-arm64': 0.74.0 + '@oxc-parser/binding-darwin-x64': 0.74.0 + '@oxc-parser/binding-freebsd-x64': 0.74.0 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.74.0 + '@oxc-parser/binding-linux-arm-musleabihf': 0.74.0 + '@oxc-parser/binding-linux-arm64-gnu': 0.74.0 + '@oxc-parser/binding-linux-arm64-musl': 0.74.0 + '@oxc-parser/binding-linux-riscv64-gnu': 0.74.0 + '@oxc-parser/binding-linux-s390x-gnu': 0.74.0 + '@oxc-parser/binding-linux-x64-gnu': 0.74.0 + '@oxc-parser/binding-linux-x64-musl': 0.74.0 + '@oxc-parser/binding-wasm32-wasi': 0.74.0 + '@oxc-parser/binding-win32-arm64-msvc': 0.74.0 + '@oxc-parser/binding-win32-x64-msvc': 0.74.0 + + oxlint-tsgolint@0.0.4: + optionalDependencies: + '@oxlint-tsgolint/darwin-arm64': 0.0.4 + '@oxlint-tsgolint/darwin-x64': 0.0.4 + '@oxlint-tsgolint/linux-arm64': 0.0.4 + '@oxlint-tsgolint/linux-x64': 0.0.4 + '@oxlint-tsgolint/win32-arm64': 0.0.4 + '@oxlint-tsgolint/win32-x64': 0.0.4 + optional: true + + oxlint@1.12.0: + optionalDependencies: + '@oxlint/darwin-arm64': 1.12.0 + '@oxlint/darwin-x64': 1.12.0 + '@oxlint/linux-arm64-gnu': 1.12.0 + '@oxlint/linux-arm64-musl': 1.12.0 + '@oxlint/linux-x64-gnu': 1.12.0 + '@oxlint/linux-x64-musl': 1.12.0 + '@oxlint/win32-arm64': 1.12.0 + '@oxlint/win32-x64': 1.12.0 + oxlint-tsgolint: 0.0.4 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + package-json-from-dist@1.0.1: {} + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-ms@4.0.0: {} + + parse5@7.3.0: + dependencies: + entities: 6.0.1 + + path-browserify@1.0.1: {} + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + path-key@4.0.0: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + + pathe@2.0.3: {} + + pathval@2.0.1: {} + + perfect-debounce@1.0.0: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.3: {} + + pidtree@0.6.0: {} + + pinia@3.0.3(typescript@5.9.2)(vue@3.5.19(typescript@5.9.2)): + dependencies: + '@vue/devtools-api': 7.7.7 + vue: 3.5.19(typescript@5.9.2) + optionalDependencies: + typescript: 5.9.2 + + playwright-core@1.55.0: {} + + playwright@1.55.0: + dependencies: + playwright-core: 1.55.0 + optionalDependencies: + fsevents: 2.3.2 + + postcss-selector-parser@6.1.2: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-value-parser@4.2.0: {} + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + prettier-linter-helpers@1.0.0: + dependencies: + fast-diff: 1.3.0 + + prettier@3.6.2: {} + + pretty-ms@9.2.0: + dependencies: + parse-ms: 4.0.0 + + proto-list@1.2.4: {} + + punycode@2.3.1: {} + + queue-microtask@1.2.3: {} + + react@19.1.1: {} + + read-package-json-fast@4.0.0: + dependencies: + json-parse-even-better-errors: 4.0.0 + npm-normalize-package-bin: 4.0.0 + + require-directory@2.1.1: {} + + resolve-from@4.0.0: {} + + reusify@1.1.0: {} + + rfdc@1.4.1: {} + + rolldown-vite@7.1.4(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(yaml@2.8.1): + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + lightningcss: 1.30.1 + picomatch: 4.0.3 + postcss: 8.5.6 + rolldown: 1.0.0-beta.33 + tinyglobby: 0.2.14 + optionalDependencies: + '@types/node': 24.3.0 + esbuild: 0.25.9 + fsevents: 2.3.3 + jiti: 2.5.1 + yaml: 2.8.1 + + rolldown@1.0.0-beta.33: + dependencies: + '@oxc-project/runtime': 0.82.2 + '@oxc-project/types': 0.82.2 + '@rolldown/pluginutils': 1.0.0-beta.33 + ansis: 4.1.0 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.0.0-beta.33 + '@rolldown/binding-darwin-arm64': 1.0.0-beta.33 + '@rolldown/binding-darwin-x64': 1.0.0-beta.33 + '@rolldown/binding-freebsd-x64': 1.0.0-beta.33 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.33 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.33 + '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.33 + '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.33 + '@rolldown/binding-linux-x64-musl': 1.0.0-beta.33 + '@rolldown/binding-openharmony-arm64': 1.0.0-beta.33 + '@rolldown/binding-wasm32-wasi': 1.0.0-beta.33 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.33 + '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.33 + '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.33 + + rollup@4.48.0: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.48.0 + '@rollup/rollup-android-arm64': 4.48.0 + '@rollup/rollup-darwin-arm64': 4.48.0 + '@rollup/rollup-darwin-x64': 4.48.0 + '@rollup/rollup-freebsd-arm64': 4.48.0 + '@rollup/rollup-freebsd-x64': 4.48.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.48.0 + '@rollup/rollup-linux-arm-musleabihf': 4.48.0 + '@rollup/rollup-linux-arm64-gnu': 4.48.0 + '@rollup/rollup-linux-arm64-musl': 4.48.0 + '@rollup/rollup-linux-loongarch64-gnu': 4.48.0 + '@rollup/rollup-linux-ppc64-gnu': 4.48.0 + '@rollup/rollup-linux-riscv64-gnu': 4.48.0 + '@rollup/rollup-linux-riscv64-musl': 4.48.0 + '@rollup/rollup-linux-s390x-gnu': 4.48.0 + '@rollup/rollup-linux-x64-gnu': 4.48.0 + '@rollup/rollup-linux-x64-musl': 4.48.0 + '@rollup/rollup-win32-arm64-msvc': 4.48.0 + '@rollup/rollup-win32-ia32-msvc': 4.48.0 + '@rollup/rollup-win32-x64-msvc': 4.48.0 + fsevents: 2.3.3 + + rrweb-cssom@0.8.0: {} + + run-applescript@7.0.0: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + rxjs@7.8.2: + dependencies: + tslib: 2.8.1 + + safer-buffer@2.1.2: {} + + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + + semver@6.3.1: {} + + semver@7.7.2: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + shell-quote@1.8.3: {} + + siginfo@2.0.0: {} + + signal-exit@4.1.0: {} + + sirv@3.0.1: + dependencies: + '@polka/url': 1.0.0-next.29 + mrmime: 2.0.1 + totalist: 3.0.1 + + source-map-js@1.2.1: {} + + spawn-command@0.0.2: {} + + speakingurl@14.0.1: {} + + stackback@0.0.2: {} + + std-env@3.9.0: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.2.0 + + strip-final-newline@4.0.0: {} + + strip-json-comments@3.1.1: {} + + strip-literal@3.0.0: + dependencies: + js-tokens: 9.0.1 + + superjson@2.2.2: + dependencies: + copy-anything: 3.0.5 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + symbol-tree@3.2.4: {} + + synckit@0.11.11: + dependencies: + '@pkgr/core': 0.2.9 + + tailwindcss@4.1.12: {} + + tapable@2.2.3: {} + + tar@7.4.3: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.2 + minizlib: 3.0.2 + mkdirp: 3.0.1 + yallist: 5.0.0 + + tinybench@2.9.0: {} + + tinyexec@0.3.2: {} + + tinyglobby@0.2.14: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + tinypool@1.1.1: {} + + tinyrainbow@2.0.0: {} + + tinyspy@4.0.3: {} + + tldts-core@6.1.86: {} + + tldts@6.1.86: + dependencies: + tldts-core: 6.1.86 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + totalist@3.0.1: {} + + tough-cookie@5.1.2: + dependencies: + tldts: 6.1.86 + + tr46@5.1.1: + dependencies: + punycode: 2.3.1 + + tree-kill@1.2.2: {} + + ts-api-utils@2.1.0(typescript@5.9.2): + dependencies: + typescript: 5.9.2 + + tslib@2.8.1: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-fest@0.20.2: {} + + typescript-eslint@8.40.0(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2): + dependencies: + '@typescript-eslint/eslint-plugin': 8.40.0(@typescript-eslint/parser@8.40.0(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2) + '@typescript-eslint/parser': 8.40.0(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2) + '@typescript-eslint/typescript-estree': 8.40.0(typescript@5.9.2) + '@typescript-eslint/utils': 8.40.0(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2) + eslint: 9.34.0(jiti@2.5.1) + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + typescript@5.9.2: {} + + undici-types@7.10.0: {} + + unicorn-magic@0.3.0: {} + + unplugin-utils@0.2.5: + dependencies: + pathe: 2.0.3 + picomatch: 4.0.3 + + update-browserslist-db@1.1.3(browserslist@4.25.3): + dependencies: + browserslist: 4.25.3 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + util-deprecate@1.0.2: {} + + vite-dev-rpc@1.1.0(rolldown-vite@7.1.4(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(yaml@2.8.1)): + dependencies: + birpc: 2.5.0 + vite: rolldown-vite@7.1.4(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(yaml@2.8.1) + vite-hot-client: 2.1.0(rolldown-vite@7.1.4(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(yaml@2.8.1)) + + vite-hot-client@2.1.0(rolldown-vite@7.1.4(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(yaml@2.8.1)): + dependencies: + vite: rolldown-vite@7.1.4(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(yaml@2.8.1) + + vite-node@3.2.4(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(yaml@2.8.1): + dependencies: + cac: 6.7.14 + debug: 4.4.1 + es-module-lexer: 1.7.0 + pathe: 2.0.3 + vite: 7.1.3(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(yaml@2.8.1) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vite-plugin-inspect@11.3.2(rolldown-vite@7.1.4(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(yaml@2.8.1)): + dependencies: + ansis: 4.1.0 + debug: 4.4.1 + error-stack-parser-es: 1.0.5 + ohash: 2.0.11 + open: 10.2.0 + perfect-debounce: 1.0.0 + sirv: 3.0.1 + unplugin-utils: 0.2.5 + vite: rolldown-vite@7.1.4(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(yaml@2.8.1) + vite-dev-rpc: 1.1.0(rolldown-vite@7.1.4(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(yaml@2.8.1)) + transitivePeerDependencies: + - supports-color + + vite-plugin-vue-devtools@8.0.1(rolldown-vite@7.1.4(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(yaml@2.8.1))(vue@3.5.19(typescript@5.9.2)): + dependencies: + '@vue/devtools-core': 8.0.1(rolldown-vite@7.1.4(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(yaml@2.8.1))(vue@3.5.19(typescript@5.9.2)) + '@vue/devtools-kit': 8.0.1 + '@vue/devtools-shared': 8.0.1 + execa: 9.6.0 + sirv: 3.0.1 + vite: rolldown-vite@7.1.4(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(yaml@2.8.1) + vite-plugin-inspect: 11.3.2(rolldown-vite@7.1.4(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(yaml@2.8.1)) + vite-plugin-vue-inspector: 5.3.2(rolldown-vite@7.1.4(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(yaml@2.8.1)) + transitivePeerDependencies: + - '@nuxt/kit' + - supports-color + - vue + + vite-plugin-vue-inspector@5.3.2(rolldown-vite@7.1.4(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(yaml@2.8.1)): + dependencies: + '@babel/core': 7.28.3 + '@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.28.3) + '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.3) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.28.3) + '@babel/plugin-transform-typescript': 7.28.0(@babel/core@7.28.3) + '@vue/babel-plugin-jsx': 1.5.0(@babel/core@7.28.3) + '@vue/compiler-dom': 3.5.19 + kolorist: 1.8.0 + magic-string: 0.30.18 + vite: rolldown-vite@7.1.4(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(yaml@2.8.1) + transitivePeerDependencies: + - supports-color + + vite@7.1.3(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(yaml@2.8.1): + dependencies: + esbuild: 0.25.9 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.48.0 + tinyglobby: 0.2.14 + optionalDependencies: + '@types/node': 24.3.0 + fsevents: 2.3.3 + jiti: 2.5.1 + lightningcss: 1.30.1 + yaml: 2.8.1 + + vitest@3.2.4(@types/node@24.3.0)(jiti@2.5.1)(jsdom@26.1.0)(lightningcss@1.30.1)(yaml@2.8.1): + dependencies: + '@types/chai': 5.2.2 + '@vitest/expect': 3.2.4 + '@vitest/mocker': 3.2.4(vite@7.1.3(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(yaml@2.8.1)) + '@vitest/pretty-format': 3.2.4 + '@vitest/runner': 3.2.4 + '@vitest/snapshot': 3.2.4 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + debug: 4.4.1 + expect-type: 1.2.2 + magic-string: 0.30.18 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.9.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.14 + tinypool: 1.1.1 + tinyrainbow: 2.0.0 + vite: 7.1.3(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(yaml@2.8.1) + vite-node: 3.2.4(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(yaml@2.8.1) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 24.3.0 + jsdom: 26.1.0 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vscode-uri@3.1.0: {} + + vue-component-type-helpers@2.2.12: {} + + vue-eslint-parser@10.2.0(eslint@9.34.0(jiti@2.5.1)): + dependencies: + debug: 4.4.1 + eslint: 9.34.0(jiti@2.5.1) + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.6.0 + semver: 7.7.2 + transitivePeerDependencies: + - supports-color + + vue-router@4.5.1(vue@3.5.19(typescript@5.9.2)): + dependencies: + '@vue/devtools-api': 6.6.4 + vue: 3.5.19(typescript@5.9.2) + + vue-tsc@3.0.6(typescript@5.9.2): + dependencies: + '@volar/typescript': 2.4.23 + '@vue/language-core': 3.0.6(typescript@5.9.2) + typescript: 5.9.2 + + vue@3.5.19(typescript@5.9.2): + dependencies: + '@vue/compiler-dom': 3.5.19 + '@vue/compiler-sfc': 3.5.19 + '@vue/runtime-dom': 3.5.19 + '@vue/server-renderer': 3.5.19(vue@3.5.19(typescript@5.9.2)) + '@vue/shared': 3.5.19 + optionalDependencies: + typescript: 5.9.2 + + w3c-xmlserializer@5.0.0: + dependencies: + xml-name-validator: 5.0.0 + + webidl-conversions@7.0.0: {} + + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + + whatwg-mimetype@4.0.0: {} + + whatwg-url@14.2.0: + dependencies: + tr46: 5.1.1 + webidl-conversions: 7.0.0 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + which@5.0.0: + dependencies: + isexe: 3.1.1 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + word-wrap@1.2.5: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + + ws@8.18.3: {} + + wsl-utils@0.1.0: + dependencies: + is-wsl: 3.1.0 + + xml-name-validator@4.0.0: {} + + xml-name-validator@5.0.0: {} + + xmlchars@2.2.0: {} + + y18n@5.0.8: {} + + yallist@3.1.1: {} + + yallist@5.0.0: {} + + yaml@2.8.1: + optional: true + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yocto-queue@0.1.0: {} + + yoctocolors@2.1.2: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 1efc8908..285bdbd4 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,3 +1,5 @@ packages: - - 'frontend' - # Backend is managed separately with uv \ No newline at end of file + - frontend + +onlyBuiltDependencies: + - '@tailwindcss/oxide' diff --git a/shared/docs/api/README.md b/shared/docs/api/README.md new file mode 100644 index 00000000..4c31137a --- /dev/null +++ b/shared/docs/api/README.md @@ -0,0 +1,465 @@ +# ThrillWiki API Documentation + +Complete API reference for the ThrillWiki Django REST API backend. + +## Overview + +The ThrillWiki API provides comprehensive access to theme park and roller coaster data through a RESTful interface designed specifically for the Vue.js frontend. The API uses Django REST Framework with custom frontend-compatible serializers that convert Django's snake_case to JavaScript's camelCase convention. + +**Base URL:** `http://localhost:8000/api/` + +## Authentication + +The API currently supports anonymous access for read operations. Authentication will be added in future versions for write operations and user-specific features. + +## API Endpoints + +### Parks + +#### List Parks +```http +GET /api/parks/ +``` + +**Response Format:** +```json +{ + "count": 150, + "next": "http://localhost:8000/api/parks/?page=2", + "previous": null, + "results": [ + { + "id": 1, + "name": "Cedar Point", + "slug": "cedar-point", + "location": "Sandusky, Ohio", + "operator": "Cedar Fair", + "openingYear": 1870, + "status": "open", + "description": "America's Roller Coast", + "website": "https://www.cedarpoint.com", + "imageUrl": "/placeholder-park.jpg" + } + ] +} +``` + +**Query Parameters:** +- `page` - Page number for pagination +- `search` - Search parks by name or location +- `status` - Filter by park status (`open`, `closed`, `seasonal`) +- `operator` - Filter by operator name + +#### Get Park Details +```http +GET /api/parks/{id}/ +``` + +**Response Format:** +```json +{ + "id": 1, + "name": "Cedar Point", + "slug": "cedar-point", + "location": "Sandusky, Ohio", + "operator": "Cedar Fair", + "openingYear": 1870, + "status": "open", + "description": "America's Roller Coast", + "website": "https://www.cedarpoint.com", + "imageUrl": "/placeholder-park.jpg", + "averageRating": 4.5, + "rideCount": 72, + "coasterCount": 17, + "sizeAcres": 364, + "operatingSeason": "May-October" +} +``` + +### Rides + +#### List Rides +```http +GET /api/rides/ +``` + +**Response Format:** +```json +{ + "count": 500, + "next": "http://localhost:8000/api/rides/?page=2", + "previous": null, + "results": [ + { + "id": 1, + "name": "Millennium Force", + "slug": "millennium-force", + "parkId": 1, + "parkName": "Cedar Point", + "parkSlug": "cedar-point", + "category": "roller_coaster", + "manufacturer": "Intamin", + "designer": "Werner Stengel", + "openingYear": 2000, + "height": 310, + "speed": 93, + "length": 6595, + "inversions": 0, + "status": "operating", + "description": "World's tallest complete-circuit roller coaster", + "imageUrl": "/placeholder-ride.jpg", + "thrillLevel": "extreme" + } + ] +} +``` + +**Query Parameters:** +- `page` - Page number for pagination +- `search` - Search rides by name +- `park` - Filter by park ID +- `category` - Filter by ride category (`roller_coaster`, `dark_ride`, `flat_ride`, `water_ride`, `transport`, `other`) +- `status` - Filter by ride status (`operating`, `closed`, `sbno`, `under_construction`) +- `manufacturer` - Filter by manufacturer name +- `thrill_level` - Filter by thrill level (`family`, `moderate`, `intense`, `extreme`) + +#### Get Ride Details +```http +GET /api/rides/{id}/ +``` + +**Response Format:** +```json +{ + "id": 1, + "name": "Millennium Force", + "slug": "millennium-force", + "parkId": 1, + "parkName": "Cedar Point", + "parkSlug": "cedar-point", + "category": "roller_coaster", + "manufacturer": "Intamin", + "designer": "Werner Stengel", + "openingYear": 2000, + "height": 310, + "speed": 93, + "length": 6595, + "inversions": 0, + "status": "operating", + "description": "World's tallest complete-circuit roller coaster", + "imageUrl": "/placeholder-ride.jpg", + "thrillLevel": "extreme", + "minHeightIn": 48, + "maxHeightIn": 78, + "capacityPerHour": 1300, + "rideDurationSeconds": 120, + "averageRating": 4.8, + "closingDate": null, + "statusSince": "2000-05-13", + "coasterStats": { + "trackMaterial": "steel", + "coasterType": "hypercoaster", + "launchType": "chain_lift", + "maxDropHeightFt": 300, + "rideTimeSeconds": 120, + "trainsCount": 2, + "carsPerTrain": 9, + "seatsPerCar": 4, + "trainStyle": "open_air", + "trackType": "complete_circuit" + } +} +``` + +#### Get Rides by Park +```http +GET /api/parks/{park_id}/rides/ +``` + +Returns all rides for a specific park using the same format as the main rides endpoint. + +## Data Models + +### Park Data Structure + +| Field | Type | Description | +|-------|------|-------------| +| `id` | integer | Unique park identifier | +| `name` | string | Park name | +| `slug` | string | URL-friendly park identifier | +| `location` | string | Formatted location (e.g., "Sandusky, Ohio") | +| `operator` | string | Park operating company | +| `openingYear` | integer | Year park opened | +| `status` | string | Park status: `open`, `closed`, `seasonal` | +| `description` | string | Park description | +| `website` | string | Official website URL | +| `imageUrl` | string | Primary park image URL | +| `averageRating` | float | Average user rating (detail view only) | +| `rideCount` | integer | Total number of rides (detail view only) | +| `coasterCount` | integer | Number of roller coasters (detail view only) | +| `sizeAcres` | float | Park size in acres (detail view only) | +| `operatingSeason` | string | Operating season description (detail view only) | + +### Ride Data Structure + +| Field | Type | Description | +|-------|------|-------------| +| `id` | integer | Unique ride identifier | +| `name` | string | Ride name | +| `slug` | string | URL-friendly ride identifier | +| `parkId` | integer | Parent park ID | +| `parkName` | string | Parent park name | +| `parkSlug` | string | Parent park slug | +| `category` | string | Ride category | +| `manufacturer` | string | Ride manufacturer | +| `designer` | string | Ride designer | +| `openingYear` | integer | Year ride opened | +| `height` | float | Height in feet (coasters only) | +| `speed` | float | Speed in mph (coasters only) | +| `length` | float | Length in feet (coasters only) | +| `inversions` | integer | Number of inversions (coasters only) | +| `status` | string | Ride status | +| `description` | string | Ride description | +| `imageUrl` | string | Primary ride image URL | +| `thrillLevel` | string | Calculated thrill level | +| `minHeightIn` | integer | Minimum height requirement (detail view only) | +| `maxHeightIn` | integer | Maximum height requirement (detail view only) | +| `capacityPerHour` | integer | Hourly capacity (detail view only) | +| `rideDurationSeconds` | integer | Ride duration in seconds (detail view only) | +| `averageRating` | float | Average user rating (detail view only) | +| `closingDate` | date | Date ride closed (detail view only) | +| `statusSince` | date | Date status changed (detail view only) | +| `coasterStats` | object | Detailed coaster statistics (detail view only) | + +### Coaster Statistics Structure + +| Field | Type | Description | +|-------|------|-------------| +| `trackMaterial` | string | Track material (steel, wood) | +| `coasterType` | string | Coaster type (hypercoaster, inverted, etc.) | +| `launchType` | string | Launch mechanism | +| `maxDropHeightFt` | float | Maximum drop height in feet | +| `rideTimeSeconds` | integer | Total ride time in seconds | +| `trainsCount` | integer | Number of trains | +| `carsPerTrain` | integer | Cars per train | +| `seatsPerCar` | integer | Seats per car | +| `trainStyle` | string | Train style (open_air, enclosed) | +| `trackType` | string | Track configuration | + +## Field Mappings + +### Django to Frontend Field Conversion + +The API automatically converts Django's snake_case field names to JavaScript's camelCase convention: + +| Django Model | Frontend API | +|--------------|--------------| +| `opening_date` | `openingYear` | +| `min_height_in` | `minHeightIn` | +| `max_height_in` | `maxHeightIn` | +| `capacity_per_hour` | `capacityPerHour` | +| `ride_duration_seconds` | `rideDurationSeconds` | +| `coaster_stats` | `coasterStats` | +| `size_acres` | `sizeAcres` | +| `ride_count` | `rideCount` | +| `coaster_count` | `coasterCount` | +| `average_rating` | `averageRating` | + +### Status Mappings + +#### Park Status +| Django | Frontend | +|--------|----------| +| `OPERATING` | `open` | +| `CLOSED_TEMP` | `seasonal` | +| `CLOSED_PERM` | `closed` | +| `UNDER_CONSTRUCTION` | `closed` | +| `DEMOLISHED` | `closed` | +| `RELOCATED` | `closed` | + +#### Ride Status +| Django | Frontend | +|--------|----------| +| `OPERATING` | `operating` | +| `CLOSED_TEMP` | `closed` | +| `SBNO` | `sbno` | +| `CLOSING` | `closed` | +| `CLOSED_PERM` | `closed` | +| `UNDER_CONSTRUCTION` | `under_construction` | +| `DEMOLISHED` | `closed` | +| `RELOCATED` | `closed` | + +### Category Mappings + +#### Ride Categories +| Django | Frontend | +|--------|----------| +| `RC` | `roller_coaster` | +| `DR` | `dark_ride` | +| `FR` | `flat_ride` | +| `WR` | `water_ride` | +| `TR` | `transport` | +| `OT` | `other` | + +## Error Handling + +The API returns standard HTTP status codes with detailed error information: + +### Error Response Format +```json +{ + "error": { + "code": "NOT_FOUND", + "message": "Park with id 999 not found", + "details": {} + } +} +``` + +### Common HTTP Status Codes +- `200 OK` - Successful request +- `201 Created` - Resource created successfully +- `400 Bad Request` - Invalid request data +- `401 Unauthorized` - Authentication required +- `403 Forbidden` - Insufficient permissions +- `404 Not Found` - Resource not found +- `429 Too Many Requests` - Rate limit exceeded +- `500 Internal Server Error` - Server error + +## Pagination + +List endpoints support pagination with the following format: + +```json +{ + "count": 150, + "next": "http://localhost:8000/api/parks/?page=2", + "previous": null, + "results": [...] +} +``` + +**Query Parameters:** +- `page` - Page number (default: 1) +- `page_size` - Items per page (default: 20, max: 100) + +## Rate Limiting + +The API implements rate limiting to prevent abuse: +- **Anonymous users:** 100 requests per hour +- **Authenticated users:** 1000 requests per hour + +Rate limit headers are included in responses: +- `X-RateLimit-Limit` - Request limit per hour +- `X-RateLimit-Remaining` - Remaining requests +- `X-RateLimit-Reset` - Time until reset (Unix timestamp) + +## CORS Configuration + +The API is configured to work with the Vue.js frontend: +- **Allowed origins:** `http://localhost:5174` (development) +- **Allowed methods:** `GET`, `POST`, `PUT`, `DELETE`, `OPTIONS` +- **Allowed headers:** `Content-Type`, `Authorization`, `X-Requested-With` + +## Frontend Integration + +### Vue.js Service Layer + +The frontend uses dedicated service functions for API communication: + +```typescript +// services/parkService.ts +export const parkService = { + async getParks(params?: ParkQueryParams): Promise> { + const response = await apiClient.get('/parks/', { params }); + return response.data; + }, + + async getPark(id: number): Promise { + const response = await apiClient.get(`/parks/${id}/`); + return response.data; + } +}; +``` + +### Pinia Store Integration + +API responses are managed through Pinia stores: + +```typescript +// stores/parks.ts +export const useParksStore = defineStore('parks', () => { + const parks = ref([]); + const loading = ref(false); + + const fetchParks = async () => { + loading.value = true; + try { + const response = await parkService.getParks(); + parks.value = response.results; + } finally { + loading.value = false; + } + }; + + return { parks, loading, fetchParks }; +}); +``` + +## Development & Testing + +### API Testing with curl + +```bash +# Get list of parks +curl "http://localhost:8000/api/parks/" + +# Get specific park +curl "http://localhost:8000/api/parks/1/" + +# Search parks +curl "http://localhost:8000/api/parks/?search=cedar" + +# Filter by status +curl "http://localhost:8000/api/parks/?status=open" +``` + +### Django REST Framework Browsable API + +When `DEBUG=True`, the API provides a browsable interface at each endpoint URL. This interface allows: +- Interactive API browsing +- Form-based testing +- Authentication testing +- Request/response inspection + +## Future Enhancements + +### Planned Features +- **Authentication & Authorization** - JWT-based user authentication +- **User Preferences** - Personalized park/ride recommendations +- **Image Upload** - User-contributed photos +- **Review System** - User ratings and reviews +- **Social Features** - Following parks/rides, activity feeds +- **Advanced Search** - Full-text search with filters +- **Real-time Updates** - WebSocket support for live data + +### API Versioning +Future API versions will be supported via URL versioning: +- `/api/v1/parks/` - Version 1 (current) +- `/api/v2/parks/` - Version 2 (future) + +## Support + +For API-related questions or issues: +- Check the [Django REST Framework documentation](https://www.django-rest-framework.org/) +- Review the [frontend integration guide](../frontend/README.md) +- Create an issue in the project repository + +## Changelog + +### Version 1.0.0 +- Initial API release +- Parks and rides endpoints +- Frontend-compatible serialization +- Pagination and filtering support +- CORS configuration for Vue.js integration \ No newline at end of file diff --git a/shared/docs/development/workflow.md b/shared/docs/development/workflow.md new file mode 100644 index 00000000..54d77576 --- /dev/null +++ b/shared/docs/development/workflow.md @@ -0,0 +1,649 @@ +# Development Workflow + +Comprehensive guide to daily development processes, Git workflow, and team collaboration for the ThrillWiki monorepo. + +## Overview + +This document outlines the development workflow for the ThrillWiki Django + Vue.js monorepo. Following these practices ensures consistent code quality, smooth collaboration, and reliable deployments. + +## ๐Ÿ—๏ธ Project Structure + +``` +thrillwiki-monorepo/ +โ”œโ”€โ”€ backend/ # Django REST API +โ”œโ”€โ”€ frontend/ # Vue.js SPA +โ”œโ”€โ”€ shared/ # Shared resources and scripts +โ”œโ”€โ”€ architecture/ # Architecture documentation +โ””โ”€โ”€ profiles/ # Development profiles +``` + +## ๐Ÿš€ Daily Development Workflow + +### 1. Environment Setup + +#### First Time Setup +```bash +# Clone the repository +git clone +cd thrillwiki-monorepo + +# Run the automated setup script +./shared/scripts/dev/setup-dev.sh + +# Or set up manually +cd backend && uv sync && cd .. +pnpm install +``` + +#### Daily Setup +```bash +# Start all development servers +./shared/scripts/dev/start-all.sh + +# Or start individually +pnpm run dev:backend # Django on :8000 +pnpm run dev:frontend # Vue.js on :5174 +``` + +### 2. Development Process + +#### Feature Development +1. **Create a feature branch** + ```bash + git checkout -b feature/your-feature-name + ``` + +2. **Make your changes** + - Follow the coding standards for your language + - Write tests for new functionality + - Update documentation as needed + +3. **Test your changes** + ```bash + # Backend tests + cd backend && uv run manage.py test + + # Frontend tests + cd frontend && pnpm test:unit + cd frontend && pnpm test:e2e + + # Full test suite + pnpm run test + ``` + +4. **Code quality checks** + ```bash + # Backend + cd backend && uv run black . && uv run flake8 . + + # Frontend + cd frontend && pnpm lint && pnpm type-check + ``` + +#### Bug Fixes +1. **Create a bug fix branch** + ```bash + git checkout -b bugfix/issue-description + ``` + +2. **Reproduce the issue** + - Add test cases that reproduce the bug + - Verify the fix resolves the issue + +3. **Implement the fix** + - Keep changes minimal and focused + - Ensure no regressions + +### 3. Code Review Process + +#### Before Submitting +- [ ] All tests pass +- [ ] Code follows style guidelines +- [ ] Documentation updated +- [ ] No linting errors +- [ ] TypeScript types correct (frontend) +- [ ] Database migrations created (backend) + +#### Pull Request Process +1. **Create Pull Request** + - Use descriptive title + - Fill out PR template + - Link related issues + - Add screenshots for UI changes + +2. **Code Review** + - At least one approval required + - Address all review comments + - Rebase and resolve conflicts + +3. **Merge Process** + - Use "Squash and merge" for feature branches + - Use "Rebase and merge" for maintenance branches + - Delete branch after merge + +## ๐Ÿ”„ Git Workflow + +### Branch Naming Convention + +``` +feature/feature-name # New features +bugfix/issue-description # Bug fixes +hotfix/critical-issue # Critical production fixes +refactor/component-name # Code refactoring +docs/update-documentation # Documentation updates +test/add-test-coverage # Test improvements +``` + +### Commit Message Format + +``` +type(scope): description + +[optional body] + +[optional footer] +``` + +**Types:** +- `feat` - New feature +- `fix` - Bug fix +- `docs` - Documentation +- `style` - Code style changes +- `refactor` - Code refactoring +- `test` - Adding tests +- `chore` - Maintenance tasks + +**Examples:** +``` +feat: add park search functionality +fix: resolve ride detail page crash +docs: update API documentation +refactor: simplify park service layer +``` + +### Git Workflow + +#### Feature Development +```bash +# Start from main +git checkout main +git pull origin main + +# Create feature branch +git checkout -b feature/new-park-search + +# Make changes and commit +git add . +git commit -m "feat: implement park search with filters" + +# Push and create PR +git push origin feature/new-park-search +``` + +#### Handling Conflicts +```bash +# Update your branch with latest main +git fetch origin +git rebase origin/main + +# Resolve conflicts if any +# Test your changes +# Force push if needed +git push --force-with-lease origin feature/new-park-search +``` + +## ๐Ÿงช Testing Strategy + +### Backend Testing + +#### Unit Tests +```bash +cd backend + +# Run all tests +uv run manage.py test + +# Run specific app tests +uv run manage.py test apps.parks + +# Run with coverage +uv run coverage run manage.py test +uv run coverage report +``` + +#### Test Structure +``` +backend/tests/ +โ”œโ”€โ”€ test_models.py # Model tests +โ”œโ”€โ”€ test_views.py # View tests +โ”œโ”€โ”€ test_serializers.py # Serializer tests +โ””โ”€โ”€ test_services.py # Service layer tests +``` + +### Frontend Testing + +#### Unit Tests (Vitest) +```bash +cd frontend + +# Run unit tests +pnpm test:unit + +# Run with coverage +pnpm test:unit --coverage + +# Watch mode for development +pnpm test:unit --watch +``` + +#### End-to-End Tests (Playwright) +```bash +cd frontend + +# Run E2E tests +pnpm test:e2e + +# Run specific test +pnpm test:e2e tests/park-search.spec.ts + +# Debug mode +pnpm test:e2e --debug +``` + +#### Test Structure +``` +frontend/src/__tests__/ +โ”œโ”€โ”€ components/ # Component tests +โ”œโ”€โ”€ views/ # Page tests +โ”œโ”€โ”€ services/ # Service tests +โ””โ”€โ”€ stores/ # Store tests +``` + +### Test Coverage Requirements + +- **Backend:** Minimum 80% coverage +- **Frontend:** Minimum 70% coverage +- **Critical paths:** 90%+ coverage + +## ๐Ÿ”ง Code Quality + +### Backend Standards + +#### Python Code Style +```bash +cd backend + +# Format code +uv run black . + +# Check style +uv run flake8 . + +# Sort imports +uv run isort . +``` + +#### Django Best Practices +- Use Django REST Framework best practices +- Implement proper error handling +- Add database indexes for performance +- Use select_related and prefetch_related for optimization + +### Frontend Standards + +#### Vue.js Best Practices +```bash +cd frontend + +# Lint code +pnpm lint + +# Type checking +pnpm type-check + +# Format code +pnpm format +``` + +#### Code Organization +- Use Composition API with `