diff --git a/README.md b/README.md index 0d90a26..3dc49ba 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # PwrProgram -A comprehensive fitness program management API built with TypeScript, Node.js, and PostgreSQL. Manage workout programs, cycles, blocks, sessions, exercises, and sets with a hierarchical structure designed for coaches and athletes. +A comprehensive fitness program management application built with TypeScript, React, Node.js, and PostgreSQL. Manage workout programs, cycles, blocks, sessions, exercises, and sets with a hierarchical structure designed for coaches and athletes. ## 🚀 Features @@ -11,6 +11,14 @@ A comprehensive fitness program management API built with TypeScript, Node.js, a - **Pagination**: Efficient data retrieval with configurable page sizes - **HATEOAS**: Discoverable REST API with hypermedia links +### Frontend (React + Vite) +- **Modern Stack**: React 19, TypeScript, Vite +- **Routing**: React Router v7 with protected routes +- **Authentication**: Context-based auth with session management +- **Component Library**: Reusable UI components (Button, Input, Card) +- **CSS Modules**: Scoped styling for maintainability +- **API Integration**: Type-safe API client with error handling + ### Security - ✅ **Password Hashing**: Bcrypt with configurable rounds - ✅ **Session Management**: Database-backed sessions (TypeORM store, scalable to Redis) @@ -142,26 +150,32 @@ All configuration is done through environment variables. Copy `.env.example` to ### Development ```bash -# Start dev server with hot reload -pnpm --filter @pwrprogram/api dev +# Start API server with hot reload +pnpm dev:api -# Run tests -pnpm --filter @pwrprogram/api test +# Start frontend dev server +pnpm dev:web + +# Run API tests +pnpm test:api # Run tests with coverage -pnpm --filter @pwrprogram/api test:coverage +pnpm test:api:coverage -# Lint code -pnpm --filter @pwrprogram/api lint +# Lint frontend code +pnpm lint:web ``` ### Production ```bash -# Build the application +# Build the API pnpm --filter @pwrprogram/api build -# Start production server -pnpm --filter @pwrprogram/api start +# Build the frontend +pnpm build:web + +# Start production API server +pnpm start:api ``` ### Docker @@ -430,18 +444,34 @@ docker run -d \ ``` PwrProgram/ ├── apps/ -│ └── pwrprogram/ # Main API application +│ ├── pwrprogram/ # Main API application +│ │ ├── src/ +│ │ │ ├── entity/ # TypeORM entities +│ │ │ ├── routes/ # Express route handlers +│ │ │ ├── middleware/ # Custom middleware +│ │ │ ├── mappers/ # Entity to DTO mappers +│ │ │ ├── utils/ # Utility functions +│ │ │ ├── openapi/ # OpenAPI spec +│ │ │ ├── testing/ # Test files +│ │ │ ├── data-source.ts # TypeORM configuration +│ │ │ └── index.ts # Application entry point +│ │ ├── .env # Environment variables (gitignored) +│ │ └── package.json +│ └── web/ # React frontend application │ ├── src/ -│ │ ├── entity/ # TypeORM entities -│ │ ├── routes/ # Express route handlers -│ │ ├── middleware/ # Custom middleware -│ │ ├── mappers/ # Entity to DTO mappers -│ │ ├── utils/ # Utility functions -│ │ ├── openapi/ # OpenAPI spec -│ │ ├── testing/ # Test files -│ │ ├── data-source.ts # TypeORM configuration -│ │ └── index.ts # Application entry point -│ ├── .env # Environment variables (gitignored) +│ │ ├── components/ # React components +│ │ │ ├── auth/ # Auth-related components +│ │ │ ├── layout/ # Layout components +│ │ │ ├── programs/ # Program management +│ │ │ └── ui/ # Reusable UI components +│ │ ├── context/ # React contexts (Auth) +│ │ ├── hooks/ # Custom React hooks +│ │ ├── lib/ # Utilities and API client +│ │ ├── pages/ # Page components +│ │ ├── types/ # TypeScript types +│ │ ├── App.tsx # App with routing +│ │ └── main.tsx # Entry point +│ ├── vite.config.ts # Vite configuration │ └── package.json ├── packages/ │ └── shared/ # Shared DTOs and types @@ -546,7 +576,5 @@ PwrProgram/ - [Express.js](https://expressjs.com/) - [PostgreSQL](https://www.postgresql.org/) - [TypeScript](https://www.typescriptlang.org/) - ---- - -**Note**: This is a backend API. Frontend coming soon! +- [React](https://react.dev/) +- [Vite](https://vitejs.dev/) diff --git a/apps/web/.gitignore b/apps/web/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/apps/web/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/apps/web/README.md b/apps/web/README.md new file mode 100644 index 0000000..d2e7761 --- /dev/null +++ b/apps/web/README.md @@ -0,0 +1,73 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +## React Compiler + +The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation). + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: + +```js +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + // Other configs... + + // Remove tseslint.configs.recommended and replace with this + tseslint.configs.recommendedTypeChecked, + // Alternatively, use this for stricter rules + tseslint.configs.strictTypeChecked, + // Optionally, add this for stylistic rules + tseslint.configs.stylisticTypeChecked, + + // Other configs... + ], + languageOptions: { + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]) +``` + +You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: + +```js +// eslint.config.js +import reactX from 'eslint-plugin-react-x' +import reactDom from 'eslint-plugin-react-dom' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + // Other configs... + // Enable lint rules for React + reactX.configs['recommended-typescript'], + // Enable lint rules for React DOM + reactDom.configs.recommended, + ], + languageOptions: { + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]) +``` diff --git a/apps/web/eslint.config.js b/apps/web/eslint.config.js new file mode 100644 index 0000000..0785165 --- /dev/null +++ b/apps/web/eslint.config.js @@ -0,0 +1,29 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' +import { defineConfig, globalIgnores } from 'eslint/config' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + js.configs.recommended, + tseslint.configs.recommended, + reactHooks.configs.flat.recommended, + reactRefresh.configs.vite, + ], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + rules: { + 'react-refresh/only-export-components': [ + 'warn', + { allowExportNames: ['AuthContext', 'AuthContextType'] }, + ], + }, + }, +]) diff --git a/apps/web/index.html b/apps/web/index.html new file mode 100644 index 0000000..5482e67 --- /dev/null +++ b/apps/web/index.html @@ -0,0 +1,13 @@ + + + + + + + PwrProgram - Fitness Program Management + + +
+ + + diff --git a/apps/web/package.json b/apps/web/package.json new file mode 100644 index 0000000..267e907 --- /dev/null +++ b/apps/web/package.json @@ -0,0 +1,33 @@ +{ + "name": "@pwrprogram/web", + "private": true, + "version": "1.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@pwrprogram/shared": "workspace:*", + "react": "^19.2.0", + "react-dom": "^19.2.0", + "react-router-dom": "^7.6.1" + }, + "devDependencies": { + "@eslint/js": "^9.39.1", + "@types/node": "^24.10.1", + "@types/react": "^19.2.5", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.1.1", + "eslint": "^9.39.1", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.4.24", + "globals": "^16.5.0", + "typescript": "~5.9.3", + "typescript-eslint": "^8.46.4", + "vite": "^7.2.4" + } +} diff --git a/apps/web/public/vite.svg b/apps/web/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/apps/web/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/src/App.css b/apps/web/src/App.css new file mode 100644 index 0000000..928faf3 --- /dev/null +++ b/apps/web/src/App.css @@ -0,0 +1,20 @@ +/* Global App Styles */ +* { + box-sizing: border-box; +} + +#root { + min-height: 100vh; +} + +a { + text-decoration: none; + color: inherit; +} + +/* Links styled by components */ +a:focus-visible { + outline: 2px solid #3b82f6; + outline-offset: 2px; +} + diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx new file mode 100644 index 0000000..cd36b34 --- /dev/null +++ b/apps/web/src/App.tsx @@ -0,0 +1,58 @@ +import { BrowserRouter, Routes, Route } from 'react-router-dom'; +import { AuthProvider } from '@/context'; +import { MainLayout } from '@/components/layout'; +import { ProtectedRoute } from '@/components/auth'; +import { + HomePage, + LoginPage, + RegisterPage, + DashboardPage, + ProgramsPage, + ProgramDetailPage, +} from '@/pages'; +import './App.css'; + +function App() { + return ( + + + + }> + {/* Public routes */} + } /> + } /> + } /> + + {/* Protected routes */} + + + + } + /> + + + + } + /> + + + + } + /> + + + + + ); +} + +export default App; diff --git a/apps/web/src/assets/react.svg b/apps/web/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/apps/web/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/src/components/auth/ProtectedRoute.tsx b/apps/web/src/components/auth/ProtectedRoute.tsx new file mode 100644 index 0000000..5385128 --- /dev/null +++ b/apps/web/src/components/auth/ProtectedRoute.tsx @@ -0,0 +1,31 @@ +import { Navigate, useLocation } from 'react-router-dom'; +import { useAuth } from '@/context'; + +interface ProtectedRouteProps { + children: React.ReactNode; +} + +export function ProtectedRoute({ children }: ProtectedRouteProps) { + const { isAuthenticated, isLoading } = useAuth(); + const location = useLocation(); + + if (isLoading) { + return ( +
+ Loading... +
+ ); + } + + if (!isAuthenticated) { + return ; + } + + return <>{children}; +} diff --git a/apps/web/src/components/auth/index.ts b/apps/web/src/components/auth/index.ts new file mode 100644 index 0000000..f9cff2a --- /dev/null +++ b/apps/web/src/components/auth/index.ts @@ -0,0 +1 @@ +export { ProtectedRoute } from './ProtectedRoute'; diff --git a/apps/web/src/components/layout/MainLayout.module.css b/apps/web/src/components/layout/MainLayout.module.css new file mode 100644 index 0000000..71078f9 --- /dev/null +++ b/apps/web/src/components/layout/MainLayout.module.css @@ -0,0 +1,92 @@ +.layout { + min-height: 100vh; + display: flex; + flex-direction: column; +} + +.header { + background: white; + border-bottom: 1px solid #e5e7eb; + position: sticky; + top: 0; + z-index: 100; +} + +.headerContent { + max-width: 1280px; + margin: 0 auto; + padding: 1rem 2rem; + display: flex; + align-items: center; + justify-content: space-between; + gap: 2rem; +} + +.logo { + display: flex; + align-items: center; + gap: 0.5rem; + text-decoration: none; + color: #1e293b; +} + +.logoIcon { + font-size: 1.5rem; +} + +.logoText { + font-size: 1.25rem; + font-weight: 700; +} + +.nav { + display: flex; + gap: 1.5rem; + flex: 1; +} + +.navLink { + text-decoration: none; + color: #64748b; + font-weight: 500; + transition: color 0.2s; +} + +.navLink:hover { + color: #3b82f6; +} + +.actions { + display: flex; + align-items: center; +} + +.userMenu { + display: flex; + align-items: center; + gap: 1rem; +} + +.userName { + color: #374151; + font-weight: 500; +} + +.authButtons { + display: flex; + gap: 0.5rem; +} + +.main { + flex: 1; + background: #f8fafc; +} + +.footer { + background: white; + border-top: 1px solid #e5e7eb; + padding: 1.5rem 2rem; + text-align: center; + color: #64748b; + font-size: 0.875rem; +} diff --git a/apps/web/src/components/layout/MainLayout.tsx b/apps/web/src/components/layout/MainLayout.tsx new file mode 100644 index 0000000..e016559 --- /dev/null +++ b/apps/web/src/components/layout/MainLayout.tsx @@ -0,0 +1,70 @@ +import { Outlet, Link, useNavigate } from 'react-router-dom'; +import { useAuth } from '@/context'; +import { Button } from '@/components/ui'; +import styles from './MainLayout.module.css'; + +export function MainLayout() { + const { user, logout, isAuthenticated } = useAuth(); + const navigate = useNavigate(); + + const handleLogout = async () => { + await logout(); + navigate('/login'); + }; + + return ( +
+
+
+ + 💪 + PwrProgram + + + + +
+ {isAuthenticated ? ( +
+ + {user?.firstName} {user?.lastName} + + +
+ ) : ( +
+ + + + + + +
+ )} +
+
+
+ +
+ +
+ +
+

© {new Date().getFullYear()} PwrProgram. All rights reserved.

+
+
+ ); +} diff --git a/apps/web/src/components/layout/index.ts b/apps/web/src/components/layout/index.ts new file mode 100644 index 0000000..bfa7d49 --- /dev/null +++ b/apps/web/src/components/layout/index.ts @@ -0,0 +1 @@ +export { MainLayout } from './MainLayout'; diff --git a/apps/web/src/components/ui/Button.module.css b/apps/web/src/components/ui/Button.module.css new file mode 100644 index 0000000..09c6809 --- /dev/null +++ b/apps/web/src/components/ui/Button.module.css @@ -0,0 +1,86 @@ +.button { + display: inline-flex; + align-items: center; + justify-content: center; + font-weight: 500; + border-radius: 8px; + border: none; + cursor: pointer; + transition: all 0.2s ease; + font-family: inherit; +} + +.button:disabled { + opacity: 0.6; + cursor: not-allowed; +} + +/* Variants */ +.primary { + background: #3b82f6; + color: white; +} + +.primary:hover:not(:disabled) { + background: #2563eb; +} + +.secondary { + background: #f1f5f9; + color: #1e293b; +} + +.secondary:hover:not(:disabled) { + background: #e2e8f0; +} + +.danger { + background: #ef4444; + color: white; +} + +.danger:hover:not(:disabled) { + background: #dc2626; +} + +.ghost { + background: transparent; + color: #64748b; +} + +.ghost:hover:not(:disabled) { + background: #f1f5f9; + color: #1e293b; +} + +/* Sizes */ +.sm { + padding: 0.5rem 1rem; + font-size: 0.875rem; +} + +.md { + padding: 0.75rem 1.5rem; + font-size: 1rem; +} + +.lg { + padding: 1rem 2rem; + font-size: 1.125rem; +} + +/* Loading spinner */ +.spinner { + width: 1em; + height: 1em; + border: 2px solid transparent; + border-top-color: currentColor; + border-radius: 50%; + animation: spin 0.7s linear infinite; +} + +@keyframes spin { + to { + transform: rotate(360deg); + } +} diff --git a/apps/web/src/components/ui/Button.tsx b/apps/web/src/components/ui/Button.tsx new file mode 100644 index 0000000..408e1e9 --- /dev/null +++ b/apps/web/src/components/ui/Button.tsx @@ -0,0 +1,29 @@ +import type { ButtonHTMLAttributes, ReactNode } from 'react'; +import styles from './Button.module.css'; + +interface ButtonProps extends ButtonHTMLAttributes { + variant?: 'primary' | 'secondary' | 'danger' | 'ghost'; + size?: 'sm' | 'md' | 'lg'; + isLoading?: boolean; + children: ReactNode; +} + +export function Button({ + variant = 'primary', + size = 'md', + isLoading = false, + children, + disabled, + className = '', + ...props +}: ButtonProps) { + return ( + + ); +} diff --git a/apps/web/src/components/ui/Card.module.css b/apps/web/src/components/ui/Card.module.css new file mode 100644 index 0000000..eb5a6b9 --- /dev/null +++ b/apps/web/src/components/ui/Card.module.css @@ -0,0 +1,32 @@ +.card { + background: white; + border-radius: 12px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + border: 1px solid #e5e7eb; + overflow: hidden; +} + +.clickable { + cursor: pointer; + transition: box-shadow 0.2s, transform 0.2s; +} + +.clickable:hover { + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + transform: translateY(-2px); +} + +.header { + padding: 1.25rem 1.5rem; + border-bottom: 1px solid #e5e7eb; +} + +.content { + padding: 1.5rem; +} + +.footer { + padding: 1rem 1.5rem; + border-top: 1px solid #e5e7eb; + background: #f9fafb; +} diff --git a/apps/web/src/components/ui/Card.tsx b/apps/web/src/components/ui/Card.tsx new file mode 100644 index 0000000..28340a1 --- /dev/null +++ b/apps/web/src/components/ui/Card.tsx @@ -0,0 +1,33 @@ +import type { ReactNode } from 'react'; +import styles from './Card.module.css'; + +interface CardProps { + children: ReactNode; + className?: string; + onClick?: () => void; +} + +export function Card({ children, className = '', onClick }: CardProps) { + return ( +
+ {children} +
+ ); +} + +export function CardHeader({ children, className = '' }: { children: ReactNode; className?: string }) { + return
{children}
; +} + +export function CardContent({ children, className = '' }: { children: ReactNode; className?: string }) { + return
{children}
; +} + +export function CardFooter({ children, className = '' }: { children: ReactNode; className?: string }) { + return
{children}
; +} diff --git a/apps/web/src/components/ui/Input.module.css b/apps/web/src/components/ui/Input.module.css new file mode 100644 index 0000000..efbc26c --- /dev/null +++ b/apps/web/src/components/ui/Input.module.css @@ -0,0 +1,46 @@ +.container { + display: flex; + flex-direction: column; + gap: 0.5rem; + width: 100%; +} + +.label { + font-size: 0.875rem; + font-weight: 500; + color: #374151; +} + +.input { + padding: 0.75rem 1rem; + border: 1px solid #d1d5db; + border-radius: 8px; + font-size: 1rem; + font-family: inherit; + transition: border-color 0.2s, box-shadow 0.2s; + outline: none; + width: 100%; + box-sizing: border-box; +} + +.input:focus { + border-color: #3b82f6; + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); +} + +.input.error { + border-color: #ef4444; +} + +.input.error:focus { + box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1); +} + +.errorText { + font-size: 0.875rem; + color: #ef4444; +} + +.input::placeholder { + color: #9ca3af; +} diff --git a/apps/web/src/components/ui/Input.tsx b/apps/web/src/components/ui/Input.tsx new file mode 100644 index 0000000..d0ef246 --- /dev/null +++ b/apps/web/src/components/ui/Input.tsx @@ -0,0 +1,33 @@ +import type { InputHTMLAttributes } from 'react'; +import { forwardRef } from 'react'; +import styles from './Input.module.css'; + +interface InputProps extends InputHTMLAttributes { + label?: string; + error?: string; +} + +export const Input = forwardRef( + ({ label, error, className = '', id, ...props }, ref) => { + const inputId = id || props.name; + + return ( +
+ {label && ( + + )} + + {error && {error}} +
+ ); + } +); + +Input.displayName = 'Input'; diff --git a/apps/web/src/components/ui/index.ts b/apps/web/src/components/ui/index.ts new file mode 100644 index 0000000..8800505 --- /dev/null +++ b/apps/web/src/components/ui/index.ts @@ -0,0 +1,3 @@ +export { Button } from './Button'; +export { Input } from './Input'; +export { Card, CardHeader, CardContent, CardFooter } from './Card'; diff --git a/apps/web/src/context/AuthContext.tsx b/apps/web/src/context/AuthContext.tsx new file mode 100644 index 0000000..340f1ab --- /dev/null +++ b/apps/web/src/context/AuthContext.tsx @@ -0,0 +1,124 @@ +import { + createContext, + useState, + useEffect, + type ReactNode, +} from 'react'; +import { api } from '@/lib/api'; +import type { AuthState } from '@/types'; + +export interface AuthContextType extends AuthState { + login: (email: string, password: string) => Promise; + register: (data: { + email: string; + password: string; + firstName: string; + lastName?: string; + }) => Promise; + logout: () => Promise; + refreshUser: () => Promise; +} + +export const AuthContext = createContext(undefined); + +export function AuthProvider({ children }: { children: ReactNode }) { + const [state, setState] = useState({ + user: null, + isAuthenticated: false, + isLoading: true, + }); + + const refreshUser = async () => { + try { + const user = await api.getCurrentUser(); + setState({ + user, + isAuthenticated: true, + isLoading: false, + }); + } catch { + setState({ + user: null, + isAuthenticated: false, + isLoading: false, + }); + } + }; + + useEffect(() => { + let cancelled = false; + + async function checkAuth() { + try { + const user = await api.getCurrentUser(); + if (!cancelled) { + setState({ + user, + isAuthenticated: true, + isLoading: false, + }); + } + } catch { + if (!cancelled) { + setState({ + user: null, + isAuthenticated: false, + isLoading: false, + }); + } + } + } + + checkAuth(); + + return () => { + cancelled = true; + }; + }, []); + + const login = async (email: string, password: string) => { + const user = await api.login(email, password); + setState({ + user, + isAuthenticated: true, + isLoading: false, + }); + }; + + const register = async (data: { + email: string; + password: string; + firstName: string; + lastName?: string; + }) => { + const user = await api.register(data); + setState({ + user, + isAuthenticated: true, + isLoading: false, + }); + }; + + const logout = async () => { + await api.logout(); + setState({ + user: null, + isAuthenticated: false, + isLoading: false, + }); + }; + + return ( + + {children} + + ); +} diff --git a/apps/web/src/context/index.ts b/apps/web/src/context/index.ts new file mode 100644 index 0000000..41f0cf0 --- /dev/null +++ b/apps/web/src/context/index.ts @@ -0,0 +1,2 @@ +export { AuthProvider, AuthContext, type AuthContextType } from './AuthContext'; +export { useAuth } from './useAuth'; diff --git a/apps/web/src/context/useAuth.ts b/apps/web/src/context/useAuth.ts new file mode 100644 index 0000000..43a99ec --- /dev/null +++ b/apps/web/src/context/useAuth.ts @@ -0,0 +1,10 @@ +import { useContext } from 'react'; +import { AuthContext, type AuthContextType } from './AuthContext'; + +export function useAuth(): AuthContextType { + const context = useContext(AuthContext); + if (context === undefined) { + throw new Error('useAuth must be used within an AuthProvider'); + } + return context; +} diff --git a/apps/web/src/index.css b/apps/web/src/index.css new file mode 100644 index 0000000..2fb8383 --- /dev/null +++ b/apps/web/src/index.css @@ -0,0 +1,40 @@ +:root { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color: #1e293b; + background-color: #f8fafc; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +body { + margin: 0; + min-width: 320px; + min-height: 100vh; +} + +h1, h2, h3, h4, h5, h6 { + margin: 0; + line-height: 1.2; +} + +p { + margin: 0; +} + +input, button, textarea, select { + font: inherit; +} + +/* Remove default button styling */ +button { + background: none; + border: none; + padding: 0; + cursor: pointer; +} diff --git a/apps/web/src/lib/api.ts b/apps/web/src/lib/api.ts new file mode 100644 index 0000000..bcba218 --- /dev/null +++ b/apps/web/src/lib/api.ts @@ -0,0 +1,120 @@ +import type { User, Program, PaginatedResponse, ApiError } from '@/types'; + +const API_BASE = '/api'; + +class ApiClient { + private async request( + endpoint: string, + options: RequestInit = {} + ): Promise { + const url = `${API_BASE}${endpoint}`; + + const response = await fetch(url, { + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers, + }, + credentials: 'include', + }); + + if (!response.ok) { + const error: ApiError = await response.json().catch(() => ({ + message: 'An unexpected error occurred', + statusCode: response.status, + })); + throw error; + } + + // Handle 204 No Content + if (response.status === 204) { + return undefined as T; + } + + return response.json(); + } + + // Auth endpoints + async login(email: string, password: string): Promise { + return this.request('/auth/login', { + method: 'POST', + body: JSON.stringify({ email, password }), + }); + } + + async register(data: { + email: string; + password: string; + firstName: string; + lastName?: string; + }): Promise { + return this.request('/auth/register', { + method: 'POST', + body: JSON.stringify(data), + }); + } + + async logout(): Promise { + return this.request('/auth/logout', { + method: 'POST', + }); + } + + async getCurrentUser(): Promise { + return this.request('/auth/me'); + } + + // User endpoints + async getUser(id: string): Promise { + return this.request(`/users/${id}`); + } + + async updateUser( + id: string, + data: Partial> + ): Promise { + return this.request(`/users/${id}`, { + method: 'PATCH', + body: JSON.stringify(data), + }); + } + + // Program endpoints + async getPrograms(page = 1, limit = 10): Promise> { + return this.request>( + `/programs?page=${page}&limit=${limit}` + ); + } + + async getProgram(id: string): Promise { + return this.request(`/programs/${id}`); + } + + async createProgram(data: { + name: string; + description?: string; + }): Promise { + return this.request('/programs', { + method: 'POST', + body: JSON.stringify(data), + }); + } + + async updateProgram( + id: string, + data: { name?: string; description?: string } + ): Promise { + return this.request(`/programs/${id}`, { + method: 'PATCH', + body: JSON.stringify(data), + }); + } + + async deleteProgram(id: string): Promise { + return this.request(`/programs/${id}`, { + method: 'DELETE', + }); + } +} + +export const api = new ApiClient(); diff --git a/apps/web/src/main.tsx b/apps/web/src/main.tsx new file mode 100644 index 0000000..bef5202 --- /dev/null +++ b/apps/web/src/main.tsx @@ -0,0 +1,10 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import './index.css' +import App from './App.tsx' + +createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/apps/web/src/pages/AuthPages.module.css b/apps/web/src/pages/AuthPages.module.css new file mode 100644 index 0000000..0f98156 --- /dev/null +++ b/apps/web/src/pages/AuthPages.module.css @@ -0,0 +1,66 @@ +.container { + min-height: calc(100vh - 180px); + display: flex; + align-items: center; + justify-content: center; + padding: 2rem; +} + +.card { + width: 100%; + max-width: 420px; +} + +.title { + font-size: 1.5rem; + font-weight: 700; + color: #1e293b; + margin: 0; +} + +.subtitle { + color: #64748b; + margin: 0.5rem 0 0; +} + +.form { + display: flex; + flex-direction: column; + gap: 1.25rem; +} + +.row { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1rem; +} + +.error { + padding: 0.75rem 1rem; + background: #fef2f2; + border: 1px solid #fee2e2; + border-radius: 8px; + color: #dc2626; + font-size: 0.875rem; +} + +.submitBtn { + width: 100%; + margin-top: 0.5rem; +} + +.footer { + text-align: center; + margin-top: 1.5rem; + color: #64748b; +} + +.link { + color: #3b82f6; + text-decoration: none; + font-weight: 500; +} + +.link:hover { + text-decoration: underline; +} diff --git a/apps/web/src/pages/DashboardPage.module.css b/apps/web/src/pages/DashboardPage.module.css new file mode 100644 index 0000000..b6921e0 --- /dev/null +++ b/apps/web/src/pages/DashboardPage.module.css @@ -0,0 +1,92 @@ +.container { + max-width: 1200px; + margin: 0 auto; + padding: 2rem; +} + +.header { + margin-bottom: 2rem; +} + +.title { + font-size: 2rem; + font-weight: 700; + color: #1e293b; + margin: 0; +} + +.subtitle { + color: #64748b; + margin: 0.5rem 0 0; + font-size: 1.125rem; +} + +.grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); + gap: 1.5rem; +} + +.card { + height: fit-content; +} + +.cardTitle { + font-size: 1.125rem; + font-weight: 600; + color: #1e293b; + margin: 0; +} + +.cardText { + color: #64748b; + margin: 0 0 1rem; +} + +.stats { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1rem; +} + +.stat { + text-align: center; +} + +.statValue { + display: block; + font-size: 2rem; + font-weight: 700; + color: #3b82f6; +} + +.statLabel { + font-size: 0.875rem; + color: #64748b; +} + +.list { + margin: 0; + padding-left: 1.25rem; + color: #374151; +} + +.list li { + margin-bottom: 0.5rem; +} + +.list li:last-child { + margin-bottom: 0; +} + +.profileInfo { + color: #374151; +} + +.profileInfo p { + margin: 0.5rem 0; +} + +.profileInfo p:first-child { + margin-top: 0; +} diff --git a/apps/web/src/pages/DashboardPage.tsx b/apps/web/src/pages/DashboardPage.tsx new file mode 100644 index 0000000..a832b15 --- /dev/null +++ b/apps/web/src/pages/DashboardPage.tsx @@ -0,0 +1,81 @@ +import { Link } from 'react-router-dom'; +import { useAuth } from '@/context'; +import { Card, CardHeader, CardContent, Button } from '@/components/ui'; +import styles from './DashboardPage.module.css'; + +export function DashboardPage() { + const { user } = useAuth(); + + return ( +
+
+

Welcome back, {user?.firstName}!

+

+ Here's an overview of your fitness journey +

+
+ +
+ + +

📋 Programs

+
+ +

+ View and manage your workout programs +

+ + + +
+
+ + + +

🎯 Quick Stats

+
+ +
+
+ - + Active Programs +
+
+ - + Total Sessions +
+
+
+
+ + + +

💡 Getting Started

+
+ +
    +
  • Create your first program
  • +
  • Add cycles to structure your training
  • +
  • Design sessions with exercises
  • +
  • Track your sets and progress
  • +
+
+
+ + + +

👤 Profile

+
+ +
+

Name: {user?.firstName} {user?.lastName}

+

Email: {user?.email}

+
+
+
+
+
+ ); +} diff --git a/apps/web/src/pages/HomePage.module.css b/apps/web/src/pages/HomePage.module.css new file mode 100644 index 0000000..4fbb943 --- /dev/null +++ b/apps/web/src/pages/HomePage.module.css @@ -0,0 +1,100 @@ +.container { + min-height: calc(100vh - 180px); +} + +/* Hero Section */ +.hero { + text-align: center; + padding: 4rem 2rem 5rem; + background: linear-gradient(180deg, #f8fafc 0%, white 100%); +} + +.heroTitle { + font-size: 3rem; + font-weight: 800; + color: #1e293b; + margin: 0 0 1.5rem; + line-height: 1.2; +} + +.highlight { + color: #3b82f6; +} + +.heroSubtitle { + font-size: 1.25rem; + color: #64748b; + max-width: 600px; + margin: 0 auto 2rem; + line-height: 1.6; +} + +.heroCta { + display: flex; + gap: 1rem; + justify-content: center; + flex-wrap: wrap; +} + +/* Features Section */ +.features { + padding: 4rem 2rem; + max-width: 1200px; + margin: 0 auto; +} + +.featuresTitle { + text-align: center; + font-size: 2rem; + font-weight: 700; + color: #1e293b; + margin: 0 0 3rem; +} + +.featuresGrid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 2rem; +} + +.feature { + background: white; + padding: 2rem; + border-radius: 12px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + border: 1px solid #e5e7eb; + text-align: center; +} + +.featureIcon { + font-size: 2.5rem; + display: block; + margin-bottom: 1rem; +} + +.feature h3 { + font-size: 1.25rem; + font-weight: 600; + color: #1e293b; + margin: 0 0 0.75rem; +} + +.feature p { + color: #64748b; + margin: 0; + line-height: 1.5; +} + +@media (max-width: 768px) { + .heroTitle { + font-size: 2rem; + } + + .heroSubtitle { + font-size: 1rem; + } + + .features { + padding: 2rem 1rem; + } +} diff --git a/apps/web/src/pages/HomePage.tsx b/apps/web/src/pages/HomePage.tsx new file mode 100644 index 0000000..ab22c1e --- /dev/null +++ b/apps/web/src/pages/HomePage.tsx @@ -0,0 +1,80 @@ +import { Link } from 'react-router-dom'; +import { useAuth } from '@/context'; +import { Button } from '@/components/ui'; +import styles from './HomePage.module.css'; + +export function HomePage() { + const { isAuthenticated } = useAuth(); + + return ( +
+
+

+ Build Better Programs. +
+ Achieve Better Results. +

+

+ PwrProgram helps you design, manage, and track comprehensive workout + programs with a hierarchical structure for coaches and athletes. +

+
+ {isAuthenticated ? ( + + + + ) : ( + <> + + + + + + + + )} +
+
+ +
+

Everything You Need

+
+
+ 📊 +

Program Hierarchy

+

+ Structure your training with Programs → Cycles → Blocks → + Sessions → Exercises → Sets +

+
+
+ 🔒 +

Secure & Private

+

+ Your data is protected with industry-standard security and + encryption +

+
+
+ 📱 +

Easy to Use

+

+ Intuitive interface designed for quick program creation and + management +

+
+
+ 🤝 +

Coach Integration

+

+ Built-in support for coach-athlete relationships and program + sharing +

+
+
+
+
+ ); +} diff --git a/apps/web/src/pages/LoginPage.tsx b/apps/web/src/pages/LoginPage.tsx new file mode 100644 index 0000000..7ca0e52 --- /dev/null +++ b/apps/web/src/pages/LoginPage.tsx @@ -0,0 +1,80 @@ +import { useState, type FormEvent } from 'react'; +import { Link, useNavigate, useLocation } from 'react-router-dom'; +import { useAuth } from '@/context'; +import { Button, Input, Card, CardHeader, CardContent } from '@/components/ui'; +import type { ApiError } from '@/types'; +import styles from './AuthPages.module.css'; + +export function LoginPage() { + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [error, setError] = useState(''); + const [isLoading, setIsLoading] = useState(false); + + const { login } = useAuth(); + const navigate = useNavigate(); + const location = useLocation(); + + const from = (location.state as { from?: { pathname: string } })?.from?.pathname || '/dashboard'; + + const handleSubmit = async (e: FormEvent) => { + e.preventDefault(); + setError(''); + setIsLoading(true); + + try { + await login(email, password); + navigate(from, { replace: true }); + } catch (err) { + const apiError = err as ApiError; + setError(apiError.message || 'Login failed. Please try again.'); + } finally { + setIsLoading(false); + } + }; + + return ( +
+ + +

Welcome Back

+

Sign in to your account

+
+ +
+ {error &&
{error}
} + + setEmail(e.target.value)} + placeholder="you@example.com" + required + /> + + setPassword(e.target.value)} + placeholder="••••••••" + required + /> + + +
+ +

