diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b682f4f..39b3e76 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,30 +1,163 @@ -name: Rust CI +name: Full Stack CI on: push: branches: [ "main" ] + paths-ignore: + - '**.md' + - '.gitignore' + - 'LICENSE' pull_request: branches: [ "main" ] + paths-ignore: + - '**.md' + - '.gitignore' + - 'LICENSE' workflow_dispatch: env: CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 jobs: - build: - runs-on: macos-latest + # Check if we should skip CI + changes: + name: Detect Changes + runs-on: ubuntu-latest + outputs: + backend: ${{ steps.changes.outputs.backend }} + frontend: ${{ steps.changes.outputs.frontend }} + general: ${{ steps.changes.outputs.general }} + gha: ${{ steps.changes.outputs.gha }} + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Check for changes + uses: dorny/paths-filter@v3 + id: changes + with: + # Define filters for different parts of the project to conditionally run jobs + filters: | + backend: + - 'src-tauri/**' + frontend: + - 'src/**' + - 'public/**' + - 'package.json' + - 'pnpm-lock.yaml' + - 'index.html' + - 'vite.config.*' + - 'tsconfig.json' + - 'tsconfig.node.json' + - 'postcss.config.js' + - 'tailwind.config.*' + - 'components.json' + - 'app-icon.png' + - 'pnpm-lock.yaml' + general: + - '**.md' + - '.gitignore' + - 'LICENSE' + - 'docs/**' + gha: + - '.github/**' + + + # Combined Full Stack Test and Build + full-stack-test-build: + name: Full Stack Test & Build + runs-on: ${{ matrix.os }} + needs: changes + if: needs.changes.outputs.general == 'false' || (needs.changes.outputs.backend == 'true' || needs.changes.outputs.frontend == 'true' || needs.changes.outputs.gha == 'true') + + strategy: + fail-fast: false + matrix: + os: [ macos-latest ] steps: - - name: Fetch Repository + - name: Checkout Repository uses: actions/checkout@v4 - - name: Install stable Rust toolchain + # Setup all required tools + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 9 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'pnpm' + + - name: Install Rust Toolchain uses: actions-rust-lang/setup-rust-toolchain@v1 with: toolchain: stable - target: x86_64-unknown-linux-gnu + cache: true + + # Enhanced caching strategy + - name: Get pnpm store directory + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV - - name: Run tests + - name: Cache All Dependencies + uses: actions/cache@v4 + with: + path: | + ${{ env.STORE_PATH }} + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + src-tauri/target/ + node_modules + dist/ + key: ${{ runner.os }}-fullstack-${{ hashFiles('**/Cargo.lock', '**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-fullstack- + ${{ runner.os }}- + + # Install all dependencies + - name: Install Frontend Dependencies + run: pnpm install --frozen-lockfile + + # Backend tests and checks (if backend changed) + - name: Run Rust Unit Tests + if: needs.changes.outputs.backend == 'true' + run: | + cd src-tauri + cargo test --verbose --all-features --lib + + # Frontend tests and checks (if frontend changed) + - name: Run Frontend Unit Tests + if: needs.changes.outputs.frontend == 'true' + run: pnpm test + env: + NODE_ENV: test + + # Build frontend first (required for Tauri) + - name: Build Frontend + run: pnpm run build + + # Run Tauri integration tests + - name: Run Tauri Integration Tests + run: | + cd src-tauri + cargo test + + # Start backend for integration tests (if needed) + - name: Start Backend for Integration Tests + if: needs.changes.outputs.frontend == 'true' && needs.changes.outputs.backend == 'true' run: | cd src-tauri - cargo test --verbose \ No newline at end of file + cargo check --release + + # Build Tauri application (includes both frontend and backend) + - name: Build Tauri Application + run: pnpm tauri build --verbose + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/update-version.yml b/.github/workflows/update-version.yml new file mode 100644 index 0000000..43e30c0 --- /dev/null +++ b/.github/workflows/update-version.yml @@ -0,0 +1,72 @@ +name: Update Version Badge in Markdown Files + +on: + push: + branches: [ "main", "master" ] + paths: [ 'src-tauri/Cargo.toml' ] # Trigger only when Cargo.toml changes + workflow_dispatch: + +jobs: + update-badge: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract version from Cargo.toml + id: get_version + run: | + # Extract version using multiple methods for robustness + if command -v toml &> /dev/null; then + VERSION=$(toml get Cargo.toml package.version --raw 2>/dev/null) + fi + + if [ -z "$VERSION" ]; then + VERSION=$(grep '^version = ' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/') + fi + + if [ -z "$VERSION" ]; then + echo "Error: Could not extract version from Cargo.toml" + exit 1 + fi + + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "Extracted version: $VERSION" + + - name: Update version badges in all markdown files + run: | + VERSION="${{ steps.get_version.outputs.version }}" + echo "Updating badges to version: $VERSION" + + # Find all markdown files and update the version badge + find . -name "*.md" -type f | while read -r file; do + echo "Checking file: $file" + if grep -q "img.shields.io/badge/Version-" "$file"; then + echo "Updating badge in: $file" + sed -i "s|https://img\.shields\.io/badge/Version-[^-]*-informational|https://img.shields.io/badge/Version-$VERSION-informational|g" "$file" + fi + done + + - name: Check for changes + id: check_changes + run: | + if git diff --quiet; then + echo "changes=false" >> $GITHUB_OUTPUT + echo "No changes detected" + else + echo "changes=true" >> $GITHUB_OUTPUT + echo "Changes detected:" + git diff --name-only + fi + + - name: Commit and push changes + if: steps.check_changes.outputs.changes == 'true' + run: | + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + git add -A + git commit -m "๐Ÿ”– Update version badge to v${{ steps.get_version.outputs.version }}" + git push \ No newline at end of file diff --git a/README.md b/README.md index 1cecb63..98be83c 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@
Rust Tauri - Version + Version
@@ -78,6 +78,32 @@ To quickly test FileFlow: 4. **Upload and Insert**: Upload your CSV file and start the insertion process. Monitor progress with the on-screen loader. +## ๐Ÿงช Code Quality + +### Unit Tests available + +To run unit tests, use the following command: + +#### For the Rust backend: + +```bash +cargo test +``` + +#### For the Tauri frontend: + +```bash +pnpm test +``` + +### Benchmarks available + +To run benchmarks, use the following command: + +```bash +cargo bench +``` + ## ๐Ÿค Contributing Contributions are welcome! To contribute: diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..ca04617 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,31 @@ +export default { + preset: 'ts-jest/presets/default-esm', + extensionsToTreatAsEsm: ['.ts', '.tsx'], + testEnvironment: 'node', + roots: ['/src'], + testMatch: [ + '**/__tests__/**/*.+(ts|tsx|js)', + '**/*.(test|spec).+(ts|tsx|js)' + ], + transform: { + '^.+\\.(ts|tsx)$': ['ts-jest', { + useESM: true + }] + }, + moduleNameMapping: { + '^(\\.{1,2}/.*)\\.js$': '$1' + }, + collectCoverageFrom: [ + 'src/**/*.{ts,tsx}', + '!src/**/*.d.ts', + '!src/**/*.test.{ts,tsx}' + ], + coverageThreshold: { + global: { + branches: 90, + functions: 90, + lines: 90, + statements: 90 + } + } +}; \ No newline at end of file diff --git a/package.json b/package.json index bd4dc10..5c4459d 100644 --- a/package.json +++ b/package.json @@ -7,29 +7,32 @@ "dev": "vite", "build": "tsc && vite build", "preview": "vite preview", - "tauri": "tauri" + "tauri": "tauri", + "test": "jest", + "test:watch": "jest --watch", + "test:coverage": "jest --coverage" }, "dependencies": { - "@radix-ui/react-checkbox": "^1.3.2", - "@radix-ui/react-dialog": "^1.1.14", - "@radix-ui/react-dropdown-menu": "^2.1.15", + "@radix-ui/react-checkbox": "^1.3.3", + "@radix-ui/react-dialog": "^1.1.15", + "@radix-ui/react-dropdown-menu": "^2.1.16", "@radix-ui/react-icons": "^1.3.2", "@radix-ui/react-label": "^2.1.7", - "@radix-ui/react-menubar": "^1.1.15", - "@radix-ui/react-popover": "^1.1.14", - "@radix-ui/react-radio-group": "^1.3.7", - "@radix-ui/react-select": "^2.2.5", + "@radix-ui/react-menubar": "^1.1.16", + "@radix-ui/react-popover": "^1.1.15", + "@radix-ui/react-radio-group": "^1.3.8", + "@radix-ui/react-select": "^2.2.6", "@radix-ui/react-separator": "^1.1.7", "@radix-ui/react-slot": "^1.2.3", - "@radix-ui/react-switch": "^1.2.5", - "@radix-ui/react-tabs": "^1.1.12", - "@radix-ui/react-tooltip": "^1.2.7", - "@tauri-apps/api": "^2.5.0", + "@radix-ui/react-switch": "^1.2.6", + "@radix-ui/react-tabs": "^1.1.13", + "@radix-ui/react-tooltip": "^1.2.8", + "@tauri-apps/api": "^2.8.0", "@tauri-apps/plugin-dialog": "~2.2.2", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "1.0.0", - "framer-motion": "^12.17.0", + "framer-motion": "^12.23.18", "lucide-react": "^0.441.0", "next-themes": "^0.4.6", "react": "^18.3.1", @@ -40,15 +43,18 @@ "tailwindcss-animate": "^1.0.7" }, "devDependencies": { - "@tauri-apps/cli": "^2.5.0", - "@types/node": "^22.15.31", - "@types/react": "^18.3.23", + "@tauri-apps/cli": "^2.8.4", + "@types/jest": "^29.5.14", + "@types/node": "^22.18.6", + "@types/react": "^18.3.24", "@types/react-dom": "^18.3.7", - "@vitejs/plugin-react": "^4.5.2", + "@vitejs/plugin-react": "^4.7.0", "autoprefixer": "^10.4.21", - "postcss": "^8.5.4", + "jest": "^29.7.0", + "postcss": "^8.5.6", "tailwindcss": "^3.4.17", - "typescript": "^5.8.3", + "ts-jest": "^29.4.4", + "typescript": "^5.9.2", "vite": "^5.4.20" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1dad12d..2c6787b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,50 +9,50 @@ importers: .: dependencies: '@radix-ui/react-checkbox': - specifier: ^1.3.2 - version: 1.3.2(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^1.3.3 + version: 1.3.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-dialog': - specifier: ^1.1.14 - version: 1.1.14(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^1.1.15 + version: 1.1.15(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-dropdown-menu': - specifier: ^2.1.15 - version: 2.1.15(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^2.1.16 + version: 2.1.16(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-icons': specifier: ^1.3.2 version: 1.3.2(react@18.3.1) '@radix-ui/react-label': specifier: ^2.1.7 - version: 2.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 2.1.7(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-menubar': - specifier: ^1.1.15 - version: 1.1.15(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^1.1.16 + version: 1.1.16(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-popover': - specifier: ^1.1.14 - version: 1.1.14(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^1.1.15 + version: 1.1.15(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-radio-group': - specifier: ^1.3.7 - version: 1.3.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^1.3.8 + version: 1.3.8(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-select': - specifier: ^2.2.5 - version: 2.2.5(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^2.2.6 + version: 2.2.6(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-separator': specifier: ^1.1.7 - version: 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-slot': specifier: ^1.2.3 - version: 1.2.3(@types/react@18.3.23)(react@18.3.1) + version: 1.2.3(@types/react@18.3.24)(react@18.3.1) '@radix-ui/react-switch': - specifier: ^1.2.5 - version: 1.2.5(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^1.2.6 + version: 1.2.6(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-tabs': - specifier: ^1.1.12 - version: 1.1.12(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^1.1.13 + version: 1.1.13(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-tooltip': - specifier: ^1.2.7 - version: 1.2.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^1.2.8 + version: 1.2.8(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@tauri-apps/api': - specifier: ^2.5.0 - version: 2.5.0 + specifier: ^2.8.0 + version: 2.8.0 '@tauri-apps/plugin-dialog': specifier: ~2.2.2 version: 2.2.2 @@ -64,10 +64,10 @@ importers: version: 2.1.1 cmdk: specifier: 1.0.0 - version: 1.0.0(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 1.0.0(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) framer-motion: - specifier: ^12.17.0 - version: 12.17.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^12.23.18 + version: 12.23.18(react-dom@18.3.1(react@18.3.1))(react@18.3.1) lucide-react: specifier: ^0.441.0 version: 0.441.0(react@18.3.1) @@ -94,35 +94,44 @@ importers: version: 1.0.7(tailwindcss@3.4.17) devDependencies: '@tauri-apps/cli': - specifier: ^2.5.0 - version: 2.5.0 + specifier: ^2.8.4 + version: 2.8.4 + '@types/jest': + specifier: ^29.5.14 + version: 29.5.14 '@types/node': - specifier: ^22.15.31 - version: 22.15.31 + specifier: ^22.18.6 + version: 22.18.6 '@types/react': - specifier: ^18.3.23 - version: 18.3.23 + specifier: ^18.3.24 + version: 18.3.24 '@types/react-dom': specifier: ^18.3.7 - version: 18.3.7(@types/react@18.3.23) + version: 18.3.7(@types/react@18.3.24) '@vitejs/plugin-react': - specifier: ^4.5.2 - version: 4.5.2(vite@5.4.20(@types/node@22.15.31)) + specifier: ^4.7.0 + version: 4.7.0(vite@5.4.20(@types/node@22.18.6)) autoprefixer: specifier: ^10.4.21 - version: 10.4.21(postcss@8.5.4) + version: 10.4.21(postcss@8.5.6) + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@22.18.6) postcss: - specifier: ^8.5.4 - version: 8.5.4 + specifier: ^8.5.6 + version: 8.5.6 tailwindcss: specifier: ^3.4.17 version: 3.4.17 + ts-jest: + specifier: ^29.4.4 + version: 29.4.4(@babel/core@7.28.4)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.4))(jest-util@29.7.0)(jest@29.7.0(@types/node@22.18.6))(typescript@5.9.2) typescript: - specifier: ^5.8.3 - version: 5.8.3 + specifier: ^5.9.2 + version: 5.9.2 vite: specifier: ^5.4.20 - version: 5.4.20(@types/node@22.15.31) + version: 5.4.20(@types/node@22.18.6) packages: @@ -130,36 +139,36 @@ packages: resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} - '@ampproject/remapping@2.3.0': - resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} - engines: {node: '>=6.0.0'} - '@babel/code-frame@7.27.1': resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.27.5': - resolution: {integrity: sha512-KiRAp/VoJaWkkte84TvUd9qjdbZAdiqyvMxrGl1N6vzFogKmaLgoM3L1kgtLicp2HP5fBJS8JrZKLVIZGVJAVg==} + '@babel/compat-data@7.28.4': + resolution: {integrity: sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==} engines: {node: '>=6.9.0'} - '@babel/core@7.27.4': - resolution: {integrity: sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==} + '@babel/core@7.28.4': + resolution: {integrity: sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==} engines: {node: '>=6.9.0'} - '@babel/generator@7.27.5': - resolution: {integrity: sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==} + '@babel/generator@7.28.3': + resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==} engines: {node: '>=6.9.0'} '@babel/helper-compilation-targets@7.27.2': resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} engines: {node: '>=6.9.0'} + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + '@babel/helper-module-imports@7.27.1': resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} engines: {node: '>=6.9.0'} - '@babel/helper-module-transforms@7.27.3': - resolution: {integrity: sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==} + '@babel/helper-module-transforms@7.28.3': + resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -180,15 +189,106 @@ packages: resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.27.6': - resolution: {integrity: sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==} + '@babel/helpers@7.28.4': + resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} engines: {node: '>=6.9.0'} - '@babel/parser@7.27.5': - resolution: {integrity: sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==} + '@babel/parser@7.28.4': + resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==} engines: {node: '>=6.0.0'} hasBin: true + '@babel/plugin-syntax-async-generators@7.8.4': + resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-bigint@7.8.3': + resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-class-properties@7.12.13': + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-class-static-block@7.14.5': + resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-attributes@7.27.1': + resolution: {integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-meta@7.10.4': + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-json-strings@7.8.3': + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-jsx@7.27.1': + resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4': + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3': + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-numeric-separator@7.10.4': + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-object-rest-spread@7.8.3': + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3': + resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-optional-chaining@7.8.3': + resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-private-property-in-object@7.14.5': + resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-top-level-await@7.14.5': + resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.27.1': + resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-react-jsx-self@7.27.1': resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} engines: {node: '>=6.9.0'} @@ -201,22 +301,25 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/runtime@7.27.6': - resolution: {integrity: sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==} + '@babel/runtime@7.28.4': + resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} engines: {node: '>=6.9.0'} '@babel/template@7.27.2': resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.27.4': - resolution: {integrity: sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==} + '@babel/traverse@7.28.4': + resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==} engines: {node: '>=6.9.0'} - '@babel/types@7.27.6': - resolution: {integrity: sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==} + '@babel/types@7.28.4': + resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} engines: {node: '>=6.9.0'} + '@bcoe/v8-coverage@0.2.3': + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + '@esbuild/aix-ppc64@0.21.5': resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} engines: {node: '>=12'} @@ -355,42 +458,114 @@ packages: cpu: [x64] os: [win32] - '@floating-ui/core@1.7.1': - resolution: {integrity: sha512-azI0DrjMMfIug/ExbBaeDVJXcY0a7EPvPjb2xAJPa4HeimBX+Z18HK8QQR3jb6356SnDDdxx+hinMLcJEDdOjw==} + '@floating-ui/core@1.7.3': + resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} - '@floating-ui/dom@1.7.1': - resolution: {integrity: sha512-cwsmW/zyw5ltYTUeeYJ60CnQuPqmGwuGVhG9w0PRaRKkAyi38BT5CKrpIbb+jtahSwUl04cWzSx9ZOIxeS6RsQ==} + '@floating-ui/dom@1.7.4': + resolution: {integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==} - '@floating-ui/react-dom@2.1.3': - resolution: {integrity: sha512-huMBfiU9UnQ2oBwIhgzyIiSpVgvlDstU8CX0AF+wS+KzmYMs0J2a3GwuFHV1Lz+jlrQGeC1fF+Nv0QoumyV0bA==} + '@floating-ui/react-dom@2.1.6': + resolution: {integrity: sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==} peerDependencies: react: '>=16.8.0' react-dom: '>=16.8.0' - '@floating-ui/utils@0.2.9': - resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==} + '@floating-ui/utils@0.2.10': + resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} - '@jridgewell/gen-mapping@0.3.8': - resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} - engines: {node: '>=6.0.0'} + '@istanbuljs/load-nyc-config@1.1.0': + resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} + engines: {node: '>=8'} + + '@istanbuljs/schema@0.1.3': + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + engines: {node: '>=8'} + + '@jest/console@29.7.0': + resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/core@29.7.0': + resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + '@jest/environment@29.7.0': + resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/expect-utils@29.7.0': + resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/expect@29.7.0': + resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/fake-timers@29.7.0': + resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/globals@29.7.0': + resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/reporters@29.7.0': + resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + '@jest/schemas@29.6.3': + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/source-map@29.6.3': + resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/test-result@29.7.0': + resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/test-sequencer@29.7.0': + resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/transform@29.7.0': + resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/types@29.6.3': + resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - '@jridgewell/set-array@1.2.1': - resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} - engines: {node: '>=6.0.0'} - - '@jridgewell/sourcemap-codec@1.5.0': - resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - '@jridgewell/trace-mapping@0.3.25': - resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} @@ -414,8 +589,8 @@ packages: '@radix-ui/primitive@1.0.1': resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==} - '@radix-ui/primitive@1.1.2': - resolution: {integrity: sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==} + '@radix-ui/primitive@1.1.3': + resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} '@radix-ui/react-arrow@1.1.7': resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==} @@ -430,8 +605,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-checkbox@1.3.2': - resolution: {integrity: sha512-yd+dI56KZqawxKZrJ31eENUwqc1QSqg4OZ15rybGjF2ZNwMO+wCyHzAVLRp9qoYJf7kYy0YpZ2b0JCzJ42HZpA==} + '@radix-ui/react-checkbox@1.3.3': + resolution: {integrity: sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -505,8 +680,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-dialog@1.1.14': - resolution: {integrity: sha512-+CpweKjqpzTmwRwcYECQcNYbI8V9VSQt0SNFKeEBLgfucbsLssU6Ppq7wUdNXEGb573bMjFhVjKVll8rmV6zMw==} + '@radix-ui/react-dialog@1.1.15': + resolution: {integrity: sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -540,8 +715,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-dismissable-layer@1.1.10': - resolution: {integrity: sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ==} + '@radix-ui/react-dismissable-layer@1.1.11': + resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -553,8 +728,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-dropdown-menu@2.1.15': - resolution: {integrity: sha512-mIBnOjgwo9AH3FyKaSWoSu/dYj6VdhJ7frEPiGTeXCdUFHjl9h3mFh2wwhEtINOmYXWhdpf1rY2minFsmaNgVQ==} + '@radix-ui/react-dropdown-menu@2.1.16': + resolution: {integrity: sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -575,8 +750,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-focus-guards@1.1.2': - resolution: {integrity: sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==} + '@radix-ui/react-focus-guards@1.1.3': + resolution: {integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -646,8 +821,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-menu@2.1.15': - resolution: {integrity: sha512-tVlmA3Vb9n8SZSd+YSbuFR66l87Wiy4du+YE+0hzKQEANA+7cWKH1WgqcEX4pXqxUFQKrWQGHdvEfw00TjFiew==} + '@radix-ui/react-menu@2.1.16': + resolution: {integrity: sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -659,8 +834,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-menubar@1.1.15': - resolution: {integrity: sha512-Z71C7LGD+YDYo3TV81paUs8f3Zbmkvg6VLRQpKYfzioOE6n7fOhA3ApK/V/2Odolxjoc4ENk8AYCjohCNayd5A==} + '@radix-ui/react-menubar@1.1.16': + resolution: {integrity: sha512-EB1FktTz5xRRi2Er974AUQZWg2yVBb1yjip38/lgwtCVRd3a+maUoGHN/xs9Yv8SY8QwbSEb+YrxGadVWbEutA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -672,8 +847,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-popover@1.1.14': - resolution: {integrity: sha512-ODz16+1iIbGUfFEfKx2HTPKizg2MN39uIOV8MXeHnmdd3i/N9Wt7vU46wbHsqA0xoaQyXVcs0KIlBdOA2Y95bw==} + '@radix-ui/react-popover@1.1.15': + resolution: {integrity: sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -685,8 +860,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-popper@1.2.7': - resolution: {integrity: sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ==} + '@radix-ui/react-popper@1.2.8': + resolution: {integrity: sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -737,8 +912,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-presence@1.1.4': - resolution: {integrity: sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==} + '@radix-ui/react-presence@1.1.5': + resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -776,8 +951,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-radio-group@1.3.7': - resolution: {integrity: sha512-9w5XhD0KPOrm92OTTE0SysH3sYzHsSTHNvZgUBo/VZ80VdYyB5RneDbc0dKpURS24IxkoFRu/hI0i4XyfFwY6g==} + '@radix-ui/react-radio-group@1.3.8': + resolution: {integrity: sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -789,8 +964,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-roving-focus@1.1.10': - resolution: {integrity: sha512-dT9aOXUen9JSsxnMPv/0VqySQf5eDQ6LCk5Sw28kamz8wSOW2bJdlX2Bg5VUIIcV+6XlHpWTIuTPCf/UNIyq8Q==} + '@radix-ui/react-roving-focus@1.1.11': + resolution: {integrity: sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -802,8 +977,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-select@2.2.5': - resolution: {integrity: sha512-HnMTdXEVuuyzx63ME0ut4+sEMYW6oouHWNGUZc7ddvUWIcfCva/AMoqEW/3wnEllriMWBa0RHspCYnfCWJQYmA==} + '@radix-ui/react-select@2.2.6': + resolution: {integrity: sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -846,8 +1021,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-switch@1.2.5': - resolution: {integrity: sha512-5ijLkak6ZMylXsaImpZ8u4Rlf5grRmoc0p0QeX9VJtlrM4f5m3nCTX8tWga/zOA8PZYIR/t0p2Mnvd7InrJ6yQ==} + '@radix-ui/react-switch@1.2.6': + resolution: {integrity: sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -859,8 +1034,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-tabs@1.1.12': - resolution: {integrity: sha512-GTVAlRVrQrSw3cEARM0nAx73ixrWDPNZAruETn3oHCNP6SbZ/hNxdxp+u7VkIEv3/sFoLq1PfcHrl7Pnp0CDpw==} + '@radix-ui/react-tabs@1.1.13': + resolution: {integrity: sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -872,8 +1047,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-tooltip@1.2.7': - resolution: {integrity: sha512-Ap+fNYwKTYJ9pzqW+Xe2HtMRbQ/EeWkj2qykZ6SuEV4iS/o1bZI5ssJbk4D2r8XuDuOBVz/tIx2JObtuqU+5Zw==} + '@radix-ui/react-tooltip@1.2.8': + resolution: {integrity: sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -1013,185 +1188,199 @@ packages: resolution: {integrity: sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==} engines: {node: '>=14.0.0'} - '@rolldown/pluginutils@1.0.0-beta.11': - resolution: {integrity: sha512-L/gAA/hyCSuzTF1ftlzUSI/IKr2POHsv1Dd78GfqkR83KMNuswWD61JxGV2L7nRwBBBSDr6R1gCkdTmoN7W4ag==} + '@rolldown/pluginutils@1.0.0-beta.27': + resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} - '@rollup/rollup-android-arm-eabi@4.50.1': - resolution: {integrity: sha512-HJXwzoZN4eYTdD8bVV22DN8gsPCAj3V20NHKOs8ezfXanGpmVPR7kalUHd+Y31IJp9stdB87VKPFbsGY3H/2ag==} + '@rollup/rollup-android-arm-eabi@4.52.0': + resolution: {integrity: sha512-VxDYCDqOaR7NXzAtvRx7G1u54d2kEHopb28YH/pKzY6y0qmogP3gG7CSiWsq9WvDFxOQMpNEyjVAHZFXfH3o/A==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.50.1': - resolution: {integrity: sha512-PZlsJVcjHfcH53mOImyt3bc97Ep3FJDXRpk9sMdGX0qgLmY0EIWxCag6EigerGhLVuL8lDVYNnSo8qnTElO4xw==} + '@rollup/rollup-android-arm64@4.52.0': + resolution: {integrity: sha512-pqDirm8koABIKvzL59YI9W9DWbRlTX7RWhN+auR8HXJxo89m4mjqbah7nJZjeKNTNYopqL+yGg+0mhCpf3xZtQ==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.50.1': - resolution: {integrity: sha512-xc6i2AuWh++oGi4ylOFPmzJOEeAa2lJeGUGb4MudOtgfyyjr4UPNK+eEWTPLvmPJIY/pgw6ssFIox23SyrkkJw==} + '@rollup/rollup-darwin-arm64@4.52.0': + resolution: {integrity: sha512-YCdWlY/8ltN6H78HnMsRHYlPiKvqKagBP1r+D7SSylxX+HnsgXGCmLiV3Y4nSyY9hW8qr8U9LDUx/Lo7M6MfmQ==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.50.1': - resolution: {integrity: sha512-2ofU89lEpDYhdLAbRdeyz/kX3Y2lpYc6ShRnDjY35bZhd2ipuDMDi6ZTQ9NIag94K28nFMofdnKeHR7BT0CATw==} + '@rollup/rollup-darwin-x64@4.52.0': + resolution: {integrity: sha512-z4nw6y1j+OOSGzuVbSWdIp1IUks9qNw4dc7z7lWuWDKojY38VMWBlEN7F9jk5UXOkUcp97vA1N213DF+Lz8BRg==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.50.1': - resolution: {integrity: sha512-wOsE6H2u6PxsHY/BeFHA4VGQN3KUJFZp7QJBmDYI983fgxq5Th8FDkVuERb2l9vDMs1D5XhOrhBrnqcEY6l8ZA==} + '@rollup/rollup-freebsd-arm64@4.52.0': + resolution: {integrity: sha512-Q/dv9Yvyr5rKlK8WQJZVrp5g2SOYeZUs9u/t2f9cQ2E0gJjYB/BWoedXfUT0EcDJefi2zzVfhcOj8drWCzTviw==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.50.1': - resolution: {integrity: sha512-A/xeqaHTlKbQggxCqispFAcNjycpUEHP52mwMQZUNqDUJFFYtPHCXS1VAG29uMlDzIVr+i00tSFWFLivMcoIBQ==} + '@rollup/rollup-freebsd-x64@4.52.0': + resolution: {integrity: sha512-kdBsLs4Uile/fbjZVvCRcKB4q64R+1mUq0Yd7oU1CMm1Av336ajIFqNFovByipciuUQjBCPMxwJhCgfG2re3rg==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.50.1': - resolution: {integrity: sha512-54v4okehwl5TaSIkpp97rAHGp7t3ghinRd/vyC1iXqXMfjYUTm7TfYmCzXDoHUPTTf36L8pr0E7YsD3CfB3ZDg==} + '@rollup/rollup-linux-arm-gnueabihf@4.52.0': + resolution: {integrity: sha512-aL6hRwu0k7MTUESgkg7QHY6CoqPgr6gdQXRJI1/VbFlUMwsSzPGSR7sG5d+MCbYnJmJwThc2ol3nixj1fvI/zQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.50.1': - resolution: {integrity: sha512-p/LaFyajPN/0PUHjv8TNyxLiA7RwmDoVY3flXHPSzqrGcIp/c2FjwPPP5++u87DGHtw+5kSH5bCJz0mvXngYxw==} + '@rollup/rollup-linux-arm-musleabihf@4.52.0': + resolution: {integrity: sha512-BTs0M5s1EJejgIBJhCeiFo7GZZ2IXWkFGcyZhxX4+8usnIo5Mti57108vjXFIQmmJaRyDwmV59Tw64Ap1dkwMw==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.50.1': - resolution: {integrity: sha512-2AbMhFFkTo6Ptna1zO7kAXXDLi7H9fGTbVaIq2AAYO7yzcAsuTNWPHhb2aTA6GPiP+JXh85Y8CiS54iZoj4opw==} + '@rollup/rollup-linux-arm64-gnu@4.52.0': + resolution: {integrity: sha512-uj672IVOU9m08DBGvoPKPi/J8jlVgjh12C9GmjjBxCTQc3XtVmRkRKyeHSmIKQpvJ7fIm1EJieBUcnGSzDVFyw==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.50.1': - resolution: {integrity: sha512-Cgef+5aZwuvesQNw9eX7g19FfKX5/pQRIyhoXLCiBOrWopjo7ycfB292TX9MDcDijiuIJlx1IzJz3IoCPfqs9w==} + '@rollup/rollup-linux-arm64-musl@4.52.0': + resolution: {integrity: sha512-/+IVbeDMDCtB/HP/wiWsSzduD10SEGzIZX2945KSgZRNi4TSkjHqRJtNTVtVb8IRwhJ65ssI56krlLik+zFWkw==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.50.1': - resolution: {integrity: sha512-RPhTwWMzpYYrHrJAS7CmpdtHNKtt2Ueo+BlLBjfZEhYBhK00OsEqM08/7f+eohiF6poe0YRDDd8nAvwtE/Y62Q==} + '@rollup/rollup-linux-loong64-gnu@4.52.0': + resolution: {integrity: sha512-U1vVzvSWtSMWKKrGoROPBXMh3Vwn93TA9V35PldokHGqiUbF6erSzox/5qrSMKp6SzakvyjcPiVF8yB1xKr9Pg==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-ppc64-gnu@4.50.1': - resolution: {integrity: sha512-eSGMVQw9iekut62O7eBdbiccRguuDgiPMsw++BVUg+1K7WjZXHOg/YOT9SWMzPZA+w98G+Fa1VqJgHZOHHnY0Q==} + '@rollup/rollup-linux-ppc64-gnu@4.52.0': + resolution: {integrity: sha512-X/4WfuBAdQRH8cK3DYl8zC00XEE6aM472W+QCycpQJeLWVnHfkv7RyBFVaTqNUMsTgIX8ihMjCvFF9OUgeABzw==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.50.1': - resolution: {integrity: sha512-S208ojx8a4ciIPrLgazF6AgdcNJzQE4+S9rsmOmDJkusvctii+ZvEuIC4v/xFqzbuP8yDjn73oBlNDgF6YGSXQ==} + '@rollup/rollup-linux-riscv64-gnu@4.52.0': + resolution: {integrity: sha512-xIRYc58HfWDBZoLmWfWXg2Sq8VCa2iJ32B7mqfWnkx5mekekl0tMe7FHpY8I72RXEcUkaWawRvl3qA55og+cwQ==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.50.1': - resolution: {integrity: sha512-3Ag8Ls1ggqkGUvSZWYcdgFwriy2lWo+0QlYgEFra/5JGtAd6C5Hw59oojx1DeqcA2Wds2ayRgvJ4qxVTzCHgzg==} + '@rollup/rollup-linux-riscv64-musl@4.52.0': + resolution: {integrity: sha512-mbsoUey05WJIOz8U1WzNdf+6UMYGwE3fZZnQqsM22FZ3wh1N887HT6jAOjXs6CNEK3Ntu2OBsyQDXfIjouI4dw==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.50.1': - resolution: {integrity: sha512-t9YrKfaxCYe7l7ldFERE1BRg/4TATxIg+YieHQ966jwvo7ddHJxPj9cNFWLAzhkVsbBvNA4qTbPVNsZKBO4NSg==} + '@rollup/rollup-linux-s390x-gnu@4.52.0': + resolution: {integrity: sha512-qP6aP970bucEi5KKKR4AuPFd8aTx9EF6BvutvYxmZuWLJHmnq4LvBfp0U+yFDMGwJ+AIJEH5sIP+SNypauMWzg==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.50.1': - resolution: {integrity: sha512-MCgtFB2+SVNuQmmjHf+wfI4CMxy3Tk8XjA5Z//A0AKD7QXUYFMQcns91K6dEHBvZPCnhJSyDWLApk40Iq/H3tA==} + '@rollup/rollup-linux-x64-gnu@4.52.0': + resolution: {integrity: sha512-nmSVN+F2i1yKZ7rJNKO3G7ZzmxJgoQBQZ/6c4MuS553Grmr7WqR7LLDcYG53Z2m9409z3JLt4sCOhLdbKQ3HmA==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.50.1': - resolution: {integrity: sha512-nEvqG+0jeRmqaUMuwzlfMKwcIVffy/9KGbAGyoa26iu6eSngAYQ512bMXuqqPrlTyfqdlB9FVINs93j534UJrg==} + '@rollup/rollup-linux-x64-musl@4.52.0': + resolution: {integrity: sha512-2d0qRo33G6TfQVjaMR71P+yJVGODrt5V6+T0BDYH4EMfGgdC/2HWDVjSSFw888GSzAZUwuska3+zxNUCDco6rQ==} cpu: [x64] os: [linux] - '@rollup/rollup-openharmony-arm64@4.50.1': - resolution: {integrity: sha512-RDsLm+phmT3MJd9SNxA9MNuEAO/J2fhW8GXk62G/B4G7sLVumNFbRwDL6v5NrESb48k+QMqdGbHgEtfU0LCpbA==} + '@rollup/rollup-openharmony-arm64@4.52.0': + resolution: {integrity: sha512-A1JalX4MOaFAAyGgpO7XP5khquv/7xKzLIyLmhNrbiCxWpMlnsTYr8dnsWM7sEeotNmxvSOEL7F65j0HXFcFsw==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.50.1': - resolution: {integrity: sha512-hpZB/TImk2FlAFAIsoElM3tLzq57uxnGYwplg6WDyAxbYczSi8O2eQ+H2Lx74504rwKtZ3N2g4bCUkiamzS6TQ==} + '@rollup/rollup-win32-arm64-msvc@4.52.0': + resolution: {integrity: sha512-YQugafP/rH0eOOHGjmNgDURrpYHrIX0yuojOI8bwCyXwxC9ZdTd3vYkmddPX0oHONLXu9Rb1dDmT0VNpjkzGGw==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.50.1': - resolution: {integrity: sha512-SXjv8JlbzKM0fTJidX4eVsH+Wmnp0/WcD8gJxIZyR6Gay5Qcsmdbi9zVtnbkGPG8v2vMR1AD06lGWy5FLMcG7A==} + '@rollup/rollup-win32-ia32-msvc@4.52.0': + resolution: {integrity: sha512-zYdUYhi3Qe2fndujBqL5FjAFzvNeLxtIqfzNEVKD1I7C37/chv1VxhscWSQHTNfjPCrBFQMnynwA3kpZpZ8w4A==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.50.1': - resolution: {integrity: sha512-StxAO/8ts62KZVRAm4JZYq9+NqNsV7RvimNK+YM7ry//zebEH6meuugqW/P5OFUCjyQgui+9fUxT6d5NShvMvA==} + '@rollup/rollup-win32-x64-gnu@4.52.0': + resolution: {integrity: sha512-fGk03kQylNaCOQ96HDMeT7E2n91EqvCDd3RwvT5k+xNdFCeMGnj5b5hEgTGrQuyidqSsD3zJDQ21QIaxXqTBJw==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.52.0': + resolution: {integrity: sha512-6iKDCVSIUQ8jPMoIV0OytRKniaYyy5EbY/RRydmLW8ZR3cEBhxbWl5ro0rkUNe0ef6sScvhbY79HrjRm8i3vDQ==} cpu: [x64] os: [win32] - '@tauri-apps/api@2.5.0': - resolution: {integrity: sha512-Ldux4ip+HGAcPUmuLT8EIkk6yafl5vK0P0c0byzAKzxJh7vxelVtdPONjfgTm96PbN24yjZNESY8CKo8qniluA==} + '@sinclair/typebox@0.27.8': + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + + '@sinonjs/commons@3.0.1': + resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} + + '@sinonjs/fake-timers@10.3.0': + resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + + '@tauri-apps/api@2.8.0': + resolution: {integrity: sha512-ga7zdhbS2GXOMTIZRT0mYjKJtR9fivsXzsyq5U3vjDL0s6DTMwYRm0UHNjzTY5dh4+LSC68Sm/7WEiimbQNYlw==} - '@tauri-apps/cli-darwin-arm64@2.5.0': - resolution: {integrity: sha512-VuVAeTFq86dfpoBDNYAdtQVLbP0+2EKCHIIhkaxjeoPARR0sLpFHz2zs0PcFU76e+KAaxtEtAJAXGNUc8E1PzQ==} + '@tauri-apps/cli-darwin-arm64@2.8.4': + resolution: {integrity: sha512-BKu8HRkYV01SMTa7r4fLx+wjgtRK8Vep7lmBdHDioP6b8XH3q2KgsAyPWfEZaZIkZ2LY4SqqGARaE9oilNe0oA==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@tauri-apps/cli-darwin-x64@2.5.0': - resolution: {integrity: sha512-hUF01sC06cZVa8+I0/VtsHOk9BbO75rd+YdtHJ48xTdcYaQ5QIwL4yZz9OR1AKBTaUYhBam8UX9Pvd5V2/4Dpw==} + '@tauri-apps/cli-darwin-x64@2.8.4': + resolution: {integrity: sha512-imb9PfSd/7G6VAO7v1bQ2A3ZH4NOCbhGJFLchxzepGcXf9NKkfun157JH9mko29K6sqAwuJ88qtzbKCbWJTH9g==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@tauri-apps/cli-linux-arm-gnueabihf@2.5.0': - resolution: {integrity: sha512-LQKqttsK252LlqYyX8R02MinUsfFcy3+NZiJwHFgi5Y3+ZUIAED9cSxJkyNtuY5KMnR4RlpgWyLv4P6akN1xhg==} + '@tauri-apps/cli-linux-arm-gnueabihf@2.8.4': + resolution: {integrity: sha512-Ml215UnDdl7/fpOrF1CNovym/KjtUbCuPgrcZ4IhqUCnhZdXuphud/JT3E8X97Y03TZ40Sjz8raXYI2ET0exzw==} engines: {node: '>= 10'} cpu: [arm] os: [linux] - '@tauri-apps/cli-linux-arm64-gnu@2.5.0': - resolution: {integrity: sha512-mTQufsPcpdHg5RW0zypazMo4L55EfeE5snTzrPqbLX4yCK2qalN7+rnP8O8GT06xhp6ElSP/Ku1M2MR297SByQ==} + '@tauri-apps/cli-linux-arm64-gnu@2.8.4': + resolution: {integrity: sha512-pbcgBpMyI90C83CxE5REZ9ODyIlmmAPkkJXtV398X3SgZEIYy5TACYqlyyv2z5yKgD8F8WH4/2fek7+jH+ZXAw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tauri-apps/cli-linux-arm64-musl@2.5.0': - resolution: {integrity: sha512-rQO1HhRUQqyEaal5dUVOQruTRda/TD36s9kv1hTxZiFuSq3558lsTjAcUEnMAtBcBkps20sbyTJNMT0AwYIk8Q==} + '@tauri-apps/cli-linux-arm64-musl@2.8.4': + resolution: {integrity: sha512-zumFeaU1Ws5Ay872FTyIm7z8kfzEHu8NcIn8M6TxbJs0a7GRV21KBdpW1zNj2qy7HynnpQCqjAYXTUUmm9JAOw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tauri-apps/cli-linux-riscv64-gnu@2.5.0': - resolution: {integrity: sha512-7oS18FN46yDxyw1zX/AxhLAd7T3GrLj3Ai6s8hZKd9qFVzrAn36ESL7d3G05s8wEtsJf26qjXnVF4qleS3dYsA==} + '@tauri-apps/cli-linux-riscv64-gnu@2.8.4': + resolution: {integrity: sha512-qiqbB3Zz6IyO201f+1ojxLj65WYj8mixL5cOMo63nlg8CIzsP23cPYUrx1YaDPsCLszKZo7tVs14pc7BWf+/aQ==} engines: {node: '>= 10'} cpu: [riscv64] os: [linux] - '@tauri-apps/cli-linux-x64-gnu@2.5.0': - resolution: {integrity: sha512-SG5sFNL7VMmDBdIg3nO3EzNRT306HsiEQ0N90ILe3ZABYAVoPDO/ttpCO37ApLInTzrq/DLN+gOlC/mgZvLw1w==} + '@tauri-apps/cli-linux-x64-gnu@2.8.4': + resolution: {integrity: sha512-TaqaDd9Oy6k45Hotx3pOf+pkbsxLaApv4rGd9mLuRM1k6YS/aw81YrsMryYPThrxrScEIUcmNIHaHsLiU4GMkw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@tauri-apps/cli-linux-x64-musl@2.5.0': - resolution: {integrity: sha512-QXDM8zp/6v05PNWju5ELsVwF0VH1n6b5pk2E6W/jFbbiwz80Vs1lACl9pv5kEHkrxBj+aWU/03JzGuIj2g3SkQ==} + '@tauri-apps/cli-linux-x64-musl@2.8.4': + resolution: {integrity: sha512-ot9STAwyezN8w+bBHZ+bqSQIJ0qPZFlz/AyscpGqB/JnJQVDFQcRDmUPFEaAtt2UUHSWzN3GoTJ5ypqLBp2WQA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@tauri-apps/cli-win32-arm64-msvc@2.5.0': - resolution: {integrity: sha512-pFSHFK6b+o9y4Un8w0gGLwVyFTZaC3P0kQ7umRt/BLDkzD5RnQ4vBM7CF8BCU5nkwmEBUCZd7Wt3TWZxe41o6Q==} + '@tauri-apps/cli-win32-arm64-msvc@2.8.4': + resolution: {integrity: sha512-+2aJ/g90dhLiOLFSD1PbElXX3SoMdpO7HFPAZB+xot3CWlAZD1tReUFy7xe0L5GAR16ZmrxpIDM9v9gn5xRy/w==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@tauri-apps/cli-win32-ia32-msvc@2.5.0': - resolution: {integrity: sha512-EArv1IaRlogdLAQyGlKmEqZqm5RfHCUMhJoedWu7GtdbOMUfSAz6FMX2boE1PtEmNO4An+g188flLeVErrxEKg==} + '@tauri-apps/cli-win32-ia32-msvc@2.8.4': + resolution: {integrity: sha512-yj7WDxkL1t9Uzr2gufQ1Hl7hrHuFKTNEOyascbc109EoiAqCp0tgZ2IykQqOZmZOHU884UAWI1pVMqBhS/BfhA==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] - '@tauri-apps/cli-win32-x64-msvc@2.5.0': - resolution: {integrity: sha512-lj43EFYbnAta8pd9JnUq87o+xRUR0odz+4rixBtTUwUgdRdwQ2V9CzFtsMu6FQKpFQ6mujRK6P1IEwhL6ADRsQ==} + '@tauri-apps/cli-win32-x64-msvc@2.8.4': + resolution: {integrity: sha512-XuvGB4ehBdd7QhMZ9qbj/8icGEatDuBNxyYHbLKsTYh90ggUlPa/AtaqcC1Fo69lGkTmq9BOKrs1aWSi7xDonA==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - '@tauri-apps/cli@2.5.0': - resolution: {integrity: sha512-rAtHqG0Gh/IWLjN2zTf3nZqYqbo81oMbqop56rGTjrlWk9pTTAjkqOjSL9XQLIMZ3RbeVjveCqqCA0s8RnLdMg==} + '@tauri-apps/cli@2.8.4': + resolution: {integrity: sha512-ejUZBzuQRcjFV+v/gdj/DcbyX/6T4unZQjMSBZwLzP/CymEjKcc2+Fc8xTORThebHDUvqoXMdsCZt8r+hyN15g==} engines: {node: '>= 10'} hasBin: true @@ -1207,14 +1396,29 @@ packages: '@types/babel__template@7.4.4': resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} - '@types/babel__traverse@7.20.7': - resolution: {integrity: sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==} + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - '@types/node@22.15.31': - resolution: {integrity: sha512-jnVe5ULKl6tijxUhvQeNbQG/84fHfg+yMak02cT8QVhBx/F05rAVxCGBYYTh2EKz22D6JF5ktXuNwdx7b9iEGw==} + '@types/graceful-fs@4.1.9': + resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} + + '@types/istanbul-lib-coverage@2.0.6': + resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} + + '@types/istanbul-lib-report@3.0.3': + resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} + + '@types/istanbul-reports@3.0.4': + resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} + + '@types/jest@29.5.14': + resolution: {integrity: sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==} + + '@types/node@22.18.6': + resolution: {integrity: sha512-r8uszLPpeIWbNKtvWRt/DbVi5zbqZyj1PTmhRMqBMvDnaz1QpmSKujUtJLrqGZeoM8v72MfYggDceY4K1itzWQ==} '@types/prop-types@15.7.15': resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} @@ -1224,29 +1428,46 @@ packages: peerDependencies: '@types/react': ^18.0.0 - '@types/react@18.3.23': - resolution: {integrity: sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w==} + '@types/react@18.3.24': + resolution: {integrity: sha512-0dLEBsA1kI3OezMBF8nSsb7Nk19ZnsyE1LLhB8r27KbgU5H4pvuqZLdtE+aUkJVoXgTVuA+iLIwmZ0TuK4tx6A==} + + '@types/stack-utils@2.0.3': + resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} + + '@types/yargs-parser@21.0.3': + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} + + '@types/yargs@17.0.33': + resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} - '@vitejs/plugin-react@4.5.2': - resolution: {integrity: sha512-QNVT3/Lxx99nMQWJWF7K4N6apUEuT0KlZA3mx/mVaoGj3smm/8rc8ezz15J1pcbcjDK0V15rpHetVfya08r76Q==} + '@vitejs/plugin-react@4.7.0': + resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: - vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0 + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - ansi-regex@6.1.0: - resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} engines: {node: '>=12'} ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} any-promise@1.3.0: @@ -1259,6 +1480,9 @@ packages: arg@5.0.2: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + aria-hidden@1.2.6: resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} engines: {node: '>=10'} @@ -1270,13 +1494,45 @@ packages: peerDependencies: postcss: ^8.1.0 + babel-jest@29.7.0: + resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.8.0 + + babel-plugin-istanbul@6.1.1: + resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} + engines: {node: '>=8'} + + babel-plugin-jest-hoist@29.6.3: + resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + babel-preset-current-node-syntax@1.2.0: + resolution: {integrity: sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==} + peerDependencies: + '@babel/core': ^7.0.0 || ^8.0.0-0 + + babel-preset-jest@29.6.3: + resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.0.0 + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + baseline-browser-mapping@2.8.6: + resolution: {integrity: sha512-wrH5NNqren/QMtKUEEJf7z86YjfqW/2uw3IL3/xpqZUC95SSVIFXYQeeGjL6FT/X68IROu6RMehZQS5foy2BXw==} + hasBin: true + binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + brace-expansion@2.0.2: resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} @@ -1284,25 +1540,66 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.25.0: - resolution: {integrity: sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==} + browserslist@4.26.2: + resolution: {integrity: sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + bs-logger@0.2.6: + resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} + engines: {node: '>= 6'} + + bser@2.1.1: + resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + camelcase-css@2.0.1: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} - caniuse-lite@1.0.30001722: - resolution: {integrity: sha512-DCQHBBZtiK6JVkAGw7drvAMK0Q0POD/xZvEmDp6baiMMP6QXXk9HpD6mNYBZWhOPG6LvIDb82ITqtWjhDckHCA==} + camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + caniuse-lite@1.0.30001743: + resolution: {integrity: sha512-e6Ojr7RV14Un7dz6ASD0aZDmQPT/A+eZU+nuTNfjqmRrmkmQlnTNWH0SKmqagx9PeW87UVqapSurtAXifmtdmw==} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + char-regex@1.0.2: + resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} + engines: {node: '>=10'} chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} + ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} + + cjs-module-lexer@1.4.3: + resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} + class-variance-authority@0.7.1: resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} @@ -1313,6 +1610,13 @@ packages: react: ^18.0.0 react-dom: ^18.0.0 + co@4.6.0: + resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} + engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + + collect-v8-coverage@1.0.2: + resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -1324,9 +1628,17 @@ packages: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + create-jest@29.7.0: + resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -1339,8 +1651,8 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - debug@4.4.1: - resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -1348,20 +1660,44 @@ packages: supports-color: optional: true + dedent@1.7.0: + resolution: {integrity: sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==} + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + detect-newline@3.1.0: + resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} + engines: {node: '>=8'} + detect-node-es@1.1.0: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dlv@1.1.3: resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - electron-to-chromium@1.5.166: - resolution: {integrity: sha512-QPWqHL0BglzPYyJJ1zSSmwFFL6MFXhbACOCcsCdUMCkzPdS9/OIBVxg516X/Ado2qwAq8k0nJJ7phQPCqiaFAw==} + electron-to-chromium@1.5.222: + resolution: {integrity: sha512-gA7psSwSwQRE60CEoLz6JBCQPIxNeuzB2nL8vE03GK/OHxlvykbLyeiumQy1iH5C2f3YbRAZpGCMT12a/9ih9w==} + + emittery@0.13.1: + resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} + engines: {node: '>=12'} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -1369,6 +1705,9 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + esbuild@0.21.5: resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} engines: {node: '>=12'} @@ -1378,17 +1717,48 @@ packages: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} + escape-string-regexp@2.0.0: + resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} + engines: {node: '>=8'} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + + exit@0.1.2: + resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} + engines: {node: '>= 0.8.0'} + + expect@29.7.0: + resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + fast-glob@3.3.3: resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + fastq@1.19.1: resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + fb-watchman@2.0.2: + resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + foreground-child@3.3.1: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} @@ -1396,8 +1766,8 @@ packages: fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} - framer-motion@12.17.0: - resolution: {integrity: sha512-2hISKgDk49yCLStwG1wf4Kdy/D6eBw9/eRNaWFIYoI9vMQ/Mqd1Fz+gzVlEtxJmtQ9y4IWnXm19/+UXD3dAYAA==} + framer-motion@12.23.18: + resolution: {integrity: sha512-HBVXBL5x3nk/0WrYM5G4VgjBey99ytVYET5AX17s/pcnlH90cyaxVUqgoN8cpF4+PqZRVOhwWsv28F+hxA9Tzg==} peerDependencies: '@emotion/is-prop-valid': '*' react: ^18.0.0 || ^19.0.0 @@ -1410,6 +1780,9 @@ packages: react-dom: optional: true + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1422,10 +1795,22 @@ packages: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + get-nonce@1.0.1: resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} engines: {node: '>=6'} + get-package-type@0.1.0: + resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} + engines: {node: '>=8.0.0'} + + get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -1438,14 +1823,52 @@ packages: resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} hasBin: true - globals@11.12.0: - resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} - engines: {node: '>=4'} + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + handlebars@4.7.8: + resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} + engines: {node: '>=0.4.7'} + hasBin: true + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + + human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + + import-local@3.2.0: + resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} + engines: {node: '>=8'} + hasBin: true + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} @@ -1462,6 +1885,10 @@ packages: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} + is-generator-fn@2.1.0: + resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} + engines: {node: '>=6'} + is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -1470,72 +1897,274 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-instrument@5.2.1: + resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} + engines: {node: '>=8'} + + istanbul-lib-instrument@6.0.3: + resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} + engines: {node: '>=10'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-lib-source-maps@4.0.1: + resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} + engines: {node: '>=10'} + + istanbul-reports@3.2.0: + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} + engines: {node: '>=8'} + jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - jiti@1.21.7: - resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} - hasBin: true + jest-changed-files@29.7.0: + resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + jest-circus@29.7.0: + resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jsesc@3.1.0: - resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} - engines: {node: '>=6'} + jest-cli@29.7.0: + resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true - json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true + jest-config@29.7.0: + resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@types/node': '*' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + ts-node: + optional: true - lilconfig@3.1.3: - resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} - engines: {node: '>=14'} + jest-diff@29.7.0: + resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + jest-docblock@29.7.0: + resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - loose-envify@1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} - hasBin: true + jest-each@29.7.0: + resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - lru-cache@10.4.3: - resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + jest-environment-node@29.7.0: + resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + jest-get-type@29.6.3: + resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - lucide-react@0.441.0: - resolution: {integrity: sha512-0vfExYtvSDhkC2lqg0zYVW1Uu9GsI4knuV9GP9by5z0Xhc4Zi5RejTxfz9LsjRmCyWVzHCJvxGKZWcRyvQCWVg==} - peerDependencies: - react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc + jest-haste-map@29.7.0: + resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} + jest-leak-detector@29.7.0: + resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - micromatch@4.0.8: - resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} - engines: {node: '>=8.6'} + jest-matcher-utils@29.7.0: + resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - minimatch@9.0.5: + jest-message-util@29.7.0: + resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-mock@29.7.0: + resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-pnp-resolver@1.2.3: + resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} + engines: {node: '>=6'} + peerDependencies: + jest-resolve: '*' + peerDependenciesMeta: + jest-resolve: + optional: true + + jest-regex-util@29.6.3: + resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-resolve-dependencies@29.7.0: + resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-resolve@29.7.0: + resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-runner@29.7.0: + resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-runtime@29.7.0: + resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-snapshot@29.7.0: + resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-util@29.7.0: + resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-validate@29.7.0: + resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-watcher@29.7.0: + resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-worker@29.7.0: + resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest@29.7.0: + resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + jiti@1.21.7: + resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} + hasBin: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + + leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + + lodash.memoize@4.1.2: + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lucide-react@0.441.0: + resolution: {integrity: sha512-0vfExYtvSDhkC2lqg0zYVW1Uu9GsI4knuV9GP9by5z0Xhc4Zi5RejTxfz9LsjRmCyWVzHCJvxGKZWcRyvQCWVg==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + + make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + + makeerror@1.0.12: + resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + minipass@7.1.2: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} - motion-dom@12.17.0: - resolution: {integrity: sha512-FA6/c70R9NKs3g41XDVONzmUUrEmyaifLVGCWtAmHP0usDnX9W+RN/tmbC4EUl0w6yLGvMTOwnWCFVgA5luhRg==} + motion-dom@12.23.18: + resolution: {integrity: sha512-9piw3uOcP6DpS0qpnDF95bLDzmgMxLOg/jghLnHwYJ0YFizzuvbH/L8106dy39JNgHYmXFUTztoP9JQvUqlBwQ==} - motion-utils@12.12.1: - resolution: {integrity: sha512-f9qiqUHm7hWSLlNW8gS9pisnsN7CRFRD58vNjptKdsqFLpkVnX00TNeD6Q0d27V9KzT7ySFyK1TZ/DShfVOv6w==} + motion-utils@12.23.6: + resolution: {integrity: sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==} ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -1548,14 +2177,23 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + next-themes@0.4.6: resolution: {integrity: sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==} peerDependencies: react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc - node-releases@2.0.19: - resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + node-int64@0.4.0: + resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + + node-releases@2.0.21: + resolution: {integrity: sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==} normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} @@ -1565,6 +2203,10 @@ packages: resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} engines: {node: '>=0.10.0'} + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -1573,9 +2215,44 @@ packages: resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} engines: {node: '>= 6'} + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -1602,14 +2279,18 @@ packages: resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} engines: {node: '>= 6'} + pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + postcss-import@15.1.0: resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} engines: {node: '>=14.0.0'} peerDependencies: postcss: ^8.0.0 - postcss-js@4.0.1: - resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} + postcss-js@4.1.0: + resolution: {integrity: sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==} engines: {node: ^12 || ^14 || >= 16} peerDependencies: postcss: ^8.4.21 @@ -1639,10 +2320,21 @@ packages: postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - postcss@8.5.4: - resolution: {integrity: sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w==} + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} + pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + + pure-rand@6.1.0: + resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -1651,6 +2343,9 @@ packages: peerDependencies: react: ^18.3.1 + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + react-refresh@0.17.0: resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} engines: {node: '>=0.10.0'} @@ -1719,6 +2414,22 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + resolve-cwd@3.0.0: + resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} + engines: {node: '>=8'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve.exports@2.0.3: + resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} + engines: {node: '>=10'} + resolve@1.22.10: resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} engines: {node: '>= 0.4'} @@ -1728,8 +2439,8 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rollup@4.50.1: - resolution: {integrity: sha512-78E9voJHwnXQMiQdiqswVLZwJIzdBKJ1GdI5Zx6XwoFKUIk09/sSrr+05QFzvYb8q6Y9pPV45zzDuYa3907TZA==} + rollup@4.52.0: + resolution: {integrity: sha512-+IuescNkTJQgX7AkIDtITipZdIGcWF0pnVvZTWStiazUmcGA2ag8dfg0urest2XlXUi9kuhfQ+qmdc5Stc3z7g==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -1743,6 +2454,11 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + engines: {node: '>=10'} + hasBin: true + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -1751,10 +2467,20 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + sonner@1.7.4: resolution: {integrity: sha512-DIS8z4PfJRbIyfVFDVnK9rO3eYDtse4Omcm6bt0oEr5/jtLgysmjuBl1frJ9E/EQZrFmKx2A8m/s5s9CRXIzhw==} peerDependencies: @@ -1765,6 +2491,24 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + source-map-support@0.5.13: + resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + stack-utils@2.0.6: + resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} + engines: {node: '>=10'} + + string-length@4.0.2: + resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} + engines: {node: '>=10'} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -1777,15 +2521,35 @@ packages: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} - strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + strip-ansi@7.1.2: + resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} engines: {node: '>=12'} + strip-bom@4.0.0: + resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} + engines: {node: '>=8'} + + strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + sucrase@3.35.0: resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} engines: {node: '>=16 || 14 >=14.17'} hasBin: true + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} @@ -1803,6 +2567,10 @@ packages: engines: {node: '>=14.0.0'} hasBin: true + test-exclude@6.0.0: + resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} + engines: {node: '>=8'} + thenify-all@1.6.0: resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} engines: {node: '>=0.8'} @@ -1810,6 +2578,9 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + tmpl@1.0.5: + resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -1817,14 +2588,58 @@ packages: ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + ts-jest@29.4.4: + resolution: {integrity: sha512-ccVcRABct5ZELCT5U0+DZwkXMCcOCLi2doHRrKy1nK/s7J7bch6TzJMsrY09WxgUUIP/ITfmcDS8D2yl63rnXw==} + engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@babel/core': '>=7.0.0-beta.0 <8' + '@jest/transform': ^29.0.0 || ^30.0.0 + '@jest/types': ^29.0.0 || ^30.0.0 + babel-jest: ^29.0.0 || ^30.0.0 + esbuild: '*' + jest: ^29.0.0 || ^30.0.0 + jest-util: ^29.0.0 || ^30.0.0 + typescript: '>=4.3 <6' + peerDependenciesMeta: + '@babel/core': + optional: true + '@jest/transform': + optional: true + '@jest/types': + optional: true + babel-jest: + optional: true + esbuild: + optional: true + jest-util: + optional: true + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - typescript@5.8.3: - resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} + + typescript@5.9.2: + resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==} 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'} + hasBin: true + undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} @@ -1857,6 +2672,10 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + v8-to-istanbul@9.3.0: + resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} + engines: {node: '>=10.12.0'} + vite@5.4.20: resolution: {integrity: sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g==} engines: {node: ^18.0.0 || >=20.0.0} @@ -1888,11 +2707,17 @@ packages: terser: optional: true + walker@1.0.8: + resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} hasBin: true + wordwrap@1.0.0: + resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} + wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -1901,80 +2726,100 @@ packages: resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} engines: {node: '>=12'} + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + write-file-atomic@4.0.2: + resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - yaml@2.8.0: - resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==} + yaml@2.8.1: + resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==} engines: {node: '>= 14.6'} hasBin: true + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + snapshots: '@alloc/quick-lru@5.2.0': {} - '@ampproject/remapping@2.3.0': - dependencies: - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 - '@babel/code-frame@7.27.1': dependencies: '@babel/helper-validator-identifier': 7.27.1 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.27.5': {} + '@babel/compat-data@7.28.4': {} - '@babel/core@7.27.4': + '@babel/core@7.28.4': dependencies: - '@ampproject/remapping': 2.3.0 '@babel/code-frame': 7.27.1 - '@babel/generator': 7.27.5 + '@babel/generator': 7.28.3 '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.4) - '@babel/helpers': 7.27.6 - '@babel/parser': 7.27.5 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) + '@babel/helpers': 7.28.4 + '@babel/parser': 7.28.4 '@babel/template': 7.27.2 - '@babel/traverse': 7.27.4 - '@babel/types': 7.27.6 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 - debug: 4.4.1 + debug: 4.4.3 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/generator@7.27.5': + '@babel/generator@7.28.3': dependencies: - '@babel/parser': 7.27.5 - '@babel/types': 7.27.6 - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 '@babel/helper-compilation-targets@7.27.2': dependencies: - '@babel/compat-data': 7.27.5 + '@babel/compat-data': 7.28.4 '@babel/helper-validator-option': 7.27.1 - browserslist: 4.25.0 + browserslist: 4.26.2 lru-cache: 5.1.1 semver: 6.3.1 + '@babel/helper-globals@7.28.0': {} + '@babel/helper-module-imports@7.27.1': dependencies: - '@babel/traverse': 7.27.4 - '@babel/types': 7.27.6 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.27.3(@babel/core@7.27.4)': + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.28.4 '@babel/helper-module-imports': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.27.4 + '@babel/traverse': 7.28.4 transitivePeerDependencies: - supports-color @@ -1986,50 +2831,137 @@ snapshots: '@babel/helper-validator-option@7.27.1': {} - '@babel/helpers@7.27.6': + '@babel/helpers@7.28.4': dependencies: '@babel/template': 7.27.2 - '@babel/types': 7.27.6 + '@babel/types': 7.28.4 + + '@babel/parser@7.28.4': + dependencies: + '@babel/types': 7.28.4 + + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/parser@7.27.5': + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.28.4)': dependencies: - '@babel/types': 7.27.6 + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/runtime@7.27.6': {} + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/runtime@7.28.4': {} '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 - '@babel/parser': 7.27.5 - '@babel/types': 7.27.6 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 - '@babel/traverse@7.27.4': + '@babel/traverse@7.28.4': dependencies: '@babel/code-frame': 7.27.1 - '@babel/generator': 7.27.5 - '@babel/parser': 7.27.5 + '@babel/generator': 7.28.3 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.4 '@babel/template': 7.27.2 - '@babel/types': 7.27.6 - debug: 4.4.1 - globals: 11.12.0 + '@babel/types': 7.28.4 + debug: 4.4.3 transitivePeerDependencies: - supports-color - '@babel/types@7.27.6': + '@babel/types@7.28.4': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 + '@bcoe/v8-coverage@0.2.3': {} + '@esbuild/aix-ppc64@0.21.5': optional: true @@ -2099,55 +3031,229 @@ snapshots: '@esbuild/win32-x64@0.21.5': optional: true - '@floating-ui/core@1.7.1': + '@floating-ui/core@1.7.3': dependencies: - '@floating-ui/utils': 0.2.9 + '@floating-ui/utils': 0.2.10 - '@floating-ui/dom@1.7.1': + '@floating-ui/dom@1.7.4': dependencies: - '@floating-ui/core': 1.7.1 - '@floating-ui/utils': 0.2.9 + '@floating-ui/core': 1.7.3 + '@floating-ui/utils': 0.2.10 - '@floating-ui/react-dom@2.1.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@floating-ui/react-dom@2.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@floating-ui/dom': 1.7.1 + '@floating-ui/dom': 1.7.4 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@floating-ui/utils@0.2.9': {} + '@floating-ui/utils@0.2.10': {} '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.0 + strip-ansi: 7.1.2 strip-ansi-cjs: strip-ansi@6.0.1 wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 - '@jridgewell/gen-mapping@0.3.8': - dependencies: - '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.5.0 - '@jridgewell/trace-mapping': 0.3.25 - - '@jridgewell/resolve-uri@3.1.2': {} + '@istanbuljs/load-nyc-config@1.1.0': + dependencies: + camelcase: 5.3.1 + find-up: 4.1.0 + get-package-type: 0.1.0 + js-yaml: 3.14.1 + resolve-from: 5.0.0 + + '@istanbuljs/schema@0.1.3': {} + + '@jest/console@29.7.0': + dependencies: + '@jest/types': 29.6.3 + '@types/node': 22.18.6 + chalk: 4.1.2 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + + '@jest/core@29.7.0': + dependencies: + '@jest/console': 29.7.0 + '@jest/reporters': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 22.18.6 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.9.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 29.7.0 + jest-config: 29.7.0(@types/node@22.18.6) + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-resolve-dependencies: 29.7.0 + jest-runner: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + jest-watcher: 29.7.0 + micromatch: 4.0.8 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + - ts-node - '@jridgewell/set-array@1.2.1': {} + '@jest/environment@29.7.0': + dependencies: + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 22.18.6 + jest-mock: 29.7.0 - '@jridgewell/sourcemap-codec@1.5.0': {} + '@jest/expect-utils@29.7.0': + dependencies: + jest-get-type: 29.6.3 - '@jridgewell/trace-mapping@0.3.25': + '@jest/expect@29.7.0': dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.0 + expect: 29.7.0 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color - '@nodelib/fs.scandir@2.1.5': + '@jest/fake-timers@29.7.0': dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 + '@jest/types': 29.6.3 + '@sinonjs/fake-timers': 10.3.0 + '@types/node': 22.18.6 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-util: 29.7.0 - '@nodelib/fs.stat@2.0.5': {} + '@jest/globals@29.7.0': + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/types': 29.6.3 + jest-mock: 29.7.0 + transitivePeerDependencies: + - supports-color + + '@jest/reporters@29.7.0': + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.31 + '@types/node': 22.18.6 + chalk: 4.1.2 + collect-v8-coverage: 1.0.2 + exit: 0.1.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 6.0.3 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.2.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + jest-worker: 29.7.0 + slash: 3.0.0 + string-length: 4.0.2 + strip-ansi: 6.0.1 + v8-to-istanbul: 9.3.0 + transitivePeerDependencies: + - supports-color + + '@jest/schemas@29.6.3': + dependencies: + '@sinclair/typebox': 0.27.8 + + '@jest/source-map@29.6.3': + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + callsites: 3.1.0 + graceful-fs: 4.2.11 + + '@jest/test-result@29.7.0': + dependencies: + '@jest/console': 29.7.0 + '@jest/types': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + collect-v8-coverage: 1.0.2 + + '@jest/test-sequencer@29.7.0': + dependencies: + '@jest/test-result': 29.7.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + slash: 3.0.0 + + '@jest/transform@29.7.0': + dependencies: + '@babel/core': 7.28.4 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.31 + babel-plugin-istanbul: 6.1.1 + chalk: 4.1.2 + convert-source-map: 2.0.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + micromatch: 4.0.8 + pirates: 4.0.7 + slash: 3.0.0 + write-file-atomic: 4.0.2 + transitivePeerDependencies: + - supports-color + + '@jest/types@29.6.3': + dependencies: + '@jest/schemas': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 22.18.6 + '@types/yargs': 17.0.33 + chalk: 4.1.2 + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@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: @@ -2161,788 +3267,834 @@ snapshots: '@radix-ui/primitive@1.0.1': dependencies: - '@babel/runtime': 7.27.6 + '@babel/runtime': 7.28.4 - '@radix-ui/primitive@1.1.2': {} + '@radix-ui/primitive@1.1.3': {} - '@radix-ui/react-arrow@1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-arrow@1.1.7(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.23 - '@types/react-dom': 18.3.7(@types/react@18.3.23) - - '@radix-ui/react-checkbox@1.3.2(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-use-previous': 1.1.1(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-use-size': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) + + '@radix-ui/react-checkbox@1.3.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-previous': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-size': 1.1.1(@types/react@18.3.24)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.23 - '@types/react-dom': 18.3.7(@types/react@18.3.23) + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) - '@radix-ui/react-collection@1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-collection@1.1.7(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.2.3(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.24)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.23 - '@types/react-dom': 18.3.7(@types/react@18.3.23) + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) - '@radix-ui/react-compose-refs@1.0.1(@types/react@18.3.23)(react@18.3.1)': + '@radix-ui/react-compose-refs@1.0.1(@types/react@18.3.24)(react@18.3.1)': dependencies: - '@babel/runtime': 7.27.6 + '@babel/runtime': 7.28.4 react: 18.3.1 optionalDependencies: - '@types/react': 18.3.23 + '@types/react': 18.3.24 - '@radix-ui/react-compose-refs@1.1.2(@types/react@18.3.23)(react@18.3.1)': + '@radix-ui/react-compose-refs@1.1.2(@types/react@18.3.24)(react@18.3.1)': dependencies: react: 18.3.1 optionalDependencies: - '@types/react': 18.3.23 + '@types/react': 18.3.24 - '@radix-ui/react-context@1.0.1(@types/react@18.3.23)(react@18.3.1)': + '@radix-ui/react-context@1.0.1(@types/react@18.3.24)(react@18.3.1)': dependencies: - '@babel/runtime': 7.27.6 + '@babel/runtime': 7.28.4 react: 18.3.1 optionalDependencies: - '@types/react': 18.3.23 + '@types/react': 18.3.24 - '@radix-ui/react-context@1.1.2(@types/react@18.3.23)(react@18.3.1)': + '@radix-ui/react-context@1.1.2(@types/react@18.3.24)(react@18.3.1)': dependencies: react: 18.3.1 optionalDependencies: - '@types/react': 18.3.23 + '@types/react': 18.3.24 - '@radix-ui/react-dialog@1.0.5(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-dialog@1.0.5(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.27.6 + '@babel/runtime': 7.28.4 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-context': 1.0.1(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-id': 1.0.1(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.0.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-context': 1.0.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.0.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.0.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.3.24)(react@18.3.1) aria-hidden: 1.2.6 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-remove-scroll: 2.5.5(@types/react@18.3.23)(react@18.3.1) - optionalDependencies: - '@types/react': 18.3.23 - '@types/react-dom': 18.3.7(@types/react@18.3.23) - - '@radix-ui/react-dialog@1.1.14(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-focus-guards': 1.1.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-id': 1.1.1(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.2.3(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) + react-remove-scroll: 2.5.5(@types/react@18.3.24)(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) + + '@radix-ui/react-dialog@1.1.15(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.24)(react@18.3.1) aria-hidden: 1.2.6 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-remove-scroll: 2.7.1(@types/react@18.3.23)(react@18.3.1) + react-remove-scroll: 2.7.1(@types/react@18.3.24)(react@18.3.1) optionalDependencies: - '@types/react': 18.3.23 - '@types/react-dom': 18.3.7(@types/react@18.3.23) + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) - '@radix-ui/react-direction@1.1.1(@types/react@18.3.23)(react@18.3.1)': + '@radix-ui/react-direction@1.1.1(@types/react@18.3.24)(react@18.3.1)': dependencies: react: 18.3.1 optionalDependencies: - '@types/react': 18.3.23 + '@types/react': 18.3.24 - '@radix-ui/react-dismissable-layer@1.0.5(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-dismissable-layer@1.0.5(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.27.6 + '@babel/runtime': 7.28.4 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@18.3.24)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.23 - '@types/react-dom': 18.3.7(@types/react@18.3.23) + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) - '@radix-ui/react-dismissable-layer@1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@18.3.24)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.23 - '@types/react-dom': 18.3.7(@types/react@18.3.23) - - '@radix-ui/react-dropdown-menu@2.1.15(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-id': 1.1.1(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-menu': 2.1.15(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) + + '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.24)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.23 - '@types/react-dom': 18.3.7(@types/react@18.3.23) + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) - '@radix-ui/react-focus-guards@1.0.1(@types/react@18.3.23)(react@18.3.1)': + '@radix-ui/react-focus-guards@1.0.1(@types/react@18.3.24)(react@18.3.1)': dependencies: - '@babel/runtime': 7.27.6 + '@babel/runtime': 7.28.4 react: 18.3.1 optionalDependencies: - '@types/react': 18.3.23 + '@types/react': 18.3.24 - '@radix-ui/react-focus-guards@1.1.2(@types/react@18.3.23)(react@18.3.1)': + '@radix-ui/react-focus-guards@1.1.3(@types/react@18.3.24)(react@18.3.1)': dependencies: react: 18.3.1 optionalDependencies: - '@types/react': 18.3.23 + '@types/react': 18.3.24 - '@radix-ui/react-focus-scope@1.0.4(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-focus-scope@1.0.4(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.27.6 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.23)(react@18.3.1) + '@babel/runtime': 7.28.4 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.24)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.23 - '@types/react-dom': 18.3.7(@types/react@18.3.23) + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) - '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.24)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.23 - '@types/react-dom': 18.3.7(@types/react@18.3.23) + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) '@radix-ui/react-icons@1.3.2(react@18.3.1)': dependencies: react: 18.3.1 - '@radix-ui/react-id@1.0.1(@types/react@18.3.23)(react@18.3.1)': + '@radix-ui/react-id@1.0.1(@types/react@18.3.24)(react@18.3.1)': dependencies: - '@babel/runtime': 7.27.6 - '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.3.23)(react@18.3.1) + '@babel/runtime': 7.28.4 + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.3.24)(react@18.3.1) react: 18.3.1 optionalDependencies: - '@types/react': 18.3.23 + '@types/react': 18.3.24 - '@radix-ui/react-id@1.1.1(@types/react@18.3.23)(react@18.3.1)': + '@radix-ui/react-id@1.1.1(@types/react@18.3.24)(react@18.3.1)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.24)(react@18.3.1) react: 18.3.1 optionalDependencies: - '@types/react': 18.3.23 + '@types/react': 18.3.24 - '@radix-ui/react-label@2.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-label@2.1.7(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.23 - '@types/react-dom': 18.3.7(@types/react@18.3.23) - - '@radix-ui/react-menu@2.1.15(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-direction': 1.1.1(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-focus-guards': 1.1.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-id': 1.1.1(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-popper': 1.2.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-roving-focus': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.2.3(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) + + '@radix-ui/react-menu@2.1.16(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.24)(react@18.3.1) aria-hidden: 1.2.6 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-remove-scroll: 2.7.1(@types/react@18.3.23)(react@18.3.1) + react-remove-scroll: 2.7.1(@types/react@18.3.24)(react@18.3.1) optionalDependencies: - '@types/react': 18.3.23 - '@types/react-dom': 18.3.7(@types/react@18.3.23) - - '@radix-ui/react-menubar@1.1.15(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-direction': 1.1.1(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-id': 1.1.1(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-menu': 2.1.15(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-roving-focus': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) + + '@radix-ui/react-menubar@1.1.16(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.24)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.23 - '@types/react-dom': 18.3.7(@types/react@18.3.23) - - '@radix-ui/react-popover@1.1.14(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-focus-guards': 1.1.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-id': 1.1.1(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-popper': 1.2.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.2.3(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) + + '@radix-ui/react-popover@1.1.15(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.24)(react@18.3.1) aria-hidden: 1.2.6 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-remove-scroll: 2.7.1(@types/react@18.3.23)(react@18.3.1) - optionalDependencies: - '@types/react': 18.3.23 - '@types/react-dom': 18.3.7(@types/react@18.3.23) - - '@radix-ui/react-popper@1.2.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@floating-ui/react-dom': 2.1.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-arrow': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-use-rect': 1.1.1(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-use-size': 1.1.1(@types/react@18.3.23)(react@18.3.1) + react-remove-scroll: 2.7.1(@types/react@18.3.24)(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) + + '@radix-ui/react-popper@1.2.8(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@floating-ui/react-dom': 2.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-rect': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-size': 1.1.1(@types/react@18.3.24)(react@18.3.1) '@radix-ui/rect': 1.1.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.23 - '@types/react-dom': 18.3.7(@types/react@18.3.23) + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) - '@radix-ui/react-portal@1.0.4(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-portal@1.0.4(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.27.6 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@babel/runtime': 7.28.4 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.23 - '@types/react-dom': 18.3.7(@types/react@18.3.23) + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) - '@radix-ui/react-portal@1.1.9(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-portal@1.1.9(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.24)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.23 - '@types/react-dom': 18.3.7(@types/react@18.3.23) + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) - '@radix-ui/react-presence@1.0.1(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-presence@1.0.1(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.27.6 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.3.23)(react@18.3.1) + '@babel/runtime': 7.28.4 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.3.24)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.23 - '@types/react-dom': 18.3.7(@types/react@18.3.23) + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) - '@radix-ui/react-presence@1.1.4(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-presence@1.1.5(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.24)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.23 - '@types/react-dom': 18.3.7(@types/react@18.3.23) + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) - '@radix-ui/react-primitive@1.0.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-primitive@1.0.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.27.6 - '@radix-ui/react-slot': 1.0.2(@types/react@18.3.23)(react@18.3.1) + '@babel/runtime': 7.28.4 + '@radix-ui/react-slot': 1.0.2(@types/react@18.3.24)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.23 - '@types/react-dom': 18.3.7(@types/react@18.3.23) + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) - '@radix-ui/react-primitive@2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-primitive@2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/react-slot': 1.2.3(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.24)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.23 - '@types/react-dom': 18.3.7(@types/react@18.3.23) - - '@radix-ui/react-radio-group@1.3.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-direction': 1.1.1(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-roving-focus': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-use-previous': 1.1.1(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-use-size': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) + + '@radix-ui/react-radio-group@1.3.8(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-previous': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-size': 1.1.1(@types/react@18.3.24)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.23 - '@types/react-dom': 18.3.7(@types/react@18.3.23) - - '@radix-ui/react-roving-focus@1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-direction': 1.1.1(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-id': 1.1.1(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) + + '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.24)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.23 - '@types/react-dom': 18.3.7(@types/react@18.3.23) + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) - '@radix-ui/react-select@2.2.5(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-select@2.2.6(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/number': 1.1.1 - '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-direction': 1.1.1(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-focus-guards': 1.1.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-id': 1.1.1(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-popper': 1.2.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.2.3(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-use-previous': 1.1.1(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-previous': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) aria-hidden: 1.2.6 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-remove-scroll: 2.7.1(@types/react@18.3.23)(react@18.3.1) + react-remove-scroll: 2.7.1(@types/react@18.3.24)(react@18.3.1) optionalDependencies: - '@types/react': 18.3.23 - '@types/react-dom': 18.3.7(@types/react@18.3.23) + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) - '@radix-ui/react-separator@1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-separator@1.1.7(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.23 - '@types/react-dom': 18.3.7(@types/react@18.3.23) + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) - '@radix-ui/react-slot@1.0.2(@types/react@18.3.23)(react@18.3.1)': + '@radix-ui/react-slot@1.0.2(@types/react@18.3.24)(react@18.3.1)': dependencies: - '@babel/runtime': 7.27.6 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.23)(react@18.3.1) + '@babel/runtime': 7.28.4 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.24)(react@18.3.1) react: 18.3.1 optionalDependencies: - '@types/react': 18.3.23 + '@types/react': 18.3.24 - '@radix-ui/react-slot@1.2.3(@types/react@18.3.23)(react@18.3.1)': + '@radix-ui/react-slot@1.2.3(@types/react@18.3.24)(react@18.3.1)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.24)(react@18.3.1) react: 18.3.1 optionalDependencies: - '@types/react': 18.3.23 + '@types/react': 18.3.24 - '@radix-ui/react-switch@1.2.5(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-switch@1.2.6(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-use-previous': 1.1.1(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-use-size': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-previous': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-size': 1.1.1(@types/react@18.3.24)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.23 - '@types/react-dom': 18.3.7(@types/react@18.3.23) - - '@radix-ui/react-tabs@1.1.12(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-direction': 1.1.1(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-id': 1.1.1(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-roving-focus': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) + + '@radix-ui/react-tabs@1.1.13(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-context': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.24)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.23 - '@types/react-dom': 18.3.7(@types/react@18.3.23) - - '@radix-ui/react-tooltip@1.2.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-id': 1.1.1(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-popper': 1.2.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.2.3(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) + + '@radix-ui/react-tooltip@1.2.8(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.23 - '@types/react-dom': 18.3.7(@types/react@18.3.23) + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) - '@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.3.23)(react@18.3.1)': + '@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.3.24)(react@18.3.1)': dependencies: - '@babel/runtime': 7.27.6 + '@babel/runtime': 7.28.4 react: 18.3.1 optionalDependencies: - '@types/react': 18.3.23 + '@types/react': 18.3.24 - '@radix-ui/react-use-callback-ref@1.1.1(@types/react@18.3.23)(react@18.3.1)': + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@18.3.24)(react@18.3.1)': dependencies: react: 18.3.1 optionalDependencies: - '@types/react': 18.3.23 + '@types/react': 18.3.24 - '@radix-ui/react-use-controllable-state@1.0.1(@types/react@18.3.23)(react@18.3.1)': + '@radix-ui/react-use-controllable-state@1.0.1(@types/react@18.3.24)(react@18.3.1)': dependencies: - '@babel/runtime': 7.27.6 - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.23)(react@18.3.1) + '@babel/runtime': 7.28.4 + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.24)(react@18.3.1) react: 18.3.1 optionalDependencies: - '@types/react': 18.3.23 + '@types/react': 18.3.24 - '@radix-ui/react-use-controllable-state@1.2.2(@types/react@18.3.23)(react@18.3.1)': + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@18.3.24)(react@18.3.1)': dependencies: - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@18.3.23)(react@18.3.1) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.24)(react@18.3.1) react: 18.3.1 optionalDependencies: - '@types/react': 18.3.23 + '@types/react': 18.3.24 - '@radix-ui/react-use-effect-event@0.0.2(@types/react@18.3.23)(react@18.3.1)': + '@radix-ui/react-use-effect-event@0.0.2(@types/react@18.3.24)(react@18.3.1)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.24)(react@18.3.1) react: 18.3.1 optionalDependencies: - '@types/react': 18.3.23 + '@types/react': 18.3.24 - '@radix-ui/react-use-escape-keydown@1.0.3(@types/react@18.3.23)(react@18.3.1)': + '@radix-ui/react-use-escape-keydown@1.0.3(@types/react@18.3.24)(react@18.3.1)': dependencies: - '@babel/runtime': 7.27.6 - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.23)(react@18.3.1) + '@babel/runtime': 7.28.4 + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.24)(react@18.3.1) react: 18.3.1 optionalDependencies: - '@types/react': 18.3.23 + '@types/react': 18.3.24 - '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@18.3.23)(react@18.3.1)': + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@18.3.24)(react@18.3.1)': dependencies: - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.24)(react@18.3.1) react: 18.3.1 optionalDependencies: - '@types/react': 18.3.23 + '@types/react': 18.3.24 - '@radix-ui/react-use-layout-effect@1.0.1(@types/react@18.3.23)(react@18.3.1)': + '@radix-ui/react-use-layout-effect@1.0.1(@types/react@18.3.24)(react@18.3.1)': dependencies: - '@babel/runtime': 7.27.6 + '@babel/runtime': 7.28.4 react: 18.3.1 optionalDependencies: - '@types/react': 18.3.23 + '@types/react': 18.3.24 - '@radix-ui/react-use-layout-effect@1.1.1(@types/react@18.3.23)(react@18.3.1)': + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@18.3.24)(react@18.3.1)': dependencies: react: 18.3.1 optionalDependencies: - '@types/react': 18.3.23 + '@types/react': 18.3.24 - '@radix-ui/react-use-previous@1.1.1(@types/react@18.3.23)(react@18.3.1)': + '@radix-ui/react-use-previous@1.1.1(@types/react@18.3.24)(react@18.3.1)': dependencies: react: 18.3.1 optionalDependencies: - '@types/react': 18.3.23 + '@types/react': 18.3.24 - '@radix-ui/react-use-rect@1.1.1(@types/react@18.3.23)(react@18.3.1)': + '@radix-ui/react-use-rect@1.1.1(@types/react@18.3.24)(react@18.3.1)': dependencies: '@radix-ui/rect': 1.1.1 react: 18.3.1 optionalDependencies: - '@types/react': 18.3.23 + '@types/react': 18.3.24 - '@radix-ui/react-use-size@1.1.1(@types/react@18.3.23)(react@18.3.1)': + '@radix-ui/react-use-size@1.1.1(@types/react@18.3.24)(react@18.3.1)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.24)(react@18.3.1) react: 18.3.1 optionalDependencies: - '@types/react': 18.3.23 + '@types/react': 18.3.24 - '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.23 - '@types/react-dom': 18.3.7(@types/react@18.3.23) + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) '@radix-ui/rect@1.1.1': {} '@remix-run/router@1.23.0': {} - '@rolldown/pluginutils@1.0.0-beta.11': {} + '@rolldown/pluginutils@1.0.0-beta.27': {} + + '@rollup/rollup-android-arm-eabi@4.52.0': + optional: true - '@rollup/rollup-android-arm-eabi@4.50.1': + '@rollup/rollup-android-arm64@4.52.0': optional: true - '@rollup/rollup-android-arm64@4.50.1': + '@rollup/rollup-darwin-arm64@4.52.0': optional: true - '@rollup/rollup-darwin-arm64@4.50.1': + '@rollup/rollup-darwin-x64@4.52.0': optional: true - '@rollup/rollup-darwin-x64@4.50.1': + '@rollup/rollup-freebsd-arm64@4.52.0': optional: true - '@rollup/rollup-freebsd-arm64@4.50.1': + '@rollup/rollup-freebsd-x64@4.52.0': optional: true - '@rollup/rollup-freebsd-x64@4.50.1': + '@rollup/rollup-linux-arm-gnueabihf@4.52.0': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.50.1': + '@rollup/rollup-linux-arm-musleabihf@4.52.0': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.50.1': + '@rollup/rollup-linux-arm64-gnu@4.52.0': optional: true - '@rollup/rollup-linux-arm64-gnu@4.50.1': + '@rollup/rollup-linux-arm64-musl@4.52.0': optional: true - '@rollup/rollup-linux-arm64-musl@4.50.1': + '@rollup/rollup-linux-loong64-gnu@4.52.0': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.50.1': + '@rollup/rollup-linux-ppc64-gnu@4.52.0': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.50.1': + '@rollup/rollup-linux-riscv64-gnu@4.52.0': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.50.1': + '@rollup/rollup-linux-riscv64-musl@4.52.0': optional: true - '@rollup/rollup-linux-riscv64-musl@4.50.1': + '@rollup/rollup-linux-s390x-gnu@4.52.0': optional: true - '@rollup/rollup-linux-s390x-gnu@4.50.1': + '@rollup/rollup-linux-x64-gnu@4.52.0': optional: true - '@rollup/rollup-linux-x64-gnu@4.50.1': + '@rollup/rollup-linux-x64-musl@4.52.0': optional: true - '@rollup/rollup-linux-x64-musl@4.50.1': + '@rollup/rollup-openharmony-arm64@4.52.0': optional: true - '@rollup/rollup-openharmony-arm64@4.50.1': + '@rollup/rollup-win32-arm64-msvc@4.52.0': optional: true - '@rollup/rollup-win32-arm64-msvc@4.50.1': + '@rollup/rollup-win32-ia32-msvc@4.52.0': optional: true - '@rollup/rollup-win32-ia32-msvc@4.50.1': + '@rollup/rollup-win32-x64-gnu@4.52.0': optional: true - '@rollup/rollup-win32-x64-msvc@4.50.1': + '@rollup/rollup-win32-x64-msvc@4.52.0': optional: true - '@tauri-apps/api@2.5.0': {} + '@sinclair/typebox@0.27.8': {} - '@tauri-apps/cli-darwin-arm64@2.5.0': + '@sinonjs/commons@3.0.1': + dependencies: + type-detect: 4.0.8 + + '@sinonjs/fake-timers@10.3.0': + dependencies: + '@sinonjs/commons': 3.0.1 + + '@tauri-apps/api@2.8.0': {} + + '@tauri-apps/cli-darwin-arm64@2.8.4': optional: true - '@tauri-apps/cli-darwin-x64@2.5.0': + '@tauri-apps/cli-darwin-x64@2.8.4': optional: true - '@tauri-apps/cli-linux-arm-gnueabihf@2.5.0': + '@tauri-apps/cli-linux-arm-gnueabihf@2.8.4': optional: true - '@tauri-apps/cli-linux-arm64-gnu@2.5.0': + '@tauri-apps/cli-linux-arm64-gnu@2.8.4': optional: true - '@tauri-apps/cli-linux-arm64-musl@2.5.0': + '@tauri-apps/cli-linux-arm64-musl@2.8.4': optional: true - '@tauri-apps/cli-linux-riscv64-gnu@2.5.0': + '@tauri-apps/cli-linux-riscv64-gnu@2.8.4': optional: true - '@tauri-apps/cli-linux-x64-gnu@2.5.0': + '@tauri-apps/cli-linux-x64-gnu@2.8.4': optional: true - '@tauri-apps/cli-linux-x64-musl@2.5.0': + '@tauri-apps/cli-linux-x64-musl@2.8.4': optional: true - '@tauri-apps/cli-win32-arm64-msvc@2.5.0': + '@tauri-apps/cli-win32-arm64-msvc@2.8.4': optional: true - '@tauri-apps/cli-win32-ia32-msvc@2.5.0': + '@tauri-apps/cli-win32-ia32-msvc@2.8.4': optional: true - '@tauri-apps/cli-win32-x64-msvc@2.5.0': + '@tauri-apps/cli-win32-x64-msvc@2.8.4': optional: true - '@tauri-apps/cli@2.5.0': + '@tauri-apps/cli@2.8.4': optionalDependencies: - '@tauri-apps/cli-darwin-arm64': 2.5.0 - '@tauri-apps/cli-darwin-x64': 2.5.0 - '@tauri-apps/cli-linux-arm-gnueabihf': 2.5.0 - '@tauri-apps/cli-linux-arm64-gnu': 2.5.0 - '@tauri-apps/cli-linux-arm64-musl': 2.5.0 - '@tauri-apps/cli-linux-riscv64-gnu': 2.5.0 - '@tauri-apps/cli-linux-x64-gnu': 2.5.0 - '@tauri-apps/cli-linux-x64-musl': 2.5.0 - '@tauri-apps/cli-win32-arm64-msvc': 2.5.0 - '@tauri-apps/cli-win32-ia32-msvc': 2.5.0 - '@tauri-apps/cli-win32-x64-msvc': 2.5.0 + '@tauri-apps/cli-darwin-arm64': 2.8.4 + '@tauri-apps/cli-darwin-x64': 2.8.4 + '@tauri-apps/cli-linux-arm-gnueabihf': 2.8.4 + '@tauri-apps/cli-linux-arm64-gnu': 2.8.4 + '@tauri-apps/cli-linux-arm64-musl': 2.8.4 + '@tauri-apps/cli-linux-riscv64-gnu': 2.8.4 + '@tauri-apps/cli-linux-x64-gnu': 2.8.4 + '@tauri-apps/cli-linux-x64-musl': 2.8.4 + '@tauri-apps/cli-win32-arm64-msvc': 2.8.4 + '@tauri-apps/cli-win32-ia32-msvc': 2.8.4 + '@tauri-apps/cli-win32-x64-msvc': 2.8.4 '@tauri-apps/plugin-dialog@2.2.2': dependencies: - '@tauri-apps/api': 2.5.0 + '@tauri-apps/api': 2.8.0 '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.27.5 - '@babel/types': 7.27.6 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 '@types/babel__generator': 7.27.0 '@types/babel__template': 7.4.4 - '@types/babel__traverse': 7.20.7 + '@types/babel__traverse': 7.28.0 '@types/babel__generator@7.27.0': dependencies: - '@babel/types': 7.27.6 + '@babel/types': 7.28.4 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.27.5 - '@babel/types': 7.27.6 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 - '@types/babel__traverse@7.20.7': + '@types/babel__traverse@7.28.0': dependencies: - '@babel/types': 7.27.6 + '@babel/types': 7.28.4 '@types/estree@1.0.8': {} - '@types/node@22.15.31': + '@types/graceful-fs@4.1.9': + dependencies: + '@types/node': 22.18.6 + + '@types/istanbul-lib-coverage@2.0.6': {} + + '@types/istanbul-lib-report@3.0.3': + dependencies: + '@types/istanbul-lib-coverage': 2.0.6 + + '@types/istanbul-reports@3.0.4': + dependencies: + '@types/istanbul-lib-report': 3.0.3 + + '@types/jest@29.5.14': + dependencies: + expect: 29.7.0 + pretty-format: 29.7.0 + + '@types/node@22.18.6': dependencies: undici-types: 6.21.0 '@types/prop-types@15.7.15': {} - '@types/react-dom@18.3.7(@types/react@18.3.23)': + '@types/react-dom@18.3.7(@types/react@18.3.24)': dependencies: - '@types/react': 18.3.23 + '@types/react': 18.3.24 - '@types/react@18.3.23': + '@types/react@18.3.24': dependencies: '@types/prop-types': 15.7.15 csstype: 3.1.3 - '@vitejs/plugin-react@4.5.2(vite@5.4.20(@types/node@22.15.31))': + '@types/stack-utils@2.0.3': {} + + '@types/yargs-parser@21.0.3': {} + + '@types/yargs@17.0.33': dependencies: - '@babel/core': 7.27.4 - '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.27.4) - '@rolldown/pluginutils': 1.0.0-beta.11 + '@types/yargs-parser': 21.0.3 + + '@vitejs/plugin-react@4.7.0(vite@5.4.20(@types/node@22.18.6))': + dependencies: + '@babel/core': 7.28.4 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.4) + '@rolldown/pluginutils': 1.0.0-beta.27 '@types/babel__core': 7.20.5 react-refresh: 0.17.0 - vite: 5.4.20(@types/node@22.15.31) + vite: 5.4.20(@types/node@22.18.6) transitivePeerDependencies: - supports-color + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + ansi-regex@5.0.1: {} - ansi-regex@6.1.0: {} + ansi-regex@6.2.2: {} ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 - ansi-styles@6.2.1: {} + ansi-styles@5.2.0: {} + + ansi-styles@6.2.3: {} any-promise@1.3.0: {} @@ -2953,24 +4105,90 @@ snapshots: arg@5.0.2: {} + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + aria-hidden@1.2.6: dependencies: tslib: 2.8.1 - autoprefixer@10.4.21(postcss@8.5.4): + autoprefixer@10.4.21(postcss@8.5.6): dependencies: - browserslist: 4.25.0 - caniuse-lite: 1.0.30001722 + browserslist: 4.26.2 + caniuse-lite: 1.0.30001743 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 - postcss: 8.5.4 + postcss: 8.5.6 postcss-value-parser: 4.2.0 + babel-jest@29.7.0(@babel/core@7.28.4): + dependencies: + '@babel/core': 7.28.4 + '@jest/transform': 29.7.0 + '@types/babel__core': 7.20.5 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 29.6.3(@babel/core@7.28.4) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-istanbul@6.1.1: + dependencies: + '@babel/helper-plugin-utils': 7.27.1 + '@istanbuljs/load-nyc-config': 1.1.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-instrument: 5.2.1 + test-exclude: 6.0.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-jest-hoist@29.6.3: + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.4 + '@types/babel__core': 7.20.5 + '@types/babel__traverse': 7.28.0 + + babel-preset-current-node-syntax@1.2.0(@babel/core@7.28.4): + dependencies: + '@babel/core': 7.28.4 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.28.4) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.28.4) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.28.4) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.28.4) + '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.28.4) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.28.4) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.28.4) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.28.4) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.28.4) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.28.4) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.28.4) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.28.4) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.28.4) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.28.4) + + babel-preset-jest@29.6.3(@babel/core@7.28.4): + dependencies: + '@babel/core': 7.28.4 + babel-plugin-jest-hoist: 29.6.3 + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.4) + balanced-match@1.0.2: {} + baseline-browser-mapping@2.8.6: {} + binary-extensions@2.3.0: {} + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + brace-expansion@2.0.2: dependencies: balanced-match: 1.0.2 @@ -2979,16 +4197,40 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.25.0: + browserslist@4.26.2: dependencies: - caniuse-lite: 1.0.30001722 - electron-to-chromium: 1.5.166 - node-releases: 2.0.19 - update-browserslist-db: 1.1.3(browserslist@4.25.0) + baseline-browser-mapping: 2.8.6 + caniuse-lite: 1.0.30001743 + electron-to-chromium: 1.5.222 + node-releases: 2.0.21 + update-browserslist-db: 1.1.3(browserslist@4.26.2) + + bs-logger@0.2.6: + dependencies: + fast-json-stable-stringify: 2.1.0 + + bser@2.1.1: + dependencies: + node-int64: 0.4.0 + + buffer-from@1.1.2: {} + + callsites@3.1.0: {} camelcase-css@2.0.1: {} - caniuse-lite@1.0.30001722: {} + camelcase@5.3.1: {} + + camelcase@6.3.0: {} + + caniuse-lite@1.0.30001743: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + char-regex@1.0.2: {} chokidar@3.6.0: dependencies: @@ -3002,22 +4244,36 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + ci-info@3.9.0: {} + + cjs-module-lexer@1.4.3: {} + class-variance-authority@0.7.1: dependencies: clsx: 2.1.1 + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + clsx@2.1.1: {} - cmdk@1.0.0(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + cmdk@1.0.0(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@radix-ui/react-dialog': 1.0.5(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-dialog': 1.0.5(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) transitivePeerDependencies: - '@types/react' - '@types/react-dom' + co@4.6.0: {} + + collect-v8-coverage@1.0.2: {} + color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -3026,8 +4282,25 @@ snapshots: commander@4.1.1: {} + concat-map@0.0.1: {} + convert-source-map@2.0.0: {} + create-jest@29.7.0(@types/node@22.18.6): + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-config: 29.7.0(@types/node@22.18.6) + jest-util: 29.7.0 + prompts: 2.4.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -3038,24 +4311,38 @@ snapshots: csstype@3.1.3: {} - debug@4.4.1: + debug@4.4.3: dependencies: ms: 2.1.3 + dedent@1.7.0: {} + + deepmerge@4.3.1: {} + + detect-newline@3.1.0: {} + detect-node-es@1.1.0: {} didyoumean@1.2.2: {} + diff-sequences@29.6.3: {} + dlv@1.1.3: {} eastasianwidth@0.2.0: {} - electron-to-chromium@1.5.166: {} + electron-to-chromium@1.5.222: {} + + emittery@0.13.1: {} emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + esbuild@0.21.5: optionalDependencies: '@esbuild/aix-ppc64': 0.21.5 @@ -3084,6 +4371,32 @@ snapshots: escalade@3.2.0: {} + escape-string-regexp@2.0.0: {} + + esprima@4.0.1: {} + + execa@5.1.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + + exit@0.1.2: {} + + expect@29.7.0: + dependencies: + '@jest/expect-utils': 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + fast-glob@3.3.3: dependencies: '@nodelib/fs.stat': 2.0.5 @@ -3092,14 +4405,25 @@ snapshots: merge2: 1.4.1 micromatch: 4.0.8 + fast-json-stable-stringify@2.1.0: {} + fastq@1.19.1: dependencies: reusify: 1.1.0 + fb-watchman@2.0.2: + dependencies: + bser: 2.1.1 + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + foreground-child@3.3.1: dependencies: cross-spawn: 7.0.6 @@ -3107,15 +4431,17 @@ snapshots: fraction.js@4.3.7: {} - framer-motion@12.17.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + framer-motion@12.23.18(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - motion-dom: 12.17.0 - motion-utils: 12.12.1 + motion-dom: 12.23.18 + motion-utils: 12.23.6 tslib: 2.8.1 optionalDependencies: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + fs.realpath@1.0.0: {} + fsevents@2.3.3: optional: true @@ -3123,8 +4449,14 @@ snapshots: gensync@1.0.0-beta.2: {} + get-caller-file@2.0.5: {} + get-nonce@1.0.1: {} + get-package-type@0.1.0: {} + + get-stream@6.0.1: {} + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -3142,12 +4474,52 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 1.11.1 - globals@11.12.0: {} + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + graceful-fs@4.2.11: {} + + handlebars@4.7.8: + dependencies: + minimist: 1.2.8 + neo-async: 2.6.2 + source-map: 0.6.1 + wordwrap: 1.0.0 + optionalDependencies: + uglify-js: 3.19.3 + + has-flag@4.0.0: {} hasown@2.0.2: dependencies: function-bind: 1.1.2 + html-escaper@2.0.2: {} + + human-signals@2.1.0: {} + + import-local@3.2.0: + dependencies: + pkg-dir: 4.2.0 + resolve-cwd: 3.0.0 + + imurmurhash@0.1.4: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + is-arrayish@0.2.1: {} + is-binary-path@2.1.0: dependencies: binary-extensions: 2.3.0 @@ -3160,32 +4532,402 @@ snapshots: is-fullwidth-code-point@3.0.0: {} + is-generator-fn@2.1.0: {} + is-glob@4.0.3: dependencies: is-extglob: 2.1.1 is-number@7.0.0: {} + is-stream@2.0.1: {} + isexe@2.0.0: {} + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-instrument@5.2.1: + dependencies: + '@babel/core': 7.28.4 + '@babel/parser': 7.28.4 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + istanbul-lib-instrument@6.0.3: + dependencies: + '@babel/core': 7.28.4 + '@babel/parser': 7.28.4 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 7.7.2 + transitivePeerDependencies: + - supports-color + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-lib-source-maps@4.0.1: + dependencies: + debug: 4.4.3 + istanbul-lib-coverage: 3.2.2 + source-map: 0.6.1 + transitivePeerDependencies: + - supports-color + + istanbul-reports@3.2.0: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + jackspeak@3.4.3: dependencies: '@isaacs/cliui': 8.0.2 optionalDependencies: '@pkgjs/parseargs': 0.11.0 + jest-changed-files@29.7.0: + dependencies: + execa: 5.1.1 + jest-util: 29.7.0 + p-limit: 3.1.0 + + jest-circus@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 22.18.6 + chalk: 4.1.2 + co: 4.6.0 + dedent: 1.7.0 + is-generator-fn: 2.1.0 + jest-each: 29.7.0 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + p-limit: 3.1.0 + pretty-format: 29.7.0 + pure-rand: 6.1.0 + slash: 3.0.0 + stack-utils: 2.0.6 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-cli@29.7.0(@types/node@22.18.6): + dependencies: + '@jest/core': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@22.18.6) + exit: 0.1.2 + import-local: 3.2.0 + jest-config: 29.7.0(@types/node@22.18.6) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + jest-config@29.7.0(@types/node@22.18.6): + dependencies: + '@babel/core': 7.28.4 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.28.4) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 22.18.6 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-diff@29.7.0: + dependencies: + chalk: 4.1.2 + diff-sequences: 29.6.3 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-docblock@29.7.0: + dependencies: + detect-newline: 3.1.0 + + jest-each@29.7.0: + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + jest-get-type: 29.6.3 + jest-util: 29.7.0 + pretty-format: 29.7.0 + + jest-environment-node@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 22.18.6 + jest-mock: 29.7.0 + jest-util: 29.7.0 + + jest-get-type@29.6.3: {} + + jest-haste-map@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/graceful-fs': 4.1.9 + '@types/node': 22.18.6 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + jest-worker: 29.7.0 + micromatch: 4.0.8 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.3 + + jest-leak-detector@29.7.0: + dependencies: + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-matcher-utils@29.7.0: + dependencies: + chalk: 4.1.2 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-message-util@29.7.0: + dependencies: + '@babel/code-frame': 7.27.1 + '@jest/types': 29.6.3 + '@types/stack-utils': 2.0.3 + chalk: 4.1.2 + graceful-fs: 4.2.11 + micromatch: 4.0.8 + pretty-format: 29.7.0 + slash: 3.0.0 + stack-utils: 2.0.6 + + jest-mock@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/node': 22.18.6 + jest-util: 29.7.0 + + jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): + optionalDependencies: + jest-resolve: 29.7.0 + + jest-regex-util@29.6.3: {} + + jest-resolve-dependencies@29.7.0: + dependencies: + jest-regex-util: 29.6.3 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + + jest-resolve@29.7.0: + dependencies: + chalk: 4.1.2 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) + jest-util: 29.7.0 + jest-validate: 29.7.0 + resolve: 1.22.10 + resolve.exports: 2.0.3 + slash: 3.0.0 + + jest-runner@29.7.0: + dependencies: + '@jest/console': 29.7.0 + '@jest/environment': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 22.18.6 + chalk: 4.1.2 + emittery: 0.13.1 + graceful-fs: 4.2.11 + jest-docblock: 29.7.0 + jest-environment-node: 29.7.0 + jest-haste-map: 29.7.0 + jest-leak-detector: 29.7.0 + jest-message-util: 29.7.0 + jest-resolve: 29.7.0 + jest-runtime: 29.7.0 + jest-util: 29.7.0 + jest-watcher: 29.7.0 + jest-worker: 29.7.0 + p-limit: 3.1.0 + source-map-support: 0.5.13 + transitivePeerDependencies: + - supports-color + + jest-runtime@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/globals': 29.7.0 + '@jest/source-map': 29.6.3 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 22.18.6 + chalk: 4.1.2 + cjs-module-lexer: 1.4.3 + collect-v8-coverage: 1.0.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + + jest-snapshot@29.7.0: + dependencies: + '@babel/core': 7.28.4 + '@babel/generator': 7.28.3 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.4) + '@babel/types': 7.28.4 + '@jest/expect-utils': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.4) + chalk: 4.1.2 + expect: 29.7.0 + graceful-fs: 4.2.11 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + natural-compare: 1.4.0 + pretty-format: 29.7.0 + semver: 7.7.2 + transitivePeerDependencies: + - supports-color + + jest-util@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/node': 22.18.6 + chalk: 4.1.2 + ci-info: 3.9.0 + graceful-fs: 4.2.11 + picomatch: 2.3.1 + + jest-validate@29.7.0: + dependencies: + '@jest/types': 29.6.3 + camelcase: 6.3.0 + chalk: 4.1.2 + jest-get-type: 29.6.3 + leven: 3.1.0 + pretty-format: 29.7.0 + + jest-watcher@29.7.0: + dependencies: + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 22.18.6 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.13.1 + jest-util: 29.7.0 + string-length: 4.0.2 + + jest-worker@29.7.0: + dependencies: + '@types/node': 22.18.6 + jest-util: 29.7.0 + merge-stream: 2.0.0 + supports-color: 8.1.1 + + jest@29.7.0(@types/node@22.18.6): + dependencies: + '@jest/core': 29.7.0 + '@jest/types': 29.6.3 + import-local: 3.2.0 + jest-cli: 29.7.0(@types/node@22.18.6) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + jiti@1.21.7: {} js-tokens@4.0.0: {} + js-yaml@3.14.1: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + jsesc@3.1.0: {} + json-parse-even-better-errors@2.3.1: {} + json5@2.2.3: {} + kleur@3.0.3: {} + + leven@3.1.0: {} + lilconfig@3.1.3: {} lines-and-columns@1.2.4: {} + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + + lodash.memoize@4.1.2: {} + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 @@ -3200,6 +4942,18 @@ snapshots: dependencies: react: 18.3.1 + make-dir@4.0.0: + dependencies: + semver: 7.7.2 + + make-error@1.3.6: {} + + makeerror@1.0.12: + dependencies: + tmpl: 1.0.5 + + merge-stream@2.0.0: {} + merge2@1.4.1: {} micromatch@4.0.8: @@ -3207,17 +4961,25 @@ snapshots: braces: 3.0.3 picomatch: 2.3.1 + mimic-fn@2.1.0: {} + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.12 + minimatch@9.0.5: dependencies: brace-expansion: 2.0.2 + minimist@1.2.8: {} + minipass@7.1.2: {} - motion-dom@12.17.0: + motion-dom@12.23.18: dependencies: - motion-utils: 12.12.1 + motion-utils: 12.23.6 - motion-utils@12.12.1: {} + motion-utils@12.23.6: {} ms@2.1.3: {} @@ -3229,23 +4991,66 @@ snapshots: nanoid@3.3.11: {} + natural-compare@1.4.0: {} + + neo-async@2.6.2: {} + next-themes@0.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - node-releases@2.0.19: {} + node-int64@0.4.0: {} + + node-releases@2.0.21: {} normalize-path@3.0.0: {} normalize-range@0.1.2: {} + npm-run-path@4.0.1: + dependencies: + path-key: 3.1.1 + object-assign@4.1.1: {} object-hash@3.0.0: {} + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + + p-try@2.2.0: {} + package-json-from-dist@1.0.1: {} + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.27.1 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + path-exists@4.0.0: {} + + path-is-absolute@1.0.1: {} + path-key@3.1.1: {} path-parse@1.0.7: {} @@ -3263,28 +5068,32 @@ snapshots: pirates@4.0.7: {} - postcss-import@15.1.0(postcss@8.5.4): + pkg-dir@4.2.0: dependencies: - postcss: 8.5.4 + find-up: 4.1.0 + + postcss-import@15.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 postcss-value-parser: 4.2.0 read-cache: 1.0.0 resolve: 1.22.10 - postcss-js@4.0.1(postcss@8.5.4): + postcss-js@4.1.0(postcss@8.5.6): dependencies: camelcase-css: 2.0.1 - postcss: 8.5.4 + postcss: 8.5.6 - postcss-load-config@4.0.2(postcss@8.5.4): + postcss-load-config@4.0.2(postcss@8.5.6): dependencies: lilconfig: 3.1.3 - yaml: 2.8.0 + yaml: 2.8.1 optionalDependencies: - postcss: 8.5.4 + postcss: 8.5.6 - postcss-nested@6.2.0(postcss@8.5.4): + postcss-nested@6.2.0(postcss@8.5.6): dependencies: - postcss: 8.5.4 + postcss: 8.5.6 postcss-selector-parser: 6.1.2 postcss-selector-parser@6.1.2: @@ -3294,12 +5103,25 @@ snapshots: postcss-value-parser@4.2.0: {} - postcss@8.5.4: + postcss@8.5.6: dependencies: nanoid: 3.3.11 picocolors: 1.1.1 source-map-js: 1.2.1 + pretty-format@29.7.0: + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.3.1 + + prompts@2.4.2: + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + + pure-rand@6.1.0: {} + queue-microtask@1.2.3: {} react-dom@18.3.1(react@18.3.1): @@ -3308,37 +5130,39 @@ snapshots: react: 18.3.1 scheduler: 0.23.2 + react-is@18.3.1: {} + react-refresh@0.17.0: {} - react-remove-scroll-bar@2.3.8(@types/react@18.3.23)(react@18.3.1): + react-remove-scroll-bar@2.3.8(@types/react@18.3.24)(react@18.3.1): dependencies: react: 18.3.1 - react-style-singleton: 2.2.3(@types/react@18.3.23)(react@18.3.1) + react-style-singleton: 2.2.3(@types/react@18.3.24)(react@18.3.1) tslib: 2.8.1 optionalDependencies: - '@types/react': 18.3.23 + '@types/react': 18.3.24 - react-remove-scroll@2.5.5(@types/react@18.3.23)(react@18.3.1): + react-remove-scroll@2.5.5(@types/react@18.3.24)(react@18.3.1): dependencies: react: 18.3.1 - react-remove-scroll-bar: 2.3.8(@types/react@18.3.23)(react@18.3.1) - react-style-singleton: 2.2.3(@types/react@18.3.23)(react@18.3.1) + react-remove-scroll-bar: 2.3.8(@types/react@18.3.24)(react@18.3.1) + react-style-singleton: 2.2.3(@types/react@18.3.24)(react@18.3.1) tslib: 2.8.1 - use-callback-ref: 1.3.3(@types/react@18.3.23)(react@18.3.1) - use-sidecar: 1.1.3(@types/react@18.3.23)(react@18.3.1) + use-callback-ref: 1.3.3(@types/react@18.3.24)(react@18.3.1) + use-sidecar: 1.1.3(@types/react@18.3.24)(react@18.3.1) optionalDependencies: - '@types/react': 18.3.23 + '@types/react': 18.3.24 - react-remove-scroll@2.7.1(@types/react@18.3.23)(react@18.3.1): + react-remove-scroll@2.7.1(@types/react@18.3.24)(react@18.3.1): dependencies: react: 18.3.1 - react-remove-scroll-bar: 2.3.8(@types/react@18.3.23)(react@18.3.1) - react-style-singleton: 2.2.3(@types/react@18.3.23)(react@18.3.1) + react-remove-scroll-bar: 2.3.8(@types/react@18.3.24)(react@18.3.1) + react-style-singleton: 2.2.3(@types/react@18.3.24)(react@18.3.1) tslib: 2.8.1 - use-callback-ref: 1.3.3(@types/react@18.3.23)(react@18.3.1) - use-sidecar: 1.1.3(@types/react@18.3.23)(react@18.3.1) + use-callback-ref: 1.3.3(@types/react@18.3.24)(react@18.3.1) + use-sidecar: 1.1.3(@types/react@18.3.24)(react@18.3.1) optionalDependencies: - '@types/react': 18.3.23 + '@types/react': 18.3.24 react-router-dom@6.30.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: @@ -3352,13 +5176,13 @@ snapshots: '@remix-run/router': 1.23.0 react: 18.3.1 - react-style-singleton@2.2.3(@types/react@18.3.23)(react@18.3.1): + react-style-singleton@2.2.3(@types/react@18.3.24)(react@18.3.1): dependencies: get-nonce: 1.0.1 react: 18.3.1 tslib: 2.8.1 optionalDependencies: - '@types/react': 18.3.23 + '@types/react': 18.3.24 react@18.3.1: dependencies: @@ -3372,6 +5196,16 @@ snapshots: dependencies: picomatch: 2.3.1 + require-directory@2.1.1: {} + + resolve-cwd@3.0.0: + dependencies: + resolve-from: 5.0.0 + + resolve-from@5.0.0: {} + + resolve.exports@2.0.3: {} + resolve@1.22.10: dependencies: is-core-module: 2.16.1 @@ -3380,31 +5214,32 @@ snapshots: reusify@1.1.0: {} - rollup@4.50.1: + rollup@4.52.0: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.50.1 - '@rollup/rollup-android-arm64': 4.50.1 - '@rollup/rollup-darwin-arm64': 4.50.1 - '@rollup/rollup-darwin-x64': 4.50.1 - '@rollup/rollup-freebsd-arm64': 4.50.1 - '@rollup/rollup-freebsd-x64': 4.50.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.50.1 - '@rollup/rollup-linux-arm-musleabihf': 4.50.1 - '@rollup/rollup-linux-arm64-gnu': 4.50.1 - '@rollup/rollup-linux-arm64-musl': 4.50.1 - '@rollup/rollup-linux-loongarch64-gnu': 4.50.1 - '@rollup/rollup-linux-ppc64-gnu': 4.50.1 - '@rollup/rollup-linux-riscv64-gnu': 4.50.1 - '@rollup/rollup-linux-riscv64-musl': 4.50.1 - '@rollup/rollup-linux-s390x-gnu': 4.50.1 - '@rollup/rollup-linux-x64-gnu': 4.50.1 - '@rollup/rollup-linux-x64-musl': 4.50.1 - '@rollup/rollup-openharmony-arm64': 4.50.1 - '@rollup/rollup-win32-arm64-msvc': 4.50.1 - '@rollup/rollup-win32-ia32-msvc': 4.50.1 - '@rollup/rollup-win32-x64-msvc': 4.50.1 + '@rollup/rollup-android-arm-eabi': 4.52.0 + '@rollup/rollup-android-arm64': 4.52.0 + '@rollup/rollup-darwin-arm64': 4.52.0 + '@rollup/rollup-darwin-x64': 4.52.0 + '@rollup/rollup-freebsd-arm64': 4.52.0 + '@rollup/rollup-freebsd-x64': 4.52.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.52.0 + '@rollup/rollup-linux-arm-musleabihf': 4.52.0 + '@rollup/rollup-linux-arm64-gnu': 4.52.0 + '@rollup/rollup-linux-arm64-musl': 4.52.0 + '@rollup/rollup-linux-loong64-gnu': 4.52.0 + '@rollup/rollup-linux-ppc64-gnu': 4.52.0 + '@rollup/rollup-linux-riscv64-gnu': 4.52.0 + '@rollup/rollup-linux-riscv64-musl': 4.52.0 + '@rollup/rollup-linux-s390x-gnu': 4.52.0 + '@rollup/rollup-linux-x64-gnu': 4.52.0 + '@rollup/rollup-linux-x64-musl': 4.52.0 + '@rollup/rollup-openharmony-arm64': 4.52.0 + '@rollup/rollup-win32-arm64-msvc': 4.52.0 + '@rollup/rollup-win32-ia32-msvc': 4.52.0 + '@rollup/rollup-win32-x64-gnu': 4.52.0 + '@rollup/rollup-win32-x64-msvc': 4.52.0 fsevents: 2.3.3 run-parallel@1.2.0: @@ -3417,14 +5252,22 @@ snapshots: semver@6.3.1: {} + semver@7.7.2: {} + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 shebang-regex@3.0.0: {} + signal-exit@3.0.7: {} + signal-exit@4.1.0: {} + sisteransi@1.0.5: {} + + slash@3.0.0: {} + sonner@1.7.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: react: 18.3.1 @@ -3432,6 +5275,24 @@ snapshots: source-map-js@1.2.1: {} + source-map-support@0.5.13: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.6.1: {} + + sprintf-js@1.0.3: {} + + stack-utils@2.0.6: + dependencies: + escape-string-regexp: 2.0.0 + + string-length@4.0.2: + dependencies: + char-regex: 1.0.2 + strip-ansi: 6.0.1 + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -3442,19 +5303,25 @@ snapshots: dependencies: eastasianwidth: 0.2.0 emoji-regex: 9.2.2 - strip-ansi: 7.1.0 + strip-ansi: 7.1.2 strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 - strip-ansi@7.1.0: + strip-ansi@7.1.2: dependencies: - ansi-regex: 6.1.0 + ansi-regex: 6.2.2 + + strip-bom@4.0.0: {} + + strip-final-newline@2.0.0: {} + + strip-json-comments@3.1.1: {} sucrase@3.35.0: dependencies: - '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/gen-mapping': 0.3.13 commander: 4.1.1 glob: 10.4.5 lines-and-columns: 1.2.4 @@ -3462,6 +5329,14 @@ snapshots: pirates: 4.0.7 ts-interface-checker: 0.1.13 + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + supports-preserve-symlinks-flag@1.0.0: {} tailwind-merge@2.6.0: {} @@ -3486,17 +5361,23 @@ snapshots: normalize-path: 3.0.0 object-hash: 3.0.0 picocolors: 1.1.1 - postcss: 8.5.4 - postcss-import: 15.1.0(postcss@8.5.4) - postcss-js: 4.0.1(postcss@8.5.4) - postcss-load-config: 4.0.2(postcss@8.5.4) - postcss-nested: 6.2.0(postcss@8.5.4) + postcss: 8.5.6 + postcss-import: 15.1.0(postcss@8.5.6) + postcss-js: 4.1.0(postcss@8.5.6) + postcss-load-config: 4.0.2(postcss@8.5.6) + postcss-nested: 6.2.0(postcss@8.5.6) postcss-selector-parser: 6.1.2 resolve: 1.22.10 sucrase: 3.35.0 transitivePeerDependencies: - ts-node + test-exclude@6.0.0: + dependencies: + '@istanbuljs/schema': 0.1.3 + glob: 7.2.3 + minimatch: 3.1.2 + thenify-all@1.6.0: dependencies: thenify: 3.3.1 @@ -3505,54 +5386,97 @@ snapshots: dependencies: any-promise: 1.3.0 + tmpl@1.0.5: {} + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 ts-interface-checker@0.1.13: {} + ts-jest@29.4.4(@babel/core@7.28.4)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.4))(jest-util@29.7.0)(jest@29.7.0(@types/node@22.18.6))(typescript@5.9.2): + dependencies: + bs-logger: 0.2.6 + fast-json-stable-stringify: 2.1.0 + handlebars: 4.7.8 + jest: 29.7.0(@types/node@22.18.6) + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.7.2 + type-fest: 4.41.0 + typescript: 5.9.2 + yargs-parser: 21.1.1 + optionalDependencies: + '@babel/core': 7.28.4 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.28.4) + jest-util: 29.7.0 + tslib@2.8.1: {} - typescript@5.8.3: {} + type-detect@4.0.8: {} + + type-fest@0.21.3: {} + + type-fest@4.41.0: {} + + typescript@5.9.2: {} + + uglify-js@3.19.3: + optional: true undici-types@6.21.0: {} - update-browserslist-db@1.1.3(browserslist@4.25.0): + update-browserslist-db@1.1.3(browserslist@4.26.2): dependencies: - browserslist: 4.25.0 + browserslist: 4.26.2 escalade: 3.2.0 picocolors: 1.1.1 - use-callback-ref@1.3.3(@types/react@18.3.23)(react@18.3.1): + use-callback-ref@1.3.3(@types/react@18.3.24)(react@18.3.1): dependencies: react: 18.3.1 tslib: 2.8.1 optionalDependencies: - '@types/react': 18.3.23 + '@types/react': 18.3.24 - use-sidecar@1.1.3(@types/react@18.3.23)(react@18.3.1): + use-sidecar@1.1.3(@types/react@18.3.24)(react@18.3.1): dependencies: detect-node-es: 1.1.0 react: 18.3.1 tslib: 2.8.1 optionalDependencies: - '@types/react': 18.3.23 + '@types/react': 18.3.24 util-deprecate@1.0.2: {} - vite@5.4.20(@types/node@22.15.31): + v8-to-istanbul@9.3.0: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + '@types/istanbul-lib-coverage': 2.0.6 + convert-source-map: 2.0.0 + + vite@5.4.20(@types/node@22.18.6): dependencies: esbuild: 0.21.5 - postcss: 8.5.4 - rollup: 4.50.1 + postcss: 8.5.6 + rollup: 4.52.0 optionalDependencies: - '@types/node': 22.15.31 + '@types/node': 22.18.6 fsevents: 2.3.3 + walker@1.0.8: + dependencies: + makeerror: 1.0.12 + which@2.0.2: dependencies: isexe: 2.0.0 + wordwrap@1.0.0: {} + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 @@ -3561,10 +5485,33 @@ snapshots: wrap-ansi@8.1.0: dependencies: - ansi-styles: 6.2.1 + ansi-styles: 6.2.3 string-width: 5.1.2 - strip-ansi: 7.1.0 + strip-ansi: 7.1.2 + + wrappy@1.0.2: {} + + write-file-atomic@4.0.2: + dependencies: + imurmurhash: 0.1.4 + signal-exit: 3.0.7 + + y18n@5.0.8: {} yallist@3.1.1: {} - yaml@2.8.0: {} + yaml@2.8.1: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yocto-queue@0.1.0: {} diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 59b3a1c..f5e1bc1 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -4,8 +4,9 @@ version = 4 [[package]] name = "FileFlow" -version = "1.0.3" +version = "1.0.4" dependencies = [ + "criterion", "csv", "serde", "serde_json", @@ -14,6 +15,7 @@ dependencies = [ "tauri-build", "tauri-plugin-dialog", "tauri-plugin-global-shortcut", + "tempfile", "tokio", ] @@ -28,9 +30,9 @@ dependencies = [ [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aho-corasick" @@ -62,12 +64,6 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - [[package]] name = "android_system_properties" version = "0.1.5" @@ -77,11 +73,23 @@ dependencies = [ "libc", ] +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstyle" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" + [[package]] name = "anyhow" -version = "1.0.97" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "ashpd" @@ -92,12 +100,15 @@ dependencies = [ "enumflags2", "futures-channel", "futures-util", - "rand 0.9.0", + "rand 0.9.2", "raw-window-handle", "serde", "serde_repr", "tokio", "url", + "wayland-backend", + "wayland-client", + "wayland-protocols", "zbus", ] @@ -121,18 +132,18 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.106", ] [[package]] name = "async-trait" -version = "0.1.88" +version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.106", ] [[package]] @@ -167,17 +178,23 @@ dependencies = [ "num-traits", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "backtrace" -version = "0.3.74" +version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", "cfg-if", @@ -202,9 +219,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.7.3" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" [[package]] name = "bitflags" @@ -214,9 +231,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" dependencies = [ "serde", ] @@ -241,18 +258,18 @@ dependencies = [ [[package]] name = "block2" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d59b4c170e16f0405a2e95aff44432a0d41aa97675f3d52623effe95792a037" +checksum = "340d2f0bdb2a43c1d3cd40513185b2bd7def0aa1052f956455114bc98f82dcf2" dependencies = [ - "objc2 0.6.0", + "objc2 0.6.2", ] [[package]] name = "brotli" -version = "7.0.0" +version = "8.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" +checksum = "4bd8b9603c7aa97359dbd97ecf258968c95f3adddd6db2f7e7a5bef101c84560" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -261,9 +278,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "4.0.2" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74fa05ad7d803d413eb8380983b092cbbaf9a85f151b871360e7b00cd7060b37" +checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -271,15 +288,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "bytemuck" -version = "1.22.0" +version = "1.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540" +checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677" [[package]] name = "byteorder" @@ -302,7 +319,7 @@ version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.4", "cairo-sys-rs", "glib", "libc", @@ -323,11 +340,11 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.9" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +checksum = "e1de8bc0aa9e9385ceb3bf0c152e3a9b9544f6c4a912c8ae504e80c1f0368603" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -350,25 +367,32 @@ dependencies = [ "semver", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.16", ] [[package]] name = "cargo_toml" -version = "0.22.1" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02260d489095346e5cafd04dea8e8cb54d1d74fcd759022a9b72986ebe9a1257" +checksum = "374b7c592d9c00c1f4972ea58390ac6b18cbb6ab79011f3bdc90a0b82ca06b77" dependencies = [ "serde", - "toml", + "toml 0.9.7", ] +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + [[package]] name = "cc" -version = "1.2.19" +version = "1.2.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362" +checksum = "80f41ae168f955c12fb8960b057d70d0ca153fb83182b57d86380443527be7e9" dependencies = [ + "find-msvc-tools", "shlex", ] @@ -401,9 +425,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] name = "cfg_aliases" @@ -413,17 +437,68 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.40" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" dependencies = [ - "android-tzdata", "iana-time-zone", "num-traits", "serde", - "windows-link", + "windows-link 0.2.0", +] + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", ] +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.5.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" + [[package]] name = "combine" version = "4.6.7" @@ -477,9 +552,9 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" dependencies = [ "core-foundation-sys", "libc", @@ -497,8 +572,8 @@ version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" dependencies = [ - "bitflags 2.9.0", - "core-foundation 0.10.0", + "bitflags 2.9.4", + "core-foundation 0.10.1", "core-graphics-types", "foreign-types 0.5.0", "libc", @@ -510,8 +585,8 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ - "bitflags 2.9.0", - "core-foundation 0.10.0", + "bitflags 2.9.4", + "core-foundation 0.10.1", "libc", ] @@ -526,9 +601,9 @@ dependencies = [ [[package]] name = "crc" -version = "3.2.1" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" dependencies = [ "crc-catalog", ] @@ -541,13 +616,46 @@ checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" [[package]] name = "crc32fast" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ "cfg-if", ] +[[package]] +name = "criterion" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1c047a62b0cc3e145fa84415a3191f628e980b194c2755aa12300a4e6cbd928" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "itertools", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b1bcc0dc7dfae599d84ad0b1a55f80cde8af3725da8313b528da95ef783e338" +dependencies = [ + "cast", + "itertools", +] + [[package]] name = "crossbeam-channel" version = "0.5.15" @@ -557,6 +665,25 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-queue" version = "0.3.12" @@ -572,6 +699,12 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + [[package]] name = "crypto-common" version = "0.1.6" @@ -584,15 +717,15 @@ dependencies = [ [[package]] name = "cssparser" -version = "0.27.2" +version = "0.29.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" +checksum = "f93d03419cb5950ccfd3daf3ff1c7a36ace64609a1a8746d493df1ca0afde0fa" dependencies = [ "cssparser-macros", "dtoa-short", - "itoa 0.4.8", + "itoa", "matches", - "phf 0.8.0", + "phf 0.10.1", "proc-macro2", "quote", "smallvec", @@ -606,7 +739,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.100", + "syn 2.0.106", ] [[package]] @@ -616,7 +749,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" dependencies = [ "csv-core", - "itoa 1.0.15", + "itoa", "ryu", "serde", ] @@ -637,14 +770,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" dependencies = [ "quote", - "syn 2.0.100", + "syn 2.0.106", ] [[package]] name = "darling" -version = "0.20.11" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" dependencies = [ "darling_core", "darling_macro", @@ -652,34 +785,34 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.11" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 2.0.100", + "syn 2.0.106", ] [[package]] name = "darling_macro" -version = "0.20.11" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core", "quote", - "syn 2.0.100", + "syn 2.0.106", ] [[package]] name = "der" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ "const-oid", "pem-rfc7468", @@ -688,9 +821,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.4.0" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc" dependencies = [ "powerfmt", "serde", @@ -698,15 +831,15 @@ dependencies = [ [[package]] name = "derive_more" -version = "0.99.19" +version = "0.99.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da29a38df43d6f156149c9b43ded5e018ddff2a855cf2cfd62e8cd7d079c69f" +checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" dependencies = [ "convert_case", "proc-macro2", "quote", "rustc_version", - "syn 2.0.100", + "syn 2.0.106", ] [[package]] @@ -739,7 +872,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.59.0", + "windows-sys 0.61.0", ] [[package]] @@ -750,14 +883,14 @@ checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" [[package]] name = "dispatch2" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a0d569e003ff27784e0e14e4a594048698e0c0f0b66cabcb51511be55a7caa0" +checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" dependencies = [ - "bitflags 2.9.0", - "block2 0.6.0", + "bitflags 2.9.4", + "block2 0.6.1", "libc", - "objc2 0.6.0", + "objc2 0.6.2", ] [[package]] @@ -768,14 +901,23 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.106", +] + +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading 0.8.9", ] [[package]] name = "dlopen2" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6" +checksum = "b54f373ccf864bf587a89e880fb7610f8d73f3045f13580948ccbcaff26febff" dependencies = [ "dlopen2_derive", "libc", @@ -785,13 +927,13 @@ dependencies = [ [[package]] name = "dlopen2_derive" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" +checksum = "788160fb30de9cdd857af31c6a2675904b16ece8fc2737b2c7127ba368c9d0f4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.106", ] [[package]] @@ -800,11 +942,17 @@ version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + [[package]] name = "dpi" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" +checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" dependencies = [ "serde", ] @@ -832,9 +980,9 @@ checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] name = "dyn-clone" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "either" @@ -847,14 +995,14 @@ dependencies = [ [[package]] name = "embed-resource" -version = "3.0.2" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fbc6e0d8e0c03a655b53ca813f0463d2c956bc4db8138dbc89f120b066551e3" +checksum = "55a075fc573c64510038d7ee9abc7990635863992f83ebc52c8b433b8411a02e" dependencies = [ "cc", "memchr", "rustc_version", - "toml", + "toml 0.9.7", "vswhom", "winreg", ] @@ -873,9 +1021,9 @@ checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" [[package]] name = "enumflags2" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba2f4b465f5318854c6f8dd686ede6c0a9dc67d4b1ac241cf0eb51521a309147" +checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" dependencies = [ "enumflags2_derive", "serde", @@ -883,13 +1031,13 @@ dependencies = [ [[package]] name = "enumflags2_derive" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79" +checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.106", ] [[package]] @@ -900,22 +1048,23 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "erased-serde" -version = "0.4.6" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e004d887f51fcb9fef17317a2f3525c887d8aa3f4f50fed920816a688284a5b7" +checksum = "259d404d09818dec19332e31d94558aeb442fea04c817006456c24b5460bbd4b" dependencies = [ "serde", + "serde_core", "typeid", ] [[package]] name = "errno" -version = "0.3.11" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.0", ] [[package]] @@ -931,9 +1080,9 @@ dependencies = [ [[package]] name = "event-listener" -version = "5.4.0" +version = "5.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" dependencies = [ "concurrent-queue", "parking", @@ -975,11 +1124,17 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "find-msvc-tools" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959" + [[package]] name = "flate2" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" dependencies = [ "crc32fast", "miniz_oxide", @@ -1035,7 +1190,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.106", ] [[package]] @@ -1052,9 +1207,9 @@ checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" [[package]] name = "form_urlencoded" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ "percent-encoding", ] @@ -1115,9 +1270,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" -version = "2.6.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" dependencies = [ "fastrand", "futures-core", @@ -1134,7 +1289,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.106", ] [[package]] @@ -1284,6 +1439,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "gethostname" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc257fdb4038301ce4b9cd1b3b51704509692bb3ff716a410cbd07925d9dae55" +dependencies = [ + "rustix", + "windows-targets 0.52.6", +] + [[package]] name = "getrandom" version = "0.1.16" @@ -1297,25 +1462,25 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", ] [[package]] name = "getrandom" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "libc", "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "wasi 0.14.7+wasi-0.2.4", ] [[package]] @@ -1362,7 +1527,7 @@ version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.4", "futures-channel", "futures-core", "futures-executor", @@ -1386,11 +1551,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" dependencies = [ "heck 0.4.1", - "proc-macro-crate 2.0.0", + "proc-macro-crate 2.0.2", "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.106", ] [[package]] @@ -1405,25 +1570,26 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "global-hotkey" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fbb3a4e56c901ee66c190fdb3fa08344e6d09593cc6c61f8eb9add7144b271" +checksum = "b9247516746aa8e53411a0db9b62b0e24efbcf6a76e0ba73e5a91b512ddabed7" dependencies = [ "crossbeam-channel", "keyboard-types", - "objc2 0.6.0", + "objc2 0.6.2", "objc2-app-kit", "once_cell", "serde", - "thiserror 2.0.12", + "thiserror 2.0.16", "windows-sys 0.59.0", - "x11-dl", + "x11rb", + "xkeysym", ] [[package]] @@ -1486,7 +1652,17 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.106", +] + +[[package]] +name = "half" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +dependencies = [ + "cfg-if", + "crunchy", ] [[package]] @@ -1497,22 +1673,28 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", "foldhash", ] +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" + [[package]] name = "hashlink" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "hashbrown 0.15.2", + "hashbrown 0.15.5", ] [[package]] @@ -1562,16 +1744,14 @@ dependencies = [ [[package]] name = "html5ever" -version = "0.26.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" +checksum = "3b7410cae13cbc75623c98ac4cbfd1f0bedddf3227afc24f370cf0f50a44a11c" dependencies = [ "log", "mac", "markup5ever", - "proc-macro2", - "quote", - "syn 1.0.109", + "match_token", ] [[package]] @@ -1582,7 +1762,7 @@ checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", - "itoa 1.0.15", + "itoa", ] [[package]] @@ -1616,18 +1796,20 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "hyper" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" dependencies = [ + "atomic-waker", "bytes", "futures-channel", - "futures-util", + "futures-core", "http", "http-body", "httparse", - "itoa 1.0.15", + "itoa", "pin-project-lite", + "pin-utils", "smallvec", "tokio", "want", @@ -1635,17 +1817,21 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.11" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" +checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" dependencies = [ + "base64 0.22.1", "bytes", "futures-channel", + "futures-core", "futures-util", "http", "http-body", "hyper", + "ipnet", "libc", + "percent-encoding", "pin-project-lite", "socket2", "tokio", @@ -1655,9 +1841,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.63" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -1665,7 +1851,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.61.0", + "windows-core 0.62.0", ] [[package]] @@ -1689,21 +1875,22 @@ dependencies = [ [[package]] name = "icu_collections" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ "displaydoc", + "potential_utf", "yoke", "zerofrom", "zerovec", ] [[package]] -name = "icu_locid" -version = "1.5.0" +name = "icu_locale_core" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" dependencies = [ "displaydoc", "litemap", @@ -1712,31 +1899,11 @@ dependencies = [ "zerovec", ] -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" - [[package]] name = "icu_normalizer" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" dependencies = [ "displaydoc", "icu_collections", @@ -1744,67 +1911,54 @@ dependencies = [ "icu_properties", "icu_provider", "smallvec", - "utf16_iter", - "utf8_iter", - "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "1.5.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] name = "icu_properties" -version = "1.5.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" dependencies = [ "displaydoc", "icu_collections", - "icu_locid_transform", + "icu_locale_core", "icu_properties_data", "icu_provider", - "tinystr", + "potential_utf", + "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "1.5.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" [[package]] name = "icu_provider" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" dependencies = [ "displaydoc", - "icu_locid", - "icu_provider_macros", + "icu_locale_core", "stable_deref_trait", "tinystr", "writeable", "yoke", "zerofrom", + "zerotrie", "zerovec", ] -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - [[package]] name = "ident_case" version = "1.0.1" @@ -1813,9 +1967,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ "idna_adapter", "smallvec", @@ -1824,9 +1978,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", @@ -1845,13 +1999,14 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.9.0" +version = "2.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" dependencies = [ "equivalent", - "hashbrown 0.15.2", + "hashbrown 0.16.0", "serde", + "serde_core", ] [[package]] @@ -1863,6 +2018,17 @@ dependencies = [ "cfb", ] +[[package]] +name = "io-uring" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" +dependencies = [ + "bitflags 2.9.4", + "cfg-if", + "libc", +] + [[package]] name = "ipnet" version = "2.11.0" @@ -1870,10 +2036,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] -name = "itoa" -version = "0.4.8" +name = "iri-string" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "itertools" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] [[package]] name = "itoa" @@ -1928,9 +2107,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "852f13bec5eba4ba9afbeb93fd7c13fe56147f055939ae21c43a29a0ecb2702e" dependencies = [ "once_cell", "wasm-bindgen", @@ -1964,21 +2143,20 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.4", "serde", "unicode-segmentation", ] [[package]] name = "kuchikiki" -version = "0.8.2" +version = "0.8.8-speedreader" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8" +checksum = "02cb977175687f33fa4afa0c95c112b987ea1443e5a51c8f8ff27dc618270cc2" dependencies = [ "cssparser", "html5ever", - "indexmap 1.9.3", - "matches", + "indexmap 2.11.4", "selectors", ] @@ -2011,15 +2189,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" dependencies = [ "gtk-sys", - "libloading", + "libloading 0.7.4", "once_cell", ] [[package]] name = "libc" -version = "0.2.171" +version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "libloading" @@ -2031,20 +2209,31 @@ dependencies = [ "winapi", ] +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link 0.2.0", +] + [[package]] name = "libm" -version = "0.2.11" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" -version = "0.1.3" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.4", "libc", + "redox_syscall", ] [[package]] @@ -2060,21 +2249,21 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.9.4" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "litemap" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ "autocfg", "scopeguard", @@ -2082,9 +2271,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.27" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "mac" @@ -2094,18 +2283,29 @@ checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" [[package]] name = "markup5ever" -version = "0.11.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" +checksum = "c7a7213d12e1864c0f002f52c2923d4556935a43dec5e71355c2760e0f6e7a18" dependencies = [ "log", - "phf 0.10.1", - "phf_codegen 0.10.0", + "phf 0.11.3", + "phf_codegen 0.11.3", "string_cache", "string_cache_codegen", "tendril", ] +[[package]] +name = "match_token" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "matches" version = "0.1.10" @@ -2124,9 +2324,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "memoffset" @@ -2145,9 +2345,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", "simd-adler32", @@ -2155,34 +2355,34 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", ] [[package]] name = "muda" -version = "0.16.1" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4de14a9b5d569ca68d7c891d613b390cf5ab4f851c77aaa2f9e435555d3d9492" +checksum = "01c1738382f66ed56b3b9c8119e794a2e23148ac8ea214eda86622d4cb9d415a" dependencies = [ "crossbeam-channel", "dpi", "gtk", "keyboard-types", - "objc2 0.6.0", + "objc2 0.6.2", "objc2-app-kit", "objc2-core-foundation", - "objc2-foundation 0.3.0", + "objc2-foundation 0.3.1", "once_cell", "png", "serde", - "thiserror 2.0.12", - "windows-sys 0.59.0", + "thiserror 2.0.16", + "windows-sys 0.60.2", ] [[package]] @@ -2208,7 +2408,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.4", "jni-sys", "log", "ndk-sys", @@ -2240,11 +2440,11 @@ checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" [[package]] name = "nix" -version = "0.29.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.4", "cfg-if", "cfg_aliases", "libc", @@ -2312,23 +2512,24 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" dependencies = [ "num_enum_derive", + "rustversion", ] [[package]] name = "num_enum_derive" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" dependencies = [ - "proc-macro-crate 3.3.0", + "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.106", ] [[package]] @@ -2349,9 +2550,9 @@ dependencies = [ [[package]] name = "objc2" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3531f65190d9cff863b77a99857e74c314dd16bf56c538c4b57c7cbc3f3a6e59" +checksum = "561f357ba7f3a2a61563a186a163d0a3a5247e1089524a3981d49adb775078bc" dependencies = [ "objc2-encode", "objc2-exception-helper", @@ -2359,75 +2560,77 @@ dependencies = [ [[package]] name = "objc2-app-kit" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5906f93257178e2f7ae069efb89fbd6ee94f0592740b5f8a1512ca498814d0fb" +checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc" dependencies = [ - "bitflags 2.9.0", - "block2 0.6.0", + "bitflags 2.9.4", + "block2 0.6.1", "libc", - "objc2 0.6.0", + "objc2 0.6.2", "objc2-cloud-kit", "objc2-core-data", "objc2-core-foundation", "objc2-core-graphics", "objc2-core-image", - "objc2-foundation 0.3.0", - "objc2-quartz-core 0.3.0", + "objc2-foundation 0.3.1", + "objc2-quartz-core 0.3.1", ] [[package]] name = "objc2-cloud-kit" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c1948a9be5f469deadbd6bcb86ad7ff9e47b4f632380139722f7d9840c0d42c" +checksum = "17614fdcd9b411e6ff1117dfb1d0150f908ba83a7df81b1f118005fe0a8ea15d" dependencies = [ - "bitflags 2.9.0", - "objc2 0.6.0", - "objc2-foundation 0.3.0", + "bitflags 2.9.4", + "objc2 0.6.2", + "objc2-foundation 0.3.1", ] [[package]] name = "objc2-core-data" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f860f8e841f6d32f754836f51e6bc7777cd7e7053cf18528233f6811d3eceb4" +checksum = "291fbbf7d29287518e8686417cf7239c74700fd4b607623140a7d4a3c834329d" dependencies = [ - "bitflags 2.9.0", - "objc2 0.6.0", - "objc2-foundation 0.3.0", + "bitflags 2.9.4", + "objc2 0.6.2", + "objc2-foundation 0.3.1", ] [[package]] name = "objc2-core-foundation" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daeaf60f25471d26948a1c2f840e3f7d86f4109e3af4e8e4b5cd70c39690d925" +checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" dependencies = [ - "bitflags 2.9.0", - "objc2 0.6.0", + "bitflags 2.9.4", + "dispatch2", + "objc2 0.6.2", ] [[package]] name = "objc2-core-graphics" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dca602628b65356b6513290a21a6405b4d4027b8b250f0b98dddbb28b7de02" +checksum = "989c6c68c13021b5c2d6b71456ebb0f9dc78d752e86a98da7c716f4f9470f5a4" dependencies = [ - "bitflags 2.9.0", - "objc2 0.6.0", + "bitflags 2.9.4", + "dispatch2", + "objc2 0.6.2", "objc2-core-foundation", "objc2-io-surface", ] [[package]] name = "objc2-core-image" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ffa6bea72bf42c78b0b34e89c0bafac877d5f80bf91e159a5d96ea7f693ca56" +checksum = "79b3dc0cc4386b6ccf21c157591b34a7f44c8e75b064f85502901ab2188c007e" dependencies = [ - "objc2 0.6.0", - "objc2-foundation 0.3.0", + "objc2 0.6.2", + "objc2-foundation 0.3.1", ] [[package]] @@ -2451,7 +2654,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.4", "block2 0.5.1", "libc", "objc2 0.5.2", @@ -2459,25 +2662,35 @@ dependencies = [ [[package]] name = "objc2-foundation" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a21c6c9014b82c39515db5b396f91645182611c97d24637cf56ac01e5f8d998" +checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" dependencies = [ - "bitflags 2.9.0", - "block2 0.6.0", + "bitflags 2.9.4", + "block2 0.6.1", "libc", - "objc2 0.6.0", + "objc2 0.6.2", "objc2-core-foundation", ] [[package]] name = "objc2-io-surface" -version = "0.3.0" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7282e9ac92529fa3457ce90ebb15f4ecbc383e8338060960760fa2cf75420c3c" +dependencies = [ + "bitflags 2.9.4", + "objc2 0.6.2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-javascript-core" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "161a8b87e32610086e1a7a9e9ec39f84459db7b3a0881c1f16ca5a2605581c19" +checksum = "9052cb1bb50a4c161d934befcf879526fb87ae9a68858f241e693ca46225cf5a" dependencies = [ - "bitflags 2.9.0", - "objc2 0.6.0", + "objc2 0.6.2", "objc2-core-foundation", ] @@ -2487,7 +2700,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.4", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -2499,7 +2712,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.4", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -2508,39 +2721,52 @@ dependencies = [ [[package]] name = "objc2-quartz-core" -version = "0.3.0" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ffb6a0cd5f182dc964334388560b12a57f7b74b3e2dec5e2722aa2dfb2ccd5" +dependencies = [ + "bitflags 2.9.4", + "objc2 0.6.2", + "objc2-foundation 0.3.1", +] + +[[package]] +name = "objc2-security" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb3794501bb1bee12f08dcad8c61f2a5875791ad1c6f47faa71a0f033f20071" +checksum = "e1f8e0ef3ab66b08c42644dcb34dba6ec0a574bbd8adbb8bdbdc7a2779731a44" dependencies = [ - "bitflags 2.9.0", - "objc2 0.6.0", - "objc2-foundation 0.3.0", + "bitflags 2.9.4", + "objc2 0.6.2", + "objc2-core-foundation", ] [[package]] name = "objc2-ui-kit" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "777a571be14a42a3990d4ebedaeb8b54cd17377ec21b92e8200ac03797b3bee1" +checksum = "25b1312ad7bc8a0e92adae17aa10f90aae1fb618832f9b993b022b591027daed" dependencies = [ - "bitflags 2.9.0", - "objc2 0.6.0", + "bitflags 2.9.4", + "objc2 0.6.2", "objc2-core-foundation", - "objc2-foundation 0.3.0", + "objc2-foundation 0.3.1", ] [[package]] name = "objc2-web-kit" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b717127e4014b0f9f3e8bba3d3f2acec81f1bde01f656823036e823ed2c94dce" +checksum = "91672909de8b1ce1c2252e95bbee8c1649c9ad9d14b9248b3d7b4c47903c47ad" dependencies = [ - "bitflags 2.9.0", - "block2 0.6.0", - "objc2 0.6.0", + "bitflags 2.9.4", + "block2 0.6.1", + "objc2 0.6.2", "objc2-app-kit", "objc2-core-foundation", - "objc2-foundation 0.3.0", + "objc2-foundation 0.3.1", + "objc2-javascript-core", + "objc2-security", ] [[package]] @@ -2558,13 +2784,19 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + [[package]] name = "openssl" -version = "0.10.72" +version = "0.10.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da" +checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.4", "cfg-if", "foreign-types 0.3.2", "libc", @@ -2581,7 +2813,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.106", ] [[package]] @@ -2592,9 +2824,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.107" +version = "0.9.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8288979acd84749c744a9014b4382d42b8f7b2592847b5afb2ed29e5d16ede07" +checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" dependencies = [ "cc", "libc", @@ -2651,9 +2883,9 @@ checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" dependencies = [ "lock_api", "parking_lot_core", @@ -2661,9 +2893,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", @@ -2683,9 +2915,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "phf" @@ -2693,9 +2925,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" dependencies = [ - "phf_macros 0.8.0", "phf_shared 0.8.0", - "proc-macro-hack", ] [[package]] @@ -2704,7 +2934,9 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" dependencies = [ + "phf_macros 0.10.0", "phf_shared 0.10.0", + "proc-macro-hack", ] [[package]] @@ -2729,12 +2961,12 @@ dependencies = [ [[package]] name = "phf_codegen" -version = "0.10.0" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", + "phf_generator 0.11.3", + "phf_shared 0.11.3", ] [[package]] @@ -2769,12 +3001,12 @@ dependencies = [ [[package]] name = "phf_macros" -version = "0.8.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" +checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" dependencies = [ - "phf_generator 0.8.0", - "phf_shared 0.8.0", + "phf_generator 0.10.0", + "phf_shared 0.10.0", "proc-macro-hack", "proc-macro2", "quote", @@ -2791,7 +3023,7 @@ dependencies = [ "phf_shared 0.11.3", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.106", ] [[package]] @@ -2862,17 +3094,45 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "plist" -version = "1.7.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac26e981c03a6e53e0aee43c113e3202f5581d5360dae7bd2c70e800dd0451d" +checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07" dependencies = [ "base64 0.22.1", - "indexmap 2.9.0", - "quick-xml", + "indexmap 2.11.4", + "quick-xml 0.38.3", "serde", "time", ] +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + [[package]] name = "png" version = "0.17.16" @@ -2886,6 +3146,15 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "potential_utf" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" +dependencies = [ + "zerovec", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -2919,20 +3188,21 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "2.0.0" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" +checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" dependencies = [ - "toml_edit 0.20.7", + "toml_datetime 0.6.3", + "toml_edit 0.20.2", ] [[package]] name = "proc-macro-crate" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" dependencies = [ - "toml_edit 0.22.24", + "toml_edit 0.23.6", ] [[package]] @@ -2967,18 +3237,27 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.94" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] [[package]] name = "quick-xml" -version = "0.32.0" +version = "0.37.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" +checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" +dependencies = [ + "memchr", +] + +[[package]] +name = "quick-xml" +version = "0.38.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42a232e7487fc2ef313d96dde7948e7a3c05101870d8985e4fd8d26aedd27b89" dependencies = [ "memchr", ] @@ -2994,9 +3273,9 @@ dependencies = [ [[package]] name = "r-efi" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "rand" @@ -3025,13 +3304,12 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", - "zerocopy", ] [[package]] @@ -3079,7 +3357,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", ] [[package]] @@ -3088,7 +3366,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.3", ] [[package]] @@ -3115,31 +3393,71 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" +[[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "redox_syscall" -version = "0.5.11" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.4", ] [[package]] name = "redox_users" -version = "0.5.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", "libredox", - "thiserror 2.0.12", + "thiserror 2.0.16", +] + +[[package]] +name = "ref-cast" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", ] [[package]] name = "regex" -version = "1.11.1" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" dependencies = [ "aho-corasick", "memchr", @@ -3149,9 +3467,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" dependencies = [ "aho-corasick", "memchr", @@ -3160,15 +3478,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" [[package]] name = "reqwest" -version = "0.12.15" +version = "0.12.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" +checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" dependencies = [ "base64 0.22.1", "bytes", @@ -3179,11 +3497,8 @@ dependencies = [ "http-body-util", "hyper", "hyper-util", - "ipnet", "js-sys", "log", - "mime", - "once_cell", "percent-encoding", "pin-project-lite", "serde", @@ -3193,33 +3508,33 @@ dependencies = [ "tokio", "tokio-util", "tower", + "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "wasm-streams", "web-sys", - "windows-registry", ] [[package]] name = "rfd" -version = "0.15.3" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80c844748fdc82aae252ee4594a89b6e7ebef1063de7951545564cbc4e57075d" +checksum = "ef2bee61e6cffa4635c72d7d81a84294e28f0930db0ddcb0f66d10244674ebed" dependencies = [ "ashpd", - "block2 0.6.0", + "block2 0.6.1", "dispatch2", "glib-sys", "gobject-sys", "gtk-sys", "js-sys", "log", - "objc2 0.6.0", + "objc2 0.6.2", "objc2-app-kit", "objc2-core-foundation", - "objc2-foundation 0.3.0", + "objc2-foundation 0.3.1", "raw-window-handle", "wasm-bindgen", "wasm-bindgen-futures", @@ -3249,9 +3564,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustc_version" @@ -3264,22 +3579,22 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.5" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.4", "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys 0.61.0", ] [[package]] name = "rustversion" -version = "1.0.20" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" @@ -3298,11 +3613,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.0", ] [[package]] @@ -3320,6 +3635,30 @@ dependencies = [ "uuid", ] +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + [[package]] name = "schemars_derive" version = "0.8.22" @@ -3329,9 +3668,15 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.100", + "syn 2.0.106", ] +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "scopeguard" version = "1.2.0" @@ -3344,7 +3689,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.4", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -3353,9 +3698,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.14.0" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" dependencies = [ "core-foundation-sys", "libc", @@ -3363,62 +3708,72 @@ dependencies = [ [[package]] name = "selectors" -version = "0.22.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" +checksum = "0c37578180969d00692904465fb7f6b3d50b9a2b952b87c23d0e2e5cb5013416" dependencies = [ "bitflags 1.3.2", "cssparser", "derive_more", "fxhash", "log", - "matches", "phf 0.8.0", "phf_codegen 0.8.0", "precomputed-hash", "servo_arc", "smallvec", - "thin-slice", ] [[package]] name = "semver" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" dependencies = [ "serde", + "serde_core", ] [[package]] name = "serde" -version = "1.0.219" +version = "1.0.226" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "0dca6411025b24b60bfa7ec1fe1f8e710ac09782dca409ee8237ba74b51295fd" dependencies = [ + "serde_core", "serde_derive", ] [[package]] name = "serde-untagged" -version = "0.1.7" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "299d9c19d7d466db4ab10addd5703e4c615dec2a5a16dbbafe191045e87ee66e" +checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058" dependencies = [ "erased-serde", "serde", + "serde_core", "typeid", ] +[[package]] +name = "serde_core" +version = "1.0.226" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba2ba63999edb9dac981fb34b3e5c0d111a69b0924e253ed29d83f7c99e966a4" +dependencies = [ + "serde_derive", +] + [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.226" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "8db53ae22f34573731bafa1db20f04027b2d25e02d8205921b569171699cdb33" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.106", ] [[package]] @@ -3429,19 +3784,20 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.106", ] [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ - "itoa 1.0.15", + "itoa", "memchr", "ryu", "serde", + "serde_core", ] [[package]] @@ -3452,18 +3808,27 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.106", ] [[package]] name = "serde_spanned" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5417783452c2be558477e104686f7de5dae53dba813c28435e0e70f82d9b04ee" +dependencies = [ + "serde_core", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -3471,22 +3836,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 1.0.15", + "itoa", "ryu", "serde", ] [[package]] name = "serde_with" -version = "3.12.0" +version = "3.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" +checksum = "c522100790450cf78eeac1507263d0a350d4d5b30df0c8e1fe051a10c22b376e" dependencies = [ "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.9.0", + "indexmap 2.11.4", + "schemars 0.9.0", + "schemars 1.0.4", "serde", "serde_derive", "serde_json", @@ -3496,21 +3863,21 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.12.0" +version = "3.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" +checksum = "327ada00f7d64abaac1e55a6911e90cf665aa051b9a561c7006c157f4633135e" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.106", ] [[package]] name = "serialize-to-javascript" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9823f2d3b6a81d98228151fdeaf848206a7855a7a042bbf9bf870449a66cafb" +checksum = "04f3666a07a197cdb77cdf306c32be9b7f598d7060d50cfd4d5aa04bfd92f6c5" dependencies = [ "serde", "serde_json", @@ -3519,20 +3886,20 @@ dependencies = [ [[package]] name = "serialize-to-javascript-impl" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74064874e9f6a15f04c1f3cb627902d0e6b410abbf36668afa873c61889f1763" +checksum = "772ee033c0916d670af7860b6e1ef7d658a4629a6d0b4c8c3e67f09b3765b75d" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.106", ] [[package]] name = "servo_arc" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" +checksum = "d52aa42f8fdf0fed91e5ce7f23d8138441002fa31dca008acf47e6fd4721f741" dependencies = [ "nodrop", "stable_deref_trait", @@ -3551,9 +3918,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", @@ -3568,9 +3935,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.2" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" dependencies = [ "libc", ] @@ -3605,30 +3972,27 @@ checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" [[package]] name = "slab" -version = "0.4.9" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "smallvec" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" dependencies = [ "serde", ] [[package]] name = "socket2" -version = "0.5.9" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3700,9 +4064,9 @@ dependencies = [ [[package]] name = "sqlx" -version = "0.8.3" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4410e73b3c0d8442c5f99b425d7a435b5ee0ae4167b3196771dd3f7a01be745f" +checksum = "1fefb893899429669dcdd979aff487bd78f4064e5e7907e4269081e0ef7d97dc" dependencies = [ "sqlx-core", "sqlx-macros", @@ -3713,10 +4077,11 @@ dependencies = [ [[package]] name = "sqlx-core" -version = "0.8.3" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a007b6936676aa9ab40207cde35daab0a04b823be8ae004368c0793b96a61e0" +checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6" dependencies = [ + "base64 0.22.1", "bytes", "crc", "crossbeam-queue", @@ -3726,9 +4091,9 @@ dependencies = [ "futures-intrusive", "futures-io", "futures-util", - "hashbrown 0.15.2", + "hashbrown 0.15.5", "hashlink", - "indexmap 2.9.0", + "indexmap 2.11.4", "log", "memchr", "native-tls", @@ -3738,7 +4103,7 @@ dependencies = [ "serde_json", "sha2", "smallvec", - "thiserror 2.0.12", + "thiserror 2.0.16", "tokio", "tokio-stream", "tracing", @@ -3747,22 +4112,22 @@ dependencies = [ [[package]] name = "sqlx-macros" -version = "0.8.3" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3112e2ad78643fef903618d78cf0aec1cb3134b019730edb039b69eaf531f310" +checksum = "a2d452988ccaacfbf5e0bdbc348fb91d7c8af5bee192173ac3636b5fb6e6715d" dependencies = [ "proc-macro2", "quote", "sqlx-core", "sqlx-macros-core", - "syn 2.0.100", + "syn 2.0.106", ] [[package]] name = "sqlx-macros-core" -version = "0.8.3" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e9f90acc5ab146a99bf5061a7eb4976b573f560bc898ef3bf8435448dd5e7ad" +checksum = "19a9c1841124ac5a61741f96e1d9e2ec77424bf323962dd894bdb93f37d5219b" dependencies = [ "dotenvy", "either", @@ -3778,21 +4143,20 @@ dependencies = [ "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", - "syn 2.0.100", - "tempfile", + "syn 2.0.106", "tokio", "url", ] [[package]] name = "sqlx-mysql" -version = "0.8.3" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4560278f0e00ce64938540546f59f590d60beee33fffbd3b9cd47851e5fff233" +checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" dependencies = [ "atoi", "base64 0.22.1", - "bitflags 2.9.0", + "bitflags 2.9.4", "byteorder", "bytes", "crc", @@ -3807,7 +4171,7 @@ dependencies = [ "hex", "hkdf", "hmac", - "itoa 1.0.15", + "itoa", "log", "md-5", "memchr", @@ -3821,20 +4185,20 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror 2.0.12", + "thiserror 2.0.16", "tracing", "whoami", ] [[package]] name = "sqlx-postgres" -version = "0.8.3" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5b98a57f363ed6764d5b3a12bfedf62f07aa16e1856a7ddc2a0bb190a959613" +checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" dependencies = [ "atoi", "base64 0.22.1", - "bitflags 2.9.0", + "bitflags 2.9.4", "byteorder", "crc", "dotenvy", @@ -3846,7 +4210,7 @@ dependencies = [ "hkdf", "hmac", "home", - "itoa 1.0.15", + "itoa", "log", "md-5", "memchr", @@ -3858,16 +4222,16 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror 2.0.12", + "thiserror 2.0.16", "tracing", "whoami", ] [[package]] name = "sqlx-sqlite" -version = "0.8.3" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f85ca71d3a5b24e64e1d08dd8fe36c6c95c339a896cc33068148906784620540" +checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea" dependencies = [ "atoi", "flume", @@ -3882,6 +4246,7 @@ dependencies = [ "serde", "serde_urlencoded", "sqlx-core", + "thiserror 2.0.16", "tracing", "url", ] @@ -3970,9 +4335,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.100" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", @@ -3990,13 +4355,13 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.106", ] [[package]] @@ -4008,18 +4373,19 @@ dependencies = [ "cfg-expr", "heck 0.5.0", "pkg-config", - "toml", + "toml 0.8.2", "version-compare", ] [[package]] name = "tao" -version = "0.32.8" +version = "0.34.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63c8b1020610b9138dd7b1e06cf259ae91aa05c30f3bd0d6b42a03997b92dec1" +checksum = "959469667dbcea91e5485fc48ba7dd6023face91bb0f1a14681a70f99847c3f7" dependencies = [ - "bitflags 2.9.0", - "core-foundation 0.10.0", + "bitflags 2.9.4", + "block2 0.6.1", + "core-foundation 0.10.1", "core-graphics", "crossbeam-channel", "dispatch", @@ -4035,9 +4401,9 @@ dependencies = [ "ndk", "ndk-context", "ndk-sys", - "objc2 0.6.0", + "objc2 0.6.2", "objc2-app-kit", - "objc2-foundation 0.3.0", + "objc2-foundation 0.3.1", "once_cell", "parking_lot", "raw-window-handle", @@ -4046,7 +4412,7 @@ dependencies = [ "unicode-segmentation", "url", "windows", - "windows-core 0.60.1", + "windows-core 0.61.2", "windows-version", "x11-dl", ] @@ -4059,7 +4425,7 @@ checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.106", ] [[package]] @@ -4070,17 +4436,17 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" -version = "2.4.1" +version = "2.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d08db1ff9e011e04014e737ec022610d756c0eae0b3b3a9037bccaf3003173a" +checksum = "d4d1d3b3dc4c101ac989fd7db77e045cc6d91a25349cd410455cb5c57d510c1c" dependencies = [ "anyhow", "bytes", + "cookie", "dirs", "dunce", "embed_plist", - "futures-util", - "getrandom 0.2.15", + "getrandom 0.3.3", "glob", "gtk", "heck 0.5.0", @@ -4090,9 +4456,11 @@ dependencies = [ "log", "mime", "muda", - "objc2 0.6.0", + "objc2 0.6.2", "objc2-app-kit", - "objc2-foundation 0.3.0", + "objc2-foundation 0.3.1", + "objc2-ui-kit", + "objc2-web-kit", "percent-encoding", "plist", "raw-window-handle", @@ -4107,7 +4475,7 @@ dependencies = [ "tauri-runtime", "tauri-runtime-wry", "tauri-utils", - "thiserror 2.0.12", + "thiserror 2.0.16", "tokio", "tray-icon", "url", @@ -4120,9 +4488,9 @@ dependencies = [ [[package]] name = "tauri-build" -version = "2.1.1" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fd20e4661c2cce65343319e6e8da256958f5af958cafc47c0d0af66a55dcd17" +checksum = "9c432ccc9ff661803dab74c6cd78de11026a578a9307610bbc39d3c55be7943f" dependencies = [ "anyhow", "cargo_toml", @@ -4130,21 +4498,21 @@ dependencies = [ "glob", "heck 0.5.0", "json-patch", - "schemars", + "schemars 0.8.22", "semver", "serde", "serde_json", "tauri-utils", "tauri-winres", - "toml", + "toml 0.9.7", "walkdir", ] [[package]] name = "tauri-codegen" -version = "2.1.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458258b19032450ccf975840116ecf013e539eadbb74420bd890e8c56ab2b1a4" +checksum = "1ab3a62cf2e6253936a8b267c2e95839674e7439f104fa96ad0025e149d54d8a" dependencies = [ "base64 0.22.1", "brotli", @@ -4158,9 +4526,9 @@ dependencies = [ "serde", "serde_json", "sha2", - "syn 2.0.100", + "syn 2.0.106", "tauri-utils", - "thiserror 2.0.12", + "thiserror 2.0.16", "time", "url", "uuid", @@ -4169,40 +4537,40 @@ dependencies = [ [[package]] name = "tauri-macros" -version = "2.1.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d402813d3b9c773a0fa58697c457c771f10e735498fdcb7b343264d18e5a601f" +checksum = "4368ea8094e7045217edb690f493b55b30caf9f3e61f79b4c24b6db91f07995e" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.106", "tauri-codegen", "tauri-utils", ] [[package]] name = "tauri-plugin" -version = "2.1.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4190775d6ff73fe66d9af44c012739a2659720efd9c0e1e56a918678038699d" +checksum = "9946a3cede302eac0c6eb6c6070ac47b1768e326092d32efbb91f21ed58d978f" dependencies = [ "anyhow", "glob", "plist", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "tauri-utils", - "toml", + "toml 0.9.7", "walkdir", ] [[package]] name = "tauri-plugin-dialog" -version = "2.2.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcaf6e5d6062423a0f711a23c2a573ccba222b6a16a9322d8499928f27e41376" +checksum = "0beee42a4002bc695550599b011728d9dfabf82f767f134754ed6655e434824e" dependencies = [ "log", "raw-window-handle", @@ -4212,38 +4580,37 @@ dependencies = [ "tauri", "tauri-plugin", "tauri-plugin-fs", - "thiserror 2.0.12", + "thiserror 2.0.16", "url", ] [[package]] name = "tauri-plugin-fs" -version = "2.2.1" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88371e340ad2f07409a3b68294abe73f20bc9c1bc1b631a31dc37a3d0161f682" +checksum = "315784ec4be45e90a987687bae7235e6be3d6e9e350d2b75c16b8a4bf22c1db7" dependencies = [ "anyhow", "dunce", "glob", "percent-encoding", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "serde_repr", "tauri", "tauri-plugin", "tauri-utils", - "thiserror 2.0.12", - "toml", + "thiserror 2.0.16", + "toml 0.9.7", "url", - "uuid", ] [[package]] name = "tauri-plugin-global-shortcut" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00f646a09511e8d283267dcdaa08c2ef27c4116bf271d9114849d9ca215606c3" +checksum = "6df9f0f7bf2fe768b85fee4951c2505a35b72c44df1f6403e74e110bc13c5f58" dependencies = [ "global-hotkey", "log", @@ -4251,42 +4618,47 @@ dependencies = [ "serde_json", "tauri", "tauri-plugin", - "thiserror 2.0.12", + "thiserror 2.0.16", ] [[package]] name = "tauri-runtime" -version = "2.5.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00ada7ac2f9276f09b8c3afffd3215fd5d9bff23c22df8a7c70e7ef67cacd532" +checksum = "d4cfc9ad45b487d3fded5a4731a567872a4812e9552e3964161b08edabf93846" dependencies = [ "cookie", "dpi", "gtk", "http", "jni", + "objc2 0.6.2", + "objc2-ui-kit", + "objc2-web-kit", "raw-window-handle", "serde", "serde_json", "tauri-utils", - "thiserror 2.0.12", + "thiserror 2.0.16", "url", + "webkit2gtk", + "webview2-com", "windows", ] [[package]] name = "tauri-runtime-wry" -version = "2.5.1" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf2e5842c57e154af43a20a49c7efee0ce2578c20b4c2bdf266852b422d2e421" +checksum = "c1fe9d48bd122ff002064e88cfcd7027090d789c4302714e68fcccba0f4b7807" dependencies = [ "gtk", "http", "jni", "log", - "objc2 0.6.0", + "objc2 0.6.2", "objc2-app-kit", - "objc2-foundation 0.3.0", + "objc2-foundation 0.3.1", "once_cell", "percent-encoding", "raw-window-handle", @@ -4303,9 +4675,9 @@ dependencies = [ [[package]] name = "tauri-utils" -version = "2.3.1" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f037e66c7638cc0a2213f61566932b9a06882b8346486579c90e4b019bac447" +checksum = "41a3852fdf9a4f8fbeaa63dc3e9a85284dd6ef7200751f0bd66ceee30c93f212" dependencies = [ "anyhow", "brotli", @@ -4324,15 +4696,15 @@ dependencies = [ "proc-macro2", "quote", "regex", - "schemars", + "schemars 0.8.22", "semver", "serde", "serde-untagged", "serde_json", "serde_with", "swift-rs", - "thiserror 2.0.12", - "toml", + "thiserror 2.0.16", + "toml 0.9.7", "url", "urlpattern", "uuid", @@ -4341,25 +4713,25 @@ dependencies = [ [[package]] name = "tauri-winres" -version = "0.3.0" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56eaa45f707bedf34d19312c26d350bc0f3c59a47e58e8adbeecdc850d2c13a0" +checksum = "fd21509dd1fa9bd355dc29894a6ff10635880732396aa38c0066c1e6c1ab8074" dependencies = [ "embed-resource", - "toml", + "toml 0.9.7", ] [[package]] name = "tempfile" -version = "3.19.1" +version = "3.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" +checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" dependencies = [ "fastrand", - "getrandom 0.3.2", + "getrandom 0.3.3", "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys 0.61.0", ] [[package]] @@ -4373,12 +4745,6 @@ dependencies = [ "utf-8", ] -[[package]] -name = "thin-slice" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" - [[package]] name = "thiserror" version = "1.0.69" @@ -4390,11 +4756,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" dependencies = [ - "thiserror-impl 2.0.12", + "thiserror-impl 2.0.16", ] [[package]] @@ -4405,28 +4771,28 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.106", ] [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.106", ] [[package]] name = "time" -version = "0.3.41" +version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" dependencies = [ "deranged", - "itoa 1.0.15", + "itoa", "num-conv", "powerfmt", "serde", @@ -4436,15 +4802,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" [[package]] name = "time-macros" -version = "0.2.22" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" dependencies = [ "num-conv", "time-core", @@ -4452,19 +4818,29 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.7.6" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ "displaydoc", "zerovec", ] +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "tinyvec" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" dependencies = [ "tinyvec_macros", ] @@ -4477,20 +4853,22 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.44.2" +version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ "backtrace", "bytes", + "io-uring", "libc", "mio", "pin-project-lite", "signal-hook-registry", + "slab", "socket2", "tokio-macros", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -4501,7 +4879,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.106", ] [[package]] @@ -4517,9 +4895,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.14" +version = "0.7.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" dependencies = [ "bytes", "futures-core", @@ -4530,60 +4908,100 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.20" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" +checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" dependencies = [ "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.22.24", + "serde_spanned 0.6.9", + "toml_datetime 0.6.3", + "toml_edit 0.20.2", +] + +[[package]] +name = "toml" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00e5e5d9bf2475ac9d4f0d9edab68cc573dc2fd644b0dba36b0c30a92dd9eaa0" +dependencies = [ + "indexmap 2.11.4", + "serde_core", + "serde_spanned 1.0.2", + "toml_datetime 0.7.2", + "toml_parser", + "toml_writer", + "winnow 0.7.13", ] [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f1085dec27c2b6632b04c80b3bb1b4300d6495d1e129693bdda7d91e72eec1" +dependencies = [ + "serde_core", +] + [[package]] name = "toml_edit" version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.9.0", - "toml_datetime", + "indexmap 2.11.4", + "toml_datetime 0.6.3", "winnow 0.5.40", ] [[package]] name = "toml_edit" -version = "0.20.7" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" dependencies = [ - "indexmap 2.9.0", - "toml_datetime", + "indexmap 2.11.4", + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.3", "winnow 0.5.40", ] [[package]] name = "toml_edit" -version = "0.22.24" +version = "0.23.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +checksum = "f3effe7c0e86fdff4f69cdd2ccc1b96f933e24811c5441d44904e8683e27184b" dependencies = [ - "indexmap 2.9.0", - "serde", - "serde_spanned", - "toml_datetime", - "winnow 0.7.6", + "indexmap 2.11.4", + "toml_datetime 0.7.2", + "toml_parser", + "winnow 0.7.13", +] + +[[package]] +name = "toml_parser" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cf893c33be71572e0e9aa6dd15e6677937abd686b066eac3f8cd3531688a627" +dependencies = [ + "winnow 0.7.13", ] +[[package]] +name = "toml_writer" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d163a63c116ce562a22cda521fcc4d79152e7aba014456fb5eb442f6d6a10109" + [[package]] name = "tower" version = "0.5.2" @@ -4599,6 +5017,24 @@ dependencies = [ "tower-service", ] +[[package]] +name = "tower-http" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +dependencies = [ + "bitflags 2.9.4", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.3" @@ -4625,43 +5061,43 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.28" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.106", ] [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", ] [[package]] name = "tray-icon" -version = "0.20.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d433764348e7084bad2c5ea22c96c71b61b17afe3a11645710f533bd72b6a2b5" +checksum = "a0d92153331e7d02ec09137538996a7786fe679c629c279e82a6be762b7e6fe2" dependencies = [ "crossbeam-channel", "dirs", "libappindicator", "muda", - "objc2 0.6.0", + "objc2 0.6.2", "objc2-app-kit", "objc2-core-foundation", "objc2-core-graphics", - "objc2-foundation 0.3.0", + "objc2-foundation 0.3.1", "once_cell", "png", "serde", - "thiserror 2.0.12", + "thiserror 2.0.16", "windows-sys 0.59.0", ] @@ -4743,9 +5179,9 @@ checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] name = "unicode-ident" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" [[package]] name = "unicode-normalization" @@ -4770,9 +5206,9 @@ checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "url" -version = "2.5.4" +version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" dependencies = [ "form_urlencoded", "idna", @@ -4798,12 +5234,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - [[package]] name = "utf8_iter" version = "1.0.4" @@ -4812,12 +5242,14 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.16.0" +version = "1.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.3", + "js-sys", "serde", + "wasm-bindgen", ] [[package]] @@ -4885,17 +5317,26 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" -version = "0.14.2+wasi-0.2.4" +version = "0.14.7+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" +dependencies = [ + "wasip2", +] + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" dependencies = [ - "wit-bindgen-rt", + "wit-bindgen", ] [[package]] @@ -4906,35 +5347,36 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "ab10a69fbd0a177f5f649ad4d8d3305499c42bab9aef2f7ff592d0ec8f833819" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", + "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.100" +version = "0.2.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +checksum = "0bb702423545a6007bbc368fde243ba47ca275e549c8a28617f56f6ba53b1d1c" dependencies = [ "bumpalo", "log", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.106", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.50" +version = "0.4.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +checksum = "a0b221ff421256839509adbb55998214a70d829d3a28c69b4a6672e9d2a42f67" dependencies = [ "cfg-if", "js-sys", @@ -4945,9 +5387,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "fc65f4f411d91494355917b605e1480033152658d71f722a90647f56a70c88a0" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4955,22 +5397,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "ffc003a991398a8ee604a401e194b6b3a39677b3173d6e74495eb51b82e99a32" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.106", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "293c37f4efa430ca14db3721dfbe48d8c33308096bd44d80ebaa775ab71ba1cf" dependencies = [ "unicode-ident", ] @@ -4988,11 +5430,71 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wayland-backend" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673a33c33048a5ade91a6b139580fa174e19fb0d23f396dca9fa15f2e1e49b35" +dependencies = [ + "cc", + "downcast-rs", + "rustix", + "scoped-tls", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d" +dependencies = [ + "bitflags 2.9.4", + "rustix", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols" +version = "0.32.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efa790ed75fbfd71283bd2521a1cfdc022aabcc28bdcff00851f9e4ae88d9901" +dependencies = [ + "bitflags 2.9.4", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54cb1e9dc49da91950bdfd8b848c49330536d9d1fb03d4bfec8cae50caa50ae3" +dependencies = [ + "proc-macro2", + "quick-xml 0.37.5", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34949b42822155826b41db8e5d0c1be3a2bd296c747577a43a3e6daefc296142" +dependencies = [ + "dlib", + "log", + "pkg-config", +] + [[package]] name = "web-sys" -version = "0.3.77" +version = "0.3.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +checksum = "fbe734895e869dc429d78c4b433f8d17d95f8d05317440b4fad5ab2d33e596dc" dependencies = [ "js-sys", "wasm-bindgen", @@ -5044,15 +5546,15 @@ dependencies = [ [[package]] name = "webview2-com" -version = "0.36.0" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0d606f600e5272b514dbb66539dd068211cc20155be8d3958201b4b5bd79ed3" +checksum = "d4ba622a989277ef3886dd5afb3e280e3dd6d974b766118950a08f8f678ad6a4" dependencies = [ "webview2-com-macros", "webview2-com-sys", "windows", - "windows-core 0.60.1", - "windows-implement 0.59.0", + "windows-core 0.61.2", + "windows-implement", "windows-interface", ] @@ -5064,27 +5566,27 @@ checksum = "1d228f15bba3b9d56dde8bddbee66fa24545bd17b48d5128ccf4a8742b18e431" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.106", ] [[package]] name = "webview2-com-sys" -version = "0.36.0" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb27fccd3c27f68e9a6af1bcf48c2d82534b8675b83608a4d81446d095a17ac" +checksum = "36695906a1b53a3bf5c4289621efedac12b73eeb0b89e7e1a89b517302d5d75c" dependencies = [ - "thiserror 2.0.12", + "thiserror 2.0.16", "windows", - "windows-core 0.60.1", + "windows-core 0.61.2", ] [[package]] name = "whoami" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6994d13118ab492c3c80c1f81928718159254c53c472bf9ce36f8dae4add02a7" +checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d" dependencies = [ - "redox_syscall", + "libredox", "wasite", ] @@ -5106,11 +5608,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.0", ] [[package]] @@ -5125,10 +5627,10 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c" dependencies = [ - "objc2 0.6.0", + "objc2 0.6.2", "objc2-app-kit", "objc2-core-foundation", - "objc2-foundation 0.3.0", + "objc2-foundation 0.3.1", "raw-window-handle", "windows-sys 0.59.0", "windows-version", @@ -5136,71 +5638,61 @@ dependencies = [ [[package]] name = "windows" -version = "0.60.0" +version = "0.61.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddf874e74c7a99773e62b1c671427abf01a425e77c3d3fb9fb1e4883ea934529" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" dependencies = [ "windows-collections", - "windows-core 0.60.1", + "windows-core 0.61.2", "windows-future", - "windows-link", + "windows-link 0.1.3", "windows-numerics", ] [[package]] name = "windows-collections" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5467f79cc1ba3f52ebb2ed41dbb459b8e7db636cc3429458d9a852e15bc24dec" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" dependencies = [ - "windows-core 0.60.1", + "windows-core 0.61.2", ] [[package]] name = "windows-core" -version = "0.60.1" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca21a92a9cae9bf4ccae5cf8368dce0837100ddf6e6d57936749e85f152f6247" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ - "windows-implement 0.59.0", + "windows-implement", "windows-interface", - "windows-link", - "windows-result", - "windows-strings 0.3.1", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", ] [[package]] name = "windows-core" -version = "0.61.0" +version = "0.62.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +checksum = "57fe7168f7de578d2d8a05b07fd61870d2e73b4020e9f49aa00da8471723497c" dependencies = [ - "windows-implement 0.60.0", + "windows-implement", "windows-interface", - "windows-link", - "windows-result", - "windows-strings 0.4.0", + "windows-link 0.2.0", + "windows-result 0.4.0", + "windows-strings 0.5.0", ] [[package]] name = "windows-future" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a787db4595e7eb80239b74ce8babfb1363d8e343ab072f2ffe901400c03349f0" -dependencies = [ - "windows-core 0.60.1", - "windows-link", -] - -[[package]] -name = "windows-implement" -version = "0.59.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", + "windows-core 0.61.2", + "windows-link 0.1.3", + "windows-threading", ] [[package]] @@ -5211,7 +5703,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.106", ] [[package]] @@ -5222,61 +5714,65 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.106", ] [[package]] name = "windows-link" -version = "0.1.1" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-link" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" +checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" [[package]] name = "windows-numerics" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "005dea54e2f6499f2cee279b8f703b3cf3b5734a2d8d21867c8f44003182eeed" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ - "windows-core 0.60.1", - "windows-link", + "windows-core 0.61.2", + "windows-link 0.1.3", ] [[package]] -name = "windows-registry" -version = "0.4.0" +name = "windows-result" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ - "windows-result", - "windows-strings 0.3.1", - "windows-targets 0.53.0", + "windows-link 0.1.3", ] [[package]] name = "windows-result" -version = "0.3.2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f" dependencies = [ - "windows-link", + "windows-link 0.2.0", ] [[package]] name = "windows-strings" -version = "0.3.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] name = "windows-strings" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda" dependencies = [ - "windows-link", + "windows-link 0.2.0", ] [[package]] @@ -5299,20 +5795,29 @@ dependencies = [ [[package]] name = "windows-sys" -version = "0.52.0" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets 0.52.6", ] [[package]] name = "windows-sys" -version = "0.59.0" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.52.6", + "windows-targets 0.53.3", +] + +[[package]] +name = "windows-sys" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa" +dependencies = [ + "windows-link 0.2.0", ] [[package]] @@ -5363,10 +5868,11 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.0" +version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" dependencies = [ + "windows-link 0.1.3", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", @@ -5377,13 +5883,22 @@ dependencies = [ "windows_x86_64_msvc 0.53.0", ] +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link 0.1.3", +] + [[package]] name = "windows-version" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e04a5c6627e310a23ad2358483286c7df260c964eb2d003d8efd6d0f4e79265c" +checksum = "69e061eb0a22b4a1d778ad70f7575ec7845490abb35b08fa320df7895882cacb" dependencies = [ - "windows-link", + "windows-link 0.2.0", ] [[package]] @@ -5577,54 +6092,46 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.6" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" dependencies = [ "memchr", ] [[package]] name = "winreg" -version = "0.52.0" +version = "0.55.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97" dependencies = [ "cfg-if", - "windows-sys 0.48.0", -] - -[[package]] -name = "wit-bindgen-rt" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags 2.9.0", + "windows-sys 0.59.0", ] [[package]] -name = "write16" -version = "1.0.0" +name = "wit-bindgen" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "writeable" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "wry" -version = "0.50.5" +version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b19b78efae8b853c6c817e8752fc1dbf9cab8a8ffe9c30f399bd750ccf0f0730" +checksum = "31f0e9642a0d061f6236c54ccae64c2722a7879ad4ec7dff59bd376d446d8e90" dependencies = [ "base64 0.22.1", - "block2 0.6.0", + "block2 0.6.1", "cookie", "crossbeam-channel", + "dirs", "dpi", "dunce", "gdkx11", @@ -5636,10 +6143,10 @@ dependencies = [ "kuchikiki", "libc", "ndk", - "objc2 0.6.0", + "objc2 0.6.2", "objc2-app-kit", "objc2-core-foundation", - "objc2-foundation 0.3.0", + "objc2-foundation 0.3.1", "objc2-ui-kit", "objc2-web-kit", "once_cell", @@ -5648,13 +6155,13 @@ dependencies = [ "sha2", "soup3", "tao-macros", - "thiserror 2.0.12", + "thiserror 2.0.16", "url", "webkit2gtk", "webkit2gtk-sys", "webview2-com", "windows", - "windows-core 0.60.1", + "windows-core 0.61.2", "windows-version", "x11-dl", ] @@ -5681,20 +6188,33 @@ dependencies = [ ] [[package]] -name = "xdg-home" -version = "1.3.0" +name = "x11rb" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" +checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414" dependencies = [ - "libc", - "windows-sys 0.59.0", + "gethostname", + "rustix", + "x11rb-protocol", ] +[[package]] +name = "x11rb-protocol" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" + +[[package]] +name = "xkeysym" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" + [[package]] name = "yoke" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" dependencies = [ "serde", "stable_deref_trait", @@ -5704,21 +6224,21 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.106", "synstructure", ] [[package]] name = "zbus" -version = "5.5.0" +version = "5.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59c333f648ea1b647bc95dc1d34807c8e25ed7a6feff3394034dc4776054b236" +checksum = "2d07e46d035fb8e375b2ce63ba4e4ff90a7f73cf2ffb0138b29e1158d2eaadf7" dependencies = [ "async-broadcast", "async-recursion", @@ -5732,13 +6252,11 @@ dependencies = [ "ordered-stream", "serde", "serde_repr", - "static_assertions", "tokio", "tracing", "uds_windows", - "windows-sys 0.59.0", - "winnow 0.7.6", - "xdg-home", + "windows-sys 0.60.2", + "winnow 0.7.13", "zbus_macros", "zbus_names", "zvariant", @@ -5746,14 +6264,14 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.5.0" +version = "5.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f325ad10eb0d0a3eb060203494c3b7ec3162a01a59db75d2deee100339709fc0" +checksum = "57e797a9c847ed3ccc5b6254e8bcce056494b375b511b3d6edcec0aeb4defaca" dependencies = [ - "proc-macro-crate 3.3.0", + "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.106", "zbus_names", "zvariant", "zvariant_utils", @@ -5767,28 +6285,28 @@ checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97" dependencies = [ "serde", "static_assertions", - "winnow 0.7.6", + "winnow 0.7.13", "zvariant", ] [[package]] name = "zerocopy" -version = "0.8.24" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.24" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.106", ] [[package]] @@ -5808,7 +6326,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.106", "synstructure", ] @@ -5818,11 +6336,22 @@ version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + [[package]] name = "zerovec" -version = "0.10.4" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" dependencies = [ "yoke", "zerofrom", @@ -5831,54 +6360,52 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.10.3" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.106", ] [[package]] name = "zvariant" -version = "5.4.0" +version = "5.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2df9ee044893fcffbdc25de30546edef3e32341466811ca18421e3cd6c5a3ac" +checksum = "999dd3be73c52b1fccd109a4a81e4fcd20fab1d3599c8121b38d04e1419498db" dependencies = [ "endi", "enumflags2", "serde", - "static_assertions", "url", - "winnow 0.7.6", + "winnow 0.7.13", "zvariant_derive", "zvariant_utils", ] [[package]] name = "zvariant_derive" -version = "5.4.0" +version = "5.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74170caa85b8b84cc4935f2d56a57c7a15ea6185ccdd7eadb57e6edd90f94b2f" +checksum = "6643fd0b26a46d226bd90d3f07c1b5321fe9bb7f04673cb37ac6d6883885b68e" dependencies = [ - "proc-macro-crate 3.3.0", + "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.106", "zvariant_utils", ] [[package]] name = "zvariant_utils" -version = "3.2.0" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16edfee43e5d7b553b77872d99bc36afdda75c223ca7ad5e3fbecd82ca5fc34" +checksum = "c6949d142f89f6916deca2232cf26a8afacf2b9fdc35ce766105e104478be599" dependencies = [ "proc-macro2", "quote", "serde", - "static_assertions", - "syn 2.0.100", - "winnow 0.7.6", + "syn 2.0.106", + "winnow 0.7.13", ] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 9885793..43aa20c 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "FileFlow" -version = "1.0.3" +version = "1.0.4" description = "An app to manipulate DBMS tables" authors = ["Maxime-Cllt"] edition = "2021" @@ -8,16 +8,21 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [build-dependencies] -tauri-build = { version = "2", features = [] } +tauri-build = { version = "2.4.1", features = [] } [dependencies] -tauri = { version = "2", features = [] } -serde = { version = "1.0.215", features = ["derive"] } -serde_json = "1.0.133" -sqlx = { version = "0.8.2", features = ["runtime-tokio-native-tls", "mysql", "postgres", "sqlite"] } -tokio = { version = "1.42.0", features = ["rt", "macros"] } +tauri = { version = "2.8.5", features = [] } +serde = { version = "1.0.226", features = ["derive"] } +serde_json = "1.0.145" +sqlx = { version = "0.8.6", features = ["runtime-tokio-native-tls", "mysql", "postgres", "sqlite"] } +tokio = { version = "1.47.1", features = ["rt", "macros"] } csv = "1.3.1" -tauri-plugin-dialog = "2" +tauri-plugin-dialog = "2.4.0" + +[lib] +name = "fileflow" +path = "src/lib.rs" +crate-type = ["rlib"] [features] # This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!! @@ -25,15 +30,11 @@ custom-protocol = ["tauri/custom-protocol"] [dev-dependencies] - -#[[bench]] -#name = "bench_sql" -#harness = false -#path = "src/benches/bench_sql.rs" +criterion = "0.7.0" +tempfile = "^3.23.0" [profile.dev] incremental = true -codegen-units = 20 opt-level = 0 panic = "unwind" debug = true @@ -50,5 +51,23 @@ strip = true debug = false lto = true + +# Optimized profile for benchmark builds +[profile.bench] +incremental = true +codegen-units = 16 +opt-level = 1 +panic = "unwind" +debug = 1 +lto = "thin" +overflow-checks = false + + [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] tauri-plugin-global-shortcut = "2" + + +[[bench]] +name = "benchmark_application" +harness = false +path = "src/benches/application_bench.rs" diff --git a/src-tauri/src/benches/application_bench.rs b/src-tauri/src/benches/application_bench.rs new file mode 100644 index 0000000..05a0155 --- /dev/null +++ b/src-tauri/src/benches/application_bench.rs @@ -0,0 +1,82 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use csv::{Reader, ReaderBuilder}; +use fileflow::fileflow::action::database_command::fast_insert; +use fileflow::fileflow::enums::database_engine::DatabaseEngine; +use fileflow::fileflow::stuct::config::db_config::DatabaseConfig; +use fileflow::fileflow::stuct::database::database_connection::DatabaseConnection; +use fileflow::fileflow::stuct::io::file_utils::FileUtils; +use fileflow::fileflow::stuct::string_formater::StringFormatter; +use sqlx::Error; +use std::fs::File; + +#[allow(dead_code)] +async fn test_insert_file() { + const TEST_FILE: &str = "reference.csv"; + + let config: DatabaseConfig = DatabaseConfig { + db_driver: DatabaseEngine::SQLite, + username: String::new(), + password: String::new(), + db_host: String::new(), + port: String::new(), + db_name: String::new(), + sqlite_file_path: r"test.db".into(), + }; + + let conn: Result = DatabaseConnection::connect(&config).await; + + if conn.is_err() { + eprintln!("Error connecting to the database"); + return; + } + + let connection: &DatabaseConnection = conn.as_ref().unwrap(); + + let file: File = match File::open(TEST_FILE) { + Ok(file) => file, + Err(_) => return, + }; + + let first_line: String = FileUtils::read_first_line(TEST_FILE).unwrap(); + + let separator: char = FileUtils::find_separator(&first_line).unwrap(); + + let final_columns_name: Vec = StringFormatter::get_formated_column_names( + &first_line + .split(separator) + .map(StringFormatter::sanitize_column) + .collect::>(), + ); + + let mut reader: Reader = ReaderBuilder::new() + .delimiter(u8::try_from(separator).unwrap()) + .has_headers(true) + .from_reader(file); + + fast_insert( + connection, + &mut reader, + &final_columns_name, + "test_table", + &DatabaseEngine::SQLite, + ) + .await + .unwrap(); +} + +#[allow(dead_code)] +fn benchmark_application(c: &mut Criterion) { + let mut group = c.benchmark_group("benchmark_application"); + + group.bench_function("test_insert_file", |b| { + b.iter(async || { + test_insert_file().await; + }) + }); + + group.finish(); +} + +criterion_group!(benches, benchmark_application); + +criterion_main!(benches); diff --git a/src-tauri/src/benches/mod.rs b/src-tauri/src/benches/mod.rs new file mode 100644 index 0000000..419fefa --- /dev/null +++ b/src-tauri/src/benches/mod.rs @@ -0,0 +1 @@ +mod application_bench; diff --git a/src-tauri/src/fileflow/action/actions.rs b/src-tauri/src/fileflow/action/actions.rs index 8104b8b..28176a5 100644 --- a/src-tauri/src/fileflow/action/actions.rs +++ b/src-tauri/src/fileflow/action/actions.rs @@ -1,12 +1,11 @@ use crate::fileflow::action::database_command::{fast_insert, optimized_insert}; -use crate::fileflow::database::connection::Connection; -use crate::fileflow::enumeration::insertion_type::InsertionType; -use crate::fileflow::stuct::insert_config::InsertConfig; -use crate::fileflow::stuct::save_config::SaveConfig; +use crate::fileflow::enums::insertion_type::InsertionType; +use crate::fileflow::stuct::config::insert_config::InsertConfig; +use crate::fileflow::stuct::config::save_config::SaveConfig; +use crate::fileflow::stuct::database::database_connection::DatabaseConnection; +use crate::fileflow::stuct::io::file_utils::FileUtils; use crate::fileflow::stuct::string_formater::StringFormatter; use crate::fileflow::utils::constants::DATABASE_CONFIG_FILE; -use crate::fileflow::utils::csv_utils::{find_separator, read_first_line}; -use crate::fileflow::utils::fileflowlib::{get_all_saved_configs, save_config}; use csv::{Reader, ReaderBuilder}; use std::fs::{File, Metadata}; use std::sync::Arc; @@ -14,7 +13,7 @@ use std::time::Instant; use tauri::{command, State}; use tokio::sync::Mutex; -pub struct DatabaseState(pub Mutex>); +pub struct DatabaseState(pub Mutex>); #[command] pub async fn insert_csv_data( @@ -27,18 +26,24 @@ pub async fn insert_csv_data( return Err("Error: Connection is not established".into()); } - let connection: &Connection = conn_guard.as_ref().unwrap(); + let connection: &DatabaseConnection = conn_guard.as_ref().unwrap(); let mut total_lines: u64 = 0; // Counter for the total number of lines inserted let start: Instant = Instant::now(); // Timer for the insertion process - let file: File = File::open(&csv.file_path).expect("Failed to open file"); - let first_line: String = read_first_line(&csv.file_path).expect("Failed to read first line"); // Read the first line of the file to detect the separator - let separator: char = find_separator(&first_line).expect("Failed to find separator"); // Separator detection of the file + let file: File = match File::open(&csv.file_path) { + Ok(file) => file, + Err(e) => return Err(format!("Error: Failed to open file: {}", e.to_string())), + }; + + let first_line: String = FileUtils::read_first_line(&csv.file_path) + .map_err(|e| format!("Error: Failed to read first line: {}", e.to_string()))?; + + let separator: char = FileUtils::find_separator(&first_line)?; let final_columns_name: Vec = StringFormatter::get_formated_column_names( &first_line .split(separator) - .map(|s| StringFormatter::sanitize_column(s)) + .map(StringFormatter::sanitize_column) .collect::>(), ); @@ -95,7 +100,7 @@ pub async fn insert_csv_data( #[command] pub async fn save_database_config(save: SaveConfig) -> Result { - let mut existing_configs: Vec = get_all_saved_configs(DATABASE_CONFIG_FILE); + let mut existing_configs: Vec = SaveConfig::get_all_configs(DATABASE_CONFIG_FILE); for config in existing_configs.iter() { if config.config_name == save.config_name { @@ -104,14 +109,14 @@ pub async fn save_database_config(save: SaveConfig) -> Result { } existing_configs.push(save); - save_config(&existing_configs, DATABASE_CONFIG_FILE) + SaveConfig::save_config(&existing_configs, DATABASE_CONFIG_FILE) .map(|_| true) .map_err(|_| false) } #[command] pub async fn get_all_database_configs_name() -> Result { - let configs: Vec = get_all_saved_configs(DATABASE_CONFIG_FILE); // Get all saved configs + let configs: Vec = SaveConfig::get_all_configs(DATABASE_CONFIG_FILE); // Get all saved configs let configs_names: Vec = configs.iter().map(|c| c.config_name.clone()).collect(); // Get only the names let configs_json: String = match serde_json::to_string(&configs_names) { Ok(json) => json, // Convert to json string @@ -122,7 +127,7 @@ pub async fn get_all_database_configs_name() -> Result { #[command] pub async fn load_database_config_by_name(name: String) -> Result { - let configs: Vec = get_all_saved_configs(DATABASE_CONFIG_FILE); // Get all saved configs + let configs: Vec = SaveConfig::get_all_configs(DATABASE_CONFIG_FILE); // Get all saved configs for config in configs.iter() { // Find the config with the given name if config.config_name == name { @@ -137,7 +142,7 @@ pub async fn load_database_config_by_name(name: String) -> Result #[command] pub async fn delete_database_config(name: String) -> Result { - let configs: Vec = get_all_saved_configs(DATABASE_CONFIG_FILE); + let configs: Vec = SaveConfig::get_all_configs(DATABASE_CONFIG_FILE); let mut new_configs: Vec = Vec::new(); let mut found: bool = false; @@ -154,7 +159,7 @@ pub async fn delete_database_config(name: String) -> Result { return Err(false); } - save_config(&new_configs, DATABASE_CONFIG_FILE) + SaveConfig::save_config(&new_configs, DATABASE_CONFIG_FILE) .map(|_| true) .map_err(|_| false) } @@ -171,3 +176,8 @@ pub async fn get_size_of_file(file_path: String) -> Result { let size: f64 = metadata.len() as f64 / 1024.0 / 1024.0; Ok(format!("{size:.2} MB")) } + +#[command] +pub async fn get_app_version() -> String { + env!("CARGO_PKG_VERSION").to_string() +} diff --git a/src-tauri/src/fileflow/action/database_command.rs b/src-tauri/src/fileflow/action/database_command.rs index 8d57353..6564696 100644 --- a/src-tauri/src/fileflow/action/database_command.rs +++ b/src-tauri/src/fileflow/action/database_command.rs @@ -1,30 +1,28 @@ use crate::fileflow::action::actions::DatabaseState; -use crate::fileflow::database::connection::{Connection, QueryResult}; -use crate::fileflow::database::database_actions::{ - batch_insert, create_and_copy_final_table, drop_existing_tables, drop_table_if_exists, - execute_query, export_table, -}; -use crate::fileflow::database::sql_builder::{ - build_create_table_sql, build_prepared_statement_sql, build_query_all_tables, -}; -use crate::fileflow::enumeration::database_engine::DatabaseEngine; -use crate::fileflow::stuct::combo_item::ComboItem; -use crate::fileflow::stuct::db_config::DbConfig; -use crate::fileflow::stuct::download_config::DownloadConfig; +use crate::fileflow::enums::database_engine::DatabaseEngine; +use crate::fileflow::enums::query_rows::QueryRows; +use crate::fileflow::stuct::config::combo_item::ComboItem; +use crate::fileflow::stuct::config::db_config::DatabaseConfig; +use crate::fileflow::stuct::config::download_config::DownloadConfig; +use crate::fileflow::stuct::database::database_action::DatabaseAction; +use crate::fileflow::stuct::database::database_connection::DatabaseConnection; +use crate::fileflow::stuct::database::sql_builder::SqlBuilder; +use crate::fileflow::stuct::io::table_exporter::TableExporter; +use crate::fileflow::stuct::string_formater::StringFormatter; use csv::{Reader, StringRecord}; use serde_json::{json, Value}; use sqlx::Row; use std::collections::HashMap; +use std::fmt::Write; use std::fs::File; use std::sync::Arc; use std::time::Instant; use tauri::{command, State}; -use crate::fileflow::stuct::string_formater::StringFormatter; #[command] pub async fn connect_to_database( state: State<'_, Arc>, - config: DbConfig, + config: DatabaseConfig, ) -> Result { let mut conn_guard = state.0.lock().await; @@ -32,7 +30,7 @@ pub async fn connect_to_database( return Err("Already connected to the database.".into()); } - match Connection::connect(&config).await { + match DatabaseConnection::connect(&config).await { Ok(connection) => { *conn_guard = Some(connection); Ok(true) @@ -51,7 +49,7 @@ pub async fn disconnect_from_database( return Err("No active database connection to disconnect.".into()); } - let conn: Connection = match conn_guard.take() { + let conn: DatabaseConnection = match conn_guard.take() { Some(conn) => conn, None => return Err("No active database connection to disconnect.".into()), }; @@ -69,8 +67,8 @@ pub async fn is_connected(state: State<'_, Arc>) -> Result>) -> Result conn, None => return Err(false), }; - let db_config: &DbConfig = connection.get_db_config(); - let sql: &str = &build_query_all_tables(&db_config.db_driver, &db_config.db_name); + let db_config: &DatabaseConfig = connection.get_db_config(); + let sql: String = SqlBuilder::build_query_all_tables(&db_config.db_driver, &db_config.db_name); - let result: QueryResult = connection - .query_many_with_result(sql) + let result: QueryRows = connection + .query_many_with_result(&sql) .await .map_err(|_| false)?; let mut vec: Vec = Vec::new(); match result { - QueryResult::MySQL(rows) => { + QueryRows::MySQL(rows) => { for row in rows { vec.push(ComboItem { value: row.get("TABLE_NAME"), @@ -111,7 +109,7 @@ pub async fn get_table_list(state: State<'_, Arc>) -> Result { + QueryRows::Postgres(rows) => { for row in rows { vec.push(ComboItem { value: row.get("table_name"), @@ -119,7 +117,7 @@ pub async fn get_table_list(state: State<'_, Arc>) -> Result { + QueryRows::SQLite(rows) => { for row in rows { vec.push(ComboItem { value: row.get("name"), @@ -149,14 +147,14 @@ pub async fn download_table( return Err("Some required fields are missing.".into()); } - let connection: &Connection = match conn_guard.as_ref() { + let connection: &DatabaseConnection = match conn_guard.as_ref() { Some(conn) => conn, None => return Err("No active database connection.".into()), }; let mut exported_table: u32 = 0; for table_name in config.table_name_list.iter() { - if let Err(err) = export_table(connection, &config, table_name).await { + if let Err(err) = TableExporter::export_table(connection, &config, table_name).await { println!("{err}"); continue; } @@ -178,23 +176,25 @@ pub async fn download_table( /// Fast insert data the csv file into the database table pub async fn fast_insert( - connection: &Connection, + connection: &DatabaseConnection, reader: &mut Reader, final_columns_name: &[String], final_table_name: &str, db_driver: &DatabaseEngine, ) -> Result { // Drop the table if it exists - if let Err(err) = drop_table_if_exists(connection, db_driver, final_table_name).await { + if let Err(err) = + DatabaseAction::drop_table_if_exists(connection, db_driver, final_table_name).await + { eprintln!("Error: {err}"); return Err(err); } let build_create_table_statement: String = - build_create_table_sql(db_driver, final_table_name, final_columns_name); + SqlBuilder::build_create_table(db_driver, final_table_name, final_columns_name)?; // Create the table - if let Err(err) = execute_query( + if let Err(err) = DatabaseAction::execute_query( connection, &build_create_table_statement, "Failed to create table", @@ -210,32 +210,32 @@ pub async fn fast_insert( let mut batch: Vec = Vec::with_capacity(MAX_BATCH_SIZE); // Prepare the insert query - let insert_query_base: &str = - &build_prepared_statement_sql(db_driver, final_table_name, &final_columns_name); + let insert_query_base: String = + SqlBuilder::build_prepared_statement(db_driver, final_table_name, final_columns_name)?; for result in reader.records() { let values: String = match result { - Ok(record) => StringFormatter::escaped_record(record), + Ok(record) => StringFormatter::escape_record(record), Err(_) => continue, }; batch.push(format!("({values})")); if batch.len() >= MAX_BATCH_SIZE { - line_count += insert_batch(connection, insert_query_base, &batch).await; + line_count += insert_batch(connection, &insert_query_base, &batch).await; batch.clear(); } } // Insert the remaining records if any - line_count += insert_batch(connection, insert_query_base, &batch).await; + line_count += insert_batch(connection, &insert_query_base, &batch).await; Ok(line_count) } /// Insert data into the database using the optimized table creation and insertion method pub async fn optimized_insert( - connection: &Connection, + connection: &DatabaseConnection, reader: &mut Reader, final_columns_name: &[String], final_table_name: &str, @@ -243,7 +243,7 @@ pub async fn optimized_insert( ) -> Result { // Drop existing tables let temporary_table_name: String = format!("{final_table_name}_temporary"); - drop_existing_tables( + DatabaseAction::drop_existing_tables( connection, &[&temporary_table_name, final_table_name], db_driver, @@ -253,9 +253,9 @@ pub async fn optimized_insert( // Create the temporary table let create_temp_table_query: String = - build_create_table_sql(db_driver, &temporary_table_name, final_columns_name); + SqlBuilder::build_create_table(db_driver, &temporary_table_name, final_columns_name)?; - execute_query( + DatabaseAction::execute_query( connection, &create_temp_table_query, "Failed to create temporary table", @@ -266,7 +266,7 @@ pub async fn optimized_insert( // Initialize variables const MAX_BATCH_SIZE: usize = 5_000; let insert_query_base: String = - build_prepared_statement_sql(db_driver, &temporary_table_name, &final_columns_name); + SqlBuilder::build_prepared_statement(db_driver, &temporary_table_name, final_columns_name)?; let mut columns_size_map: HashMap<&str, usize> = final_columns_name .iter() @@ -281,19 +281,32 @@ pub async fn optimized_insert( Err(_) => continue, }; - let mut values: Vec = Vec::with_capacity(record.len()); + let mut row_buffer = String::with_capacity(256); + row_buffer.push('('); for (i, value) in record.iter().enumerate() { - let sanitized_value: String = StringFormatter::sanitize_value(value); + if i > 0 { + row_buffer.push_str(", "); + } + + // Assume sanitize_value returns Cow now + let sanitized_value = StringFormatter::sanitize_value(value); + + // Update column size tracking + let column_name = &final_columns_name[i]; let max_length: &mut usize = columns_size_map - .get_mut(final_columns_name[i].as_str()) + .get_mut(column_name.as_str()) .ok_or("Column name mismatch") .expect("Column name mismatch"); + *max_length = (*max_length).max(sanitized_value.len() + 1); - values.push(format!("'{sanitized_value}'")); + + // Write directly to buffer + write!(row_buffer, "'{sanitized_value}'").unwrap(); } - batch.push(format!("({})", values.join(", "))); + row_buffer.push(')'); + batch.push(row_buffer); if batch.len() >= MAX_BATCH_SIZE { line_count += insert_batch(connection, &insert_query_base, &batch).await; @@ -307,7 +320,7 @@ pub async fn optimized_insert( } // Create final table and copy data - create_and_copy_final_table( + DatabaseAction::create_and_copy_final_table( connection, db_driver, final_table_name, @@ -317,14 +330,18 @@ pub async fn optimized_insert( ) .await?; - drop_table_if_exists(connection, db_driver, &temporary_table_name).await?; // Drop the temporary table + DatabaseAction::drop_table_if_exists(connection, db_driver, &temporary_table_name).await?; // Drop the temporary table Ok(line_count) } /// Insert a batch of records into the database -async fn insert_batch(connection: &Connection, insert_query_base: &str, batch: &[String]) -> u32 { - match batch_insert( +async fn insert_batch( + connection: &DatabaseConnection, + insert_query_base: &str, + batch: &[String], +) -> u32 { + match DatabaseAction::batch_insert( connection, insert_query_base, batch, diff --git a/src-tauri/src/fileflow/action/mod.rs b/src-tauri/src/fileflow/action/mod.rs index 339c2ac..3e2040b 100644 --- a/src-tauri/src/fileflow/action/mod.rs +++ b/src-tauri/src/fileflow/action/mod.rs @@ -1,2 +1,2 @@ pub mod actions; -pub mod database_command; \ No newline at end of file +pub mod database_command; diff --git a/src-tauri/src/fileflow/database/database_actions.rs b/src-tauri/src/fileflow/database/database_actions.rs deleted file mode 100644 index e0bca4e..0000000 --- a/src-tauri/src/fileflow/database/database_actions.rs +++ /dev/null @@ -1,214 +0,0 @@ -use crate::fileflow::database::connection::{Connection, QueryResult}; -use crate::fileflow::database::sql_builder::{ - build_copy_table_sql, build_create_with_fixed_size_sql, build_drop_statement_sql, -}; -use crate::fileflow::enumeration::database_engine::DatabaseEngine; -use crate::fileflow::stuct::download_config::DownloadConfig; -use csv::{Writer, WriterBuilder}; -use sqlx::{Column, Row}; -use std::collections::HashMap; -use std::fs::File; - -/// Exports a tableโ€™s data into a CSV file. It uses offset/LIMIT pagination to retrieve data in batches -pub async fn export_table( - connection: &Connection, - download_config: &DownloadConfig, - table_name: &str, -) -> Result<(), Box> { - const LIMIT: i32 = 5_000; - let mut offset: i32 = 0; - let mut header_written: bool = false; - let file_path: String = format!("{}/{table_name}_export.csv", download_config.location); - - let mut wtr: Writer = WriterBuilder::new() - .delimiter(download_config.separator.as_u8()) - .from_path(&file_path) - .expect("Failed to create CSV writer"); - - let base_sql: String = format!("SELECT * FROM {table_name}"); - - loop { - let sql_query: String = format!("{base_sql} LIMIT {LIMIT} OFFSET {offset}"); - let query_result: QueryResult = connection.query_many_with_result(&sql_query).await?; - - let (columns, rows): (Vec, Vec>) = match query_result { - QueryResult::MySQL(rows) => { - if rows.is_empty() { - break; - } - let cols = rows[0] - .columns() - .iter() - .map(|col| col.name().to_string()) - .collect::>(); - let data = rows - .into_iter() - .map(|row| { - row.columns() - .iter() - .map(|col| { - row.try_get::, _>(col.name()) - .unwrap_or(None) - .unwrap_or_default() - }) - .collect::>() - }) - .collect(); - (cols, data) - } - QueryResult::Postgres(rows) => { - if rows.is_empty() { - break; - } - let cols = rows[0] - .columns() - .iter() - .map(|col| col.name().to_string()) - .collect::>(); - let data = rows - .into_iter() - .map(|row| { - row.columns() - .iter() - .map(|col| { - row.try_get::, _>(col.name()) - .unwrap_or(None) - .unwrap_or_default() - }) - .collect::>() - }) - .collect(); - (cols, data) - } - QueryResult::SQLite(rows) => { - if rows.is_empty() { - break; - } - let cols = rows[0] - .columns() - .iter() - .map(|col| col.name().to_string()) - .collect::>(); - let data = rows - .into_iter() - .map(|row| { - row.columns() - .iter() - .map(|col| { - row.try_get::, _>(col.name()) - .unwrap_or(None) - .unwrap_or_default() - }) - .collect::>() - }) - .collect(); - (cols, data) - } - }; - - if !header_written { - wtr.write_record(&columns).expect("Failed to write columns"); - header_written = true; - } - - for record in rows.iter() { - wtr.write_record(record) - .expect("Failed to write record to CSV"); - } - - offset += LIMIT; - } - - wtr.flush().expect("Failed to flush CSV writer"); - Ok(()) -} - -/// Helper function to drop a table if it exists -pub async fn drop_table_if_exists( - connection: &Connection, - db_driver: &DatabaseEngine, - table_name: &str, -) -> Result<(), String> { - let drop_query: &str = &build_drop_statement_sql(db_driver, table_name)?; - execute_query( - connection, - drop_query, - &format!("Failed to drop table '{table_name}'"), - ) - .await -} - -/// Helper function to execute a query and handle errors -pub async fn execute_query( - connection: &Connection, - query: &str, - context: &str, -) -> Result<(), String> { - connection - .query(query) - .await - .map_err(|err| format!("{context}: {err}")) -} - -/// Helper function to batch-insert records into a table -pub async fn batch_insert( - connection: &Connection, - insert_query_base: &str, - batch: &[String], - context: &str, -) -> Result<(), String> { - if batch.is_empty() { - return Ok(()); - } - let insert_query: String = format!("{insert_query_base}{}", batch.join(", ")); - execute_query(connection, &insert_query, context).await -} - -/// Create the final table and copy data from the temporary table -pub async fn create_and_copy_final_table( - connection: &Connection, - db_driver: &DatabaseEngine, - final_table_name: &str, - temporary_table_name: &str, - columns_size_map: &HashMap<&str, usize>, - final_columns_name: &[String], -) -> Result<(), String> { - let create_final_table_query: String = build_create_with_fixed_size_sql( - db_driver, - final_table_name, - columns_size_map, - final_columns_name, - ); - execute_query( - connection, - &create_final_table_query, - "Failed to create final table", - ) - .await?; - - let copy_data_query: String = - build_copy_table_sql(db_driver, temporary_table_name, final_table_name); - - execute_query( - connection, - ©_data_query, - "Failed to copy data to final table", - ) - .await?; - - Ok(()) -} - -/// Drop a list of tables if they exist -pub async fn drop_existing_tables( - connection: &Connection, - table_names: &[&str], - db_driver: &DatabaseEngine, -) -> Result<(), String> { - for table_name in table_names.iter() { - if let Err(e) = drop_table_if_exists(connection, db_driver, table_name).await { - eprintln!("Error: {e}"); - } - } - Ok(()) -} diff --git a/src-tauri/src/fileflow/database/mod.rs b/src-tauri/src/fileflow/database/mod.rs deleted file mode 100644 index bc2f05a..0000000 --- a/src-tauri/src/fileflow/database/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod connection; -pub mod database_actions; -pub mod sql_builder; diff --git a/src-tauri/src/fileflow/database/sql_builder.rs b/src-tauri/src/fileflow/database/sql_builder.rs deleted file mode 100644 index ea10047..0000000 --- a/src-tauri/src/fileflow/database/sql_builder.rs +++ /dev/null @@ -1,392 +0,0 @@ -use crate::fileflow::enumeration::database_engine::DatabaseEngine; -use std::collections::HashMap; - -/// This function is used to generate the DROP TABLE statement for different database drivers. -pub fn build_drop_statement_sql( - db_driver: &DatabaseEngine, - final_table_name: &str, -) -> Result { - match db_driver { - DatabaseEngine::SQLite | DatabaseEngine::Postgres => { - Ok(format!("DROP TABLE IF EXISTS \"{final_table_name}\"")) - } - DatabaseEngine::MariaDB | DatabaseEngine::MySQL => { - Ok(format!("DROP TABLE IF EXISTS `{final_table_name}`")) - } - } -} - -/// This function is used to generate the INSERT INTO statement for different database drivers. -pub fn build_prepared_statement_sql( - db_driver: &DatabaseEngine, - table_name: &str, - columns: &[String], -) -> String { - let quote: char = match db_driver { - DatabaseEngine::SQLite | DatabaseEngine::Postgres => '\"', - DatabaseEngine::MariaDB | DatabaseEngine::MySQL => '`', - }; - let mut query = format!("INSERT INTO {quote}{table_name}{quote} ("); - for column in columns.iter() { - if column.is_empty() { - continue; - } - query.push_str(&format!("{quote}{column}{quote}")); - if column != columns.last().unwrap() { - query.push_str(", "); - } - } - query.push_str(") VALUES "); - query -} - -/// This function is used to generate the COPY statement for different database drivers. -pub fn build_copy_table_sql( - db_driver: &DatabaseEngine, - temporary_table_name: &str, - final_table_name: &str, -) -> String { - let quote: char = match db_driver { - DatabaseEngine::SQLite | DatabaseEngine::Postgres => '\"', - DatabaseEngine::MySQL | DatabaseEngine::MariaDB => '`', - }; - format!( - "INSERT INTO {quote}{final_table_name}{quote} SELECT * FROM {quote}{temporary_table_name}{quote}" - ) -} - -/// This function is used to generate the CREATE TABLE statement with fixed size for each column for different database drivers. -pub fn build_create_with_fixed_size_sql( - driver: &DatabaseEngine, - final_table_name: &str, - map_column_max_length: &HashMap<&str, usize>, - snake_case_headers: &[String], -) -> String { - // Constants - const MAX_VARCHAR_LENGTH: usize = 255; - const TEXT_TYPE: &str = "TEXT"; - const VARCHAR_TYPE: &str = "VARCHAR"; - - // Determine database-specific formatting - let quote: char = match driver { - DatabaseEngine::SQLite | DatabaseEngine::Postgres => '"', - DatabaseEngine::MySQL | DatabaseEngine::MariaDB => '`', - }; - - let mut columns: Vec = Vec::with_capacity(snake_case_headers.len()); - let mut total_length: usize = 0; - - // Build column definitions - for header in snake_case_headers { - let max_length = map_column_max_length - .get(header.as_str()) - .copied() - .unwrap_or(MAX_VARCHAR_LENGTH); - - // Determine column type - let type_str: String = if max_length <= MAX_VARCHAR_LENGTH { - format!("{VARCHAR_TYPE}({max_length})") // VARCHAR(n) - } else { - TEXT_TYPE.into() - }; - - // Format column definition - let column: String = format!("{0}{1}{0} {2}", quote, header, type_str); - total_length += column.len(); - columns.push(column); - } - - // Pre-calculate final string capacity - let table_quoted: String = format!("{0}{1}{0}", quote, final_table_name); - let mut result: String = String::with_capacity( - 15 + // "CREATE TABLE ();" - table_quoted.len() + - total_length + - columns.len() * 2, // ", " separators - ); - - // Build the final SQL statement - result.push_str("CREATE TABLE "); - result.push_str(&table_quoted); - result.push_str(" ("); - result.push_str(&columns.join(", ")); - result.push_str(");"); - - result -} - -/// This function is used to generate the CREATE TABLE statement for different database drivers. -pub fn build_create_table_sql( - driver: &DatabaseEngine, - final_table_name: &str, - snake_case_headers: &[String], -) -> String { - match driver { - DatabaseEngine::SQLite | DatabaseEngine::Postgres => format!( - "CREATE TABLE \"{final_table_name}\" ({})", - snake_case_headers - .iter() - .map(|h| format!("{h} TEXT")) - .collect::>() - .join(", ") - ), - DatabaseEngine::MariaDB | DatabaseEngine::MySQL => format!( - "CREATE TABLE `{final_table_name}` ({})", - snake_case_headers - .iter() - .map(|h| format!("`{h}` TEXT")) - .collect::>() - .join(", ") - ), - } -} - -/// Get the query to fetch all tables from the database for different drivers -pub fn build_query_all_tables(driver: &DatabaseEngine, schema: &str) -> String { - let query: String = match driver { - &DatabaseEngine::MySQL | &DatabaseEngine::MariaDB => format!( - "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = '{schema}';" - ), - &DatabaseEngine::Postgres => { - "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'public';".into() - } - &DatabaseEngine::SQLite => "SELECT name FROM sqlite_master WHERE type='table';".into(), - }; - query -} - -#[cfg(test)] -mod test { - use crate::fileflow::database::sql_builder::{ - build_create_table_sql, build_create_with_fixed_size_sql, build_drop_statement_sql, - build_prepared_statement_sql, build_query_all_tables, - }; - use crate::fileflow::enumeration::database_engine::DatabaseEngine; - use std::collections::HashMap; - - #[tokio::test] - async fn test_get_drop_statement() { - assert_eq!( - build_drop_statement_sql(&DatabaseEngine::SQLite, "table_name").unwrap(), - "DROP TABLE IF EXISTS \"table_name\"" - ); - assert_eq!( - build_drop_statement_sql(&DatabaseEngine::MySQL, "table_name").unwrap(), - "DROP TABLE IF EXISTS `table_name`" - ); - assert_eq!( - build_drop_statement_sql(&DatabaseEngine::Postgres, "table_name").unwrap(), - "DROP TABLE IF EXISTS \"table_name\"" - ); - assert_eq!( - build_drop_statement_sql(&DatabaseEngine::SQLite, "").unwrap(), - "DROP TABLE IF EXISTS \"\"" - ); - assert_eq!( - build_drop_statement_sql(&DatabaseEngine::MySQL, "").unwrap(), - "DROP TABLE IF EXISTS ``" - ); - assert_eq!( - build_drop_statement_sql(&DatabaseEngine::Postgres, "").unwrap(), - "DROP TABLE IF EXISTS \"\"" - ); - } - - #[tokio::test] - async fn test_get_insert_into_statement() { - assert_eq!( - build_prepared_statement_sql( - &DatabaseEngine::SQLite, - "table_name", - &["columns".into()] - ), - "INSERT INTO \"table_name\" (\"columns\") VALUES " - ); - assert_eq!( - build_prepared_statement_sql(&DatabaseEngine::MySQL, "table_name", &["columns".into()]), - "INSERT INTO `table_name` (`columns`) VALUES " - ); - assert_eq!( - build_prepared_statement_sql( - &DatabaseEngine::Postgres, - "table_name", - &["columns".into()] - ), - "INSERT INTO \"table_name\" (\"columns\") VALUES " - ); - - assert_eq!( - build_prepared_statement_sql(&DatabaseEngine::SQLite, "table_name", &["".into()]), - "INSERT INTO \"table_name\" () VALUES " - ); - assert_eq!( - build_prepared_statement_sql(&DatabaseEngine::MySQL, "table_name", &["".into()]), - "INSERT INTO `table_name` () VALUES " - ); - assert_eq!( - build_prepared_statement_sql(&DatabaseEngine::Postgres, "table_name", &["".into()]), - "INSERT INTO \"table_name\" () VALUES " - ); - - assert_eq!( - build_prepared_statement_sql( - &DatabaseEngine::SQLite, - "table_name", - &["header1".into(), "header2".into()] - ), - "INSERT INTO \"table_name\" (\"header1\", \"header2\") VALUES " - ); - assert_eq!( - build_prepared_statement_sql( - &DatabaseEngine::MySQL, - "table_name", - &["header1".into(), "header2".into()] - ), - "INSERT INTO `table_name` (`header1`, `header2`) VALUES " - ); - assert_eq!( - build_prepared_statement_sql( - &DatabaseEngine::Postgres, - "table_name", - &["header1".into(), "header2".into()] - ), - "INSERT INTO \"table_name\" (\"header1\", \"header2\") VALUES " - ); - } - - #[tokio::test] - async fn test_get_create_statement() { - let snake_case_headers: Vec = vec!["header1".into(), "header2".into()]; - assert_eq!( - build_create_table_sql(&DatabaseEngine::SQLite, "table_name", &snake_case_headers), - "CREATE TABLE \"table_name\" (header1 TEXT, header2 TEXT)" - ); - assert_eq!( - build_create_table_sql(&DatabaseEngine::MySQL, "table_name", &snake_case_headers), - "CREATE TABLE `table_name` (`header1` TEXT, `header2` TEXT)" - ); - assert_eq!( - build_create_table_sql(&DatabaseEngine::Postgres, "table_name", &snake_case_headers), - "CREATE TABLE \"table_name\" (header1 TEXT, header2 TEXT)" - ); - - let snake_case_headers: Vec = vec!["header1".into()]; - assert_eq!( - build_create_table_sql(&DatabaseEngine::SQLite, "table_name", &snake_case_headers), - "CREATE TABLE \"table_name\" (header1 TEXT)" - ); - assert_eq!( - build_create_table_sql(&DatabaseEngine::MySQL, "table_name", &snake_case_headers), - "CREATE TABLE `table_name` (`header1` TEXT)" - ); - assert_eq!( - build_create_table_sql(&DatabaseEngine::Postgres, "table_name", &snake_case_headers), - "CREATE TABLE \"table_name\" (header1 TEXT)" - ); - } - - #[tokio::test] - async fn test_get_create_statement_with_fixed_size() { - const FINAL_TABLE_NAME: &str = "test_table"; - - let snake_case_headers: Vec = vec!["header1".into(), "header2".into()]; - let map_max_length: HashMap<&str, usize> = - snake_case_headers.iter().map(|h| (h.as_str(), 0)).collect(); - - let mut db_driver: HashMap<&DatabaseEngine, &str> = HashMap::new(); - db_driver.insert( - &DatabaseEngine::Postgres, - "CREATE TABLE \"test_table\" (\"header1\" VARCHAR(0), \"header2\" VARCHAR(0));", - ); - db_driver.insert( - &DatabaseEngine::MySQL, - "CREATE TABLE `test_table` (`header1` VARCHAR(0), `header2` VARCHAR(0));", - ); - db_driver.insert( - &DatabaseEngine::MariaDB, - "CREATE TABLE `test_table` (`header1` VARCHAR(0), `header2` VARCHAR(0));", - ); - db_driver.insert( - &DatabaseEngine::SQLite, - "CREATE TABLE \"test_table\" (\"header1\" VARCHAR(0), \"header2\" VARCHAR(0));", - ); - - let final_columns: Vec = vec!["header1".into(), "header2".into()]; - - for (driver, expected) in db_driver { - let result: String = build_create_with_fixed_size_sql( - driver, - FINAL_TABLE_NAME, - &map_max_length, - &final_columns, - ); - assert_eq!(result, expected); - } - - let map_max_length: HashMap<&str, usize> = snake_case_headers - .iter() - .map(|h| (h.as_str(), 10)) - .collect(); - let mut db_driver: HashMap<&DatabaseEngine, &str> = HashMap::new(); - db_driver.insert( - &DatabaseEngine::Postgres, - "CREATE TABLE \"test_table\" (\"header1\" VARCHAR(10), \"header2\" VARCHAR(10));", - ); - db_driver.insert( - &DatabaseEngine::MySQL, - "CREATE TABLE `test_table` (`header1` VARCHAR(10), `header2` VARCHAR(10));", - ); - db_driver.insert( - &DatabaseEngine::MariaDB, - "CREATE TABLE `test_table` (`header1` VARCHAR(10), `header2` VARCHAR(10));", - ); - db_driver.insert( - &DatabaseEngine::SQLite, - "CREATE TABLE \"test_table\" (\"header1\" VARCHAR(10), \"header2\" VARCHAR(10));", - ); - - for (driver, expected) in db_driver { - let result: String = build_create_with_fixed_size_sql( - driver, - FINAL_TABLE_NAME, - &map_max_length, - &final_columns, - ); - assert_eq!(result, expected); - } - } - - #[tokio::test] - async fn test_build_query_all_tables() { - let test_cases: Vec<(&DatabaseEngine, String)> = vec![ - ( - &DatabaseEngine::MySQL, - "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'test';" - .into(), - ), - ( - &DatabaseEngine::MariaDB, - "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'test';" - .into(), - ), - ( - &DatabaseEngine::Postgres, - "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'public';" - .into(), - ), - ( - &DatabaseEngine::SQLite, - "SELECT name FROM sqlite_master WHERE type='table';".into(), - ), - ]; - - for (driver, expected) in test_cases { - assert_eq!( - build_query_all_tables(driver, "test"), - expected, - "Failed for driver: {:?}", - driver - ); - } - } -} diff --git a/src-tauri/src/fileflow/enumeration/database_engine.rs b/src-tauri/src/fileflow/enumeration/database_engine.rs deleted file mode 100644 index 9455199..0000000 --- a/src-tauri/src/fileflow/enumeration/database_engine.rs +++ /dev/null @@ -1,10 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq, Hash)] -#[serde(rename_all = "lowercase")] -pub enum DatabaseEngine { - MariaDB, - MySQL, - Postgres, - SQLite, -} diff --git a/src-tauri/src/fileflow/enumeration/insertion_type.rs b/src-tauri/src/fileflow/enumeration/insertion_type.rs deleted file mode 100644 index b6449b6..0000000 --- a/src-tauri/src/fileflow/enumeration/insertion_type.rs +++ /dev/null @@ -1,9 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq, Hash, Default)] -#[serde(rename_all = "lowercase")] -pub enum InsertionType { - #[default] - Fast, - Optimized, -} diff --git a/src-tauri/src/fileflow/enumeration/mod.rs b/src-tauri/src/fileflow/enumeration/mod.rs deleted file mode 100644 index 11cfdf4..0000000 --- a/src-tauri/src/fileflow/enumeration/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod database_engine; -pub mod insertion_type; -pub mod separator; diff --git a/src-tauri/src/fileflow/enumeration/separator.rs b/src-tauri/src/fileflow/enumeration/separator.rs deleted file mode 100644 index ce9dfeb..0000000 --- a/src-tauri/src/fileflow/enumeration/separator.rs +++ /dev/null @@ -1,24 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Debug, PartialEq, Serialize, Deserialize, Default)] -#[serde(rename_all = "lowercase")] -pub enum SeparatorType { - #[default] - Comma, - Semicolon, - Space, - Pipe, -} - -impl SeparatorType { - - /// Returns the separator as a u8. - pub const fn as_u8(&self) -> u8 { - match self { - SeparatorType::Comma => b',', - SeparatorType::Semicolon => b';', - SeparatorType::Space => b' ', - SeparatorType::Pipe => b'|', - } - } -} diff --git a/src-tauri/src/fileflow/enums/connection_type.rs b/src-tauri/src/fileflow/enums/connection_type.rs new file mode 100644 index 0000000..1aa152f --- /dev/null +++ b/src-tauri/src/fileflow/enums/connection_type.rs @@ -0,0 +1,8 @@ +use sqlx::{MySqlPool, PgPool, SqlitePool}; + +/// A pool of database connections, supporting multiple database engines. +pub enum ConnectionPool { + Postgres(PgPool), + MySQL(MySqlPool), + SQLite(SqlitePool), +} diff --git a/src-tauri/src/fileflow/enums/database_engine.rs b/src-tauri/src/fileflow/enums/database_engine.rs new file mode 100644 index 0000000..00eae9e --- /dev/null +++ b/src-tauri/src/fileflow/enums/database_engine.rs @@ -0,0 +1,31 @@ +use serde::{Deserialize, Serialize}; + +/// Enum representing supported database engines. +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq, Hash)] +#[serde(rename_all = "lowercase")] +pub enum DatabaseEngine { + MariaDB, + MySQL, + Postgres, + SQLite, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[tokio::test] + async fn test_database_engine() { + let db_engine = DatabaseEngine::Postgres; + assert_eq!(db_engine, DatabaseEngine::Postgres); + } + + #[tokio::test] + async fn test_database_engine_serialization() { + let db_engine = DatabaseEngine::MySQL; + let serialized = serde_json::to_string(&db_engine).unwrap(); + assert_eq!(serialized, "\"mysql\""); + let deserialized: DatabaseEngine = serde_json::from_str(&serialized).unwrap(); + assert_eq!(deserialized, DatabaseEngine::MySQL); + } +} diff --git a/src-tauri/src/fileflow/enums/insertion_type.rs b/src-tauri/src/fileflow/enums/insertion_type.rs new file mode 100644 index 0000000..cc36e02 --- /dev/null +++ b/src-tauri/src/fileflow/enums/insertion_type.rs @@ -0,0 +1,39 @@ +use serde::{Deserialize, Serialize}; + +/// Defines the type of insertion to be used in database operations. +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq, Hash, Default)] +#[serde(rename_all = "lowercase")] +pub enum InsertionType { + #[default] + Fast, + Optimized, // Optimize column length based on data +} + +#[cfg(test)] +mod tests { + use super::*; + + #[tokio::test] + async fn test_insertion_type() { + let insertion_type = InsertionType::Optimized; + assert_eq!(insertion_type, InsertionType::Optimized); + } + + #[tokio::test] + async fn test_insertion_type_fast_serialization() { + let insertion_type = InsertionType::Fast; + let serialized = serde_json::to_string(&insertion_type).unwrap(); + assert_eq!(serialized, "\"fast\""); + let deserialized: InsertionType = serde_json::from_str(&serialized).unwrap(); + assert_eq!(deserialized, InsertionType::Fast); + } + + #[tokio::test] + async fn test_insertion_type_optimized_serialization() { + let insertion_type = InsertionType::Optimized; + let serialized = serde_json::to_string(&insertion_type).unwrap(); + assert_eq!(serialized, "\"optimized\""); + let deserialized: InsertionType = serde_json::from_str(&serialized).unwrap(); + assert_eq!(deserialized, InsertionType::Optimized); + } +} diff --git a/src-tauri/src/fileflow/enums/mod.rs b/src-tauri/src/fileflow/enums/mod.rs new file mode 100644 index 0000000..580b578 --- /dev/null +++ b/src-tauri/src/fileflow/enums/mod.rs @@ -0,0 +1,5 @@ +pub mod connection_type; +pub mod database_engine; +pub mod insertion_type; +pub mod query_rows; +pub mod separator_type; diff --git a/src-tauri/src/fileflow/enums/query_rows.rs b/src-tauri/src/fileflow/enums/query_rows.rs new file mode 100644 index 0000000..25fbeca --- /dev/null +++ b/src-tauri/src/fileflow/enums/query_rows.rs @@ -0,0 +1,44 @@ +/// Rows returned from a database query, supporting multiple backends. +pub enum QueryRows { + Postgres(Vec), + MySQL(Vec), + SQLite(Vec), +} + +#[cfg(test)] +mod tests { + use super::*; + + #[tokio::test] + async fn test_query_result_postgres() { + // This is a placeholder test. In a real scenario, you would set up a test database. + let rows = vec![]; + let result = QueryRows::Postgres(rows); + match result { + QueryRows::Postgres(r) => assert!(r.is_empty()), + _ => panic!("Expected Postgres variant"), + } + } + + #[tokio::test] + async fn test_query_result_mysql() { + // This is a placeholder test. In a real scenario, you would set up a test database. + let rows = vec![]; + let result = QueryRows::MySQL(rows); + match result { + QueryRows::MySQL(r) => assert!(r.is_empty()), + _ => panic!("Expected MySQL variant"), + } + } + + #[tokio::test] + async fn test_query_result_sqlite() { + // This is a placeholder test. In a real scenario, you would set up a test database. + let rows = vec![]; + let result = QueryRows::SQLite(rows); + match result { + QueryRows::SQLite(r) => assert!(r.is_empty()), + _ => panic!("Expected SQLite variant"), + } + } +} diff --git a/src-tauri/src/fileflow/enums/separator_type.rs b/src-tauri/src/fileflow/enums/separator_type.rs new file mode 100644 index 0000000..47f87f2 --- /dev/null +++ b/src-tauri/src/fileflow/enums/separator_type.rs @@ -0,0 +1,68 @@ +use serde::{Deserialize, Serialize}; + +/// Enum representing different types of separators for CSV files. +#[derive(Debug, PartialEq, Serialize, Deserialize, Default)] +#[serde(rename_all = "lowercase")] +pub enum SeparatorType { + #[default] + Comma, + Semicolon, + Space, + Pipe, +} + +impl SeparatorType { + /// Returns the separator as an u8. + pub const fn as_u8(&self) -> u8 { + match self { + SeparatorType::Comma => b',', + SeparatorType::Semicolon => b';', + SeparatorType::Space => b' ', + SeparatorType::Pipe => b'|', + } + } +} + +#[cfg(test)] +mod tests { + use super::SeparatorType; + + #[tokio::test] + async fn test_as_u8() { + assert_eq!(SeparatorType::Comma.as_u8(), b','); + assert_eq!(SeparatorType::Semicolon.as_u8(), b';'); + assert_eq!(SeparatorType::Space.as_u8(), b' '); + assert_eq!(SeparatorType::Pipe.as_u8(), b'|'); + } + + #[tokio::test] + async fn test_default() { + let default_separator = SeparatorType::default(); + assert_eq!(default_separator, SeparatorType::Comma); + } + + #[tokio::test] + async fn test_deserialize() { + let comma: SeparatorType = serde_json::from_str(r#""comma""#).unwrap(); + let semicolon: SeparatorType = serde_json::from_str(r#""semicolon""#).unwrap(); + let space: SeparatorType = serde_json::from_str(r#""space""#).unwrap(); + let pipe: SeparatorType = serde_json::from_str(r#""pipe""#).unwrap(); + + assert_eq!(comma, SeparatorType::Comma); + assert_eq!(semicolon, SeparatorType::Semicolon); + assert_eq!(space, SeparatorType::Space); + assert_eq!(pipe, SeparatorType::Pipe); + } + + #[tokio::test] + async fn test_serialize() { + let comma = serde_json::to_string(&SeparatorType::Comma).unwrap(); + let semicolon = serde_json::to_string(&SeparatorType::Semicolon).unwrap(); + let space = serde_json::to_string(&SeparatorType::Space).unwrap(); + let pipe = serde_json::to_string(&SeparatorType::Pipe).unwrap(); + assert_eq!(comma, r#""comma""#); + assert_eq!(semicolon, r#""semicolon""#); + assert_eq!(space, r#""space""#); + assert_eq!(pipe, r#""pipe""#); + } +} diff --git a/src-tauri/src/fileflow/mod.rs b/src-tauri/src/fileflow/mod.rs index b879a2f..52c93fd 100644 --- a/src-tauri/src/fileflow/mod.rs +++ b/src-tauri/src/fileflow/mod.rs @@ -1,5 +1,4 @@ pub mod action; -pub mod database; -pub mod enumeration; +pub mod enums; pub mod stuct; pub mod utils; diff --git a/src-tauri/src/fileflow/stuct/combo_item.rs b/src-tauri/src/fileflow/stuct/combo_item.rs deleted file mode 100644 index a17ce73..0000000 --- a/src-tauri/src/fileflow/stuct/combo_item.rs +++ /dev/null @@ -1,7 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Deserialize, Clone, Serialize)] -pub struct ComboItem { - pub label: String, - pub value: String, -} diff --git a/src-tauri/src/fileflow/stuct/config/combo_item.rs b/src-tauri/src/fileflow/stuct/config/combo_item.rs new file mode 100644 index 0000000..34717ce --- /dev/null +++ b/src-tauri/src/fileflow/stuct/config/combo_item.rs @@ -0,0 +1,49 @@ +use serde::{Deserialize, Serialize}; + +/// A struct representing an item in a combo box with a label and a value. +#[derive(Deserialize, Clone, Serialize)] +pub struct ComboItem { + pub label: String, + pub value: String, +} + +#[cfg(test)] +mod test { + use super::ComboItem; + + #[tokio::test] + async fn test_combo_item_serialization() { + let item = ComboItem { + label: "Option 1".to_string(), + value: "value1".to_string(), + }; + + // Serialize the ComboItem to a JSON string + let serialized = serde_json::to_string(&item).unwrap(); + assert_eq!(serialized, r#"{"label":"Option 1","value":"value1"}"#); + + // Deserialize the JSON string back to a ComboItem + let deserialized: ComboItem = serde_json::from_str(&serialized).unwrap(); + assert_eq!(deserialized.label, "Option 1"); + assert_eq!(deserialized.value, "value1"); + } + + #[tokio::test] + async fn test_combo_item_clone() { + let item = ComboItem { + label: "Option 2".to_string(), + value: "value2".to_string(), + }; + let cloned_item = item.clone(); + assert_eq!(cloned_item.label, "Option 2"); + assert_eq!(cloned_item.value, "value2"); + } + + #[tokio::test] + async fn test_deserialization() { + let json_data = r#"{"label":"Test Label","value":"Test Value"}"#; + let item: ComboItem = serde_json::from_str(json_data).unwrap(); + assert_eq!(item.label, "Test Label"); + assert_eq!(item.value, "Test Value"); + } +} diff --git a/src-tauri/src/fileflow/stuct/config/db_config.rs b/src-tauri/src/fileflow/stuct/config/db_config.rs new file mode 100644 index 0000000..9b6194a --- /dev/null +++ b/src-tauri/src/fileflow/stuct/config/db_config.rs @@ -0,0 +1,64 @@ +use crate::fileflow::enums::database_engine::DatabaseEngine; +use serde::{Deserialize, Serialize}; + +/// Database configuration structure +#[derive(Deserialize, Clone, Serialize)] +pub struct DatabaseConfig { + pub db_driver: DatabaseEngine, + pub db_host: String, + pub port: String, + pub username: String, + pub password: String, + pub db_name: String, + pub sqlite_file_path: String, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[tokio::test] + async fn test_db_config() { + let config = DatabaseConfig { + db_driver: DatabaseEngine::Postgres, + username: String::from("username"), + password: String::from("password"), + db_host: String::from("db_host"), + port: String::from("port"), + db_name: String::from("db_name"), + sqlite_file_path: "sqlite_file_path".into(), + }; + + assert_eq!(config.db_driver, DatabaseEngine::Postgres); + assert_eq!(config.username, "username"); + assert_eq!(config.password, "password"); + assert_eq!(config.db_host, "db_host"); + assert_eq!(config.port, "port"); + assert_eq!(config.db_name, "db_name"); + assert_eq!(config.sqlite_file_path, "sqlite_file_path"); + } + + #[tokio::test] + async fn test_db_config_serialization() { + let config = DatabaseConfig { + db_driver: DatabaseEngine::MySQL, + username: String::from("username"), + password: String::from("password"), + db_host: String::from("db_host"), + port: String::from("port"), + db_name: String::from("db_name"), + sqlite_file_path: "sqlite_file_path".into(), + }; + let serialized = serde_json::to_string(&config).unwrap(); + let expected = r#"{"db_driver":"mysql","db_host":"db_host","port":"port","username":"username","password":"password","db_name":"db_name","sqlite_file_path":"sqlite_file_path"}"#; + assert_eq!(serialized, expected); + let deserialized: DatabaseConfig = serde_json::from_str(&serialized).unwrap(); + assert_eq!(deserialized.db_driver, DatabaseEngine::MySQL); + assert_eq!(deserialized.username, "username"); + assert_eq!(deserialized.password, "password"); + assert_eq!(deserialized.db_host, "db_host"); + assert_eq!(deserialized.port, "port"); + assert_eq!(deserialized.db_name, "db_name"); + assert_eq!(deserialized.sqlite_file_path, "sqlite_file_path"); + } +} diff --git a/src-tauri/src/fileflow/stuct/config/download_config.rs b/src-tauri/src/fileflow/stuct/config/download_config.rs new file mode 100644 index 0000000..e7f01a1 --- /dev/null +++ b/src-tauri/src/fileflow/stuct/config/download_config.rs @@ -0,0 +1,48 @@ +use crate::fileflow::enums::separator_type::SeparatorType; +use serde::{Deserialize, Serialize}; + +/// Configuration for downloading data from database tables +#[derive(Deserialize, Serialize)] +pub struct DownloadConfig { + pub table_name_list: Vec, + pub location: String, + pub separator: SeparatorType, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[tokio::test] + async fn test_download_config() { + let config = DownloadConfig { + table_name_list: vec!["table1".into(), "table2".into()], + location: "location".into(), + separator: SeparatorType::Comma, + }; + + assert_eq!(config.table_name_list.len(), 2); + assert_eq!(config.table_name_list[0], "table1"); + assert_eq!(config.table_name_list[1], "table2"); + assert_eq!(config.location, "location"); + assert_eq!(config.separator, SeparatorType::Comma); + } + + #[tokio::test] + async fn test_download_config_serialization() { + let config = DownloadConfig { + table_name_list: vec!["table1".into(), "table2".into()], + location: "location".into(), + separator: SeparatorType::Semicolon, + }; + + let serialized = serde_json::to_string(&config).unwrap(); + let deserialized: DownloadConfig = serde_json::from_str(&serialized).unwrap(); + + assert_eq!(deserialized.table_name_list.len(), 2); + assert_eq!(deserialized.table_name_list[0], "table1"); + assert_eq!(deserialized.table_name_list[1], "table2"); + assert_eq!(deserialized.location, "location"); + assert_eq!(deserialized.separator, SeparatorType::Semicolon); + } +} diff --git a/src-tauri/src/fileflow/stuct/config/insert_config.rs b/src-tauri/src/fileflow/stuct/config/insert_config.rs new file mode 100644 index 0000000..0319b8d --- /dev/null +++ b/src-tauri/src/fileflow/stuct/config/insert_config.rs @@ -0,0 +1,51 @@ +use crate::fileflow::enums::database_engine::DatabaseEngine; +use crate::fileflow::enums::insertion_type::InsertionType; +use serde::Deserialize; + +/// Struct for inserting data into a database +#[derive(Deserialize)] +pub struct InsertConfig { + pub file_path: String, + pub table_name: String, + pub mode: InsertionType, + pub db_driver: DatabaseEngine, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[tokio::test] + async fn test_insert_config() { + let config = InsertConfig { + file_path: "file_path".into(), + table_name: "table_name".into(), + mode: InsertionType::Fast, + db_driver: DatabaseEngine::Postgres, + }; + + assert_eq!(config.db_driver, DatabaseEngine::Postgres); + assert_eq!(config.file_path, "file_path"); + assert_eq!(config.table_name, "table_name"); + assert_eq!(config.mode, InsertionType::Fast); + } + + #[tokio::test] + async fn test_insert_config_deserialization() { + let json_data = r#" + { + "file_path": "file_path", + "table_name": "table_name", + "mode": "optimized", + "db_driver": "mysql" + } + "#; + + let deserialized: InsertConfig = serde_json::from_str(json_data).unwrap(); + + assert_eq!(deserialized.db_driver, DatabaseEngine::MySQL); + assert_eq!(deserialized.file_path, "file_path"); + assert_eq!(deserialized.table_name, "table_name"); + assert_eq!(deserialized.mode, InsertionType::Optimized); + } +} diff --git a/src-tauri/src/fileflow/stuct/config/mod.rs b/src-tauri/src/fileflow/stuct/config/mod.rs new file mode 100644 index 0000000..a4a4752 --- /dev/null +++ b/src-tauri/src/fileflow/stuct/config/mod.rs @@ -0,0 +1,5 @@ +pub mod combo_item; +pub mod db_config; +pub mod download_config; +pub mod insert_config; +pub mod save_config; diff --git a/src-tauri/src/fileflow/stuct/config/save_config.rs b/src-tauri/src/fileflow/stuct/config/save_config.rs new file mode 100644 index 0000000..55f3b27 --- /dev/null +++ b/src-tauri/src/fileflow/stuct/config/save_config.rs @@ -0,0 +1,156 @@ +use crate::fileflow::enums::database_engine::DatabaseEngine; +use serde::{Deserialize, Serialize}; +use std::fs::{File, OpenOptions}; +use std::io; +use std::path::PathBuf; + +/// Struct for saving database configuration details +#[derive(Deserialize, Serialize, Clone)] +pub struct SaveConfig { + pub config_name: String, + pub db_driver: DatabaseEngine, + pub db_host: String, + pub port: String, + pub username: String, + pub password: String, + pub db_name: String, + pub sqlite_file_path: String, +} + +impl SaveConfig { + /// This function is used to get the size of a file. + pub fn get_all_configs(config_file: &str) -> Vec { + let path: PathBuf = PathBuf::from(config_file); + let file: File = match File::open(path) { + Ok(file) => file, + Err(_) => return Vec::new(), + }; + + if file.metadata().unwrap().len() == 0 { + return Vec::new(); + } + + let configs: Vec = serde_json::from_reader(file).unwrap_or(Vec::new()); + configs + } + + /// This function is used to save a vector of SaveConfig to a json file. + pub fn save_config(configs: &[SaveConfig], config_file: &str) -> io::Result<()> { + let file: File = OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(config_file) + .map_err(|e| format!("Failed to open file for writing: {e}")) + .expect("Failed to open file for writing"); + serde_json::to_writer_pretty(file, &configs) + .map_err(|e| format!("Failed to write to file: {e}")) + .expect("Failed to write to file"); + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[allow(dead_code)] + fn get_test_config() -> SaveConfig { + SaveConfig { + config_name: "config_name".into(), + db_driver: DatabaseEngine::SQLite, + db_host: String::from("db_host"), + port: String::from("port"), + username: String::from("username"), + password: String::from("password"), + db_name: String::from("db_name"), + sqlite_file_path: "sqlite_file_path".into(), + } + } + + #[tokio::test] + async fn test_save_config_struct() { + let config = get_test_config(); + + assert_eq!(config.db_driver, DatabaseEngine::SQLite); + assert_eq!(config.db_host, "db_host"); + assert_eq!(config.port, "port"); + assert_eq!(config.username, "username"); + assert_eq!(config.password, "password"); + assert_eq!(config.db_name, "db_name"); + assert_eq!(config.sqlite_file_path, "sqlite_file_path"); + } + + #[tokio::test] + async fn test_save_config_serialization() { + let config = get_test_config(); + + let serialized = serde_json::to_string(&config).unwrap(); + let deserialized: SaveConfig = serde_json::from_str(&serialized).unwrap(); + + assert_eq!(deserialized.db_driver, DatabaseEngine::SQLite); + assert_eq!(deserialized.config_name, "config_name"); + assert_eq!(deserialized.db_host, "db_host"); + assert_eq!(deserialized.port, "port"); + assert_eq!(deserialized.username, "username"); + assert_eq!(deserialized.password, "password"); + assert_eq!(deserialized.db_name, "db_name"); + assert_eq!(deserialized.sqlite_file_path, "sqlite_file_path"); + } + + #[tokio::test] + async fn test_get_all_saved_configs_empty() { + let configs = SaveConfig::get_all_configs("non_existent_file.json"); + assert!(configs.is_empty()); + } + + #[tokio::test] + async fn test_get_all_saved_configs() { + let config = get_test_config(); + let configs = vec![config.clone()]; + let config_file = "test_configs.json"; + + // Save the config + SaveConfig::save_config(&configs, config_file).unwrap(); + + // Retrieve the config + let loaded_configs = SaveConfig::get_all_configs(config_file); + assert_eq!(loaded_configs.len(), 1); + assert_eq!(loaded_configs[0].config_name, "config_name"); + assert_eq!(loaded_configs[0].db_driver, DatabaseEngine::SQLite); + assert_eq!(loaded_configs[0].db_host, "db_host"); + assert_eq!(loaded_configs[0].port, "port"); + assert_eq!(loaded_configs[0].username, "username"); + assert_eq!(loaded_configs[0].password, "password"); + assert_eq!(loaded_configs[0].db_name, "db_name"); + assert_eq!(loaded_configs[0].sqlite_file_path, "sqlite_file_path"); + + // Clean up + std::fs::remove_file(config_file).unwrap(); + } + + #[tokio::test] + async fn test_save_config() { + let config = get_test_config(); + let configs = vec![config.clone()]; + let config_file = "test_save_configs.json"; + + // Save the config + SaveConfig::save_config(&configs, config_file).unwrap(); + + // Retrieve the config + let loaded_configs = SaveConfig::get_all_configs(config_file); + assert_eq!(loaded_configs.len(), 1); + assert_eq!(loaded_configs[0].config_name, "config_name"); + assert_eq!(loaded_configs[0].db_driver, DatabaseEngine::SQLite); + assert_eq!(loaded_configs[0].db_host, "db_host"); + assert_eq!(loaded_configs[0].port, "port"); + assert_eq!(loaded_configs[0].username, "username"); + assert_eq!(loaded_configs[0].password, "password"); + assert_eq!(loaded_configs[0].db_name, "db_name"); + assert_eq!(loaded_configs[0].sqlite_file_path, "sqlite_file_path"); + + // Clean up + std::fs::remove_file(config_file).unwrap(); + } +} diff --git a/src-tauri/src/fileflow/stuct/database/database_action.rs b/src-tauri/src/fileflow/stuct/database/database_action.rs new file mode 100644 index 0000000..656d51c --- /dev/null +++ b/src-tauri/src/fileflow/stuct/database/database_action.rs @@ -0,0 +1,157 @@ +use crate::fileflow::enums::database_engine::DatabaseEngine; +use crate::fileflow::stuct::database::database_connection::DatabaseConnection; +use crate::fileflow::stuct::database::sql_builder::SqlBuilder; +use std::collections::HashMap; + +/// Database operations utility struct +pub struct DatabaseAction; + +impl DatabaseAction { + /// Helper function to drop a table if it exists + pub async fn drop_table_if_exists( + connection: &DatabaseConnection, + db_driver: &DatabaseEngine, + table_name: &str, + ) -> Result<(), String> { + Self::validate_table_name(table_name).map_err(|e| format!("Invalid table name: {}", e))?; + let drop_query: String = SqlBuilder::build_drop_statement(db_driver, table_name)?; + Self::execute_query( + connection, + &drop_query, + &format!("Failed to drop table '{table_name}'"), + ) + .await + } + + /// Helper function to execute a query and handle errors + pub async fn execute_query( + connection: &DatabaseConnection, + query: &str, + context: &str, + ) -> Result<(), String> { + if query.trim().is_empty() { + return Err(format!("{context}: Query cannot be empty")); + } + + connection + .query(query) + .await + .map_err(|err| format!("{context}: {err}")) + } + + /// Helper function to batch-insert records into a table + pub async fn batch_insert( + connection: &DatabaseConnection, + insert_query_base: &str, + batch: &[String], + context: &str, + ) -> Result<(), String> { + if batch.is_empty() { + return Ok(()); + } + + if insert_query_base.trim().is_empty() { + return Err(format!("{context}: Base query cannot be empty")); + } + + let values_clause = batch.join(", "); + let insert_query = format!("{insert_query_base}{values_clause}"); + + Self::execute_query(connection, &insert_query, context).await + } + + /// Create the final table and copy data from the temporary table + pub async fn create_and_copy_final_table( + connection: &DatabaseConnection, + db_driver: &DatabaseEngine, + final_table_name: &str, + temporary_table_name: &str, + columns_size_map: &HashMap<&str, usize>, + final_columns_name: &[String], + ) -> Result<(), String> { + Self::validate_table_name(final_table_name) + .map_err(|e| format!("Invalid final table name: {}", e))?; + Self::validate_table_name(temporary_table_name) + .map_err(|e| format!("Invalid temporary table name: {}", e))?; + + if final_columns_name.is_empty() { + return Err("Column names cannot be empty".to_string()); + } + + // Create the final table + let create_final_table_query = SqlBuilder::build_create_with_fixed_size( + db_driver, + final_table_name, + columns_size_map, + final_columns_name, + )?; + + Self::execute_query( + connection, + &create_final_table_query, + "Failed to create final table", + ) + .await?; + + // Copy data from temporary to final table + let copy_data_query = + SqlBuilder::build_copy_table(db_driver, temporary_table_name, final_table_name)?; + + Self::execute_query( + connection, + ©_data_query, + "Failed to copy data to final table", + ) + .await?; + + Ok(()) + } + + /// Drop a list of tables if they exist + pub async fn drop_existing_tables( + connection: &DatabaseConnection, + table_names: &[&str], + db_driver: &DatabaseEngine, + ) -> Result<(), String> { + if table_names.is_empty() { + return Ok(()); + } + + let mut errors = Vec::new(); + + for table_name in table_names { + if let Err(e) = Self::drop_table_if_exists(connection, db_driver, table_name).await { + eprintln!("Warning: Failed to drop table '{}': {}", table_name, e); + errors.push(format!("Table '{}': {}", table_name, e)); + } + } + + if !errors.is_empty() { + return Err(format!("Failed to drop some tables: {}", errors.join("; "))); + } + + Ok(()) + } + + // Private helper methods + + /// Validates that the table name is not empty or just whitespace + fn validate_table_name(table_name: &str) -> Result<(), Box> { + if table_name.trim().is_empty() { + return Err("Table name cannot be empty".into()); + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[tokio::test] + async fn test_validate_table_name() { + assert!(DatabaseAction::validate_table_name("valid_table").is_ok()); + assert!(DatabaseAction::validate_table_name("").is_err()); + assert!(DatabaseAction::validate_table_name(" ").is_err()); + } +} diff --git a/src-tauri/src/fileflow/database/connection.rs b/src-tauri/src/fileflow/stuct/database/database_connection.rs similarity index 55% rename from src-tauri/src/fileflow/database/connection.rs rename to src-tauri/src/fileflow/stuct/database/database_connection.rs index 85ff25e..afe6100 100644 --- a/src-tauri/src/fileflow/database/connection.rs +++ b/src-tauri/src/fileflow/stuct/database/database_connection.rs @@ -1,87 +1,84 @@ -use crate::fileflow::enumeration::database_engine::DatabaseEngine; -use crate::fileflow::stuct::db_config::DbConfig; +use crate::fileflow::enums::connection_type::ConnectionPool; +use crate::fileflow::enums::database_engine::DatabaseEngine; +use crate::fileflow::enums::query_rows::QueryRows; +use crate::fileflow::stuct::config::db_config::DatabaseConfig; use sqlx::{Error, MySql, MySqlPool, PgPool, Pool, Postgres, Sqlite, SqlitePool}; -pub enum ConnectionEnum { - Postgres(PgPool), - MySQL(MySqlPool), - SQLite(SqlitePool), +/// Represents a connection to a database, supporting multiple database engines. +#[must_use] +pub struct DatabaseConnection { + pub database_config: DatabaseConfig, + pub engine: ConnectionPool, } -pub enum QueryResult { - Postgres(Vec), - MySQL(Vec), - SQLite(Vec), -} - -pub struct Connection { - pub db_config: DbConfig, - pub connection: ConnectionEnum, -} - -impl Connection { - pub async fn connect(config: &DbConfig) -> Result { +impl DatabaseConnection { + /// Establishes a connection to the database based on the provided configuration. + /// Returns a Connection instance if successful, or an Error if the connection fails. + pub async fn connect(config: &DatabaseConfig) -> Result { let connection_str: String = Self::get_connection_url(config); - let connection_enum: ConnectionEnum = match config.db_driver { + let connection_enum: ConnectionPool = match config.db_driver { DatabaseEngine::Postgres => { let pool: Pool = PgPool::connect(&connection_str).await?; - ConnectionEnum::Postgres(pool) + ConnectionPool::Postgres(pool) } DatabaseEngine::MariaDB | DatabaseEngine::MySQL => { let pool: Pool = MySqlPool::connect(&connection_str).await?; - ConnectionEnum::MySQL(pool) + ConnectionPool::MySQL(pool) } DatabaseEngine::SQLite => { let pool: Pool = SqlitePool::connect(&connection_str).await?; - ConnectionEnum::SQLite(pool) + ConnectionPool::SQLite(pool) } }; Ok(Self { - db_config: config.clone(), - connection: connection_enum, + database_config: config.clone(), + engine: connection_enum, }) } - pub const fn get_db_config(&self) -> &DbConfig { - &self.db_config + /// Returns a reference to the database configuration. + pub const fn get_db_config(&self) -> &DatabaseConfig { + &self.database_config } /// Executes a query and returns the query result wrapped in a QueryResult enum. - pub async fn query_many_with_result(&self, query: &str) -> Result { - match &self.connection { - ConnectionEnum::Postgres(pool) => { + pub async fn query_many_with_result(&self, query: &str) -> Result { + match &self.engine { + ConnectionPool::Postgres(pool) => { let rows = sqlx::query(query).fetch_all(pool).await?; - Ok(QueryResult::Postgres(rows)) + Ok(QueryRows::Postgres(rows)) } - ConnectionEnum::MySQL(pool) => { + ConnectionPool::MySQL(pool) => { let rows = sqlx::query(query).fetch_all(pool).await?; - Ok(QueryResult::MySQL(rows)) + Ok(QueryRows::MySQL(rows)) } - ConnectionEnum::SQLite(pool) => { + ConnectionPool::SQLite(pool) => { let rows = sqlx::query(query).fetch_all(pool).await?; - Ok(QueryResult::SQLite(rows)) + Ok(QueryRows::SQLite(rows)) } } } + /// Executes a query without returning any result. pub async fn query(&self, query: &str) -> Result<(), Error> { - match &self.connection { - ConnectionEnum::Postgres(pool) => { + match &self.engine { + ConnectionPool::Postgres(pool) => { sqlx::query(query).execute(pool).await?; } - ConnectionEnum::MySQL(pool) => { + ConnectionPool::MySQL(pool) => { sqlx::query(query).execute(pool).await?; } - ConnectionEnum::SQLite(pool) => { + ConnectionPool::SQLite(pool) => { sqlx::query(query).execute(pool).await?; } } Ok(()) } - pub(crate) fn get_connection_url(config: &DbConfig) -> String { + /// Constructs the database connection URL based on the provided configuration. + pub fn get_connection_url(config: &DatabaseConfig) -> String { match config.db_driver { DatabaseEngine::Postgres => format!( "postgres://{}{}@{}:{}/{}", @@ -111,15 +108,16 @@ impl Connection { } } + /// Closes the database connection. pub fn disconnect(&self) { - match &self.connection { - ConnectionEnum::Postgres(pool) => { + match &self.engine { + ConnectionPool::Postgres(pool) => { drop(pool.close()); } - ConnectionEnum::MySQL(pool) => { + ConnectionPool::MySQL(pool) => { drop(pool.close()); } - ConnectionEnum::SQLite(pool) => { + ConnectionPool::SQLite(pool) => { drop(pool.close()); } } diff --git a/src-tauri/src/fileflow/stuct/database/mod.rs b/src-tauri/src/fileflow/stuct/database/mod.rs new file mode 100644 index 0000000..7531721 --- /dev/null +++ b/src-tauri/src/fileflow/stuct/database/mod.rs @@ -0,0 +1,3 @@ +pub mod database_action; +pub mod database_connection; +pub mod sql_builder; diff --git a/src-tauri/src/fileflow/stuct/database/sql_builder.rs b/src-tauri/src/fileflow/stuct/database/sql_builder.rs new file mode 100644 index 0000000..31e4dc5 --- /dev/null +++ b/src-tauri/src/fileflow/stuct/database/sql_builder.rs @@ -0,0 +1,432 @@ +use crate::fileflow::enums::database_engine::DatabaseEngine; +use std::collections::HashMap; + +/// SQL Builder for generating database-specific SQL statements +pub struct SqlBuilder; + +impl SqlBuilder { + /// Get the appropriate quote character for the database engine + pub fn get_quote_char(engine: &DatabaseEngine) -> char { + match engine { + DatabaseEngine::SQLite | DatabaseEngine::Postgres => '"', + DatabaseEngine::MariaDB | DatabaseEngine::MySQL => '`', + } + } + + /// Generate the DROP TABLE statement + pub fn build_drop_statement( + engine: &DatabaseEngine, + table_name: &str, + ) -> Result { + if table_name.trim().is_empty() { + return Err("Table name cannot be empty".to_string()); + } + + let quote = Self::get_quote_char(engine); + Ok(format!("DROP TABLE IF EXISTS {quote}{table_name}{quote}")) + } + + /// Generate the INSERT INTO statement + pub fn build_prepared_statement( + engine: &DatabaseEngine, + table_name: &str, + columns: &[String], + ) -> Result { + if table_name.trim().is_empty() { + return Err("Table name cannot be empty".to_string()); + } + + let quote = Self::get_quote_char(engine); + + // Filter out empty columns to fix the bug + let valid_columns: Vec<&String> = columns + .iter() + .filter(|col| !col.trim().is_empty()) + .collect(); + + if valid_columns.is_empty() { + return Err("At least one valid column name is required".to_string()); + } + + let mut query = format!("INSERT INTO {quote}{table_name}{quote} ("); + + // Join columns with proper comma separation + let column_list = valid_columns + .iter() + .map(|col| format!("{quote}{}{quote}", col.trim())) + .collect::>() + .join(", "); + + query.push_str(&column_list); + query.push_str(") VALUES "); + + Ok(query) + } + + /// Generate the COPY statement + pub fn build_copy_table( + engine: &DatabaseEngine, + temporary_table_name: &str, + final_table_name: &str, + ) -> Result { + if temporary_table_name.trim().is_empty() || final_table_name.trim().is_empty() { + return Err("Table names cannot be empty".to_string()); + } + + let quote = Self::get_quote_char(engine); + Ok(format!( + "INSERT INTO {quote}{final_table_name}{quote} SELECT * FROM {quote}{temporary_table_name}{quote}" + )) + } + + /// Generate the CREATE TABLE statement with fixed size for each column + pub fn build_create_with_fixed_size( + engine: &DatabaseEngine, + table_name: &str, + column_max_lengths: &HashMap<&str, usize>, + headers: &[String], + ) -> Result { + if table_name.trim().is_empty() { + return Err("Table name cannot be empty".to_string()); + } + + if headers.is_empty() { + return Err("At least one column header is required".to_string()); + } + + const MAX_VARCHAR_LENGTH: usize = 255; + const TEXT_TYPE: &str = "TEXT"; + const VARCHAR_TYPE: &str = "VARCHAR"; + + let quote = Self::get_quote_char(engine); + let mut columns = Vec::with_capacity(headers.len()); + + // Build column definitions + for header in headers.iter() { + let header = header.trim(); + if header.is_empty() { + continue; // Skip empty headers + } + + let max_length = column_max_lengths + .get(header) + .copied() + .unwrap_or(MAX_VARCHAR_LENGTH); + + // Determine column type + let type_str = if max_length <= MAX_VARCHAR_LENGTH && max_length > 0 { + format!("{VARCHAR_TYPE}({max_length})") + } else { + TEXT_TYPE.to_string() + }; + + // Format column definition + columns.push(format!("{quote}{header}{quote} {type_str}")); + } + + if columns.is_empty() { + return Err("No valid column headers provided".to_string()); + } + + // Build the final SQL statement + Ok(format!( + "CREATE TABLE {quote}{table_name}{quote} ({});", + columns.join(", ") + )) + } + + /// Generate the CREATE TABLE statement with TEXT type for all columns + pub fn build_create_table( + engine: &DatabaseEngine, + table_name: &str, + headers: &[String], + ) -> Result { + if table_name.trim().is_empty() { + return Err("Table name cannot be empty".to_string()); + } + + if headers.is_empty() { + return Err("At least one column header is required".to_string()); + } + + let quote = Self::get_quote_char(engine); + + // Filter out empty headers and build column definitions + let valid_headers: Vec<&String> = headers.iter().filter(|h| !h.trim().is_empty()).collect(); + + if valid_headers.is_empty() { + return Err("No valid column headers provided".to_string()); + } + + let columns = valid_headers + .iter() + .map(|h| format!("{quote}{}{quote} TEXT", h.trim())) + .collect::>() + .join(", "); + + Ok(format!( + "CREATE TABLE {quote}{table_name}{quote} ({columns})" + )) + } + + /// Get the query to fetch all tables from the database + pub fn build_query_all_tables(engine: &DatabaseEngine, schema: &str) -> String { + match engine { + DatabaseEngine::MySQL | DatabaseEngine::MariaDB => { + format!("SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = '{schema}';") + } + DatabaseEngine::Postgres => { + "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'public';" + .to_string() + } + DatabaseEngine::SQLite => { + "SELECT name FROM sqlite_master WHERE type='table';".to_string() + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + /// List of database engines to test + const ENGINES: [DatabaseEngine; 4] = [ + DatabaseEngine::SQLite, + DatabaseEngine::Postgres, + DatabaseEngine::MySQL, + DatabaseEngine::MariaDB, + ]; + + #[tokio::test] + async fn test_sql_builder_drop_statement() { + for engine in ENGINES { + let table_name = "test_table"; + let expected = match engine { + DatabaseEngine::SQLite | DatabaseEngine::Postgres => { + format!("DROP TABLE IF EXISTS \"{table_name}\"") + } + DatabaseEngine::MySQL | DatabaseEngine::MariaDB => { + format!("DROP TABLE IF EXISTS `{table_name}`") + } + }; + assert_eq!( + SqlBuilder::build_drop_statement(&engine, table_name).unwrap(), + expected + ); + } + + // Test error case + assert!(SqlBuilder::build_drop_statement(&DatabaseEngine::SQLite, "").is_err()); + assert!(SqlBuilder::build_drop_statement(&DatabaseEngine::MySQL, " ").is_err()); + } + + #[tokio::test] + async fn test_quote_characters() { + for engine in ENGINES { + let quote = SqlBuilder::get_quote_char(&engine); + let expected = match engine { + DatabaseEngine::SQLite | DatabaseEngine::Postgres => '"', + DatabaseEngine::MySQL | DatabaseEngine::MariaDB => '`', + }; + assert_eq!(quote, expected); + } + } + + #[tokio::test] + async fn test_sql_builder_prepared_statement() { + for engine in ENGINES { + let table_name = "test_table"; + let columns = vec!["col1".to_string(), "col2".to_string(), "col3".to_string()]; + let expected = match engine { + DatabaseEngine::SQLite | DatabaseEngine::Postgres => { + format!("INSERT INTO \"{table_name}\" (\"col1\", \"col2\", \"col3\") VALUES ") + } + DatabaseEngine::MySQL | DatabaseEngine::MariaDB => { + format!("INSERT INTO `{table_name}` (`col1`, `col2`, `col3`) VALUES ") + } + }; + assert_eq!( + SqlBuilder::build_prepared_statement(&engine, table_name, &columns).unwrap(), + expected + ); + } + + // Test error cases + assert!(SqlBuilder::build_prepared_statement( + &DatabaseEngine::SQLite, + "", + &["col1".to_string()] + ) + .is_err()); + assert!(SqlBuilder::build_prepared_statement( + &DatabaseEngine::MySQL, + " ", + &["col1".to_string()] + ) + .is_err()); + assert!( + SqlBuilder::build_prepared_statement(&DatabaseEngine::Postgres, "test_table", &[]) + .is_err() + ); + assert!(SqlBuilder::build_prepared_statement( + &DatabaseEngine::MariaDB, + "test_table", + &[" ".to_string()] + ) + .is_err()); + } + + #[tokio::test] + async fn test_sql_builder_copy_table() { + for engine in ENGINES { + let temp_table = "temp_table"; + let final_table = "final_table"; + let expected = match engine { + DatabaseEngine::SQLite | DatabaseEngine::Postgres => { + format!("INSERT INTO \"{final_table}\" SELECT * FROM \"{temp_table}\"") + } + DatabaseEngine::MySQL | DatabaseEngine::MariaDB => { + format!("INSERT INTO `{final_table}` SELECT * FROM `{temp_table}`") + } + }; + assert_eq!( + SqlBuilder::build_copy_table(&engine, temp_table, final_table).unwrap(), + expected + ); + } + + // Test error cases + assert!(SqlBuilder::build_copy_table(&DatabaseEngine::SQLite, "", "final_table").is_err()); + assert!(SqlBuilder::build_copy_table(&DatabaseEngine::MySQL, "temp_table", "").is_err()); + assert!(SqlBuilder::build_copy_table(&DatabaseEngine::Postgres, " ", " ").is_err()); + } + + #[tokio::test] + async fn test_sql_builder_create_with_fixed_size() { + use std::collections::HashMap; + + for engine in ENGINES { + let table_name = "test_table"; + let headers = vec![ + "col1".to_string(), + "col2".to_string(), + "col3".to_string(), + "col4".to_string(), + ]; + let mut column_max_lengths = HashMap::new(); + column_max_lengths.insert("col1", 100); + column_max_lengths.insert("col2", 300); // Should default to TEXT + column_max_lengths.insert("col3", 255); + // col4 not specified, should default to VARCHAR(255) + + let expected = match engine { + DatabaseEngine::SQLite | DatabaseEngine::Postgres => { + format!( + "CREATE TABLE \"{table_name}\" (\"col1\" VARCHAR(100), \"col2\" TEXT, \"col3\" VARCHAR(255), \"col4\" VARCHAR(255));" + ) + } + DatabaseEngine::MySQL | DatabaseEngine::MariaDB => { + format!( + "CREATE TABLE `{table_name}` (`col1` VARCHAR(100), `col2` TEXT, `col3` VARCHAR(255), `col4` VARCHAR(255));" + ) + } + }; + assert_eq!( + SqlBuilder::build_create_with_fixed_size( + &engine, + table_name, + &column_max_lengths, + &headers + ) + .unwrap(), + expected + ); + } + + // Test error cases + let empty_map: HashMap<&str, usize> = HashMap::new(); + assert!(SqlBuilder::build_create_with_fixed_size( + &DatabaseEngine::SQLite, + "", + &empty_map, + &["col1".to_string()] + ) + .is_err()); + assert!(SqlBuilder::build_create_with_fixed_size( + &DatabaseEngine::MySQL, + " ", + &empty_map, + &["col1".to_string()] + ) + .is_err()); + assert!(SqlBuilder::build_create_with_fixed_size( + &DatabaseEngine::Postgres, + "test_table", + &empty_map, + &[] + ) + .is_err()); + assert!(SqlBuilder::build_create_with_fixed_size( + &DatabaseEngine::MariaDB, + "test_table", + &empty_map, + &[" ".to_string()] + ) + .is_err()); + } + + #[tokio::test] + async fn test_sql_builder_create_table() { + for engine in ENGINES { + let table_name = "test_table"; + let headers = vec!["col1".to_string(), "col2".to_string(), "col3".to_string()]; + let expected = match engine { + DatabaseEngine::SQLite | DatabaseEngine::Postgres => { + format!( + "CREATE TABLE \"{table_name}\" (\"col1\" TEXT, \"col2\" TEXT, \"col3\" TEXT)" + ) + } + DatabaseEngine::MySQL | DatabaseEngine::MariaDB => { + format!("CREATE TABLE `{table_name}` (`col1` TEXT, `col2` TEXT, `col3` TEXT)") + } + }; + assert_eq!( + SqlBuilder::build_create_table(&engine, table_name, &headers).unwrap(), + expected + ); + } + } + + #[tokio::test] + async fn test_sql_builder_create_table_with_empty_headers() { + for engine in ENGINES { + let table_name = "test_table"; + let headers = vec![ + "col1".to_string(), + " ".to_string(), + "".to_string(), + "col3".to_string(), + ]; + let expected = match engine { + DatabaseEngine::SQLite | DatabaseEngine::Postgres => { + format!("CREATE TABLE \"{table_name}\" (\"col1\" TEXT, \"col3\" TEXT)") + } + DatabaseEngine::MySQL | DatabaseEngine::MariaDB => { + format!("CREATE TABLE `{table_name}` (`col1` TEXT, `col3` TEXT)") + } + }; + assert_eq!( + SqlBuilder::build_create_table(&engine, table_name, &headers).unwrap(), + expected + ); + } + + // Test error case with all empty headers + for engine in ENGINES { + let table_name = "test_table"; + let headers = vec![" ".to_string(), "".to_string()]; + assert!(SqlBuilder::build_create_table(&engine, table_name, &headers).is_err()); + } + } +} diff --git a/src-tauri/src/fileflow/stuct/db_config.rs b/src-tauri/src/fileflow/stuct/db_config.rs deleted file mode 100644 index d81fe75..0000000 --- a/src-tauri/src/fileflow/stuct/db_config.rs +++ /dev/null @@ -1,13 +0,0 @@ -use crate::fileflow::enumeration::database_engine::DatabaseEngine; -use serde::{Deserialize, Serialize}; - -#[derive(Deserialize, Clone, Serialize)] -pub struct DbConfig { - pub db_driver: DatabaseEngine, - pub db_host: String, - pub port: String, - pub username: String, - pub password: String, - pub db_name: String, - pub sqlite_file_path: String, -} diff --git a/src-tauri/src/fileflow/stuct/download_config.rs b/src-tauri/src/fileflow/stuct/download_config.rs deleted file mode 100644 index 8fa219d..0000000 --- a/src-tauri/src/fileflow/stuct/download_config.rs +++ /dev/null @@ -1,9 +0,0 @@ -use crate::fileflow::enumeration::separator::SeparatorType; -use serde::{Deserialize, Serialize}; - -#[derive(Deserialize, Serialize)] -pub struct DownloadConfig { - pub table_name_list: Vec, - pub location: String, - pub separator: SeparatorType, -} diff --git a/src-tauri/src/fileflow/stuct/insert_config.rs b/src-tauri/src/fileflow/stuct/insert_config.rs deleted file mode 100644 index 1c38979..0000000 --- a/src-tauri/src/fileflow/stuct/insert_config.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::fileflow::enumeration::database_engine::DatabaseEngine; -use crate::fileflow::enumeration::insertion_type::InsertionType; -use serde::Deserialize; - -#[derive(Deserialize)] -pub struct InsertConfig { - pub file_path: String, - pub table_name: String, - pub mode: InsertionType, - pub db_driver: DatabaseEngine, -} diff --git a/src-tauri/src/fileflow/stuct/io/batch_processor.rs b/src-tauri/src/fileflow/stuct/io/batch_processor.rs new file mode 100644 index 0000000..b734f1e --- /dev/null +++ b/src-tauri/src/fileflow/stuct/io/batch_processor.rs @@ -0,0 +1,44 @@ +use crate::fileflow::stuct::database::database_connection::DatabaseConnection; +use crate::fileflow::stuct::io::row_extractor::RowExtractor; +use crate::fileflow::stuct::io::table_exporter::TableExporter; + +/// Handles paginated database queries for table export +pub(crate) struct BatchProcessor<'a> { + connection: &'a DatabaseConnection, + base_query: String, + offset: u32, +} + +impl<'a> BatchProcessor<'a> { + /// Creates a new BatchProcessor for the given table + #[inline] + pub(crate) fn new(connection: &'a DatabaseConnection, table_name: &str) -> Self { + Self { + connection, + base_query: format!("SELECT * FROM {table_name}"), + offset: 0, + } + } + + /// Fetches the next batch of rows from the database + pub(crate) async fn next_batch( + &mut self, + ) -> Result, Vec>)>, Box> { + let query = format!( + "{} LIMIT {} OFFSET {}", + self.base_query, + TableExporter::BATCH_SIZE, + self.offset + ); + + let query_result = self.connection.query_many_with_result(&query).await?; + let (columns, rows) = RowExtractor::extract_data(query_result)?; + + if rows.is_empty() { + return Ok(None); + } + + self.offset += u32::from(TableExporter::BATCH_SIZE); + Ok(Some((columns, rows))) + } +} diff --git a/src-tauri/src/fileflow/stuct/io/file_utils.rs b/src-tauri/src/fileflow/stuct/io/file_utils.rs new file mode 100644 index 0000000..7e955ea --- /dev/null +++ b/src-tauri/src/fileflow/stuct/io/file_utils.rs @@ -0,0 +1,269 @@ +use std::fs::File; +use std::io::{self, BufRead, BufReader, ErrorKind, Read}; +use std::path::Path; + +/// File separator detection and first line reading utilities +pub struct FileUtils; + +impl FileUtils { + /// Common separators ordered by likelihood of occurrence + const SEPARATORS: [char; 6] = [',', ';', '\t', '|', ' ', '\0']; + + /// Maximum bytes to read for separator detection (optimization) + const MAX_DETECTION_BYTES: usize = 4096; + + /// Detects the most likely separator character from a text line + pub fn find_separator(line: &str) -> Result { + Self::validate_input(line)?; + + // Find the first separator that appears in the line + Self::SEPARATORS + .iter() + .find(|&&sep| line.contains(sep)) + .copied() + .ok_or_else(|| "Could not detect a valid separator".to_string()) + } + + /// Reads the first line of a file efficiently + pub fn read_first_line>(file_path: P) -> io::Result { + let file = Self::open_file(file_path)?; + let mut reader = BufReader::new(file); + + Self::extract_first_line(&mut reader) + } + + /// Detects separator from the first line of a file + pub fn detect_separator_from_file>( + file_path: P, + ) -> Result> { + let first_line = Self::read_first_line(file_path)?; + Self::find_separator(&first_line).map_err(|e| e.into()) + } + + /// Reads first line with separator detection in one operation + pub fn read_first_line_with_separator>( + file_path: P, + ) -> Result<(String, char), Box> { + let first_line = Self::read_first_line(file_path)?; + let separator = Self::find_separator(&first_line)?; + Ok((first_line, separator)) + } + + /// Validates that the input line is not empty or just whitespace + fn validate_input(line: &str) -> Result<(), String> { + if line.trim().is_empty() { + return Err("Input line cannot be empty".to_string()); + } + Ok(()) + } + + /// Opens a file with error handling + fn open_file>(file_path: P) -> io::Result { + let path = file_path.as_ref(); + + if !path.exists() { + return Err(io::Error::new( + ErrorKind::NotFound, + format!("File not found: {}", path.display()), + )); + } + + if !path.is_file() { + return Err(io::Error::new( + ErrorKind::InvalidInput, + format!("Path is not a file: {}", path.display()), + )); + } + + File::open(path) + } + + /// Extracts the first line from a buffered reader + fn extract_first_line(reader: &mut BufReader) -> io::Result { + let mut line = String::new(); + + match reader.read_line(&mut line) { + Ok(0) => Err(io::Error::new(ErrorKind::NotFound, "File is empty")), + Ok(_) => { + // Remove trailing newline characters + Self::clean_line_ending(&mut line); + Ok(line) + } + Err(e) => Err(e), + } + } + + /// Cleans trailing newline characters from a line + fn clean_line_ending(line: &mut String) { + // Remove trailing \r\n or \n + while line.ends_with('\n') || line.ends_with('\r') { + line.pop(); + } + } + + /// Alternative implementation using byte-by-byte reading (for comparison) + pub fn read_first_line_bytes>(file_path: P) -> io::Result { + let file = Self::open_file(file_path)?; + let mut reader = BufReader::new(file); + + let mut line_bytes = Vec::with_capacity(256); // Pre-allocate reasonable capacity + let mut byte = [0u8; 1]; + let mut bytes_read = 0; + + while bytes_read < Self::MAX_DETECTION_BYTES { + match reader.read_exact(&mut byte) { + Ok(()) => { + bytes_read += 1; + match byte[0] { + b'\n' => break, // End of line found + b'\r' => continue, // Skip carriage return + b => line_bytes.push(b), + } + } + Err(e) if e.kind() == ErrorKind::UnexpectedEof => { + if line_bytes.is_empty() { + return Err(io::Error::new(ErrorKind::NotFound, "File is empty")); + } + break; + } + Err(e) => return Err(e), + } + } + + // Convert to string with proper error handling + String::from_utf8(line_bytes) + .map_err(|e| io::Error::new(ErrorKind::InvalidData, format!("Invalid UTF-8: {}", e))) + } + + /// Advanced separator detection with confidence scoring + pub fn detect_separator_with_confidence(line: &str) -> Result<(char, f32), String> { + Self::validate_input(line)?; + + let mut best_separator = None; + let mut best_score = 0.0; + + for &separator in &Self::SEPARATORS { + let score = Self::calculate_separator_score(line, separator); + if score > best_score { + best_score = score; + best_separator = Some(separator); + } + } + + best_separator + .map(|sep| (sep, best_score)) + .ok_or_else(|| "Could not detect a valid separator".to_string()) + } + + /// Calculates a score for how well a separator fits the line + /// Higher scores indicate a better fit + fn calculate_separator_score(line: &str, separator: char) -> f32 { + if !line.contains(separator) { + return 0.0; + } + + let count = line.matches(separator).count() as f32; + let length = line.len() as f32; + + // Normalize by line length and apply separator preference + let base_score = count / length; + let preference_multiplier = match separator { + ',' => 1.0, // Highest preference + ';' => 0.9, // High preference + '\t' => 0.8, // Medium preference + '|' => 0.7, // Lower preference + ' ' => 0.3, // Low preference (spaces are often part of data) + _ => 0.1, // Lowest preference + }; + + base_score * preference_multiplier + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::io::Write; + use tempfile::NamedTempFile; + + fn create_test_file(content: &str) -> NamedTempFile { + let mut file = NamedTempFile::new().expect("Failed to create temp file"); + file.write_all(content.as_bytes()) + .expect("Failed to write to temp file"); + file + } + + #[tokio::test] + async fn test_find_separator() { + assert_eq!(FileUtils::find_separator("a,b,c").unwrap(), ','); + assert_eq!(FileUtils::find_separator("a;b;c").unwrap(), ';'); + assert_eq!(FileUtils::find_separator("a\tb\tc").unwrap(), '\t'); + assert_eq!(FileUtils::find_separator("a|b|c").unwrap(), '|'); + assert_eq!(FileUtils::find_separator("a b c").unwrap(), ' '); + assert_eq!(FileUtils::find_separator("a\0b\0c").unwrap(), '\0'); + + // Test error case + assert!(FileUtils::find_separator("abc").is_err()); + assert!(FileUtils::find_separator("").is_err()); + assert!(FileUtils::find_separator(" ").is_err()); + } + + #[tokio::test] + async fn test_read_first_line() { + let file = create_test_file("first line\nsecond line\nthird line"); + let result = FileUtils::read_first_line(file.path()).unwrap(); + assert_eq!(result, "first line"); + } + + #[tokio::test] + async fn test_read_first_line_with_carriage_return() { + let file = create_test_file("first line\r\nsecond line"); + let result = FileUtils::read_first_line(file.path()).unwrap(); + assert_eq!(result, "first line"); + } + + #[tokio::test] + async fn test_read_first_line_single_line() { + let file = create_test_file("only line"); + let result = FileUtils::read_first_line(file.path()).unwrap(); + assert_eq!(result, "only line"); + } + + #[tokio::test] + async fn test_read_first_line_empty_file() { + let file = create_test_file(""); + let result = FileUtils::read_first_line(file.path()); + assert!(result.is_err()); + assert_eq!(result.unwrap_err().kind(), ErrorKind::NotFound); + } + + #[tokio::test] + async fn test_detect_separator_from_file() { + let file = create_test_file("name,age,city\nJohn,25,NYC"); + let separator = FileUtils::detect_separator_from_file(file.path()).unwrap(); + assert_eq!(separator, ','); + } + + #[tokio::test] + async fn test_read_first_line_with_separator() { + let file = create_test_file("name;age;city\nJohn;25;NYC"); + let (line, separator) = FileUtils::read_first_line_with_separator(file.path()).unwrap(); + assert_eq!(line, "name;age;city"); + assert_eq!(separator, ';'); + } + + #[tokio::test] + async fn test_detect_separator_with_confidence() { + let (sep, confidence) = FileUtils::detect_separator_with_confidence("a,b,c,d").unwrap(); + assert_eq!(sep, ','); + assert!(confidence > 0.0); + + // Test that comma has higher confidence than space for mixed content + let (sep1, conf1) = FileUtils::detect_separator_with_confidence("a, b, c, d").unwrap(); + let (sep2, conf2) = FileUtils::detect_separator_with_confidence("a b c d").unwrap(); + + if sep1 == ',' && sep2 == ' ' { + assert!(conf1 > conf2); + } + } +} diff --git a/src-tauri/src/fileflow/stuct/io/mod.rs b/src-tauri/src/fileflow/stuct/io/mod.rs new file mode 100644 index 0000000..76418d0 --- /dev/null +++ b/src-tauri/src/fileflow/stuct/io/mod.rs @@ -0,0 +1,4 @@ +mod batch_processor; +pub mod file_utils; +mod row_extractor; +pub mod table_exporter; diff --git a/src-tauri/src/fileflow/stuct/io/row_extractor.rs b/src-tauri/src/fileflow/stuct/io/row_extractor.rs new file mode 100644 index 0000000..4969dd7 --- /dev/null +++ b/src-tauri/src/fileflow/stuct/io/row_extractor.rs @@ -0,0 +1,224 @@ +use crate::fileflow::enums::query_rows::QueryRows; +use sqlx::{Column, Row}; + +/// Extracts data from different database row types +pub(crate) struct RowExtractor; + +impl RowExtractor { + /// Extracts column names and row data from the given QueryRows enum + pub(crate) fn extract_data( + query_result: QueryRows, + ) -> Result<(Vec, Vec>), Box> { + match query_result { + QueryRows::MySQL(rows) => Self::extract_mysql_data(rows), + QueryRows::Postgres(rows) => Self::extract_postgres_data(rows), + QueryRows::SQLite(rows) => Self::extract_sqlite_data(rows), + } + } + + /// Extracts column names and row data from MySQL rows + fn extract_mysql_data( + rows: Vec, + ) -> Result<(Vec, Vec>), Box> { + if rows.is_empty() { + return Ok((Vec::new(), Vec::new())); + } + + let columns: Vec = Self::extract_column_names(&rows[0]); // Extract column names + let data: Vec> = Self::extract_mysql_row_data(rows)?; // Extract row data + + Ok((columns, data)) + } + + /// Extracts column names and row data from PostgreSQL rows + fn extract_postgres_data( + rows: Vec, + ) -> Result<(Vec, Vec>), Box> { + if rows.is_empty() { + return Ok((Vec::new(), Vec::new())); + } + + let columns = Self::extract_column_names(&rows[0]); + let data = Self::extract_postgres_row_data(rows)?; + + Ok((columns, data)) + } + + /// Extracts column names and row data from SQLite rows + fn extract_sqlite_data( + rows: Vec, + ) -> Result<(Vec, Vec>), Box> { + if rows.is_empty() { + return Ok((Vec::new(), Vec::new())); + } + + let columns = Self::extract_column_names(&rows[0]); + let data = Self::extract_sqlite_row_data(rows)?; + + Ok((columns, data)) + } + + /// Extracts column names from a row + fn extract_column_names(row: &R) -> Vec { + row.columns() + .iter() + .map(|col| col.name().to_string()) + .collect() + } + + /// Extracts row data from MySQL rows + fn extract_mysql_row_data( + rows: Vec, + ) -> Result>, Box> { + let mut result = Vec::with_capacity(rows.len()); + + for row in rows { + let mut row_data = Vec::with_capacity(row.len()); + + for (idx, column) in row.columns().iter().enumerate() { + let value = Self::get_mysql_column_value(&row, idx, column.name())?; + row_data.push(value); + } + + result.push(row_data); + } + + Ok(result) + } + + /// Extracts row data from PostgreSQL rows + fn extract_postgres_row_data( + rows: Vec, + ) -> Result>, Box> { + let mut result = Vec::with_capacity(rows.len()); + + for row in rows { + let mut row_data = Vec::with_capacity(row.len()); + + for (idx, _) in row.columns().iter().enumerate() { + let value = Self::get_postgres_column_value(&row, idx)?; + row_data.push(value); + } + + result.push(row_data); + } + + Ok(result) + } + + /// Extracts row data from SQLite rows + fn extract_sqlite_row_data( + rows: Vec, + ) -> Result>, Box> { + let mut result = Vec::with_capacity(rows.len()); + + for row in rows { + let mut row_data = Vec::with_capacity(row.len()); + + for (idx, column) in row.columns().iter().enumerate() { + let value = Self::get_sqlite_column_value(&row, idx, column.name())?; + row_data.push(value); + } + + result.push(row_data); + } + + Ok(result) + } + + /// Helper methods to extract column values with type handling + fn get_mysql_column_value( + row: &sqlx::mysql::MySqlRow, + index: usize, + column_name: &str, + ) -> Result> { + // Try different types in order of likelihood + if let Ok(value) = row.try_get::, _>(index) { + return Ok(value.unwrap_or_default()); + } + + if let Ok(value) = row.try_get::, _>(index) { + return Ok(value.map(|v| v.to_string()).unwrap_or_default()); + } + + if let Ok(value) = row.try_get::, _>(index) { + return Ok(value.map(|v| v.to_string()).unwrap_or_default()); + } + + if let Ok(value) = row.try_get::, _>(index) { + return Ok(value.map(|v| v.to_string()).unwrap_or_default()); + } + + // If all else fails, try to get the raw value as bytes and convert to string + match row.try_get::>, _>(index) { + Ok(Some(bytes)) => String::from_utf8(bytes).map_err(|e| { + format!("UTF-8 conversion error for column '{}': {}", column_name, e).into() + }), + Ok(None) => Ok(String::new()), + Err(_) => Ok(String::new()), // Default to empty string if we can't extract the value + } + } + + /// Extracts a column value from a PostgreSQL row with type handling + fn get_postgres_column_value( + row: &sqlx::postgres::PgRow, + index: usize, + ) -> Result> { + // Try different types in order of likelihood + if let Ok(value) = row.try_get::, _>(index) { + return Ok(value.unwrap_or_default()); + } + + if let Ok(value) = row.try_get::, _>(index) { + return Ok(value.map(|v| v.to_string()).unwrap_or_default()); + } + + if let Ok(value) = row.try_get::, _>(index) { + return Ok(value.map(|v| v.to_string()).unwrap_or_default()); + } + + if let Ok(value) = row.try_get::, _>(index) { + return Ok(value.map(|v| v.to_string()).unwrap_or_default()); + } + + // PostgreSQL specific types + if let Ok(value) = row.try_get::, _>(index) { + return Ok(value.map(|v| v.to_string()).unwrap_or_default()); + } + + // Default to empty string if we can't extract the value + Ok(String::new()) + } + + /// Extracts a column value from a SQLite row with type handling + fn get_sqlite_column_value( + row: &sqlx::sqlite::SqliteRow, + index: usize, + column_name: &str, + ) -> Result> { + // SQLite is more flexible with types, try common ones + if let Ok(value) = row.try_get::, _>(index) { + return Ok(value.unwrap_or_default()); + } + + if let Ok(value) = row.try_get::, _>(index) { + return Ok(value.map(|v| v.to_string()).unwrap_or_default()); + } + + if let Ok(value) = row.try_get::, _>(index) { + return Ok(value.map(|v| v.to_string()).unwrap_or_default()); + } + + if let Ok(value) = row.try_get::>, _>(index) { + return match value { + Some(bytes) => String::from_utf8(bytes).map_err(|e| { + format!("UTF-8 conversion error for column '{}': {}", column_name, e).into() + }), + None => Ok(String::new()), + }; + } + + // Default to empty string if we can't extract the value + Ok(String::new()) + } +} diff --git a/src-tauri/src/fileflow/stuct/io/table_exporter.rs b/src-tauri/src/fileflow/stuct/io/table_exporter.rs new file mode 100644 index 0000000..2fa114a --- /dev/null +++ b/src-tauri/src/fileflow/stuct/io/table_exporter.rs @@ -0,0 +1,102 @@ +use crate::fileflow::stuct::config::download_config::DownloadConfig; +use crate::fileflow::stuct::database::database_connection::DatabaseConnection; +use crate::fileflow::stuct::io::batch_processor::BatchProcessor; +use csv::{Writer, WriterBuilder}; +use std::fs::File; + +/// Export utility for database tables to CSV +pub struct TableExporter; + +impl TableExporter { + pub const BATCH_SIZE: u16 = 5_000; + + /// Exports a table's data to CSV using paginated queries + pub async fn export_table( + connection: &DatabaseConnection, + download_config: &DownloadConfig, + table_name: &str, + ) -> Result<(), Box> { + // Validate inputs + Self::validate_inputs(table_name, download_config)?; + + // Setup CSV writer + let file_path = Self::build_file_path(download_config, table_name); + let mut csv_writer = Self::create_csv_writer(download_config, &file_path)?; + + // Export data in batches + let mut batch_processor = BatchProcessor::new(connection, table_name); + let mut is_first_batch = true; + + while let Some((columns, rows)) = batch_processor.next_batch().await? { + if is_first_batch { + Self::write_header(&mut csv_writer, &columns)?; + is_first_batch = false; + } + Self::write_rows(&mut csv_writer, &rows)?; + } + + Self::finalize_writer(csv_writer)?; + Ok(()) + } + + // Private helper methods + fn validate_inputs( + table_name: &str, + download_config: &DownloadConfig, + ) -> Result<(), Box> { + if table_name.trim().is_empty() { + return Err("Table name cannot be empty".into()); + } + if download_config.location.trim().is_empty() { + return Err("Download location cannot be empty".into()); + } + Ok(()) + } + + fn build_file_path(download_config: &DownloadConfig, table_name: &str) -> String { + format!("{}/{}_export.csv", download_config.location, table_name) + } + + /// Creates a CSV writer with the specified configuration + fn create_csv_writer( + download_config: &DownloadConfig, + file_path: &str, + ) -> Result, Box> { + WriterBuilder::new() + .delimiter(download_config.separator.as_u8()) + .from_path(file_path) + .map_err(|e| format!("Failed to create CSV writer: {}", e).into()) + } + + fn write_header( + writer: &mut Writer, + columns: &[String], + ) -> Result<(), Box> { + writer + .write_record(columns) + .map_err(|e| format!("Failed to write CSV header: {}", e).into()) + } + + /// Writes multiple rows to the CSV file + fn write_rows( + writer: &mut Writer, + rows: &[Vec], + ) -> Result<(), Box> { + for row in rows { + writer + .write_record(row) + .map_err(|e| format!("Failed to write CSV row: {}", e))?; + } + Ok(()) + } + + /// Finalizes the CSV writer by flushing any remaining data + fn finalize_writer(mut writer: Writer) -> Result<(), Box> { + writer + .flush() + .map_err(|e| format!("Failed to flush CSV writer: {}", e).into()) + } +} + +#[cfg(test)] +mod tests {} diff --git a/src-tauri/src/fileflow/stuct/mod.rs b/src-tauri/src/fileflow/stuct/mod.rs index aa91105..c6b2ed3 100644 --- a/src-tauri/src/fileflow/stuct/mod.rs +++ b/src-tauri/src/fileflow/stuct/mod.rs @@ -1,6 +1,4 @@ -pub mod combo_item; -pub mod db_config; -pub mod download_config; -pub mod insert_config; -pub mod save_config; +pub mod config; +pub mod database; +pub mod io; pub mod string_formater; diff --git a/src-tauri/src/fileflow/stuct/save_config.rs b/src-tauri/src/fileflow/stuct/save_config.rs deleted file mode 100644 index 194ab03..0000000 --- a/src-tauri/src/fileflow/stuct/save_config.rs +++ /dev/null @@ -1,14 +0,0 @@ -use crate::fileflow::enumeration::database_engine::DatabaseEngine; -use serde::{Deserialize, Serialize}; - -#[derive(Deserialize, Serialize, Clone)] -pub struct SaveConfig { - pub config_name: String, - pub db_driver: DatabaseEngine, - pub db_host: String, - pub port: String, - pub username: String, - pub password: String, - pub db_name: String, - pub sqlite_file_path: String, -} diff --git a/src-tauri/src/fileflow/stuct/string_formater.rs b/src-tauri/src/fileflow/stuct/string_formater.rs index 0746854..776f263 100644 --- a/src-tauri/src/fileflow/stuct/string_formater.rs +++ b/src-tauri/src/fileflow/stuct/string_formater.rs @@ -1,4 +1,5 @@ use csv::StringRecord; +use std::borrow::Cow; pub struct StringFormatter; @@ -13,17 +14,33 @@ impl StringFormatter { if trimmed_column_name.is_empty() { safe_headers.push(format!("{COLUMN_PREFIX}{}", index + 1)); } else { - safe_headers.push(Self::sanitize_column(trimmed_column_name)); + let sanitized_column_name: String = Self::sanitize_column(trimmed_column_name); + safe_headers.push(if sanitized_column_name.is_empty() { + format!("{COLUMN_PREFIX}{}", index + 1) + } else { + sanitized_column_name + }); } } safe_headers } /// Sanitize a value for safe insertion into the database - pub fn sanitize_value(value: &str) -> String { - let mut sanitized: String = String::with_capacity(value.len()); + #[inline] + pub fn sanitize_value(value: &'_ str) -> Cow<'_, str> { + let trimmed: &str = value.trim(); - for c in value.trim().chars() { + let needs_sanitization: bool = trimmed + .chars() + .any(|c| matches!(c, '\'' | '\\' | '\"' | '\0' | '\r' | '\n')); + + if !needs_sanitization { + return Cow::Borrowed(trimmed); + } + + let mut sanitized = String::with_capacity(trimmed.len()); + + for c in trimmed.chars() { match c { '\'' => sanitized.push_str("''"), // Escape single quotes '\\' => sanitized.push_str("\\\\"), // Escape backslashes @@ -33,30 +50,38 @@ impl StringFormatter { } } - sanitized + Cow::Owned(sanitized) } /// Sanitize a column name for safe insertion into the database + #[inline] pub fn sanitize_column(value: &str) -> String { - let trimmed = value.trim(); - let mut result = String::with_capacity(trimmed.len()); // Pre-allocate + let trimmed: &str = value.trim(); + let mut result: String = String::with_capacity(trimmed.len()); - for c in trimmed.chars() { + for char in trimmed.chars() { // Filter out unwanted characters first (early return) - if (c.is_control() && c != '\n' && c != '\t') - || c == '\u{feff}' // BOM - || c == '\u{200b}' // Zero-width space - || c == '\u{200c}' // Zero-width non-joiner - || c == '\u{200d}' - // Zero-width joiner + if (char.is_control() && char != '\n' && char != '\t') + || char == '\u{feff}' // BOM + || char == '\u{200b}' // Zero-width space + || char == '\u{200c}' // Zero-width non-joiner + || char == '\u{200d}' // Zero-width joiner + || !char.is_ascii() + // Filter out non-ASCII characters { continue; } - match c { - '\'' | '\\' | '\"' => continue, // Skip these + match char { + '\'' | '\\' | '\"' | '.' | '/' => continue, // Skip ' ' => result.push('_'), - _ => result.push(c.to_ascii_lowercase()), + _ => { + if char.is_ascii() { + result.push(char.to_ascii_lowercase()); + } else { + continue; + } + } } } @@ -64,11 +89,126 @@ impl StringFormatter { } /// Escape values for SQL insert statement to avoid SQL injection attacks and other issues with special characters in values. - pub fn escaped_record(values: StringRecord) -> String { - let vec: Vec = values - .iter() - .map(|v| format!("'{}'", Self::sanitize_value(v))) - .collect(); - vec.join(", ") + #[inline] + pub fn escape_record(values: StringRecord) -> String { + if values.is_empty() { + return String::new(); + } + + // Estimate capacity: each value + 2 quotes + 2 chars for ", " + let estimated_capacity: usize = values.iter().map(|v| v.len() + 4).sum::(); + + let mut result: String = String::with_capacity(estimated_capacity); + + for (i, value) in values.iter().enumerate() { + if i > 0 { + result.push_str(", "); + } + result.push('\''); + result.push_str(&Self::sanitize_value(value)); + result.push('\''); + } + + result + } +} + +#[cfg(test)] +mod tests { + use crate::fileflow::stuct::string_formater::StringFormatter; + use csv::StringRecord; + + #[tokio::test] + async fn test_escape_values() { + let record: StringRecord = StringRecord::from(vec!["value1", "value2"]); + let values: String = StringFormatter::escape_record(record); + assert_eq!(values, "'value1', 'value2'"); + + let record: StringRecord = StringRecord::from(vec![ + "\"INSERT INTO test_table VALUES (1,2);\"", + "UPDATE test_table SET column1 = 1;", + "DELETE FROM test_table WHERE column1 = 1;", + "SELECT * FROM test_table;", + ]); + let values: String = StringFormatter::escape_record(record); + assert_eq!(values, "'INSERT INTO test_table VALUES (1,2);', 'UPDATE test_table SET column1 = 1;', 'DELETE FROM test_table WHERE column1 = 1;', 'SELECT * FROM test_table;'"); + } + + #[tokio::test] + async fn test_sanitize_value() { + assert_eq!(StringFormatter::sanitize_value("value1"), "value1"); + assert_eq!(StringFormatter::sanitize_value("'value1'"), "''value1''"); + assert_eq!(StringFormatter::sanitize_value("value1'"), "value1''"); + assert_eq!( + StringFormatter::sanitize_value("value1\\value2"), + "value1\\\\value2" + ); + assert_eq!( + StringFormatter::sanitize_value("value1\"value2"), + "value1value2" + ); + } + + #[tokio::test] + async fn test_sanitize_column() { + // Standard cases + assert_eq!(StringFormatter::sanitize_column("column1"), "column1"); + assert_eq!(StringFormatter::sanitize_column("'column1'"), "column1"); + assert_eq!(StringFormatter::sanitize_column("column1'"), "column1"); + assert_eq!( + StringFormatter::sanitize_column("column1\\column2"), + "column1column2" + ); + assert_eq!( + StringFormatter::sanitize_column("column1\"column2"), + "column1column2" + ); + assert_eq!(StringFormatter::sanitize_column("column 1"), "column_1"); + assert_eq!(StringFormatter::sanitize_column("column 1'"), "column_1"); + assert_eq!(StringFormatter::sanitize_column("COlUMn 1'"), "column_1"); + assert_eq!(StringFormatter::sanitize_column(" CoDE_DEP "), "code_dep"); + assert_eq!(StringFormatter::sanitize_column(" column\\1 "), "column1"); + assert_eq!(StringFormatter::sanitize_column(" COlu.Mn/1"), "column1"); + + // Non-printable characters + assert_eq!( + StringFormatter::sanitize_column("\u{feff}code_departement"), + "code_departement" + ); // BOM + assert_eq!( + StringFormatter::sanitize_column("code\u{0000}_dep"), + "code_dep" + ); // NULL + assert_eq!( + StringFormatter::sanitize_column("code\u{001F}_dep"), + "code_dep" + ); // Unit Separator + assert_eq!(StringFormatter::sanitize_column("code\n_dep"), "code\n_dep"); // newline is preserved (can change if undesired) + assert_eq!(StringFormatter::sanitize_column("code\tdep"), "code\tdep"); // tab is preserved (can change if undesired) + assert_eq!(StringFormatter::sanitize_column("code\rdep"), "codedep"); // carriage return removed + } + + #[tokio::test] + async fn test_get_formated_column_names() { + assert_eq!( + StringFormatter::get_formated_column_names(&vec!["header 1".into(), " header2".into()]), + vec!["header_1", "header2"] + ); + assert_eq!( + StringFormatter::get_formated_column_names(&vec![ + "header 1".into(), + String::new(), + "header2".into(), + "".into() + ]), + vec!["header____1", "column_2", "header2", "column_4"] + ); + assert_eq!( + StringFormatter::get_formated_column_names(&vec![ + "header'\" 1".into(), + "header 2''\\".into() + ]), + vec!["header_1", "header_2"] + ); } } diff --git a/src-tauri/src/fileflow/utils/csv_utils.rs b/src-tauri/src/fileflow/utils/csv_utils.rs deleted file mode 100644 index 94b8246..0000000 --- a/src-tauri/src/fileflow/utils/csv_utils.rs +++ /dev/null @@ -1,24 +0,0 @@ -use std::fs::File; -use std::io; -use std::io::{BufRead, BufReader}; - -/// This function is used to detect the separator from a string. -pub fn find_separator(line: &str) -> Result { - const POSSIBLE_SEPARATORS: [char; 6] = [',', ';', '\t', '|', ' ', '\0']; - for sep in POSSIBLE_SEPARATORS.iter() { - if line.contains(*sep) { - return Ok(*sep); - } - } - Err("Could not detect a valid separator".into()) -} - -/// Read the first line of a file -pub fn read_first_line(file_path: &str) -> io::Result { - let file: File = File::open(file_path).expect("Could not open file"); - let reader: BufReader = BufReader::new(file); - if let Some(line) = reader.lines().next() { - return line; - } - Err(io::Error::new(io::ErrorKind::NotFound, "File is empty")) -} diff --git a/src-tauri/src/fileflow/utils/fileflowlib.rs b/src-tauri/src/fileflow/utils/fileflowlib.rs deleted file mode 100644 index 65325b0..0000000 --- a/src-tauri/src/fileflow/utils/fileflowlib.rs +++ /dev/null @@ -1,37 +0,0 @@ -use crate::fileflow::stuct::save_config::SaveConfig; -use std::fs::{File, OpenOptions}; -use std::io; -use std::path::PathBuf; - -/// This function is used to get the size of a file. -pub fn get_all_saved_configs(config_file: &str) -> Vec { - let default_configs: Vec = Vec::new(); - - let path: PathBuf = PathBuf::from(config_file); - let file: File = match File::open(path) { - Ok(file) => file, - Err(_) => return default_configs, - }; - - if file.metadata().unwrap().len() == 0 { - return default_configs; - } - - let configs: Vec = serde_json::from_reader(file).unwrap_or(default_configs); - configs -} - -/// This function is used to save a vector of SaveConfig to a json file. -pub fn save_config(configs: &[SaveConfig], config_file: &str) -> io::Result<()> { - let file: File = OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .open(config_file) - .map_err(|e| format!("Failed to open file for writing: {e}")) - .expect("Failed to open file for writing"); - serde_json::to_writer_pretty(file, &configs) - .map_err(|e| format!("Failed to write to file: {e}")) - .expect("Failed to write to file"); - Ok(()) -} diff --git a/src-tauri/src/fileflow/utils/mod.rs b/src-tauri/src/fileflow/utils/mod.rs index 7bd2963..0b6d2c4 100644 --- a/src-tauri/src/fileflow/utils/mod.rs +++ b/src-tauri/src/fileflow/utils/mod.rs @@ -1,3 +1 @@ pub mod constants; -pub mod csv_utils; -pub mod fileflowlib; diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs new file mode 100644 index 0000000..ef1863d --- /dev/null +++ b/src-tauri/src/lib.rs @@ -0,0 +1 @@ +pub mod fileflow; diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 7b344e6..338cc33 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -1,14 +1,15 @@ -// main.rs #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] -mod fileflow; - #[cfg(test)] mod tests; -use crate::fileflow::action::database_command::get_table_list; -use fileflow::action::actions::*; -use fileflow::action::database_command::*; +#[cfg(test)] +mod benches; + +use fileflow::fileflow::action::actions::DatabaseState; +use fileflow::fileflow::action::actions::*; +use fileflow::fileflow::action::database_command::get_table_list; +use fileflow::fileflow::action::database_command::*; use std::sync::Arc; use tokio::sync::Mutex; @@ -29,7 +30,8 @@ fn main() { get_all_database_configs_name, delete_database_config, get_table_list, - download_table + download_table, + get_app_version ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/src-tauri/src/tests/action_test.rs b/src-tauri/src/tests/action_test.rs index 8c5c1b2..af8211c 100644 --- a/src-tauri/src/tests/action_test.rs +++ b/src-tauri/src/tests/action_test.rs @@ -1,27 +1,24 @@ -use crate::fileflow::database::connection::Connection; -use crate::fileflow::enumeration::database_engine::DatabaseEngine; -use crate::fileflow::stuct::db_config::DbConfig; -use crate::fileflow::stuct::save_config::SaveConfig; -use crate::fileflow::utils::fileflowlib::{get_all_saved_configs, save_config}; use crate::tests::utils_tests::{ - create_test_db, delete_config_file, generate_csv_file, get_test_save_config, - get_test_sqlite_config, remove_csv_file, remove_test_db, + create_test_db, generate_csv_file, get_test_sqlite_config, remove_csv_file, remove_test_db, }; use csv::{Reader, ReaderBuilder}; +use fileflow::fileflow::action::database_command::fast_insert; +use fileflow::fileflow::enums::database_engine::DatabaseEngine; +use fileflow::fileflow::stuct::config::db_config::DatabaseConfig; +use fileflow::fileflow::stuct::database::database_connection::DatabaseConnection; use sqlx::sqlite::SqliteRow; use sqlx::{Error, Pool, Row, Sqlite}; use std::fs::File; -use crate::fileflow::action::database_command::fast_insert; #[tokio::test] async fn test_fast_insert() { let sqlite_file_path: String = create_test_db("fast_insert"); - let config: DbConfig = get_test_sqlite_config(sqlite_file_path.clone()); - let conn: Result = Connection::connect(&config).await; + let config: DatabaseConfig = get_test_sqlite_config(sqlite_file_path.clone()); + let conn: Result = DatabaseConnection::connect(&config).await; assert!(conn.is_ok(), "Failed to connect to the database"); - let conn: Connection = conn.unwrap(); + let conn: DatabaseConnection = conn.unwrap(); let csv_file_path: String = generate_csv_file("test_fast_insert").expect("Failed to generate csv file"); @@ -82,34 +79,3 @@ async fn test_fast_insert() { remove_test_db("fast_insert").expect("Failed to remove test table"); remove_csv_file("test_fast_insert").expect("Failed to remove CSV file"); } - -#[tokio::test] -async fn test_configs_serealization_deserialization() { - const CONFIG_NAME: &str = "test_get_all_configs.json"; - - let config1: SaveConfig = get_test_save_config("config1"); - let config2: SaveConfig = get_test_save_config("config2"); - let config3: SaveConfig = get_test_save_config("config3"); - let configs_list: Vec = vec![config1, config2, config3]; - - save_config(&configs_list, CONFIG_NAME).expect("Failed to save configs"); - - let deserialized_configs: Vec = get_all_saved_configs("test_get_all_configs.json"); - - assert_eq!(3, deserialized_configs.len()); - - assert_eq!( - configs_list[0].config_name, - deserialized_configs[0].config_name - ); - assert_eq!( - configs_list[1].config_name, - deserialized_configs[1].config_name - ); - assert_eq!( - configs_list[2].config_name, - deserialized_configs[2].config_name - ); - - delete_config_file(CONFIG_NAME).expect("Failed to delete config file"); -} diff --git a/src-tauri/src/tests/csv_utils_test.rs b/src-tauri/src/tests/csv_utils_test.rs deleted file mode 100644 index 6cc0894..0000000 --- a/src-tauri/src/tests/csv_utils_test.rs +++ /dev/null @@ -1,31 +0,0 @@ -use crate::fileflow::utils::csv_utils::{find_separator, read_first_line}; -use crate::tests::utils_tests::{generate_csv_file, remove_csv_file}; - -#[tokio::test] -async fn test_detect_separator() { - let test_cases = [ - ("header1,header2", Some(',')), - ("header1;header2", Some(';')), - ("header1\theader2", Some('\t')), - ("header1|header2", Some('|')), - ("header1 header2", Some(' ')), - ("header1\0header2", Some('\0')), - ("header1header2", None), - ]; - - for (input, expected) in test_cases { - match expected { - Some(separator) => assert_eq!(find_separator(input).unwrap(), separator), - None => assert!(find_separator(input).is_err()), - } - } -} - -#[tokio::test] -async fn test_read_first_line() { - let csv_file_path: String = - generate_csv_file("test_read_first_line").expect("Failed to generate csv file"); - let first_line: String = read_first_line(&csv_file_path).expect("Failed to read first line"); - assert_eq!(first_line, "header1,header2"); - remove_csv_file("test_read_first_line").expect("Failed to remove csv file"); -} diff --git a/src-tauri/src/tests/database_test.rs b/src-tauri/src/tests/database_test.rs index fe0ea02..4f642b6 100644 --- a/src-tauri/src/tests/database_test.rs +++ b/src-tauri/src/tests/database_test.rs @@ -1,12 +1,13 @@ -use crate::fileflow::database::connection::{Connection, QueryResult}; -use crate::fileflow::database::database_actions::export_table; -use crate::fileflow::enumeration::separator::SeparatorType; -use crate::fileflow::stuct::db_config::DbConfig; -use crate::fileflow::stuct::download_config::DownloadConfig; use crate::tests::utils_tests::{ create_test_db, get_test_maridb_config, get_test_mysql_config, get_test_pg_config, get_test_sqlite_config, remove_test_db, }; +use fileflow::fileflow::enums::query_rows::QueryRows; +use fileflow::fileflow::enums::separator_type::SeparatorType; +use fileflow::fileflow::stuct::config::db_config::DatabaseConfig; +use fileflow::fileflow::stuct::config::download_config::DownloadConfig; +use fileflow::fileflow::stuct::database::database_connection::DatabaseConnection; +use fileflow::fileflow::stuct::io::table_exporter::TableExporter; use sqlx::testing::TestTermination; use sqlx::{Error, Row}; use std::path::PathBuf; @@ -14,19 +15,19 @@ use std::path::PathBuf; #[tokio::test] async fn test_get_connection_url() { assert_eq!( - Connection::get_connection_url(&get_test_pg_config()), + DatabaseConnection::get_connection_url(&get_test_pg_config()), "postgres://postgres:password@localhost:5432/test_db" ); assert_eq!( - Connection::get_connection_url(&get_test_maridb_config()), + DatabaseConnection::get_connection_url(&get_test_maridb_config()), "mysql://root:password@localhost:3306/test_db" ); assert_eq!( - Connection::get_connection_url(&get_test_mysql_config()), + DatabaseConnection::get_connection_url(&get_test_mysql_config()), "mysql://root:password@localhost:3306/test_db" ); assert_eq!( - Connection::get_connection_url(&get_test_sqlite_config(String::new())), + DatabaseConnection::get_connection_url(&get_test_sqlite_config(String::new())), "test_db.db" ); } @@ -34,13 +35,13 @@ async fn test_get_connection_url() { #[tokio::test] async fn test_sqlite_connection() { let file_path: String = create_test_db("sqlite_connection"); - let config: DbConfig = get_test_sqlite_config(file_path); - let conn = Connection::connect(&config).await; + let config: DatabaseConfig = get_test_sqlite_config(file_path); + let conn = DatabaseConnection::connect(&config).await; assert!(conn.is_success(), "Failed to connect to the database"); assert!(conn.is_ok()); - let conn: Connection = conn.unwrap(); + let conn: DatabaseConnection = conn.unwrap(); conn.disconnect(); drop(conn); remove_test_db("sqlite_connection").expect("Failed to remove test table"); @@ -49,13 +50,13 @@ async fn test_sqlite_connection() { #[tokio::test] async fn test_query_many_with_result() { let file_path: String = create_test_db("test_query_many_with_result"); - let config: DbConfig = get_test_sqlite_config(file_path); - let conn: Result = Connection::connect(&config).await; + let config: DatabaseConfig = get_test_sqlite_config(file_path); + let conn: Result = DatabaseConnection::connect(&config).await; assert!(conn.is_success(), "Failed to connect to the database"); assert!(conn.is_ok()); - let conn: Connection = conn.unwrap(); + let conn: DatabaseConnection = conn.unwrap(); const SQL_ARRAY: [&str; 3] = [ "DROP TABLE IF EXISTS test_table", @@ -69,14 +70,14 @@ async fn test_query_many_with_result() { } } - let query_result: Result = conn + let query_result: Result = conn .query_many_with_result("SELECT * FROM test_table") .await; assert!(query_result.is_ok()); - let query_result: QueryResult = query_result.unwrap(); + let query_result: QueryRows = query_result.unwrap(); - if let QueryResult::SQLite(rows) = query_result { + if let QueryRows::SQLite(rows) = query_result { assert_eq!(rows.len(), 1); assert_eq!(rows[0].len(), 2); } @@ -89,13 +90,13 @@ async fn test_query_many_with_result() { #[tokio::test] async fn test_download_table() { let file_path: String = create_test_db("test_download_table"); - let config: DbConfig = get_test_sqlite_config(file_path); - let conn: Result = Connection::connect(&config).await; + let config: DatabaseConfig = get_test_sqlite_config(file_path); + let conn: Result = DatabaseConnection::connect(&config).await; assert!(conn.is_success(), "Failed to connect to the database"); assert!(conn.is_ok()); - let conn: Connection = conn.unwrap(); + let conn: DatabaseConnection = conn.unwrap(); const SQL_ARRAY: [&str; 4] = [ "DROP TABLE IF EXISTS test_table", @@ -110,12 +111,12 @@ async fn test_download_table() { } } - let query_result: QueryResult = conn + let query_result: QueryRows = conn .query_many_with_result("SELECT * FROM test_table") .await .unwrap(); - if let QueryResult::SQLite(rows) = query_result { + if let QueryRows::SQLite(rows) = query_result { assert_eq!(rows.len(), 2); assert_eq!(rows[0].len(), 2); assert_eq!(rows[1].len(), 2); @@ -132,7 +133,7 @@ async fn test_download_table() { download_config.location, download_config.table_name_list[0] )); - export_table(&conn, &download_config, &download_config.table_name_list[0]) + TableExporter::export_table(&conn, &download_config, &download_config.table_name_list[0]) .await .expect("Failed to export table"); @@ -151,7 +152,7 @@ async fn test_download_table() { location: "./".into(), }; - export_table(&conn, &download_config, &download_config.table_name_list[0]) + TableExporter::export_table(&conn, &download_config, &download_config.table_name_list[0]) .await .expect("Failed to export table"); @@ -165,7 +166,6 @@ async fn test_download_table() { "Failed to export table" ); - // Clean up std::fs::remove_file(&file_path).expect("Failed to remove file"); remove_test_db("test_download_table").expect("Failed to remove test table"); diff --git a/src-tauri/src/tests/mod.rs b/src-tauri/src/tests/mod.rs index 3824798..65cd0d1 100644 --- a/src-tauri/src/tests/mod.rs +++ b/src-tauri/src/tests/mod.rs @@ -1,12 +1,8 @@ #[cfg(test)] mod action_test; -#[cfg(test)] -mod csv_utils_test; + #[cfg(test)] mod database_test; -#[cfg(test)] -mod string_formater_test; -#[cfg(test)] -mod struct_test; + #[cfg(test)] mod utils_tests; diff --git a/src-tauri/src/tests/string_formater_test.rs b/src-tauri/src/tests/string_formater_test.rs deleted file mode 100644 index c42c35c..0000000 --- a/src-tauri/src/tests/string_formater_test.rs +++ /dev/null @@ -1,66 +0,0 @@ -use crate::fileflow::stuct::string_formater::{StringFormatter}; -use csv::StringRecord; - -#[tokio::test] -async fn test_escape_values() { - let record: StringRecord = StringRecord::from(vec!["value1", "value2"]); - let values: String = StringFormatter::escaped_record(record); - assert_eq!(values, "'value1', 'value2'"); - - let record: StringRecord = StringRecord::from(vec![ - "\"INSERT INTO test_table VALUES (1,2);\"", - "UPDATE test_table SET column1 = 1;", - "DELETE FROM test_table WHERE column1 = 1;", - "SELECT * FROM test_table;", - ]); - let values: String = StringFormatter::escaped_record(record); - assert_eq!(values, "'INSERT INTO test_table VALUES (1,2);', 'UPDATE test_table SET column1 = 1;', 'DELETE FROM test_table WHERE column1 = 1;', 'SELECT * FROM test_table;'"); -} - -#[tokio::test] -async fn test_sanitize_value() { - assert_eq!(StringFormatter::sanitize_value("value1"), "value1"); - assert_eq!(StringFormatter::sanitize_value("'value1'"), "''value1''"); - assert_eq!(StringFormatter::sanitize_value("value1'"), "value1''"); - assert_eq!(StringFormatter::sanitize_value("value1\\value2"), "value1\\\\value2"); - assert_eq!(StringFormatter::sanitize_value("value1\"value2"), "value1value2"); -} - -#[tokio::test] -async fn test_sanitize_column() { - // Standard cases - assert_eq!(StringFormatter::sanitize_column("column1"), "column1"); - assert_eq!(StringFormatter::sanitize_column("'column1'"), "column1"); - assert_eq!(StringFormatter::sanitize_column("column1'"), "column1"); - assert_eq!(StringFormatter::sanitize_column("column1\\column2"), "column1column2"); - assert_eq!(StringFormatter::sanitize_column("column1\"column2"), "column1column2"); - assert_eq!(StringFormatter::sanitize_column("column 1"), "column_1"); - assert_eq!(StringFormatter::sanitize_column("column 1'"), "column_1"); - assert_eq!(StringFormatter::sanitize_column("COlUMn 1'"), "column_1"); - assert_eq!(StringFormatter::sanitize_column("CoDE_DEP"), "code_dep"); - assert_eq!(StringFormatter::sanitize_column(" CoDE_DEP "), "code_dep"); - - // Non-printable characters - assert_eq!(StringFormatter::sanitize_column("\u{feff}code_departement"), "code_departement"); // BOM - assert_eq!(StringFormatter::sanitize_column("code\u{0000}_dep"), "code_dep"); // NULL - assert_eq!(StringFormatter::sanitize_column("code\u{001F}_dep"), "code_dep"); // Unit Separator - assert_eq!(StringFormatter::sanitize_column("code\n_dep"), "code\n_dep"); // newline is preserved (can change if undesired) - assert_eq!(StringFormatter::sanitize_column("code\tdep"), "code\tdep"); // tab is preserved (can change if undesired) - assert_eq!(StringFormatter::sanitize_column("code\rdep"), "codedep"); // carriage return removed -} - -#[tokio::test] -async fn test_get_formated_column_names() { - assert_eq!( - StringFormatter::get_formated_column_names(&vec!["header 1".into(), " header2".into()]), - vec!["header_1", "header2"] - ); - assert_eq!( - StringFormatter::get_formated_column_names(&vec!["header 1".into(), String::new(), "header2".into(), "".into()]), - vec!["header____1", "column_2", "header2", "column_4"] - ); - assert_eq!( - StringFormatter::get_formated_column_names(&vec!["header'\" 1".into(), "header 2''\\".into()]), - vec!["header_1", "header_2"] - ); -} diff --git a/src-tauri/src/tests/struct_test.rs b/src-tauri/src/tests/struct_test.rs deleted file mode 100644 index fbbb6bd..0000000 --- a/src-tauri/src/tests/struct_test.rs +++ /dev/null @@ -1,80 +0,0 @@ -use crate::fileflow::enumeration::database_engine::DatabaseEngine; -use crate::fileflow::enumeration::insertion_type::InsertionType; -use crate::fileflow::enumeration::separator::SeparatorType; -use crate::fileflow::stuct::db_config::DbConfig; -use crate::fileflow::stuct::download_config::DownloadConfig; -use crate::fileflow::stuct::insert_config::InsertConfig; -use crate::fileflow::stuct::save_config::SaveConfig; - -#[tokio::test] -async fn test_db_config() { - let config = DbConfig { - db_driver: DatabaseEngine::Postgres, - username: String::from("username"), - password: String::from("password"), - db_host: String::from("db_host"), - port: String::from("port"), - db_name: String::from("db_name"), - sqlite_file_path: "sqlite_file_path".into(), - }; - - assert_eq!(config.db_driver, DatabaseEngine::Postgres); - assert_eq!(config.username, "username"); - assert_eq!(config.password, "password"); - assert_eq!(config.db_host, "db_host"); - assert_eq!(config.port, "port"); - assert_eq!(config.db_name, "db_name"); - assert_eq!(config.sqlite_file_path, "sqlite_file_path"); -} - -#[tokio::test] -async fn test_insert_config() { - let config = InsertConfig { - file_path: "file_path".into(), - table_name: "table_name".into(), - mode: InsertionType::Fast, - db_driver: DatabaseEngine::Postgres, - }; - - assert_eq!(config.db_driver, DatabaseEngine::Postgres); - assert_eq!(config.file_path, "file_path"); - assert_eq!(config.table_name, "table_name"); - assert_eq!(config.mode, InsertionType::Fast); -} - -#[tokio::test] -async fn test_save_config() { - let config = SaveConfig { - config_name: "config_name".into(), - db_driver: DatabaseEngine::Postgres, - db_host: String::from("db_host"), - port: String::from("port"), - username: String::from("username"), - password: String::from("password"), - db_name: String::from("db_name"), - sqlite_file_path: "sqlite_file_path".into(), - }; - - assert_eq!(config.db_driver, DatabaseEngine::Postgres); - assert_eq!(config.db_host, "db_host"); - assert_eq!(config.port, "port"); - assert_eq!(config.username, "username"); - assert_eq!(config.password, "password"); - assert_eq!(config.db_name, "db_name"); - assert_eq!(config.sqlite_file_path, "sqlite_file_path"); -} - -#[tokio::test] -async fn test_downlopad_config() { - let config = DownloadConfig { - table_name_list: vec!["table1".into(), "table2".into()], - location: "location".into(), - separator: SeparatorType::Comma, - }; - - assert_eq!(config.table_name_list.len(), 2); - assert_eq!(config.table_name_list[0], "table1"); - assert_eq!(config.table_name_list[1], "table2"); - assert_eq!(config.location, "location"); - assert_eq!(config.separator, SeparatorType::Comma); -} diff --git a/src-tauri/src/tests/utils_tests.rs b/src-tauri/src/tests/utils_tests.rs index 613b899..52119c5 100644 --- a/src-tauri/src/tests/utils_tests.rs +++ b/src-tauri/src/tests/utils_tests.rs @@ -1,14 +1,13 @@ -use crate::fileflow::enumeration::database_engine::DatabaseEngine; -use crate::fileflow::stuct::db_config::DbConfig; -use crate::fileflow::stuct::save_config::SaveConfig; use csv::Writer; +use fileflow::fileflow::enums::database_engine::DatabaseEngine; +use fileflow::fileflow::stuct::config::db_config::DatabaseConfig; use std::error::Error; use std::fs::File; use std::path::PathBuf; /// Get a test PostgreSQL configuration -pub fn get_test_pg_config() -> DbConfig { - DbConfig { +pub fn get_test_pg_config() -> DatabaseConfig { + DatabaseConfig { db_driver: DatabaseEngine::Postgres, username: "postgres".into(), password: String::from("password"), @@ -20,8 +19,8 @@ pub fn get_test_pg_config() -> DbConfig { } /// Get a test SQLite configuration -pub fn get_test_sqlite_config(str: String) -> DbConfig { - DbConfig { +pub fn get_test_sqlite_config(str: String) -> DatabaseConfig { + DatabaseConfig { db_driver: DatabaseEngine::SQLite, username: String::new(), password: String::new(), @@ -37,8 +36,8 @@ pub fn get_test_sqlite_config(str: String) -> DbConfig { } /// Get a test MySQL configuration -pub fn get_test_mysql_config() -> DbConfig { - DbConfig { +pub fn get_test_mysql_config() -> DatabaseConfig { + DatabaseConfig { db_driver: DatabaseEngine::MySQL, username: "root".into(), password: "password".into(), @@ -50,8 +49,8 @@ pub fn get_test_mysql_config() -> DbConfig { } /// Get a test MariaDB configuration -pub fn get_test_maridb_config() -> DbConfig { - DbConfig { +pub fn get_test_maridb_config() -> DatabaseConfig { + DatabaseConfig { db_driver: DatabaseEngine::MariaDB, username: String::from("root"), password: String::from("password"), @@ -61,29 +60,6 @@ pub fn get_test_maridb_config() -> DbConfig { sqlite_file_path: String::new(), } } -/// Get a test save configuration -pub fn get_test_save_config(config_name: &str) -> SaveConfig { - SaveConfig { - config_name: config_name.into(), - db_driver: DatabaseEngine::SQLite, - db_host: String::new(), - port: String::new(), - username: String::new(), - password: String::new(), - db_name: "test_db".into(), - sqlite_file_path: String::new(), - } -} - -/// Delete the configuration file with the given file name -pub fn delete_config_file(config_file_name: &str) -> Result<(), Box> { - if !std::path::Path::new(&config_file_name).exists() { - println!("File does not exist"); - Err("Failed to create SQLite file")?; - } - std::fs::remove_file(config_file_name).expect("Failed to remove SQLite file"); - Ok(()) -} /// Create a test SQLite database file if it does not exist and return the file path pub fn create_test_db(db_name: &str) -> String { diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 98387af..c6b2dc1 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -18,7 +18,7 @@ }, "productName": "FileFlow", "mainBinaryName": "FileFlow", - "version" : "1.0.3", + "version": "1.0.4", "identifier": "com.fileflow.app", "plugins": {}, "app": { diff --git a/src/__tests__/DatabaseEngineType.test.tsx b/src/__tests__/DatabaseEngineType.test.tsx new file mode 100644 index 0000000..5754a12 --- /dev/null +++ b/src/__tests__/DatabaseEngineType.test.tsx @@ -0,0 +1,393 @@ +import { DatabaseEngineType, databaseEngineTypeToString, stringToDatabaseEngineType } from "../state/DatabaseEngineType.tsx"; + +describe('DatabaseEngineType Enum', () => { + describe('Enum Values', () => { + it('should have correct string values for each database engine type', () => { + expect(DatabaseEngineType.MYSQL).toBe('MySQL'); + expect(DatabaseEngineType.MARIADB).toBe('MariaDB'); + expect(DatabaseEngineType.POSTGRES).toBe('Postgres'); + expect(DatabaseEngineType.SQLITE).toBe('SQLite'); + expect(DatabaseEngineType.UNDEFINED).toBe('Undefined'); + }); + + it('should have exactly 5 database engine types', () => { + const engineCount = Object.keys(DatabaseEngineType).length; + expect(engineCount).toBe(5); + }); + + it('should contain all expected database engine types', () => { + const expectedTypes = ['MYSQL', 'MARIADB', 'POSTGRES', 'SQLITE', 'UNDEFINED']; + const actualTypes = Object.keys(DatabaseEngineType); + expect(actualTypes).toEqual(expect.arrayContaining(expectedTypes)); + }); + + it('should have unique values for each enum member', () => { + const values = Object.values(DatabaseEngineType); + const uniqueValues = new Set(values); + expect(uniqueValues.size).toBe(values.length); + }); + }); +}); + +describe('databaseEngineTypeToString', () => { + describe('Valid Database Engine Types', () => { + it('should convert MYSQL to "MySQL"', () => { + const result = databaseEngineTypeToString(DatabaseEngineType.MYSQL); + expect(result).toBe('MySQL'); + }); + + it('should convert MARIADB to "MariaDB"', () => { + const result = databaseEngineTypeToString(DatabaseEngineType.MARIADB); + expect(result).toBe('MariaDB'); + }); + + it('should convert POSTGRES to "Postgres"', () => { + const result = databaseEngineTypeToString(DatabaseEngineType.POSTGRES); + expect(result).toBe('Postgres'); + }); + + it('should convert SQLITE to "SQLite"', () => { + const result = databaseEngineTypeToString(DatabaseEngineType.SQLITE); + expect(result).toBe('SQLite'); + }); + + it('should convert UNDEFINED to "Undefined"', () => { + const result = databaseEngineTypeToString(DatabaseEngineType.UNDEFINED); + expect(result).toBe('Undefined'); + }); + }); + + describe('Invalid Database Engine Types', () => { + it('should return "Undefined" for invalid engine type', () => { + const invalidEngine = 'invalid' as DatabaseEngineType; + const result = databaseEngineTypeToString(invalidEngine); + expect(result).toBe(DatabaseEngineType.UNDEFINED); + }); + + it('should return "Undefined" for null value', () => { + const result = databaseEngineTypeToString(null as any); + expect(result).toBe(DatabaseEngineType.UNDEFINED); + }); + + it('should return "Undefined" for undefined value', () => { + const result = databaseEngineTypeToString(undefined as any); + expect(result).toBe(DatabaseEngineType.UNDEFINED); + }); + + it('should return "Undefined" for empty string', () => { + const result = databaseEngineTypeToString('' as any); + expect(result).toBe(DatabaseEngineType.UNDEFINED); + }); + }); + + describe('Type Safety', () => { + it('should return string type', () => { + const result = databaseEngineTypeToString(DatabaseEngineType.MYSQL); + expect(typeof result).toBe('string'); + }); + + it('should handle all enum values without compilation errors', () => { + Object.values(DatabaseEngineType).forEach(engine => { + expect(() => databaseEngineTypeToString(engine)).not.toThrow(); + }); + }); + + it('should return the same value as the enum value for all valid engines', () => { + Object.values(DatabaseEngineType).forEach(engine => { + const result = databaseEngineTypeToString(engine); + expect(result).toBe(engine); + }); + }); + }); +}); + +describe('stringToDatabaseEngineType', () => { + describe('Valid Strings - Lowercase', () => { + it('should convert "mysql" to DatabaseEngineType.MYSQL', () => { + const result = stringToDatabaseEngineType('mysql'); + expect(result).toBe(DatabaseEngineType.MYSQL); + }); + + it('should convert "mariadb" to DatabaseEngineType.MARIADB', () => { + const result = stringToDatabaseEngineType('mariadb'); + expect(result).toBe(DatabaseEngineType.MARIADB); + }); + + it('should convert "postgres" to DatabaseEngineType.POSTGRES', () => { + const result = stringToDatabaseEngineType('postgres'); + expect(result).toBe(DatabaseEngineType.POSTGRES); + }); + + it('should convert "sqlite" to DatabaseEngineType.SQLITE', () => { + const result = stringToDatabaseEngineType('sqlite'); + expect(result).toBe(DatabaseEngineType.SQLITE); + }); + + it('should convert "undefined" to DatabaseEngineType.UNDEFINED', () => { + const result = stringToDatabaseEngineType('undefined'); + expect(result).toBe(DatabaseEngineType.UNDEFINED); + }); + }); + + describe('Valid Strings - Mixed Case', () => { + it('should convert "MySQL" to DatabaseEngineType.MYSQL', () => { + const result = stringToDatabaseEngineType('MySQL'); + expect(result).toBe(DatabaseEngineType.MYSQL); + }); + + it('should convert "MYSQL" to DatabaseEngineType.MYSQL', () => { + const result = stringToDatabaseEngineType('MYSQL'); + expect(result).toBe(DatabaseEngineType.MYSQL); + }); + + it('should convert "MariaDB" to DatabaseEngineType.MARIADB', () => { + const result = stringToDatabaseEngineType('MariaDB'); + expect(result).toBe(DatabaseEngineType.MARIADB); + }); + + it('should convert "MARIADB" to DatabaseEngineType.MARIADB', () => { + const result = stringToDatabaseEngineType('MARIADB'); + expect(result).toBe(DatabaseEngineType.MARIADB); + }); + + it('should convert "PostgreS" to DatabaseEngineType.POSTGRES', () => { + const result = stringToDatabaseEngineType('PostgreS'); + expect(result).toBe(DatabaseEngineType.POSTGRES); + }); + + it('should convert "POSTGRES" to DatabaseEngineType.POSTGRES', () => { + const result = stringToDatabaseEngineType('POSTGRES'); + expect(result).toBe(DatabaseEngineType.POSTGRES); + }); + + it('should convert "SQLite" to DatabaseEngineType.SQLITE', () => { + const result = stringToDatabaseEngineType('SQLite'); + expect(result).toBe(DatabaseEngineType.SQLITE); + }); + + it('should convert "SQLITE" to DatabaseEngineType.SQLITE', () => { + const result = stringToDatabaseEngineType('SQLITE'); + expect(result).toBe(DatabaseEngineType.SQLITE); + }); + + it('should convert "UNDEFINED" to DatabaseEngineType.UNDEFINED', () => { + const result = stringToDatabaseEngineType('UNDEFINED'); + expect(result).toBe(DatabaseEngineType.UNDEFINED); + }); + }); + + describe('Invalid Strings', () => { + it('should throw error for unsupported engine "oracle"', () => { + expect(() => stringToDatabaseEngineType('oracle')).toThrow('Unsupported database engine: oracle'); + }); + + it('should throw error for unsupported engine "mongodb"', () => { + expect(() => stringToDatabaseEngineType('mongodb')).toThrow('Unsupported database engine: mongodb'); + }); + + it('should throw error for unsupported engine "redis"', () => { + expect(() => stringToDatabaseEngineType('redis')).toThrow('Unsupported database engine: redis'); + }); + + it('should throw error for empty string', () => { + expect(() => stringToDatabaseEngineType('')).toThrow('Unsupported database engine: '); + }); + + it('should throw error for numeric input', () => { + expect(() => stringToDatabaseEngineType('123')).toThrow('Unsupported database engine: 123'); + }); + + it('should throw error for special characters', () => { + expect(() => stringToDatabaseEngineType('my-sql')).toThrow('Unsupported database engine: my-sql'); + }); + + it('should throw error for partial matches', () => { + expect(() => stringToDatabaseEngineType('my')).toThrow('Unsupported database engine: my'); + expect(() => stringToDatabaseEngineType('sql')).toThrow('Unsupported database engine: sql'); + }); + }); + + describe('Edge Cases', () => { + it('should be case insensitive but exact match required', () => { + const validInputs = ['mysql', 'MySQL', 'MYSQL', 'mYsQl']; + validInputs.forEach(input => { + expect(() => stringToDatabaseEngineType(input)).not.toThrow(); + expect(stringToDatabaseEngineType(input)).toBe(DatabaseEngineType.MYSQL); + }); + }); + }); + + describe('Error Messages', () => { + it('should include the invalid engine in error message', () => { + const invalidEngine = 'invalidengine'; + expect(() => stringToDatabaseEngineType(invalidEngine)) + .toThrow(`Unsupported database engine: ${invalidEngine}`); + }); + + it('should handle special characters in error message', () => { + const specialChars = ['mysql@', 'postgres#', 'sqlite$', 'mariadb%']; + specialChars.forEach(char => { + expect(() => stringToDatabaseEngineType(char)) + .toThrow(`Unsupported database engine: ${char}`); + }); + }); + }); + + describe('Type Safety', () => { + it('should return DatabaseEngineType enum value', () => { + const result = stringToDatabaseEngineType('mysql'); + expect(Object.values(DatabaseEngineType)).toContain(result); + }); + + it('should handle all valid engine strings', () => { + const validStrings = ['mysql', 'mariadb', 'postgres', 'sqlite', 'undefined']; + validStrings.forEach(engineString => { + expect(() => stringToDatabaseEngineType(engineString)).not.toThrow(); + expect(Object.values(DatabaseEngineType)).toContain(stringToDatabaseEngineType(engineString)); + }); + }); + }); +}); + +describe('Round-trip Conversion Tests', () => { + it('should maintain consistency between enum and string conversion', () => { + Object.values(DatabaseEngineType).forEach(engine => { + const stringRepresentation = databaseEngineTypeToString(engine); + expect(stringRepresentation).toBe(engine); + + // Convert back using the string representation (lowercase) + const backToEnum = stringToDatabaseEngineType(stringRepresentation.toLowerCase()); + expect(backToEnum).toBe(engine); + }); + }); + + it('should handle all database engine strings bidirectionally', () => { + const testCases = [ + { string: 'mysql', enum: DatabaseEngineType.MYSQL, display: 'MySQL' }, + { string: 'mariadb', enum: DatabaseEngineType.MARIADB, display: 'MariaDB' }, + { string: 'postgres', enum: DatabaseEngineType.POSTGRES, display: 'Postgres' }, + { string: 'sqlite', enum: DatabaseEngineType.SQLITE, display: 'SQLite' }, + { string: 'undefined', enum: DatabaseEngineType.UNDEFINED, display: 'Undefined' } + ]; + + testCases.forEach(({ string, enum: engineEnum, display }) => { + // String to enum + expect(stringToDatabaseEngineType(string)).toBe(engineEnum); + + // Enum to string + expect(databaseEngineTypeToString(engineEnum)).toBe(display); + + // Enum value should match display string + expect(engineEnum).toBe(display); + }); + }); + + it('should handle case-insensitive round-trip conversion', () => { + const testInputs = [ + 'mysql', 'MySQL', 'MYSQL', + 'mariadb', 'MariaDB', 'MARIADB', + 'postgres', 'Postgres', 'POSTGRES', + 'sqlite', 'SQLite', 'SQLITE', + 'undefined', 'Undefined', 'UNDEFINED' + ]; + + testInputs.forEach(input => { + const engine = stringToDatabaseEngineType(input); + const backToString = databaseEngineTypeToString(engine); + const finalEngine = stringToDatabaseEngineType(backToString.toLowerCase()); + expect(finalEngine).toBe(engine); + }); + }); +}); + +describe('Edge Cases and Performance', () => { + it('should handle repeated calls efficiently', () => { + const iterations = 1000; + const startTime = performance.now(); + + for (let i = 0; i < iterations; i++) { + databaseEngineTypeToString(DatabaseEngineType.MYSQL); + stringToDatabaseEngineType('mysql'); + } + + const endTime = performance.now(); + const executionTime = endTime - startTime; + + // Should complete within reasonable time (adjust threshold as needed) + expect(executionTime).toBeLessThan(100); // 100ms for 1000 iterations + }); + + it('should handle all enum values in performance test', () => { + const startTime = performance.now(); + + for (let i = 0; i < 100; i++) { + Object.values(DatabaseEngineType).forEach(engine => { + databaseEngineTypeToString(engine); + stringToDatabaseEngineType(engine.toLowerCase()); + }); + } + + const endTime = performance.now(); + const executionTime = endTime - startTime; + + expect(executionTime).toBeLessThan(50); // 50ms for 500 total operations + }); + + it('should maintain enum integrity', () => { + // Ensure enum hasn't been accidentally modified + expect(Object.keys(DatabaseEngineType)).toHaveLength(5); + expect(Object.values(DatabaseEngineType)).toHaveLength(5); + + // Ensure no duplicates + const values = Object.values(DatabaseEngineType); + expect(new Set(values).size).toBe(values.length); + }); + + it('should handle database engine aliases consistently', () => { + // Test common aliases/variations (should all throw errors) + const aliases = [ + 'postgresql', 'pg', 'psql', // PostgreSQL aliases + 'mysql8', 'mysql5.7', // MySQL versions + 'maria', 'mdb', // MariaDB aliases + 'sqlite3', 'db', // SQLite aliases + ]; + + aliases.forEach(alias => { + expect(() => stringToDatabaseEngineType(alias)) + .toThrow(`Unsupported database engine: ${alias}`); + }); + }); +}); + +describe('Integration with Real-World Scenarios', () => { + it('should handle connection string parsing scenarios', () => { + const connectionStrings = [ + { input: 'mysql://localhost:3306/db', expected: DatabaseEngineType.MYSQL }, + { input: 'postgres://localhost:5432/db', expected: DatabaseEngineType.POSTGRES }, + { input: 'sqlite:///path/to/db.sqlite', expected: DatabaseEngineType.SQLITE }, + { input: 'mariadb://localhost:3306/db', expected: DatabaseEngineType.MARIADB } + ]; + + connectionStrings.forEach(({ input, expected }) => { + const engineName = input.split('://')[0]; + if (['mysql', 'postgres', 'sqlite', 'mariadb'].includes(engineName)) { + const result = stringToDatabaseEngineType(engineName); + expect(result).toBe(expected); + } + }); + }); + + it('should handle configuration file scenarios', () => { + const configValues = [ + { config: { engine: 'MySQL' }, expected: DatabaseEngineType.MYSQL }, + { config: { engine: 'postgres' }, expected: DatabaseEngineType.POSTGRES }, + { config: { engine: 'SQLITE' }, expected: DatabaseEngineType.SQLITE }, + { config: { engine: 'mariadb' }, expected: DatabaseEngineType.MARIADB } + ]; + + configValues.forEach(({ config, expected }) => { + const result = stringToDatabaseEngineType(config.engine); + expect(result).toBe(expected); + }); + }); +}); \ No newline at end of file diff --git a/src/__tests__/SeparatorType.test.tsx b/src/__tests__/SeparatorType.test.tsx new file mode 100644 index 0000000..529156d --- /dev/null +++ b/src/__tests__/SeparatorType.test.tsx @@ -0,0 +1,239 @@ +import {charToSeparatorType, SeparatorType, separatorTypeToString} from "../state/SeparatorType"; + +describe('SeparatorType Enum', () => { + describe('Enum Values', () => { + it('should have correct string values for each separator type', () => { + expect(SeparatorType.COMMA).toBe(','); + expect(SeparatorType.SEMICOLON).toBe(';'); + expect(SeparatorType.SPACE).toBe(' '); + expect(SeparatorType.PIPE).toBe('|'); + }); + + it('should have exactly 4 separator types', () => { + const separatorCount = Object.keys(SeparatorType).length; + expect(separatorCount).toBe(4); + }); + + it('should contain all expected separator types', () => { + const expectedTypes = ['COMMA', 'SEMICOLON', 'SPACE', 'PIPE']; + const actualTypes = Object.keys(SeparatorType); + expect(actualTypes).toEqual(expect.arrayContaining(expectedTypes)); + }); + }); +}); + +describe('separatorTypeToString', () => { + describe('Valid Separator Types', () => { + it('should convert COMMA to "Comma"', () => { + const result = separatorTypeToString(SeparatorType.COMMA); + expect(result).toBe('Comma'); + }); + + it('should convert SEMICOLON to "Semicolon"', () => { + const result = separatorTypeToString(SeparatorType.SEMICOLON); + expect(result).toBe('Semicolon'); + }); + + it('should convert SPACE to "Space"', () => { + const result = separatorTypeToString(SeparatorType.SPACE); + expect(result).toBe('Space'); + }); + + it('should convert PIPE to "Pipe"', () => { + const result = separatorTypeToString(SeparatorType.PIPE); + expect(result).toBe('Pipe'); + }); + }); + + describe('Invalid Separator Types', () => { + it('should return "Unknown" for invalid separator type', () => { + // Force TypeScript to accept an invalid value for testing + const invalidSeparator = 'invalid' as SeparatorType; + const result = separatorTypeToString(invalidSeparator); + expect(result).toBe('Unknown'); + }); + + it('should return "Unknown" for null value', () => { + const result = separatorTypeToString(null as any); + expect(result).toBe('Unknown'); + }); + + it('should return "Unknown" for undefined value', () => { + const result = separatorTypeToString(undefined as any); + expect(result).toBe('Unknown'); + }); + }); + + describe('Type Safety', () => { + it('should return string type', () => { + const result = separatorTypeToString(SeparatorType.COMMA); + expect(typeof result).toBe('string'); + }); + + it('should handle all enum values without compilation errors', () => { + // This test ensures all enum values are handled in the switch statement + Object.values(SeparatorType).forEach(separator => { + expect(() => separatorTypeToString(separator)).not.toThrow(); + }); + }); + }); +}); + +describe('charToSeparatorType', () => { + describe('Valid Characters', () => { + it('should convert "," to SeparatorType.COMMA', () => { + const result = charToSeparatorType(','); + expect(result).toBe(SeparatorType.COMMA); + }); + + it('should convert ";" to SeparatorType.SEMICOLON', () => { + const result = charToSeparatorType(';'); + expect(result).toBe(SeparatorType.SEMICOLON); + }); + + it('should convert " " to SeparatorType.SPACE', () => { + const result = charToSeparatorType(' '); + expect(result).toBe(SeparatorType.SPACE); + }); + + it('should convert "|" to SeparatorType.PIPE', () => { + const result = charToSeparatorType('|'); + expect(result).toBe(SeparatorType.PIPE); + }); + }); + + describe('Invalid Characters', () => { + it('should throw error for unsupported character "."', () => { + expect(() => charToSeparatorType('.')).toThrow('Unsupported separator: .'); + }); + + it('should throw error for unsupported character "/"', () => { + expect(() => charToSeparatorType('/')).toThrow('Unsupported separator: /'); + }); + + it('should throw error for unsupported character "\t"', () => { + expect(() => charToSeparatorType('\t')).toThrow('Unsupported separator: \t'); + }); + + it('should throw error for empty string', () => { + expect(() => charToSeparatorType('')).toThrow('Unsupported separator: '); + }); + + it('should throw error for multi-character string', () => { + expect(() => charToSeparatorType(',;')).toThrow('Unsupported separator: ,;'); + }); + + it('should throw error for null value', () => { + expect(() => charToSeparatorType(null as any)).toThrow('Unsupported separator: null'); + }); + + it('should throw error for undefined value', () => { + expect(() => charToSeparatorType(undefined as any)).toThrow('Unsupported separator: undefined'); + }); + + it('should throw error for numeric input', () => { + expect(() => charToSeparatorType('1')).toThrow('Unsupported separator: 1'); + }); + + it('should throw error for alphabetic character', () => { + expect(() => charToSeparatorType('a')).toThrow('Unsupported separator: a'); + }); + }); + + describe('Error Messages', () => { + it('should include the invalid character in error message', () => { + const invalidChar = '#'; + expect(() => charToSeparatorType(invalidChar)) + .toThrow(`Unsupported separator: ${invalidChar}`); + }); + + it('should handle special characters in error message', () => { + const specialChars = ['\n', '\r', '\t', '\\']; + specialChars.forEach(char => { + expect(() => charToSeparatorType(char)) + .toThrow(`Unsupported separator: ${char}`); + }); + }); + }); + + describe('Type Safety', () => { + it('should return SeparatorType enum value', () => { + const result = charToSeparatorType(','); + expect(Object.values(SeparatorType)).toContain(result); + }); + + it('should handle all valid separator characters', () => { + const validChars = [',', ';', ' ', '|']; + validChars.forEach(char => { + expect(() => charToSeparatorType(char)).not.toThrow(); + expect(Object.values(SeparatorType)).toContain(charToSeparatorType(char)); + }); + }); + }); +}); + +describe('Round-trip Conversion Tests', () => { + it('should maintain consistency between enum and string conversion', () => { + // Test that converting enum to string and back to enum works correctly + Object.values(SeparatorType).forEach(separator => { + const stringRepresentation = separatorTypeToString(separator); + expect(stringRepresentation).not.toBe('Unknown'); + + // Convert back using the actual separator character + const backToEnum = charToSeparatorType(separator); + expect(backToEnum).toBe(separator); + }); + }); + + it('should handle all separator characters bidirectionally', () => { + const testCases = [ + {char: ',', enum: SeparatorType.COMMA, name: 'Comma'}, + {char: ';', enum: SeparatorType.SEMICOLON, name: 'Semicolon'}, + {char: ' ', enum: SeparatorType.SPACE, name: 'Space'}, + {char: '|', enum: SeparatorType.PIPE, name: 'Pipe'} + ]; + + testCases.forEach(({char, enum: separatorEnum, name}) => { + // Character to enum + expect(charToSeparatorType(char)).toBe(separatorEnum); + + // Enum to string name + expect(separatorTypeToString(separatorEnum)).toBe(name); + + // Enum value should match character + expect(separatorEnum).toBe(char); + }); + }); +}); + +describe('Edge Cases and Performance', () => { + it('should handle repeated calls efficiently', () => { + // Performance test - should not degrade with repeated calls + const iterations = 1000; + const startTime = performance.now(); + + for (let i = 0; i < iterations; i++) { + separatorTypeToString(SeparatorType.COMMA); + charToSeparatorType(','); + } + + const endTime = performance.now(); + const executionTime = endTime - startTime; + + // Should complete within reasonable time (adjust threshold as needed) + expect(executionTime).toBeLessThan(100); // 100ms for 1000 iterations + }); + + it('should be case-sensitive for character conversion', () => { + // Uppercase versions should throw errors + expect(() => charToSeparatorType('A')).toThrow(); + expect(() => charToSeparatorType('COMMA')).toThrow(); + }); + + it('should handle Unicode characters appropriately', () => { + const unicodeChars = ['๏ผŒ', '๏ผ›', '๏ฝœ', 'ใ€€']; // Similar Unicode characters + unicodeChars.forEach(char => { + expect(() => charToSeparatorType(char)).toThrow(); + }); + }); +}); \ No newline at end of file diff --git a/src/components/fileflowui/extract/download/Download.tsx b/src/components/fileflowui/extract/download/Download.tsx index ddb49b1..b8ad12f 100644 --- a/src/components/fileflowui/extract/download/Download.tsx +++ b/src/components/fileflowui/extract/download/Download.tsx @@ -11,24 +11,26 @@ import ConnectionForm from "@/components/hooks/database/ConnectionForm.tsx"; import {AnimatePresence, motion} from 'framer-motion'; import {DatabaseConfig} from "@/interfaces/DatabaseConfig.tsx"; import {CheckBoxCombo} from "@/components/hooks/component/CheckBoxCombo.tsx"; +import {SeparatorType, separatorTypeToString} from "@/state/SeparatorType.tsx"; +import {DatabaseEngineType} from "@/state/DatabaseEngineType.tsx"; const Download: React.FC = () => { const [dbConfig, setDbConfig] = useState({ config_name: '', - db_driver: '', + db_driver: DatabaseEngineType.UNDEFINED, db_host: '', db_name: '', password: '', port: '', sqlite_file_path: '', username: '', - is_connected: false + isConnected: false }); const [tables, setTables] = useState>([]); const [selectedTables, setSelectedTables] = useState([]); - const [separator, setSeparator] = useState<',' | ';' | ' ' | '|'>(','); + const [separator, setSeparator] = useState(SeparatorType.COMMA); const [absolutePath, setAbsolutePath] = useState(''); const [showLoader, setShowLoader] = useState(false); @@ -58,18 +60,13 @@ const Download: React.FC = () => { throw new Error('Please fill in all required fields'); } - selectedTables.forEach((table) => { - console.log(`Selected table: ${table}`); - } - ); - setShowLoader(true); const download_table_response: string = await invoke('download_table', { config: { table_name_list: selectedTables, location: absolutePath, - separator: getSeparatorName(separator).toLocaleLowerCase() + separator: separatorTypeToString(separator).toLocaleLowerCase() } }); @@ -85,24 +82,12 @@ const Download: React.FC = () => { setShowLoader(false); } - const getSeparatorName = (separator: ',' | ';' | ' ' | '|') => { - switch (separator) { - case ',': - return 'Comma'; - case ';': - return 'Semicolon'; - case ' ': - return 'Space'; - case '|': - return 'Pipe'; - } - } useEffect(() => { - if (dbConfig.is_connected) { + if (dbConfig.isConnected) { retrieveTables().then() } - }, [dbConfig.is_connected]); + }, [dbConfig.isConnected]); return (
@@ -133,7 +118,7 @@ const Download: React.FC = () => {
{/* Tables Display Section */} - {(tables.length > 0 && dbConfig.is_connected) && ( + {(tables.length > 0 && dbConfig.isConnected) && ( { {/* Export Configuration Section */} -
+
@@ -167,10 +152,10 @@ const Download: React.FC = () => { props.updateDbConfigField("username", e.target.value)} -// className="w-full border rounded-md p-2 shadow-sm focus:ring-purple-300 focus:border-purple-500" -// /> -//
-//
-// -// props.updateDbConfigField("password", e.target.value)} -// className="w-full border rounded-md p-2 shadow-sm focus:ring-purple-300 focus:border-purple-500" -// /> -//
-//
-// -// {/* Second Row: URL and Port */} -//
-//
-// -// props.updateDbConfigField("db_host", e.target.value)} -// className="w-full border rounded-md p-2 shadow-sm focus:ring-purple-300 focus:border-purple-500" -// /> -//
-//
-// -// props.updateDbConfigField("port", e.target.value)} -// className="w-full border rounded-md p-2 shadow-sm focus:ring-purple-300 focus:border-purple-500" -// /> -//
-//
-// -// {/* Third Row: Database Name */} -//
-// -// props.updateDbConfigField("db_name", e.target.value)} -// className="w-full border rounded-md p-2 shadow-sm focus:ring-purple-300 focus:border-purple-500" -// /> -//
-//
-// -// {/* Dialog Footer */} -// -//
-// -// -// -//
-// -//
-// -// -//
-// ); -// }; -// -// export default DataBaseDialog; diff --git a/src/components/hooks/database/DatabaseForm.tsx b/src/components/hooks/database/DatabaseForm.tsx index 723a481..250e876 100644 --- a/src/components/hooks/database/DatabaseForm.tsx +++ b/src/components/hooks/database/DatabaseForm.tsx @@ -3,13 +3,22 @@ import SelectDBMS from "@/components/hooks/database/SelectDatabaseEngine.tsx"; import {Input} from "@/components/ui/input.tsx"; import SqliteForm from "@/components/hooks/database/SqliteForm.tsx"; import {DatabaseFormProps} from "@/interfaces/DatabaseFormProps.tsx"; +import {DatabaseEngineType} from "@/state/DatabaseEngineType.tsx"; const DatabaseForm: React.FC = ({dbConfig, updateDbConfigField}) => { const renderForm = () => { - if (dbConfig.db_driver === 'sqlite') { + if (dbConfig.db_driver === DatabaseEngineType.SQLITE) { return ( - + ); } else { return ( diff --git a/src/components/hooks/database/SelectDatabaseConfig.tsx b/src/components/hooks/database/SelectDatabaseConfig.tsx index 40489c9..8cebd2d 100644 --- a/src/components/hooks/database/SelectDatabaseConfig.tsx +++ b/src/components/hooks/database/SelectDatabaseConfig.tsx @@ -3,11 +3,12 @@ import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from "@/c import {Label} from "@/components/ui/label.tsx"; import {DatabaseConfig} from "@/interfaces/DatabaseConfig.tsx"; import {loadConfig, log_error} from "@/components/hooks/utils.tsx"; +import {stringToDatabaseEngineType} from "@/state/DatabaseEngineType.tsx"; interface SelectDatabaseDialogProps { updateConfigName: (name: string) => void; - configName: string; + config_name: string; configNameList: Array; updateDbConfigField: (field: keyof DatabaseConfig, value: DatabaseConfig[keyof DatabaseConfig]) => void; } @@ -15,15 +16,15 @@ interface SelectDatabaseDialogProps { const SelectDatabaseDialog: React.FC = (props) => { - const loadSelectedConfig = async (configName: string): Promise => { + const loadSelectedConfig = async (config_name: string): Promise => { try { - if (!configName) { + if (!config_name) { throw new Error('Please select a configuration'); } - const config: string | boolean = await loadConfig(configName); + const config: string | boolean = await loadConfig(config_name); if (typeof config !== "string") { throw new Error('Failed to load config'); @@ -31,9 +32,14 @@ const SelectDatabaseDialog: React.FC = (props) => { const loadDbConfig: DatabaseConfig = JSON.parse(config) as DatabaseConfig; Object.keys(loadDbConfig).forEach((key) => { - props.updateDbConfigField(key as keyof DatabaseConfig, loadDbConfig[key as keyof DatabaseConfig]); + if (key !== 'db_driver') { + props.updateDbConfigField(key as keyof DatabaseConfig, loadDbConfig[key as keyof DatabaseConfig]); + } else { + props.updateDbConfigField('db_driver', stringToDatabaseEngineType(loadDbConfig.db_driver)); + } }); - props.updateConfigName(configName); + props.updateConfigName(config_name); + } catch (error) { log_error(error); } @@ -46,7 +52,7 @@ const SelectDatabaseDialog: React.FC = (props) => { Choose a database: - loadSelectedConfig(value)} value={props.config_name}> diff --git a/src/components/hooks/database/SelectDatabaseEngine.tsx b/src/components/hooks/database/SelectDatabaseEngine.tsx index 0466e1a..ce40669 100644 --- a/src/components/hooks/database/SelectDatabaseEngine.tsx +++ b/src/components/hooks/database/SelectDatabaseEngine.tsx @@ -1,25 +1,23 @@ import React from 'react'; import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from "@/components/ui/select.tsx"; import {DatabaseConfig} from "@/interfaces/DatabaseConfig.tsx"; +import { + DatabaseEngineType, + databaseEngineTypeToString, + stringToDatabaseEngineType +} from "@/state/DatabaseEngineType.tsx"; interface SelectDatabaseProps { - db_driver: string; + db_driver: DatabaseEngineType; updateDbConfigField: (field: keyof DatabaseConfig, value: DatabaseConfig[keyof DatabaseConfig]) => void; } const SelectDatabaseEngine: React.FC = (props: SelectDatabaseProps) => { - const databaseOptions = { - mysql: "MySQL", - mariadb: "MariaDB", - postgres: "Postgres", - sqlite: "SQLite", - }; - const handledbDriverChange = (value: string) => { const portMap: Record = {mysql: '3306', mariadb: '3306', postgres: '5432'}; props.updateDbConfigField('port', portMap[value] || ''); - props.updateDbConfigField('db_driver', value); + props.updateDbConfigField('db_driver', stringToDatabaseEngineType(value)); }; return ( @@ -29,9 +27,9 @@ const SelectDatabaseEngine: React.FC = (props: SelectDataba - {Object.entries(databaseOptions).map(([value, label]) => ( - - {label} + {Object.entries(DatabaseEngineType).map(([key, value]) => ( + + {value} ))} diff --git a/src/components/hooks/database/SqliteForm.tsx b/src/components/hooks/database/SqliteForm.tsx index b2004aa..91058e6 100644 --- a/src/components/hooks/database/SqliteForm.tsx +++ b/src/components/hooks/database/SqliteForm.tsx @@ -6,11 +6,12 @@ import SelectDBMS from "@/components/hooks/database/SelectDatabaseEngine.tsx"; import * as dialog from "@tauri-apps/plugin-dialog"; import {toast} from "sonner"; import {DatabaseConfig} from "@/interfaces/DatabaseConfig.tsx"; +import {DatabaseEngineType} from "@/state/DatabaseEngineType.tsx"; interface SqliteFormProps { dbConfig: { sqlite_file_path: string; - db_driver: string; + db_driver: DatabaseEngineType; }; updateDbConfigField: (field: keyof DatabaseConfig, value: DatabaseConfig[keyof DatabaseConfig]) => void; } @@ -41,7 +42,7 @@ const SqliteForm: React.FC = ({dbConfig, updateDbConfigField}) onClick={openFileDialog} className="flex items-center gap-2 bg-gradient-to-r from-blue-600 to-purple-600 hover:opacity-90 text-white font-medium py-3 px-5 rounded-lg shadow-md transition duration-300" > - + Select SQLite File { if (!path) return ''; @@ -95,7 +96,7 @@ export const disconnect_from_database = async () => { } // Connect to the database and disconnect if already connected for the user -export const connect_to_database = async (db_driver: string, db_host: string, port: string, username: string, password: string, db_name: string, sqlite_file_path: string): Promise => { +export const connect_to_database = async (db_driver: DatabaseEngineType, db_host: string, port: string, username: string, password: string, db_name: string, sqlite_file_path: string): Promise => { try { const is_connected_response: string | boolean = await is_connected(); @@ -103,15 +104,15 @@ export const connect_to_database = async (db_driver: string, db_host: string, po await disconnect_from_database(); } - if (db_driver !== 'sqlite' && (!db_host || !port || !username)) { + if (db_driver !== DatabaseEngineType.SQLITE && (!db_host || !port || !username)) { throw new Error("Please fill in all the required fields"); - } else if (db_driver === 'sqlite' && sqlite_file_path === '') { + } else if (db_driver === DatabaseEngineType.SQLITE && sqlite_file_path === '') { throw new Error('Please select a SQLite file'); } const response: string | boolean = await invoke('connect_to_database', { config: { - db_driver: db_driver.toLowerCase(), + db_driver: databaseEngineTypeToString(db_driver).toLowerCase(), db_host: db_host, port: port, username: username, @@ -169,3 +170,13 @@ export const requestAllTablesFromConnection = async (): Promise { + try { + return await invoke('get_app_version'); + } catch (error) { + log_error(error); + return "1.0.0"; + } +} diff --git a/src/interfaces/DatabaseConfig.tsx b/src/interfaces/DatabaseConfig.tsx index 96815ea..77c8936 100644 --- a/src/interfaces/DatabaseConfig.tsx +++ b/src/interfaces/DatabaseConfig.tsx @@ -1,11 +1,36 @@ +import {DatabaseEngineType} from "@/state/DatabaseEngineType.tsx"; + +/** + * Database configuration interface + * + * Defines the structure for database connection configuration objects + * used throughout the application for database connectivity. + */ export interface DatabaseConfig { + /** Human-readable name for this database configuration */ config_name: string; - db_driver: string; + + /** Database driver type (e.g., 'mysql', 'postgresql', 'sqlite') */ + db_driver: DatabaseEngineType; + + /** Database server hostname or IP address */ db_host: string; + + /** Name of the database to connect to */ db_name: string; + + /** Database user password */ password: string; + + /** Database server port number */ port: string; - sqlite_file_path: string; + + /** File path for SQLite database (only used when db_driver is 'sqlite') */ + sqlite_file_path?: string; + + /** Database username for authentication */ username: string; - is_connected: boolean; -} + + /** Current connection status */ + isConnected: boolean; +} \ No newline at end of file diff --git a/src/interfaces/DatabaseFormProps.tsx b/src/interfaces/DatabaseFormProps.tsx index f0a12f3..831a53c 100644 --- a/src/interfaces/DatabaseFormProps.tsx +++ b/src/interfaces/DatabaseFormProps.tsx @@ -1,5 +1,9 @@ import {DatabaseConfig} from "@/interfaces/DatabaseConfig.tsx"; +/** + * DatabaseFormProps interface + * This interface defines the props for the DatabaseForm component. + */ export interface DatabaseFormProps { dbConfig: DatabaseConfig; updateDbConfigField: (field: keyof DatabaseConfig, value: DatabaseConfig[keyof DatabaseConfig]) => void; diff --git a/src/state/DatabaseEngineType.tsx b/src/state/DatabaseEngineType.tsx new file mode 100644 index 0000000..a034040 --- /dev/null +++ b/src/state/DatabaseEngineType.tsx @@ -0,0 +1,61 @@ +/** + * DatabaseEngineType enum + * This enum represents the different types of database engines that can be used in the application. + */ +export enum DatabaseEngineType { + MYSQL = 'MySQL', + MARIADB = 'MariaDB', + POSTGRES = 'Postgres', + SQLITE = 'SQLite', + UNDEFINED = 'Undefined', +} + +/** + * Converts a DatabaseEngineType enum value to its string representation. + * @param engine - The DatabaseEngineType enum value to convert. + * @returns The string representation of the database engine type. + */ +export function databaseEngineTypeToString(engine: DatabaseEngineType): string { + switch (engine) { + case DatabaseEngineType.MYSQL: + return 'MySQL'; + case DatabaseEngineType.MARIADB: + return 'MariaDB'; + case DatabaseEngineType.POSTGRES: + return 'Postgres'; + case DatabaseEngineType.SQLITE: + return 'SQLite'; + case DatabaseEngineType.UNDEFINED: + return 'Undefined'; + default: + return DatabaseEngineType.UNDEFINED + } +} + +/** + * Converts a string to its corresponding DatabaseEngineType enum value. + * @param engine - The string representing the database engine type. + * @returns The corresponding DatabaseEngineType enum value. + * @throws Error if the string is not a valid database engine type. + */ +export function stringToDatabaseEngineType(engine: string | null | undefined): DatabaseEngineType { + + if (engine === null || engine === undefined) { + return DatabaseEngineType.UNDEFINED; + } + + switch (engine.trim().toLowerCase()) { + case 'mysql': + return DatabaseEngineType.MYSQL; + case 'mariadb': + return DatabaseEngineType.MARIADB; + case 'postgres': + return DatabaseEngineType.POSTGRES; + case 'sqlite': + return DatabaseEngineType.SQLITE; + case 'undefined': + return DatabaseEngineType.UNDEFINED; + default: + throw new Error(`Unsupported database engine: ${engine}`); + } +} \ No newline at end of file diff --git a/src/state/SeparatorType.tsx b/src/state/SeparatorType.tsx new file mode 100644 index 0000000..6686374 --- /dev/null +++ b/src/state/SeparatorType.tsx @@ -0,0 +1,52 @@ +/** + * Enum representing different types of separators used in data processing. + * Each enum value corresponds to a specific character used to separate values. + */ +export enum SeparatorType { + COMMA = ',', + SEMICOLON = ';', + SPACE = ' ', + PIPE = '|' +} + + +/** + * Converts a SeparatorType enum value to its string representation. + * @param separator - The SeparatorType enum value to convert. + * @returns The string representation of the separator type. + */ +export function separatorTypeToString(separator: SeparatorType): string { + switch (separator) { + case SeparatorType.COMMA: + return 'Comma'; + case SeparatorType.SEMICOLON: + return 'Semicolon'; + case SeparatorType.SPACE: + return 'Space'; + case SeparatorType.PIPE: + return 'Pipe'; + default: + return 'Unknown'; + } +} + +/** + * Converts a character to its corresponding SeparatorType enum value. + * @param separator - The character representing the separator. + * @returns The corresponding SeparatorType enum value. + * @throws Error if the character is not a valid separator. + */ +export function charToSeparatorType(separator: string): SeparatorType { + switch (separator) { + case ',': + return SeparatorType.COMMA; + case ';': + return SeparatorType.SEMICOLON; + case ' ': + return SeparatorType.SPACE; + case '|': + return SeparatorType.PIPE; + default: + throw new Error(`Unsupported separator: ${separator}`); + } +}