+ Don't have an account?{' '} + + Sign up + +

+
+
+
+ ); +} diff --git a/apps/web/src/pages/ProgramDetailPage.module.css b/apps/web/src/pages/ProgramDetailPage.module.css new file mode 100644 index 0000000..0d91d59 --- /dev/null +++ b/apps/web/src/pages/ProgramDetailPage.module.css @@ -0,0 +1,107 @@ +.container { + max-width: 1000px; + margin: 0 auto; + padding: 2rem; +} + +.loading { + text-align: center; + padding: 3rem; + color: #64748b; +} + +.error { + padding: 1rem; + background: #fef2f2; + border: 1px solid #fee2e2; + border-radius: 8px; + color: #dc2626; + margin-bottom: 1.5rem; +} + +.header { + margin-bottom: 2rem; +} + +.backLink { + color: #3b82f6; + text-decoration: none; + font-size: 0.875rem; + display: inline-block; + margin-bottom: 1rem; +} + +.backLink:hover { + text-decoration: underline; +} + +.headerContent { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 1rem; +} + +.title { + font-size: 2rem; + font-weight: 700; + color: #1e293b; + margin: 0; +} + +.description { + color: #64748b; + margin: 0.5rem 0 0; + font-size: 1.125rem; +} + +.actions { + display: flex; + gap: 0.75rem; +} + +.infoCard { + margin-top: 1.5rem; +} + +.sectionTitle { + margin: 0; + font-size: 1.25rem; + color: #1e293b; +} + +.infoText { + color: #64748b; + margin: 0 0 1.5rem; +} + +.structure { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); + gap: 1rem; +} + +.structureItem { + display: flex; + gap: 0.75rem; + padding: 1rem; + background: #f8fafc; + border-radius: 8px; + align-items: flex-start; +} + +.structureIcon { + font-size: 1.5rem; +} + +.structureItem h4 { + margin: 0 0 0.25rem; + color: #1e293b; + font-size: 0.9375rem; +} + +.structureItem p { + margin: 0; + color: #64748b; + font-size: 0.8125rem; +} diff --git a/apps/web/src/pages/ProgramDetailPage.tsx b/apps/web/src/pages/ProgramDetailPage.tsx new file mode 100644 index 0000000..320f16a --- /dev/null +++ b/apps/web/src/pages/ProgramDetailPage.tsx @@ -0,0 +1,134 @@ +import { useState, useEffect } from 'react'; +import { useParams, useNavigate, Link } from 'react-router-dom'; +import { api } from '@/lib/api'; +import { Button, Card, CardContent, CardHeader } from '@/components/ui'; +import type { Program, ApiError } from '@/types'; +import styles from './ProgramDetailPage.module.css'; + +export function ProgramDetailPage() { + const { id } = useParams<{ id: string }>(); + const navigate = useNavigate(); + const [program, setProgram] = useState(null); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(''); + const [isDeleting, setIsDeleting] = useState(false); + + useEffect(() => { + if (id) { + loadProgram(id); + } + }, [id]); + + const loadProgram = async (programId: string) => { + try { + setIsLoading(true); + const data = await api.getProgram(programId); + setProgram(data); + } catch (err) { + const apiError = err as ApiError; + setError(apiError.message || 'Failed to load program'); + } finally { + setIsLoading(false); + } + }; + + const handleDelete = async () => { + if (!program || !window.confirm('Are you sure you want to delete this program?')) { + return; + } + + try { + setIsDeleting(true); + await api.deleteProgram(program.id); + navigate('/programs'); + } catch (err) { + const apiError = err as ApiError; + setError(apiError.message || 'Failed to delete program'); + setIsDeleting(false); + } + }; + + if (isLoading) { + return ( +
+
Loading program...
+
+ ); + } + + if (error || !program) { + return ( +
+
{error || 'Program not found'}
+ + + +
+ ); + } + + return ( +
+
+ + ← Back to Programs + +
+
+

{program.name}

+ {program.description && ( +

{program.description}

+ )} +
+
+ +
+
+
+ + + +

Program Structure

+
+ +

+ This is where you can view and manage cycles, blocks, sessions, and exercises. + The full program structure management UI will be implemented as the application grows. +

+
+
+ 🔄 +
+

Cycles

+

Major training phases

+
+
+
+ 📦 +
+

Blocks

+

Weekly training blocks

+
+
+
+ 📅 +
+

Sessions

+

Individual workouts

+
+
+
+ 💪 +
+

Exercises

+

Movements and sets

+
+
+
+
+
+
+ ); +} diff --git a/apps/web/src/pages/ProgramsPage.module.css b/apps/web/src/pages/ProgramsPage.module.css new file mode 100644 index 0000000..54af0ea --- /dev/null +++ b/apps/web/src/pages/ProgramsPage.module.css @@ -0,0 +1,147 @@ +.container { + max-width: 1200px; + margin: 0 auto; + padding: 2rem; +} + +.header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 2rem; +} + +.title { + font-size: 2rem; + font-weight: 700; + color: #1e293b; + margin: 0; +} + +.subtitle { + color: #64748b; + margin: 0.5rem 0 0; +} + +.error { + padding: 1rem; + background: #fef2f2; + border: 1px solid #fee2e2; + border-radius: 8px; + color: #dc2626; + margin-bottom: 1.5rem; +} + +.loading { + text-align: center; + padding: 3rem; + color: #64748b; +} + +.grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 1.5rem; +} + +.programCard { + cursor: pointer; +} + +.programName { + font-size: 1.25rem; + font-weight: 600; + color: #1e293b; + margin: 0 0 0.5rem; +} + +.programDesc { + color: #64748b; + margin: 0; + font-size: 0.875rem; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.viewLink { + color: #3b82f6; + font-weight: 500; + font-size: 0.875rem; +} + +/* Empty State */ +.emptyState { + max-width: 400px; + margin: 3rem auto; +} + +.emptyContent { + text-align: center; + padding: 1rem; +} + +.emptyIcon { + font-size: 3rem; + display: block; + margin-bottom: 1rem; +} + +.emptyContent h3 { + margin: 0 0 0.5rem; + color: #1e293b; +} + +.emptyContent p { + color: #64748b; + margin: 0 0 1.5rem; +} + +/* Modal */ +.overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + z-index: 200; + padding: 1rem; +} + +.modal { + width: 100%; + max-width: 480px; +} + +.modalTitle { + margin: 0; + font-size: 1.25rem; + color: #1e293b; +} + +.form { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.formError { + padding: 0.75rem 1rem; + background: #fef2f2; + border: 1px solid #fee2e2; + border-radius: 8px; + color: #dc2626; + font-size: 0.875rem; +} + +.formActions { + display: flex; + gap: 0.75rem; + justify-content: flex-end; + margin-top: 0.5rem; +} diff --git a/apps/web/src/pages/ProgramsPage.tsx b/apps/web/src/pages/ProgramsPage.tsx new file mode 100644 index 0000000..bd9e512 --- /dev/null +++ b/apps/web/src/pages/ProgramsPage.tsx @@ -0,0 +1,168 @@ +import { useState, useEffect, type FormEvent } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { api } from '@/lib/api'; +import { Button, Input, Card, CardHeader, CardContent, CardFooter } from '@/components/ui'; +import type { Program, ApiError } from '@/types'; +import styles from './ProgramsPage.module.css'; + +export function ProgramsPage() { + const [programs, setPrograms] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(''); + const [showCreateForm, setShowCreateForm] = useState(false); + const navigate = useNavigate(); + + useEffect(() => { + loadPrograms(); + }, []); + + const loadPrograms = async () => { + try { + setIsLoading(true); + const response = await api.getPrograms(); + setPrograms(response.data); + } catch (err) { + const apiError = err as ApiError; + setError(apiError.message || 'Failed to load programs'); + } finally { + setIsLoading(false); + } + }; + + const handleProgramClick = (programId: string) => { + navigate(`/programs/${programId}`); + }; + + return ( +
+
+
+

Programs

+

Manage your workout programs

+
+ +
+ + {error &&
{error}
} + + {showCreateForm && ( + setShowCreateForm(false)} + onCreated={() => { + setShowCreateForm(false); + loadPrograms(); + }} + /> + )} + + {isLoading ? ( +
Loading programs...
+ ) : programs.length === 0 ? ( + + +
+ 📋 +

No programs yet

+

Create your first program to get started!

+ +
+
+
+ ) : ( +
+ {programs.map((program) => ( + handleProgramClick(program.id)} + > + +

{program.name}

+ {program.description && ( +

{program.description}

+ )} +
+ + View Details → + +
+ ))} +
+ )} +
+ ); +} + +interface CreateProgramFormProps { + onClose: () => void; + onCreated: () => void; +} + +function CreateProgramForm({ onClose, onCreated }: CreateProgramFormProps) { + const [name, setName] = useState(''); + const [description, setDescription] = useState(''); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(''); + + const handleSubmit = async (e: FormEvent) => { + e.preventDefault(); + setError(''); + setIsLoading(true); + + try { + await api.createProgram({ + name, + description: description || undefined, + }); + onCreated(); + } catch (err) { + const apiError = err as ApiError; + setError(apiError.message || 'Failed to create program'); + } finally { + setIsLoading(false); + } + }; + + return ( +
+ + +

Create New Program

+
+ +
+ {error &&
{error}
} + + setName(e.target.value)} + placeholder="e.g., Strength Building Phase 1" + required + /> + + setDescription(e.target.value)} + placeholder="Describe your program..." + /> + +
+ + +
+
+
+
+
+ ); +} diff --git a/apps/web/src/pages/RegisterPage.tsx b/apps/web/src/pages/RegisterPage.tsx new file mode 100644 index 0000000..d450d14 --- /dev/null +++ b/apps/web/src/pages/RegisterPage.tsx @@ -0,0 +1,125 @@ +import { useState, type FormEvent } from 'react'; +import { Link, useNavigate } from 'react-router-dom'; +import { useAuth } from '@/context'; +import { Button, Input, Card, CardHeader, CardContent } from '@/components/ui'; +import type { ApiError } from '@/types'; +import styles from './AuthPages.module.css'; + +export function RegisterPage() { + const [firstName, setFirstName] = useState(''); + const [lastName, setLastName] = useState(''); + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [confirmPassword, setConfirmPassword] = useState(''); + const [error, setError] = useState(''); + const [isLoading, setIsLoading] = useState(false); + + const { register } = useAuth(); + const navigate = useNavigate(); + + const handleSubmit = async (e: FormEvent) => { + e.preventDefault(); + setError(''); + + if (password !== confirmPassword) { + setError('Passwords do not match'); + return; + } + + if (password.length < 6) { + setError('Password must be at least 6 characters'); + return; + } + + setIsLoading(true); + + try { + await register({ + email, + password, + firstName, + lastName: lastName || undefined, + }); + navigate('/dashboard'); + } catch (err) { + const apiError = err as ApiError; + setError(apiError.message || 'Registration failed. Please try again.'); + } finally { + setIsLoading(false); + } + }; + + return ( +
+ + +

Create Account

+

Get started with PwrProgram

+
+ +
+ {error &&
{error}
} + +
+ setFirstName(e.target.value)} + placeholder="John" + required + /> + + setLastName(e.target.value)} + placeholder="Doe" + /> +
+ + setEmail(e.target.value)} + placeholder="you@example.com" + required + /> + + setPassword(e.target.value)} + placeholder="••••••••" + required + minLength={6} + /> + + setConfirmPassword(e.target.value)} + placeholder="••••••••" + required + /> + + +
+ +

+ Already have an account?{' '} + + Sign in + +

+
+
+
+ ); +} diff --git a/apps/web/src/pages/index.ts b/apps/web/src/pages/index.ts new file mode 100644 index 0000000..fa1060a --- /dev/null +++ b/apps/web/src/pages/index.ts @@ -0,0 +1,6 @@ +export { HomePage } from './HomePage'; +export { LoginPage } from './LoginPage'; +export { RegisterPage } from './RegisterPage'; +export { DashboardPage } from './DashboardPage'; +export { ProgramsPage } from './ProgramsPage'; +export { ProgramDetailPage } from './ProgramDetailPage'; diff --git a/apps/web/src/types/index.ts b/apps/web/src/types/index.ts new file mode 100644 index 0000000..6f4cc7c --- /dev/null +++ b/apps/web/src/types/index.ts @@ -0,0 +1,115 @@ +// Types based on API responses + +export interface User { + id: string; + firstName: string; + lastName?: string; + email: string; + _links?: { + self: string; + programs: string; + coach?: string; + }; +} + +export interface Program { + id: string; + userId: string; + name: string; + description?: string; + coachId?: string; + _links?: { + self: string; + cycles: string; + coach?: string; + user: string; + }; +} + +export interface Cycle { + id: string; + programId: string; + name: string; + description?: string; + order: number; + _links?: { + self: string; + program: string; + blocks: string; + }; +} + +export interface Block { + id: string; + cycleId: string; + name: string; + description?: string; + order: number; + _links?: { + self: string; + cycle: string; + sessions: string; + }; +} + +export interface Session { + id: string; + blockId: string; + name: string; + description?: string; + order: number; + _links?: { + self: string; + block: string; + exercises: string; + }; +} + +export interface Exercise { + id: string; + sessionId: string; + name: string; + notes?: string; + order: number; + _links?: { + self: string; + session: string; + sets: string; + }; +} + +export interface Set { + id: string; + exerciseId: string; + reps?: number; + weight?: number; + rpe?: number; + notes?: string; + order: number; + _links?: { + self: string; + exercise: string; + }; +} + +export interface PaginatedResponse { + data: T[]; + pagination: { + page: number; + limit: number; + total: number; + totalPages: number; + }; +} + +export interface ApiError { + message: string; + statusCode: number; + error?: string; +} + +export interface AuthState { + user: User | null; + isAuthenticated: boolean; + isLoading: boolean; +} diff --git a/apps/web/tsconfig.app.json b/apps/web/tsconfig.app.json new file mode 100644 index 0000000..0c521d5 --- /dev/null +++ b/apps/web/tsconfig.app.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2022", + "useDefineForClassFields": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "types": ["vite/client"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Path aliases */ + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + }, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"] +} diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json new file mode 100644 index 0000000..1ffef60 --- /dev/null +++ b/apps/web/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/apps/web/tsconfig.node.json b/apps/web/tsconfig.node.json new file mode 100644 index 0000000..8a67f62 --- /dev/null +++ b/apps/web/tsconfig.node.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2023", + "lib": ["ES2023"], + "module": "ESNext", + "types": ["node"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/apps/web/vite.config.ts b/apps/web/vite.config.ts new file mode 100644 index 0000000..bc702d0 --- /dev/null +++ b/apps/web/vite.config.ts @@ -0,0 +1,22 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' +import path from 'path' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react()], + resolve: { + alias: { + '@': path.resolve(__dirname, './src'), + }, + }, + server: { + port: 5173, + proxy: { + '/api': { + target: 'http://localhost:3000', + changeOrigin: true, + }, + }, + }, +}) diff --git a/package.json b/package.json index 079cea1..e038697 100644 --- a/package.json +++ b/package.json @@ -9,15 +9,23 @@ "eslint-plugin-import": "^2.29.0" }, "scripts": { - "dev:api": "pnpm --filter pwrprogram devstart", - "start:api": "pnpm --filter pwrprogram start", - + "dev:api": "pnpm --filter @pwrprogram/api dev", + "dev:web": "pnpm --filter @pwrprogram/web dev", + "start:api": "pnpm --filter @pwrprogram/api start", + "build:web": "pnpm --filter @pwrprogram/web build", "test": "pnpm -r --workspace-concurrency=1 test", "test:watch": "pnpm -r test:watch", "test:coverage": "pnpm -r --workspace-concurrency=1 test:coverage", - - "test:api": "pnpm --filter pwrprogram test", - "test:api:watch": "pnpm --filter pwrprogram test:watch", - "test:api:coverage": "pnpm --filter pwrprogram test:coverage" + "test:api": "pnpm --filter @pwrprogram/api test", + "test:api:watch": "pnpm --filter @pwrprogram/api test:watch", + "test:api:coverage": "pnpm --filter @pwrprogram/api test:coverage", + "lint:web": "pnpm --filter @pwrprogram/web lint" + }, + "pnpm": { + "onlyBuiltDependencies": [ + "bcrypt", + "sqlite3", + "unrs-resolver" + ] } -} \ No newline at end of file +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8c916a0..ea4dfa0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,16 +13,16 @@ importers: version: 8.57.1 '@typescript-eslint/eslint-plugin': specifier: ^7.18.0 - version: 7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.1)(typescript@5.8.3) + version: 7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) '@typescript-eslint/parser': specifier: ^7.18.0 - version: 7.18.0(eslint@8.57.1)(typescript@5.8.3) + version: 7.18.0(eslint@8.57.1)(typescript@5.9.3) eslint: specifier: ^8.57.0 version: 8.57.1 eslint-plugin-import: specifier: ^2.29.0 - version: 2.32.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.1) + version: 2.32.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1) apps/pwrprogram: dependencies: @@ -139,6 +139,58 @@ importers: specifier: 5.8.3 version: 5.8.3 + apps/web: + dependencies: + '@pwrprogram/shared': + specifier: workspace:* + version: link:../../packages/shared + react: + specifier: ^19.2.0 + version: 19.2.0 + react-dom: + specifier: ^19.2.0 + version: 19.2.0(react@19.2.0) + react-router-dom: + specifier: ^7.6.1 + version: 7.9.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + devDependencies: + '@eslint/js': + specifier: ^9.39.1 + version: 9.39.1 + '@types/node': + specifier: ^24.10.1 + version: 24.10.1 + '@types/react': + specifier: ^19.2.5 + version: 19.2.7 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.7) + '@vitejs/plugin-react': + specifier: ^5.1.1 + version: 5.1.1(vite@7.2.4(@types/node@24.10.1)(yaml@2.8.1)) + eslint: + specifier: ^9.39.1 + version: 9.39.1 + eslint-plugin-react-hooks: + specifier: ^7.0.1 + version: 7.0.1(eslint@9.39.1) + eslint-plugin-react-refresh: + specifier: ^0.4.24 + version: 0.4.24(eslint@9.39.1) + globals: + specifier: ^16.5.0 + version: 16.5.0 + typescript: + specifier: ~5.9.3 + version: 5.9.3 + typescript-eslint: + specifier: ^8.46.4 + version: 8.48.0(eslint@9.39.1)(typescript@5.9.3) + vite: + specifier: ^7.2.4 + version: 7.2.4(@types/node@24.10.1)(yaml@2.8.1) + packages/shared: dependencies: class-transformer: @@ -166,10 +218,18 @@ packages: resolution: {integrity: sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==} engines: {node: '>=6.9.0'} + '@babel/core@7.28.5': + resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==} + engines: {node: '>=6.9.0'} + '@babel/generator@7.28.0': resolution: {integrity: sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==} engines: {node: '>=6.9.0'} + '@babel/generator@7.28.5': + resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} + engines: {node: '>=6.9.0'} + '@babel/helper-compilation-targets@7.27.2': resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} engines: {node: '>=6.9.0'} @@ -188,6 +248,12 @@ packages: peerDependencies: '@babel/core': ^7.0.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-plugin-utils@7.27.1': resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} engines: {node: '>=6.9.0'} @@ -200,6 +266,10 @@ packages: resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.27.1': resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} @@ -208,11 +278,20 @@ packages: resolution: {integrity: sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw==} engines: {node: '>=6.9.0'} + '@babel/helpers@7.28.4': + resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} + engines: {node: '>=6.9.0'} + '@babel/parser@7.28.0': resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==} engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.28.5': + resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/plugin-syntax-async-generators@7.8.4': resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} peerDependencies: @@ -304,6 +383,18 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-react-jsx-self@7.27.1': + resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.27.1': + resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/template@7.27.2': resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} @@ -312,10 +403,18 @@ packages: resolution: {integrity: sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==} engines: {node: '>=6.9.0'} + '@babel/traverse@7.28.5': + resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} + engines: {node: '>=6.9.0'} + '@babel/types@7.28.2': resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==} engines: {node: '>=6.9.0'} + '@babel/types@7.28.5': + resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} + engines: {node: '>=6.9.0'} + '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} @@ -339,12 +438,174 @@ packages: '@emnapi/wasi-threads@1.0.4': resolution: {integrity: sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==} + '@esbuild/aix-ppc64@0.25.12': + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.12': + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.12': + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.12': + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.12': + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.12': + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.12': + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.12': + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.12': + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.12': + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.12': + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.12': + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.12': + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.12': + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.12': + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.12': + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.12': + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.12': + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.12': + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.12': + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.12': + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.12': + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.12': + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.12': + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.12': + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.12': + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} + 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/eslint-utils@4.9.0': + resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} + 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} @@ -353,14 +614,26 @@ packages: resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/config-array@0.21.1': + resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} + 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/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + 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/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/eslintrc@2.1.4': resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -377,14 +650,26 @@ packages: resolution: {integrity: sha512-5K1/mKhWaMfreBGJTwval43JJmkip0RmM+3+IuqupeSKNC/Th2Kc7ucaq5ovTSra/OOKB9c58CGSz3QMVbWt0A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/js@9.39.1': + resolution: {integrity: sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==} + 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/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + 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} + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@gar/promisify@1.1.3': resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==} @@ -514,6 +799,9 @@ packages: '@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'} @@ -565,6 +853,119 @@ packages: resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + '@rolldown/pluginutils@1.0.0-beta.47': + resolution: {integrity: sha512-8QagwMH3kNCuzD8EWL8R2YPW5e4OrHNSAHRFDdmFqEwEaD/KcNKjVoumo+gP2vW5eKB2UPbM6vTYiGZX0ixLnw==} + + '@rollup/rollup-android-arm-eabi@4.53.3': + resolution: {integrity: sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.53.3': + resolution: {integrity: sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.53.3': + resolution: {integrity: sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.53.3': + resolution: {integrity: sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.53.3': + resolution: {integrity: sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.53.3': + resolution: {integrity: sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.53.3': + resolution: {integrity: sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.53.3': + resolution: {integrity: sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.53.3': + resolution: {integrity: sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.53.3': + resolution: {integrity: sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.53.3': + resolution: {integrity: sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.53.3': + resolution: {integrity: sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.53.3': + resolution: {integrity: sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.53.3': + resolution: {integrity: sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.53.3': + resolution: {integrity: sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.53.3': + resolution: {integrity: sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.53.3': + resolution: {integrity: sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openharmony-arm64@4.53.3': + resolution: {integrity: sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.53.3': + resolution: {integrity: sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.53.3': + resolution: {integrity: sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.53.3': + resolution: {integrity: sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.53.3': + resolution: {integrity: sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==} + cpu: [x64] + os: [win32] + '@rtsao/scc@1.1.0': resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} @@ -674,6 +1075,9 @@ packages: '@types/mime@1.3.5': resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + '@types/node@24.10.1': + resolution: {integrity: sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==} + '@types/node@24.2.1': resolution: {integrity: sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==} @@ -683,6 +1087,14 @@ packages: '@types/range-parser@1.2.7': resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.7': + resolution: {integrity: sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==} + '@types/send@0.17.6': resolution: {integrity: sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==} @@ -724,6 +1136,14 @@ packages: typescript: optional: true + '@typescript-eslint/eslint-plugin@8.48.0': + resolution: {integrity: sha512-XxXP5tL1txl13YFtrECECQYeZjBZad4fyd3cFV4a19LkAY/bIp9fev3US4S5fDVV2JaYFiKAZ/GRTOLer+mbyQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.48.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/parser@7.18.0': resolution: {integrity: sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==} engines: {node: ^18.18.0 || >=20.0.0} @@ -734,10 +1154,33 @@ packages: typescript: optional: true + '@typescript-eslint/parser@8.48.0': + resolution: {integrity: sha512-jCzKdm/QK0Kg4V4IK/oMlRZlY+QOcdjv89U2NgKHZk1CYTj82/RVSx1mV/0gqCVMJ/DA+Zf/S4NBWNF8GQ+eqQ==} + 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.48.0': + resolution: {integrity: sha512-Ne4CTZyRh1BecBf84siv42wv5vQvVmgtk8AuiEffKTUo3DrBaGYZueJSxxBZ8fjk/N3DrgChH4TOdIOwOwiqqw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/scope-manager@7.18.0': resolution: {integrity: sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==} engines: {node: ^18.18.0 || >=20.0.0} + '@typescript-eslint/scope-manager@8.48.0': + resolution: {integrity: sha512-uGSSsbrtJrLduti0Q1Q9+BF1/iFKaxGoQwjWOIVNJv0o6omrdyR8ct37m4xIl5Zzpkp69Kkmvom7QFTtue89YQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.48.0': + resolution: {integrity: sha512-WNebjBdFdyu10sR1M4OXTt2OkMd5KWIL+LLfeH9KhgP+jzfDV/LI3eXzwJ1s9+Yc0Kzo2fQCdY/OpdusCMmh6w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/type-utils@7.18.0': resolution: {integrity: sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==} engines: {node: ^18.18.0 || >=20.0.0} @@ -748,10 +1191,21 @@ packages: typescript: optional: true + '@typescript-eslint/type-utils@8.48.0': + resolution: {integrity: sha512-zbeVaVqeXhhab6QNEKfK96Xyc7UQuoFWERhEnj3mLVnUWrQnv15cJNseUni7f3g557gm0e46LZ6IJ4NJVOgOpw==} + 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@7.18.0': resolution: {integrity: sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==} engines: {node: ^18.18.0 || >=20.0.0} + '@typescript-eslint/types@8.48.0': + resolution: {integrity: sha512-cQMcGQQH7kwKoVswD1xdOytxQR60MWKM1di26xSUtxehaDs/32Zpqsu5WJlXTtTTqyAVK8R7hvsUnIXRS+bjvA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/typescript-estree@7.18.0': resolution: {integrity: sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==} engines: {node: ^18.18.0 || >=20.0.0} @@ -761,16 +1215,33 @@ packages: typescript: optional: true + '@typescript-eslint/typescript-estree@8.48.0': + resolution: {integrity: sha512-ljHab1CSO4rGrQIAyizUS6UGHHCiAYhbfcIZ1zVJr5nMryxlXMVWS3duFPSKvSUbFPwkXMFk1k0EMIjub4sRRQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/utils@7.18.0': resolution: {integrity: sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: eslint: ^8.56.0 + '@typescript-eslint/utils@8.48.0': + resolution: {integrity: sha512-yTJO1XuGxCsSfIVt1+1UrLHtue8xz16V8apzPYI06W0HbEbEWHxHXgZaAgavIkoh+GeV6hKKd5jm0sS6OYxWXQ==} + 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@7.18.0': resolution: {integrity: sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==} engines: {node: ^18.18.0 || >=20.0.0} + '@typescript-eslint/visitor-keys@8.48.0': + resolution: {integrity: sha512-T0XJMaRPOH3+LBbAfzR2jalckP1MSG/L9eUtY0DEzUyVaXJ/t6zN0nR7co5kz0Jko/nkSYCBRkz1djvjajVTTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} @@ -869,6 +1340,12 @@ packages: cpu: [x64] os: [win32] + '@vitejs/plugin-react@5.1.1': + resolution: {integrity: sha512-WQfkSw0QbQ5aJ2CHYw23ZGkqnRwqKHD/KYsMeTkZzPT4Jcf0DcBxBtwMJxnu6E7oxw5+JC6ZAiePgh28uJ1HBA==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + abbrev@1.1.1: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} @@ -1252,6 +1729,10 @@ packages: resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} engines: {node: '>= 0.6'} + cookie@1.0.2: + resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} + engines: {node: '>=18'} + cookiejar@2.1.4: resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} @@ -1266,6 +1747,9 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + data-view-buffer@1.0.2: resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} engines: {node: '>= 0.4'} @@ -1457,6 +1941,11 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} + esbuild@0.25.12: + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -1506,6 +1995,17 @@ packages: '@typescript-eslint/parser': optional: true + eslint-plugin-react-hooks@7.0.1: + resolution: {integrity: sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==} + engines: {node: '>=18'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + + eslint-plugin-react-refresh@0.4.24: + resolution: {integrity: sha512-nLHIW7TEq3aLrEYWpVaJ1dRgFR+wLDPN8e8FpYAql/bMV2oBEfC37K0gLEGgv9fy66juNShSMV8OkTqzltcG/w==} + peerDependencies: + eslint: '>=8.40' + eslint-scope@7.2.2: resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -1538,6 +2038,16 @@ packages: jiti: optional: true + eslint@9.39.1: + resolution: {integrity: sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==} + 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} @@ -1623,6 +2133,15 @@ packages: fb-watchman@2.0.2: resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + fecha@4.2.3: resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} @@ -1776,6 +2295,10 @@ packages: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} + globals@16.5.0: + resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==} + engines: {node: '>=18'} + globalthis@1.0.4: resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} engines: {node: '>= 0.4'} @@ -1837,6 +2360,12 @@ packages: resolution: {integrity: sha512-jOiHyAZsmnr8LqoPGmCjYAaiuWwjAPLgY8ZX2XrmHawt99/u1y6RgrZMTeoPfpUbV96HOalYgz1qzkRbw54Pmg==} engines: {node: '>=18.0.0'} + hermes-estree@0.25.1: + resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} + + hermes-parser@0.25.1: + resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} + highlight.js@10.7.3: resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} @@ -1879,6 +2408,10 @@ packages: 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'} @@ -2433,6 +2966,11 @@ packages: mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + napi-build-utils@2.0.0: resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} @@ -2693,6 +3231,10 @@ packages: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + postgres-array@2.0.0: resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} engines: {node: '>=4'} @@ -2779,9 +3321,39 @@ packages: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true + react-dom@19.2.0: + resolution: {integrity: sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==} + peerDependencies: + react: ^19.2.0 + react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + react-refresh@0.18.0: + resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==} + engines: {node: '>=0.10.0'} + + react-router-dom@7.9.6: + resolution: {integrity: sha512-2MkC2XSXq6HjGcihnx1s0DBWQETI4mlis4Ux7YTLvP67xnGxCvq+BcCQSO81qQHVUTM1V53tl4iVVaY5sReCOA==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + + react-router@7.9.6: + resolution: {integrity: sha512-Y1tUp8clYRXpfPITyuifmSoE2vncSME18uVLgaqyxh9H35JWpIfzHo+9y3Fzh5odk/jxPW29IgLgzcdwxGqyNA==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + peerDependenciesMeta: + react-dom: + optional: true + + react@19.2.0: + resolution: {integrity: sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==} + engines: {node: '>=0.10.0'} + readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} @@ -2838,6 +3410,11 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true + rollup@4.53.3: + resolution: {integrity: sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + router@2.2.0: resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} engines: {node: '>= 18'} @@ -2867,6 +3444,9 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -2887,6 +3467,9 @@ packages: set-blocking@2.0.0: resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + set-cookie-parser@2.7.2: + resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} + set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} @@ -2964,6 +3547,10 @@ packages: resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==} engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + source-map-support@0.5.13: resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} @@ -3124,6 +3711,10 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + tmpl@1.0.5: resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} @@ -3153,6 +3744,12 @@ packages: peerDependencies: typescript: '>=4.2.0' + ts-api-utils@2.1.0: + resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + ts-jest@29.4.1: resolution: {integrity: sha512-SaeUtjfpg9Uqu8IbeDKtdaS0g8lS6FT6OzM3ezrDfErPJPHNDo/Ey+VFGP1bQIDfagYDLyRpd7O15XpG1Es2Uw==} engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} @@ -3301,11 +3898,23 @@ packages: typeorm-aurora-data-api-driver: optional: true + typescript-eslint@8.48.0: + resolution: {integrity: sha512-fcKOvQD9GUn3Xw63EgiDqhvWJ5jsyZUaekl3KVpGsDJnN46WJTe3jWxtQP9lMZm1LJNkFLlTaWAxK2vUQR+cqw==} + 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.8.3: resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} engines: {node: '>=14.17'} hasBin: true + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + uglify-js@3.19.3: resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} engines: {node: '>=0.8.0'} @@ -3325,6 +3934,9 @@ packages: undici-types@7.10.0: resolution: {integrity: sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==} + undici-types@7.16.0: + resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + unique-filename@1.1.1: resolution: {integrity: sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==} @@ -3373,6 +3985,46 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} + vite@7.2.4: + resolution: {integrity: sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w==} + 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 + walker@1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} @@ -3473,6 +4125,15 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + zod-validation-error@4.0.2: + resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + + zod@4.1.13: + resolution: {integrity: sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==} + snapshots: '@ampproject/remapping@2.3.0': @@ -3508,6 +4169,26 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/core@7.28.5': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) + '@babel/helpers': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.1(supports-color@5.5.0) + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + '@babel/generator@7.28.0': dependencies: '@babel/parser': 7.28.0 @@ -3516,6 +4197,14 @@ snapshots: '@jridgewell/trace-mapping': 0.3.30 jsesc: 3.1.0 + '@babel/generator@7.28.5': + dependencies: + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.30 + jsesc: 3.1.0 + '@babel/helper-compilation-targets@7.27.2': dependencies: '@babel/compat-data': 7.28.0 @@ -3542,12 +4231,23 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + '@babel/helper-plugin-utils@7.27.1': {} '@babel/helper-string-parser@7.27.1': {} '@babel/helper-validator-identifier@7.27.1': {} + '@babel/helper-validator-identifier@7.28.5': {} + '@babel/helper-validator-option@7.27.1': {} '@babel/helpers@7.28.2': @@ -3555,10 +4255,19 @@ snapshots: '@babel/template': 7.27.2 '@babel/types': 7.28.2 + '@babel/helpers@7.28.4': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 + '@babel/parser@7.28.0': dependencies: '@babel/types': 7.28.2 + '@babel/parser@7.28.5': + dependencies: + '@babel/types': 7.28.5 + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 @@ -3644,6 +4353,16 @@ snapshots: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 @@ -3662,11 +4381,28 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/traverse@7.28.5': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 + debug: 4.4.1(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + '@babel/types@7.28.2': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 + '@babel/types@7.28.5': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@bcoe/v8-coverage@0.2.3': {} '@colors/colors@1.6.0': {} @@ -3697,6 +4433,84 @@ snapshots: tslib: 2.8.1 optional: true + '@esbuild/aix-ppc64@0.25.12': + optional: true + + '@esbuild/android-arm64@0.25.12': + optional: true + + '@esbuild/android-arm@0.25.12': + optional: true + + '@esbuild/android-x64@0.25.12': + optional: true + + '@esbuild/darwin-arm64@0.25.12': + optional: true + + '@esbuild/darwin-x64@0.25.12': + optional: true + + '@esbuild/freebsd-arm64@0.25.12': + optional: true + + '@esbuild/freebsd-x64@0.25.12': + optional: true + + '@esbuild/linux-arm64@0.25.12': + optional: true + + '@esbuild/linux-arm@0.25.12': + optional: true + + '@esbuild/linux-ia32@0.25.12': + optional: true + + '@esbuild/linux-loong64@0.25.12': + optional: true + + '@esbuild/linux-mips64el@0.25.12': + optional: true + + '@esbuild/linux-ppc64@0.25.12': + optional: true + + '@esbuild/linux-riscv64@0.25.12': + optional: true + + '@esbuild/linux-s390x@0.25.12': + optional: true + + '@esbuild/linux-x64@0.25.12': + optional: true + + '@esbuild/netbsd-arm64@0.25.12': + optional: true + + '@esbuild/netbsd-x64@0.25.12': + optional: true + + '@esbuild/openbsd-arm64@0.25.12': + optional: true + + '@esbuild/openbsd-x64@0.25.12': + optional: true + + '@esbuild/openharmony-arm64@0.25.12': + optional: true + + '@esbuild/sunos-x64@0.25.12': + optional: true + + '@esbuild/win32-arm64@0.25.12': + optional: true + + '@esbuild/win32-ia32@0.25.12': + optional: true + + '@esbuild/win32-x64@0.25.12': + optional: true + '@eslint-community/eslint-utils@4.7.0(eslint@8.57.1)': dependencies: eslint: 8.57.1 @@ -3707,6 +4521,16 @@ snapshots: eslint: 9.33.0 eslint-visitor-keys: 3.4.3 + '@eslint-community/eslint-utils@4.7.0(eslint@9.39.1)': + dependencies: + eslint: 9.39.1 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/eslint-utils@4.9.0(eslint@9.39.1)': + dependencies: + eslint: 9.39.1 + eslint-visitor-keys: 3.4.3 + '@eslint-community/regexpp@4.12.1': {} '@eslint/config-array@0.21.0': @@ -3717,12 +4541,28 @@ snapshots: transitivePeerDependencies: - supports-color + '@eslint/config-array@0.21.1': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.1(supports-color@5.5.0) + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + '@eslint/config-helpers@0.3.1': {} + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + '@eslint/core@0.15.2': dependencies: '@types/json-schema': 7.0.15 + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + '@eslint/eslintrc@2.1.4': dependencies: ajv: 6.12.6 @@ -3755,13 +4595,22 @@ snapshots: '@eslint/js@9.33.0': {} + '@eslint/js@9.39.1': {} + '@eslint/object-schema@2.1.6': {} + '@eslint/object-schema@2.1.7': {} + '@eslint/plugin-kit@0.3.5': dependencies: '@eslint/core': 0.15.2 levn: 0.4.1 + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + '@gar/promisify@1.1.3': optional: true @@ -3810,7 +4659,7 @@ snapshots: '@jest/console@30.0.5': dependencies: '@jest/types': 30.0.5 - '@types/node': 24.2.1 + '@types/node': 24.10.1 chalk: 4.1.2 jest-message-util: 30.0.5 jest-util: 30.0.5 @@ -3824,14 +4673,14 @@ snapshots: '@jest/test-result': 30.0.5 '@jest/transform': 30.0.5 '@jest/types': 30.0.5 - '@types/node': 24.2.1 + '@types/node': 24.10.1 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 4.3.0 exit-x: 0.2.2 graceful-fs: 4.2.11 jest-changed-files: 30.0.5 - jest-config: 30.0.5(@types/node@24.2.1)(ts-node@10.9.2(@types/node@24.2.1)(typescript@5.8.3)) + jest-config: 30.0.5(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.2.1)(typescript@5.8.3)) jest-haste-map: 30.0.5 jest-message-util: 30.0.5 jest-regex-util: 30.0.1 @@ -3858,7 +4707,7 @@ snapshots: dependencies: '@jest/fake-timers': 30.0.5 '@jest/types': 30.0.5 - '@types/node': 24.2.1 + '@types/node': 24.10.1 jest-mock: 30.0.5 '@jest/expect-utils@30.0.5': @@ -3876,7 +4725,7 @@ snapshots: dependencies: '@jest/types': 30.0.5 '@sinonjs/fake-timers': 13.0.5 - '@types/node': 24.2.1 + '@types/node': 24.10.1 jest-message-util: 30.0.5 jest-mock: 30.0.5 jest-util: 30.0.5 @@ -3894,7 +4743,7 @@ snapshots: '@jest/pattern@30.0.1': dependencies: - '@types/node': 24.2.1 + '@types/node': 24.10.1 jest-regex-util: 30.0.1 '@jest/reporters@30.0.5': @@ -3905,7 +4754,7 @@ snapshots: '@jest/transform': 30.0.5 '@jest/types': 30.0.5 '@jridgewell/trace-mapping': 0.3.30 - '@types/node': 24.2.1 + '@types/node': 24.10.1 chalk: 4.1.2 collect-v8-coverage: 1.0.2 exit-x: 0.2.2 @@ -3982,7 +4831,7 @@ snapshots: '@jest/schemas': 30.0.5 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 24.2.1 + '@types/node': 24.10.1 '@types/yargs': 17.0.33 chalk: 4.1.2 @@ -3991,6 +4840,11 @@ snapshots: '@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': {} @@ -4000,52 +4854,120 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/trace-mapping@0.3.9': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping@0.3.9': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@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 + + '@noble/hashes@1.8.0': {} + + '@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 + + '@npmcli/fs@1.1.1': + dependencies: + '@gar/promisify': 1.1.3 + semver: 7.7.2 + optional: true + + '@npmcli/move-file@1.1.2': + dependencies: + mkdirp: 1.0.4 + rimraf: 3.0.2 + optional: true + + '@paralleldrive/cuid2@2.2.2': + dependencies: + '@noble/hashes': 1.8.0 + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@pkgr/core@0.2.9': {} + + '@rolldown/pluginutils@1.0.0-beta.47': {} + + '@rollup/rollup-android-arm-eabi@4.53.3': + optional: true + + '@rollup/rollup-android-arm64@4.53.3': + optional: true + + '@rollup/rollup-darwin-arm64@4.53.3': + optional: true + + '@rollup/rollup-darwin-x64@4.53.3': + optional: true + + '@rollup/rollup-freebsd-arm64@4.53.3': + optional: true + + '@rollup/rollup-freebsd-x64@4.53.3': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.53.3': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.53.3': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.53.3': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.53.3': + optional: true - '@napi-rs/wasm-runtime@0.2.12': - dependencies: - '@emnapi/core': 1.4.5 - '@emnapi/runtime': 1.4.5 - '@tybys/wasm-util': 0.10.0 + '@rollup/rollup-linux-ppc64-gnu@4.53.3': optional: true - '@noble/hashes@1.8.0': {} + '@rollup/rollup-linux-riscv64-gnu@4.53.3': + optional: true - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 + '@rollup/rollup-linux-riscv64-musl@4.53.3': + optional: true - '@nodelib/fs.stat@2.0.5': {} + '@rollup/rollup-linux-s390x-gnu@4.53.3': + optional: true - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.19.1 + '@rollup/rollup-linux-x64-gnu@4.53.3': + optional: true - '@npmcli/fs@1.1.1': - dependencies: - '@gar/promisify': 1.1.3 - semver: 7.7.2 + '@rollup/rollup-linux-x64-musl@4.53.3': optional: true - '@npmcli/move-file@1.1.2': - dependencies: - mkdirp: 1.0.4 - rimraf: 3.0.2 + '@rollup/rollup-openharmony-arm64@4.53.3': optional: true - '@paralleldrive/cuid2@2.2.2': - dependencies: - '@noble/hashes': 1.8.0 + '@rollup/rollup-win32-arm64-msvc@4.53.3': + optional: true - '@pkgjs/parseargs@0.11.0': + '@rollup/rollup-win32-ia32-msvc@4.53.3': optional: true - '@pkgr/core@0.2.9': {} + '@rollup/rollup-win32-x64-gnu@4.53.3': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.53.3': + optional: true '@rtsao/scc@1.1.0': {} @@ -4112,11 +5034,11 @@ snapshots: '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 - '@types/node': 24.2.1 + '@types/node': 24.10.1 '@types/connect@3.4.38': dependencies: - '@types/node': 24.2.1 + '@types/node': 24.10.1 '@types/cookiejar@2.1.5': {} @@ -4130,7 +5052,7 @@ snapshots: '@types/express-serve-static-core@5.1.0': dependencies: - '@types/node': 24.2.1 + '@types/node': 24.10.1 '@types/qs': 6.14.0 '@types/range-parser': 1.2.7 '@types/send': 1.2.1 @@ -4170,6 +5092,10 @@ snapshots: '@types/mime@1.3.5': {} + '@types/node@24.10.1': + dependencies: + undici-types: 7.16.0 + '@types/node@24.2.1': dependencies: undici-types: 7.10.0 @@ -4178,19 +5104,27 @@ snapshots: '@types/range-parser@1.2.7': {} + '@types/react-dom@19.2.3(@types/react@19.2.7)': + dependencies: + '@types/react': 19.2.7 + + '@types/react@19.2.7': + dependencies: + csstype: 3.2.3 + '@types/send@0.17.6': dependencies: '@types/mime': 1.3.5 - '@types/node': 24.2.1 + '@types/node': 24.10.1 '@types/send@1.2.1': dependencies: - '@types/node': 24.2.1 + '@types/node': 24.10.1 '@types/serve-static@1.15.10': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 24.2.1 + '@types/node': 24.10.1 '@types/send': 0.17.6 '@types/stack-utils@2.0.3': {} @@ -4199,7 +5133,7 @@ snapshots: dependencies: '@types/cookiejar': 2.1.5 '@types/methods': 1.1.4 - '@types/node': 24.2.1 + '@types/node': 24.10.1 form-data: 4.0.4 '@types/supertest@6.0.3': @@ -4217,21 +5151,21 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.1)(typescript@5.8.3)': + '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 7.18.0(eslint@8.57.1)(typescript@5.8.3) + '@typescript-eslint/parser': 7.18.0(eslint@8.57.1)(typescript@5.9.3) '@typescript-eslint/scope-manager': 7.18.0 - '@typescript-eslint/type-utils': 7.18.0(eslint@8.57.1)(typescript@5.8.3) - '@typescript-eslint/utils': 7.18.0(eslint@8.57.1)(typescript@5.8.3) + '@typescript-eslint/type-utils': 7.18.0(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/utils': 7.18.0(eslint@8.57.1)(typescript@5.9.3) '@typescript-eslint/visitor-keys': 7.18.0 eslint: 8.57.1 graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 - ts-api-utils: 1.4.3(typescript@5.8.3) + ts-api-utils: 1.4.3(typescript@5.9.3) optionalDependencies: - typescript: 5.8.3 + typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -4253,16 +5187,33 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.8.3)': + '@typescript-eslint/eslint-plugin@8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 8.48.0(eslint@9.39.1)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.48.0 + '@typescript-eslint/type-utils': 8.48.0(eslint@9.39.1)(typescript@5.9.3) + '@typescript-eslint/utils': 8.48.0(eslint@9.39.1)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.48.0 + eslint: 9.39.1 + graphemer: 1.4.0 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.9.3)': dependencies: '@typescript-eslint/scope-manager': 7.18.0 '@typescript-eslint/types': 7.18.0 - '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.8.3) + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.9.3) '@typescript-eslint/visitor-keys': 7.18.0 debug: 4.4.1(supports-color@5.5.0) eslint: 8.57.1 optionalDependencies: - typescript: 5.8.3 + typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -4279,20 +5230,50 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/parser@8.48.0(eslint@9.39.1)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.48.0 + '@typescript-eslint/types': 8.48.0 + '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.48.0 + debug: 4.4.1(supports-color@5.5.0) + eslint: 9.39.1 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.48.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.48.0(typescript@5.9.3) + '@typescript-eslint/types': 8.48.0 + debug: 4.4.1(supports-color@5.5.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/scope-manager@7.18.0': dependencies: '@typescript-eslint/types': 7.18.0 '@typescript-eslint/visitor-keys': 7.18.0 - '@typescript-eslint/type-utils@7.18.0(eslint@8.57.1)(typescript@5.8.3)': + '@typescript-eslint/scope-manager@8.48.0': dependencies: - '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.8.3) - '@typescript-eslint/utils': 7.18.0(eslint@8.57.1)(typescript@5.8.3) + '@typescript-eslint/types': 8.48.0 + '@typescript-eslint/visitor-keys': 8.48.0 + + '@typescript-eslint/tsconfig-utils@8.48.0(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@7.18.0(eslint@8.57.1)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.9.3) + '@typescript-eslint/utils': 7.18.0(eslint@8.57.1)(typescript@5.9.3) debug: 4.4.1(supports-color@5.5.0) eslint: 8.57.1 - ts-api-utils: 1.4.3(typescript@5.8.3) + ts-api-utils: 1.4.3(typescript@5.9.3) optionalDependencies: - typescript: 5.8.3 + typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -4308,8 +5289,22 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/type-utils@8.48.0(eslint@9.39.1)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.48.0 + '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.48.0(eslint@9.39.1)(typescript@5.9.3) + debug: 4.4.1(supports-color@5.5.0) + eslint: 9.39.1 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/types@7.18.0': {} + '@typescript-eslint/types@8.48.0': {} + '@typescript-eslint/typescript-estree@7.18.0(typescript@5.8.3)': dependencies: '@typescript-eslint/types': 7.18.0 @@ -4325,12 +5320,42 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@7.18.0(eslint@8.57.1)(typescript@5.8.3)': + '@typescript-eslint/typescript-estree@7.18.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/visitor-keys': 7.18.0 + debug: 4.4.1(supports-color@5.5.0) + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.2 + ts-api-utils: 1.4.3(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/typescript-estree@8.48.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.48.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.48.0(typescript@5.9.3) + '@typescript-eslint/types': 8.48.0 + '@typescript-eslint/visitor-keys': 8.48.0 + debug: 4.4.1(supports-color@5.5.0) + minimatch: 9.0.5 + semver: 7.7.2 + tinyglobby: 0.2.15 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@7.18.0(eslint@8.57.1)(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.7.0(eslint@8.57.1) '@typescript-eslint/scope-manager': 7.18.0 '@typescript-eslint/types': 7.18.0 - '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.8.3) + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.9.3) eslint: 8.57.1 transitivePeerDependencies: - supports-color @@ -4347,11 +5372,27 @@ snapshots: - supports-color - typescript + '@typescript-eslint/utils@8.48.0(eslint@9.39.1)(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.39.1) + '@typescript-eslint/scope-manager': 8.48.0 + '@typescript-eslint/types': 8.48.0 + '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) + eslint: 9.39.1 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/visitor-keys@7.18.0': dependencies: '@typescript-eslint/types': 7.18.0 eslint-visitor-keys: 3.4.3 + '@typescript-eslint/visitor-keys@8.48.0': + dependencies: + '@typescript-eslint/types': 8.48.0 + eslint-visitor-keys: 4.2.1 + '@ungap/structured-clone@1.3.0': {} '@unrs/resolver-binding-android-arm-eabi@1.11.1': @@ -4413,6 +5454,18 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.11.1': optional: true + '@vitejs/plugin-react@5.1.1(vite@7.2.4(@types/node@24.10.1)(yaml@2.8.1))': + dependencies: + '@babel/core': 7.28.5 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.5) + '@rolldown/pluginutils': 1.0.0-beta.47 + '@types/babel__core': 7.20.5 + react-refresh: 0.18.0 + vite: 7.2.4(@types/node@24.10.1)(yaml@2.8.1) + transitivePeerDependencies: + - supports-color + abbrev@1.1.1: optional: true @@ -4881,6 +5934,8 @@ snapshots: cookie@0.7.2: {} + cookie@1.0.2: {} + cookiejar@2.1.4: {} cors@2.8.5: @@ -4896,6 +5951,8 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + csstype@3.2.3: {} + data-view-buffer@1.0.2: dependencies: call-bound: 1.0.4 @@ -5111,6 +6168,35 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 + esbuild@0.25.12: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.12 + '@esbuild/android-arm': 0.25.12 + '@esbuild/android-arm64': 0.25.12 + '@esbuild/android-x64': 0.25.12 + '@esbuild/darwin-arm64': 0.25.12 + '@esbuild/darwin-x64': 0.25.12 + '@esbuild/freebsd-arm64': 0.25.12 + '@esbuild/freebsd-x64': 0.25.12 + '@esbuild/linux-arm': 0.25.12 + '@esbuild/linux-arm64': 0.25.12 + '@esbuild/linux-ia32': 0.25.12 + '@esbuild/linux-loong64': 0.25.12 + '@esbuild/linux-mips64el': 0.25.12 + '@esbuild/linux-ppc64': 0.25.12 + '@esbuild/linux-riscv64': 0.25.12 + '@esbuild/linux-s390x': 0.25.12 + '@esbuild/linux-x64': 0.25.12 + '@esbuild/netbsd-arm64': 0.25.12 + '@esbuild/netbsd-x64': 0.25.12 + '@esbuild/openbsd-arm64': 0.25.12 + '@esbuild/openbsd-x64': 0.25.12 + '@esbuild/openharmony-arm64': 0.25.12 + '@esbuild/sunos-x64': 0.25.12 + '@esbuild/win32-arm64': 0.25.12 + '@esbuild/win32-ia32': 0.25.12 + '@esbuild/win32-x64': 0.25.12 + escalade@3.2.0: {} escape-html@1.0.3: {} @@ -5127,11 +6213,11 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1): + eslint-module-utils@2.12.1(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 7.18.0(eslint@8.57.1)(typescript@5.8.3) + '@typescript-eslint/parser': 7.18.0(eslint@8.57.1)(typescript@5.9.3) eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: @@ -5147,7 +6233,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.1): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -5158,7 +6244,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -5170,7 +6256,7 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 7.18.0(eslint@8.57.1)(typescript@5.8.3) + '@typescript-eslint/parser': 7.18.0(eslint@8.57.1)(typescript@5.9.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -5205,6 +6291,21 @@ snapshots: - eslint-import-resolver-webpack - supports-color + eslint-plugin-react-hooks@7.0.1(eslint@9.39.1): + dependencies: + '@babel/core': 7.28.0 + '@babel/parser': 7.28.0 + eslint: 9.39.1 + hermes-parser: 0.25.1 + zod: 4.1.13 + zod-validation-error: 4.0.2(zod@4.1.13) + transitivePeerDependencies: + - supports-color + + eslint-plugin-react-refresh@0.4.24(eslint@9.39.1): + dependencies: + eslint: 9.39.1 + eslint-scope@7.2.2: dependencies: esrecurse: 4.3.0 @@ -5302,6 +6403,45 @@ snapshots: transitivePeerDependencies: - supports-color + eslint@9.39.1: + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1) + '@eslint-community/regexpp': 4.12.1 + '@eslint/config-array': 0.21.1 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.1 + '@eslint/js': 9.39.1 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.6 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.1(supports-color@5.5.0) + 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 + transitivePeerDependencies: + - supports-color + espree@10.4.0: dependencies: acorn: 8.15.0 @@ -5429,6 +6569,10 @@ snapshots: dependencies: bser: 2.1.1 + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + fecha@4.2.3: {} file-entry-cache@6.0.1: @@ -5610,6 +6754,8 @@ snapshots: globals@14.0.0: {} + globals@16.5.0: {} + globalthis@1.0.4: dependencies: define-properties: 1.2.1 @@ -5668,6 +6814,12 @@ snapshots: helmet@8.1.0: {} + hermes-estree@0.25.1: {} + + hermes-parser@0.25.1: + dependencies: + hermes-estree: 0.25.1 + highlight.js@10.7.3: {} html-escaper@2.0.2: {} @@ -5717,6 +6869,8 @@ snapshots: ignore@5.3.2: {} + ignore@7.0.5: {} + import-fresh@3.3.1: dependencies: parent-module: 1.0.1 @@ -5933,7 +7087,7 @@ snapshots: '@jest/expect': 30.0.5 '@jest/test-result': 30.0.5 '@jest/types': 30.0.5 - '@types/node': 24.2.1 + '@types/node': 24.10.1 chalk: 4.1.2 co: 4.6.0 dedent: 1.6.0 @@ -5972,6 +7126,39 @@ snapshots: - supports-color - ts-node + jest-config@30.0.5(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.2.1)(typescript@5.8.3)): + dependencies: + '@babel/core': 7.28.0 + '@jest/get-type': 30.0.1 + '@jest/pattern': 30.0.1 + '@jest/test-sequencer': 30.0.5 + '@jest/types': 30.0.5 + babel-jest: 30.0.5(@babel/core@7.28.0) + chalk: 4.1.2 + ci-info: 4.3.0 + deepmerge: 4.3.1 + glob: 10.4.5 + graceful-fs: 4.2.11 + jest-circus: 30.0.5 + jest-docblock: 30.0.1 + jest-environment-node: 30.0.5 + jest-regex-util: 30.0.1 + jest-resolve: 30.0.5 + jest-runner: 30.0.5 + jest-util: 30.0.5 + jest-validate: 30.0.5 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 30.0.5 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 24.10.1 + ts-node: 10.9.2(@types/node@24.2.1)(typescript@5.8.3) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + jest-config@30.0.5(@types/node@24.2.1)(ts-node@10.9.2(@types/node@24.2.1)(typescript@5.8.3)): dependencies: '@babel/core': 7.28.0 @@ -6029,7 +7216,7 @@ snapshots: '@jest/environment': 30.0.5 '@jest/fake-timers': 30.0.5 '@jest/types': 30.0.5 - '@types/node': 24.2.1 + '@types/node': 24.10.1 jest-mock: 30.0.5 jest-util: 30.0.5 jest-validate: 30.0.5 @@ -6037,7 +7224,7 @@ snapshots: jest-haste-map@30.0.5: dependencies: '@jest/types': 30.0.5 - '@types/node': 24.2.1 + '@types/node': 24.10.1 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -6076,7 +7263,7 @@ snapshots: jest-mock@30.0.5: dependencies: '@jest/types': 30.0.5 - '@types/node': 24.2.1 + '@types/node': 24.10.1 jest-util: 30.0.5 jest-pnp-resolver@1.2.3(jest-resolve@30.0.5): @@ -6110,7 +7297,7 @@ snapshots: '@jest/test-result': 30.0.5 '@jest/transform': 30.0.5 '@jest/types': 30.0.5 - '@types/node': 24.2.1 + '@types/node': 24.10.1 chalk: 4.1.2 emittery: 0.13.1 exit-x: 0.2.2 @@ -6139,7 +7326,7 @@ snapshots: '@jest/test-result': 30.0.5 '@jest/transform': 30.0.5 '@jest/types': 30.0.5 - '@types/node': 24.2.1 + '@types/node': 24.10.1 chalk: 4.1.2 cjs-module-lexer: 2.1.0 collect-v8-coverage: 1.0.2 @@ -6186,7 +7373,7 @@ snapshots: jest-util@30.0.5: dependencies: '@jest/types': 30.0.5 - '@types/node': 24.2.1 + '@types/node': 24.10.1 chalk: 4.1.2 ci-info: 4.3.0 graceful-fs: 4.2.11 @@ -6205,7 +7392,7 @@ snapshots: dependencies: '@jest/test-result': 30.0.5 '@jest/types': 30.0.5 - '@types/node': 24.2.1 + '@types/node': 24.10.1 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -6214,7 +7401,7 @@ snapshots: jest-worker@30.0.5: dependencies: - '@types/node': 24.2.1 + '@types/node': 24.10.1 '@ungap/structured-clone': 1.3.0 jest-util: 30.0.5 merge-stream: 2.0.0 @@ -6447,6 +7634,8 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 + nanoid@3.3.11: {} + napi-build-utils@2.0.0: {} napi-postinstall@0.3.3: {} @@ -6707,6 +7896,12 @@ snapshots: possible-typed-array-names@1.1.0: {} + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + postgres-array@2.0.0: {} postgres-bytea@1.0.0: {} @@ -6791,8 +7986,31 @@ snapshots: minimist: 1.2.8 strip-json-comments: 2.0.1 + react-dom@19.2.0(react@19.2.0): + dependencies: + react: 19.2.0 + scheduler: 0.27.0 + react-is@18.3.1: {} + react-refresh@0.18.0: {} + + react-router-dom@7.9.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + dependencies: + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + react-router: 7.9.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + + react-router@7.9.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + dependencies: + cookie: 1.0.2 + react: 19.2.0 + set-cookie-parser: 2.7.2 + optionalDependencies: + react-dom: 19.2.0(react@19.2.0) + + react@19.2.0: {} + readable-stream@3.6.2: dependencies: inherits: 2.0.4 @@ -6852,6 +8070,34 @@ snapshots: dependencies: glob: 7.2.3 + rollup@4.53.3: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.53.3 + '@rollup/rollup-android-arm64': 4.53.3 + '@rollup/rollup-darwin-arm64': 4.53.3 + '@rollup/rollup-darwin-x64': 4.53.3 + '@rollup/rollup-freebsd-arm64': 4.53.3 + '@rollup/rollup-freebsd-x64': 4.53.3 + '@rollup/rollup-linux-arm-gnueabihf': 4.53.3 + '@rollup/rollup-linux-arm-musleabihf': 4.53.3 + '@rollup/rollup-linux-arm64-gnu': 4.53.3 + '@rollup/rollup-linux-arm64-musl': 4.53.3 + '@rollup/rollup-linux-loong64-gnu': 4.53.3 + '@rollup/rollup-linux-ppc64-gnu': 4.53.3 + '@rollup/rollup-linux-riscv64-gnu': 4.53.3 + '@rollup/rollup-linux-riscv64-musl': 4.53.3 + '@rollup/rollup-linux-s390x-gnu': 4.53.3 + '@rollup/rollup-linux-x64-gnu': 4.53.3 + '@rollup/rollup-linux-x64-musl': 4.53.3 + '@rollup/rollup-openharmony-arm64': 4.53.3 + '@rollup/rollup-win32-arm64-msvc': 4.53.3 + '@rollup/rollup-win32-ia32-msvc': 4.53.3 + '@rollup/rollup-win32-x64-gnu': 4.53.3 + '@rollup/rollup-win32-x64-msvc': 4.53.3 + fsevents: 2.3.3 + router@2.2.0: dependencies: debug: 4.4.1(supports-color@5.5.0) @@ -6891,6 +8137,8 @@ snapshots: safer-buffer@2.1.2: {} + scheduler@0.27.0: {} + semver@6.3.1: {} semver@7.7.2: {} @@ -6923,6 +8171,8 @@ snapshots: set-blocking@2.0.0: optional: true + set-cookie-parser@2.7.2: {} + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 @@ -7023,6 +8273,8 @@ snapshots: smart-buffer: 4.2.0 optional: true + source-map-js@1.2.1: {} + source-map-support@0.5.13: dependencies: buffer-from: 1.1.2 @@ -7218,6 +8470,11 @@ snapshots: dependencies: any-promise: 1.3.0 + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + tmpl@1.0.5: {} to-buffer@1.2.1: @@ -7240,6 +8497,14 @@ snapshots: dependencies: typescript: 5.8.3 + ts-api-utils@1.4.3(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + ts-api-utils@2.1.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + ts-jest@29.4.1(@babel/core@7.28.0)(@jest/transform@30.0.5)(@jest/types@30.0.5)(babel-jest@30.0.5(@babel/core@7.28.0))(jest-util@30.0.5)(jest@30.0.5(@types/node@24.2.1)(ts-node@10.9.2(@types/node@24.2.1)(typescript@5.8.3)))(typescript@5.8.3): dependencies: bs-logger: 0.2.6 @@ -7366,8 +8631,21 @@ snapshots: transitivePeerDependencies: - supports-color + typescript-eslint@8.48.0(eslint@9.39.1)(typescript@5.9.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3) + '@typescript-eslint/parser': 8.48.0(eslint@9.39.1)(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.48.0(eslint@9.39.1)(typescript@5.9.3) + eslint: 9.39.1 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + typescript@5.8.3: {} + typescript@5.9.3: {} + uglify-js@3.19.3: optional: true @@ -7386,6 +8664,8 @@ snapshots: undici-types@7.10.0: {} + undici-types@7.16.0: {} + unique-filename@1.1.1: dependencies: unique-slug: 2.0.2 @@ -7450,6 +8730,19 @@ snapshots: vary@1.1.2: {} + vite@7.2.4(@types/node@24.10.1)(yaml@2.8.1): + dependencies: + esbuild: 0.25.12 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.53.3 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 24.10.1 + fsevents: 2.3.3 + yaml: 2.8.1 + walker@1.0.8: dependencies: makeerror: 1.0.12 @@ -7584,3 +8877,9 @@ snapshots: yn@3.1.1: {} yocto-queue@0.1.0: {} + + zod-validation-error@4.0.2(zod@4.1.13): + dependencies: + zod: 4.1.13 + + zod@4.1.13: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index f3d8954..1dfbc20 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,3 +1,9 @@ -packages: - - 'packages/*' - - 'apps/*' +packages: + - packages/* + - apps/* + +ignoredBuiltDependencies: + - '@scarf/scarf' + - bcrypt + - sqlite3 + - unrs-resolver