diff --git a/.fmf/version b/.fmf/version deleted file mode 100644 index d00491fd..00000000 --- a/.fmf/version +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/.forgejo/workflows/ci.yml b/.forgejo/workflows/ci.yml deleted file mode 100644 index b6742f80..00000000 --- a/.forgejo/workflows/ci.yml +++ /dev/null @@ -1,257 +0,0 @@ ---- -name: Debian Image Builder Frontend CI/CD - -on: - push: - branches: [main, develop] - pull_request: - branches: [main] - workflow_dispatch: - -env: - NODE_VERSION: "18" - DEBIAN_FRONTEND: noninteractive - -jobs: - build-and-test: - name: Build and Test Frontend - runs-on: ubuntu-latest - container: - image: node:18-bullseye - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Node.js environment - run: | - node --version - npm --version - - - name: Install build dependencies - run: | - apt-get update - apt-get install -y \ - build-essential \ - git \ - ca-certificates \ - python3 - - - name: Install Node.js dependencies - run: | - npm ci - npm run build || echo "Build script not found" - - - name: Run tests - run: | - if [ -f package.json ] && npm run test; then - npm test - else - echo "No test script found, skipping tests" - fi - - - name: Run linting - run: | - if [ -f package.json ] && npm run lint; then - npm run lint - else - echo "No lint script found, skipping linting" - fi - - - name: Build production bundle - run: | - if [ -f package.json ] && npm run build; then - npm run build - else - echo "No build script found" - fi - - - name: Upload build artifacts - uses: actions/upload-artifact@v4 - with: - name: frontend-build - path: | - dist/ - build/ - retention-days: 30 - - package: - name: Package Frontend - runs-on: ubuntu-latest - container: - image: node:18-bullseye - needs: build-and-test - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Node.js environment - run: | - node --version - npm --version - - - name: Install build dependencies - run: | - apt-get update - apt-get install -y \ - build-essential \ - devscripts \ - debhelper \ - git \ - ca-certificates \ - python3 - - - name: Install Node.js dependencies - run: npm ci - - - name: Build production bundle - run: | - if [ -f package.json ] && npm run build; then - npm run build - else - echo "No build script found" - fi - - - name: Create debian directory - run: | - mkdir -p debian - cat > debian/control << EOF -Source: debian-image-builder-frontend -Section: web -Priority: optional -Maintainer: Debian Forge Team -Build-Depends: debhelper (>= 13), nodejs, npm, git, ca-certificates -Standards-Version: 4.6.2 - -Package: debian-image-builder-frontend -Architecture: all -Depends: \${misc:Depends}, nodejs, nginx -Description: Debian Image Builder Frontend - Web-based frontend for Debian Image Builder with Cockpit integration. - Provides a user interface for managing image builds, blueprints, - and system configurations through a modern React application. -EOF - - cat > debian/rules << EOF -#!/usr/bin/make -f -%: - dh \$@ - -override_dh_auto_install: - dh_auto_install - mkdir -p debian/debian-image-builder-frontend/usr/share/debian-image-builder-frontend - mkdir -p debian/debian-image-builder-frontend/etc/nginx/sites-available - mkdir -p debian/debian-image-builder-frontend/etc/cockpit - - # Copy built frontend files - if [ -d dist ]; then - cp -r dist/* debian/debian-image-builder-frontend/usr/share/debian-image-builder-frontend/ - elif [ -d build ]; then - cp -r build/* debian/debian-image-builder-frontend/usr/share/debian-image-builder-frontend/ - fi - - # Copy source files for development - cp -r src debian/debian-image-builder-frontend/usr/share/debian-image-builder-frontend/ - cp package.json debian/debian-image-builder-frontend/usr/share/debian-image-builder-frontend/ - - # Create nginx configuration - cat > debian/debian-image-builder-frontend/etc/nginx/sites-available/debian-image-builder-frontend << 'NGINX_EOF' -server { - listen 80; - server_name localhost; - root /usr/share/debian-image-builder-frontend; - index index.html; - - location / { - try_files \$uri \$uri/ /index.html; - } - - location /api/ { - proxy_pass http://localhost:8080/; - proxy_set_header Host \$host; - proxy_set_header X-Real-IP \$remote_addr; - } -} -NGINX_EOF - - # Create cockpit manifest - cat > debian/debian-image-builder-frontend/etc/cockpit/debian-image-builder.manifest << 'COCKPIT_EOF' -{ - "version": 1, - "manifest": { - "name": "debian-image-builder", - "version": "1.0.0", - "title": "Debian Image Builder", - "description": "Build and manage Debian atomic images", - "url": "/usr/share/debian-image-builder-frontend", - "icon": "debian-logo", - "requires": { - "cockpit": ">= 200" - } - } -} -COCKPIT_EOF -EOF - - cat > debian/changelog << EOF -debian-image-builder-frontend (1.0.0-1) unstable; urgency=medium - - * Initial release - * Debian Image Builder Frontend with Cockpit integration - * React-based web interface for image management - - -- Debian Forge Team $(date -R) -EOF - - cat > debian/compat << EOF -13 -EOF - - chmod +x debian/rules - - - name: Build Debian package - run: | - dpkg-buildpackage -us -uc -b - ls -la ../*.deb - - - name: Upload Debian package - uses: actions/upload-artifact@v4 - with: - name: debian-image-builder-frontend-deb - path: ../*.deb - retention-days: 30 - - cockpit-integration: - name: Test Cockpit Integration - runs-on: ubuntu-latest - container: - image: node:18-bullseye - needs: build-and-test - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Node.js environment - run: | - node --version - npm --version - - - name: Install Node.js dependencies - run: npm ci - - - name: Test cockpit integration - run: | - echo "Testing Cockpit integration..." - if [ -d cockpit ]; then - echo "Cockpit directory found:" - ls -la cockpit/ - else - echo "No cockpit directory found" - fi - - if [ -f package.json ]; then - echo "Package.json scripts:" - npm run - fi diff --git a/.forgejo/workflows/ci.yml.disabled b/.forgejo/workflows/ci.yml.disabled deleted file mode 100644 index b6742f80..00000000 --- a/.forgejo/workflows/ci.yml.disabled +++ /dev/null @@ -1,257 +0,0 @@ ---- -name: Debian Image Builder Frontend CI/CD - -on: - push: - branches: [main, develop] - pull_request: - branches: [main] - workflow_dispatch: - -env: - NODE_VERSION: "18" - DEBIAN_FRONTEND: noninteractive - -jobs: - build-and-test: - name: Build and Test Frontend - runs-on: ubuntu-latest - container: - image: node:18-bullseye - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Node.js environment - run: | - node --version - npm --version - - - name: Install build dependencies - run: | - apt-get update - apt-get install -y \ - build-essential \ - git \ - ca-certificates \ - python3 - - - name: Install Node.js dependencies - run: | - npm ci - npm run build || echo "Build script not found" - - - name: Run tests - run: | - if [ -f package.json ] && npm run test; then - npm test - else - echo "No test script found, skipping tests" - fi - - - name: Run linting - run: | - if [ -f package.json ] && npm run lint; then - npm run lint - else - echo "No lint script found, skipping linting" - fi - - - name: Build production bundle - run: | - if [ -f package.json ] && npm run build; then - npm run build - else - echo "No build script found" - fi - - - name: Upload build artifacts - uses: actions/upload-artifact@v4 - with: - name: frontend-build - path: | - dist/ - build/ - retention-days: 30 - - package: - name: Package Frontend - runs-on: ubuntu-latest - container: - image: node:18-bullseye - needs: build-and-test - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Node.js environment - run: | - node --version - npm --version - - - name: Install build dependencies - run: | - apt-get update - apt-get install -y \ - build-essential \ - devscripts \ - debhelper \ - git \ - ca-certificates \ - python3 - - - name: Install Node.js dependencies - run: npm ci - - - name: Build production bundle - run: | - if [ -f package.json ] && npm run build; then - npm run build - else - echo "No build script found" - fi - - - name: Create debian directory - run: | - mkdir -p debian - cat > debian/control << EOF -Source: debian-image-builder-frontend -Section: web -Priority: optional -Maintainer: Debian Forge Team -Build-Depends: debhelper (>= 13), nodejs, npm, git, ca-certificates -Standards-Version: 4.6.2 - -Package: debian-image-builder-frontend -Architecture: all -Depends: \${misc:Depends}, nodejs, nginx -Description: Debian Image Builder Frontend - Web-based frontend for Debian Image Builder with Cockpit integration. - Provides a user interface for managing image builds, blueprints, - and system configurations through a modern React application. -EOF - - cat > debian/rules << EOF -#!/usr/bin/make -f -%: - dh \$@ - -override_dh_auto_install: - dh_auto_install - mkdir -p debian/debian-image-builder-frontend/usr/share/debian-image-builder-frontend - mkdir -p debian/debian-image-builder-frontend/etc/nginx/sites-available - mkdir -p debian/debian-image-builder-frontend/etc/cockpit - - # Copy built frontend files - if [ -d dist ]; then - cp -r dist/* debian/debian-image-builder-frontend/usr/share/debian-image-builder-frontend/ - elif [ -d build ]; then - cp -r build/* debian/debian-image-builder-frontend/usr/share/debian-image-builder-frontend/ - fi - - # Copy source files for development - cp -r src debian/debian-image-builder-frontend/usr/share/debian-image-builder-frontend/ - cp package.json debian/debian-image-builder-frontend/usr/share/debian-image-builder-frontend/ - - # Create nginx configuration - cat > debian/debian-image-builder-frontend/etc/nginx/sites-available/debian-image-builder-frontend << 'NGINX_EOF' -server { - listen 80; - server_name localhost; - root /usr/share/debian-image-builder-frontend; - index index.html; - - location / { - try_files \$uri \$uri/ /index.html; - } - - location /api/ { - proxy_pass http://localhost:8080/; - proxy_set_header Host \$host; - proxy_set_header X-Real-IP \$remote_addr; - } -} -NGINX_EOF - - # Create cockpit manifest - cat > debian/debian-image-builder-frontend/etc/cockpit/debian-image-builder.manifest << 'COCKPIT_EOF' -{ - "version": 1, - "manifest": { - "name": "debian-image-builder", - "version": "1.0.0", - "title": "Debian Image Builder", - "description": "Build and manage Debian atomic images", - "url": "/usr/share/debian-image-builder-frontend", - "icon": "debian-logo", - "requires": { - "cockpit": ">= 200" - } - } -} -COCKPIT_EOF -EOF - - cat > debian/changelog << EOF -debian-image-builder-frontend (1.0.0-1) unstable; urgency=medium - - * Initial release - * Debian Image Builder Frontend with Cockpit integration - * React-based web interface for image management - - -- Debian Forge Team $(date -R) -EOF - - cat > debian/compat << EOF -13 -EOF - - chmod +x debian/rules - - - name: Build Debian package - run: | - dpkg-buildpackage -us -uc -b - ls -la ../*.deb - - - name: Upload Debian package - uses: actions/upload-artifact@v4 - with: - name: debian-image-builder-frontend-deb - path: ../*.deb - retention-days: 30 - - cockpit-integration: - name: Test Cockpit Integration - runs-on: ubuntu-latest - container: - image: node:18-bullseye - needs: build-and-test - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Node.js environment - run: | - node --version - npm --version - - - name: Install Node.js dependencies - run: npm ci - - - name: Test cockpit integration - run: | - echo "Testing Cockpit integration..." - if [ -d cockpit ]; then - echo "Cockpit directory found:" - ls -la cockpit/ - else - echo "No cockpit directory found" - fi - - if [ -f package.json ]; then - echo "Package.json scripts:" - npm run - fi diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9cbc9f7a..0948c85c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -32,7 +32,8 @@ test: - RUNNER: - aws/fedora-41-x86_64 - aws/fedora-42-x86_64 - - aws/rhel-10.1-nightly-x86_64 + - aws/rhel-9.6-nightly-x86_64 + - aws/rhel-10.0-nightly-x86_64 INTERNAL_NETWORK: ["true"] finish: diff --git a/cockpit/cockpit-image-builder.spec b/cockpit/cockpit-image-builder.spec index 2c739e91..213d9af1 100644 --- a/cockpit/cockpit-image-builder.spec +++ b/cockpit/cockpit-image-builder.spec @@ -1,5 +1,5 @@ Name: cockpit-image-builder -Version: 76 +Version: 74 Release: 1%{?dist} Summary: Image builder plugin for Cockpit diff --git a/package-lock.json b/package-lock.json index c8b41f90..19bf3fde 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,13 +49,13 @@ "@testing-library/jest-dom": "6.6.4", "@testing-library/react": "16.3.0", "@testing-library/user-event": "14.6.1", - "@types/node": "24.3.0", + "@types/node": "24.1.0", "@types/react": "18.3.12", "@types/react-dom": "18.3.1", "@types/react-redux": "7.1.34", "@types/uuid": "10.0.0", - "@typescript-eslint/eslint-plugin": "8.40.0", - "@typescript-eslint/parser": "8.40.0", + "@typescript-eslint/eslint-plugin": "8.39.1", + "@typescript-eslint/parser": "8.39.1", "@vitejs/plugin-react": "4.7.0", "@vitest/coverage-v8": "3.2.4", "babel-loader": "10.0.0", @@ -83,7 +83,7 @@ "madge": "8.0.0", "mini-css-extract-plugin": "2.9.2", "moment": "2.30.1", - "msw": "2.10.5", + "msw": "2.10.4", "npm-run-all": "4.1.5", "path-browserify": "1.0.1", "postcss-scss": "4.0.9", @@ -5577,12 +5577,12 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz", - "integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==", + "version": "24.1.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.1.0.tgz", + "integrity": "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==", "license": "MIT", "dependencies": { - "undici-types": "~7.10.0" + "undici-types": "~7.8.0" } }, "node_modules/@types/node-forge": { @@ -5712,17 +5712,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.40.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.40.0.tgz", - "integrity": "sha512-w/EboPlBwnmOBtRbiOvzjD+wdiZdgFeo17lkltrtn7X37vagKKWJABvyfsJXTlHe6XBzugmYgd4A4nW+k8Mixw==", + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.39.1.tgz", + "integrity": "sha512-yYegZ5n3Yr6eOcqgj2nJH8cH/ZZgF+l0YIdKILSDjYFRjgYQMgv/lRjV5Z7Up04b9VYUondt8EPMqg7kTWgJ2g==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.40.0", - "@typescript-eslint/type-utils": "8.40.0", - "@typescript-eslint/utils": "8.40.0", - "@typescript-eslint/visitor-keys": "8.40.0", + "@typescript-eslint/scope-manager": "8.39.1", + "@typescript-eslint/type-utils": "8.39.1", + "@typescript-eslint/utils": "8.39.1", + "@typescript-eslint/visitor-keys": "8.39.1", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -5736,11 +5736,166 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.40.0", + "@typescript-eslint/parser": "^8.39.1", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/project-service": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.39.1.tgz", + "integrity": "sha512-8fZxek3ONTwBu9ptw5nCKqZOSkXshZB7uAxuFF0J/wTMkKydjXCzqqga7MlFMpHi9DoG4BadhmTkITBcg8Aybw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.39.1", + "@typescript-eslint/types": "^8.39.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.39.1.tgz", + "integrity": "sha512-RkBKGBrjgskFGWuyUGz/EtD8AF/GW49S21J8dvMzpJitOF1slLEbbHnNEtAHtnDAnx8qDEdRrULRnWVx27wGBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/visitor-keys": "8.39.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.39.1.tgz", + "integrity": "sha512-ePUPGVtTMR8XMU2Hee8kD0Pu4NDE1CN9Q1sxGSGd/mbOtGZDM7pnhXNJnzW63zk/q+Z54zVzj44HtwXln5CvHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.1.tgz", + "integrity": "sha512-7sPDKQQp+S11laqTrhHqeAbsCfMkwJMrV7oTDvtDds4mEofJYir414bYKUEb8YPUm9QL3U+8f6L6YExSoAGdQw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.39.1.tgz", + "integrity": "sha512-EKkpcPuIux48dddVDXyQBlKdeTPMmALqBUbEk38McWv0qVEZwOpVJBi7ugK5qVNgeuYjGNQxrrnoM/5+TI/BPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.39.1", + "@typescript-eslint/tsconfig-utils": "8.39.1", + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/visitor-keys": "8.39.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.39.1.tgz", + "integrity": "sha512-VF5tZ2XnUSTuiqZFXCZfZs1cgkdd3O/sSYmdo2EpSyDlC86UM/8YytTmKnehOW3TGAlivqTDT6bS87B/GQ/jyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.39.1", + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/typescript-estree": "8.39.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.39.1.tgz", + "integrity": "sha512-W8FQi6kEh2e8zVhQ0eeRnxdvIoOkAp/CPAahcNio6nO9dsIwb9b34z90KOlheoyuVf6LSOEdjlkxSkapNEc+4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.39.1", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.4.tgz", @@ -5751,6 +5906,19 @@ "node": ">= 4" } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ts-api-utils": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", @@ -5765,16 +5933,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.40.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.40.0.tgz", - "integrity": "sha512-jCNyAuXx8dr5KJMkecGmZ8KI61KBUhkCob+SD+C+I5+Y1FWI2Y3QmY4/cxMCC5WAsZqoEtEETVhUiUMIGCf6Bw==", + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.39.1.tgz", + "integrity": "sha512-pUXGCuHnnKw6PyYq93lLRiZm3vjuslIy7tus1lIQTYVK9bL8XBgJnCWm8a0KcTtHC84Yya1Q6rtll+duSMj0dg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.40.0", - "@typescript-eslint/types": "8.40.0", - "@typescript-eslint/typescript-estree": "8.40.0", - "@typescript-eslint/visitor-keys": "8.40.0", + "@typescript-eslint/scope-manager": "8.39.1", + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/typescript-estree": "8.39.1", + "@typescript-eslint/visitor-keys": "8.39.1", "debug": "^4.3.4" }, "engines": { @@ -5789,6 +5957,163 @@ "typescript": ">=4.8.4 <6.0.0" } }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/project-service": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.39.1.tgz", + "integrity": "sha512-8fZxek3ONTwBu9ptw5nCKqZOSkXshZB7uAxuFF0J/wTMkKydjXCzqqga7MlFMpHi9DoG4BadhmTkITBcg8Aybw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.39.1", + "@typescript-eslint/types": "^8.39.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.39.1.tgz", + "integrity": "sha512-RkBKGBrjgskFGWuyUGz/EtD8AF/GW49S21J8dvMzpJitOF1slLEbbHnNEtAHtnDAnx8qDEdRrULRnWVx27wGBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/visitor-keys": "8.39.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.39.1.tgz", + "integrity": "sha512-ePUPGVtTMR8XMU2Hee8kD0Pu4NDE1CN9Q1sxGSGd/mbOtGZDM7pnhXNJnzW63zk/q+Z54zVzj44HtwXln5CvHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.1.tgz", + "integrity": "sha512-7sPDKQQp+S11laqTrhHqeAbsCfMkwJMrV7oTDvtDds4mEofJYir414bYKUEb8YPUm9QL3U+8f6L6YExSoAGdQw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.39.1.tgz", + "integrity": "sha512-EKkpcPuIux48dddVDXyQBlKdeTPMmALqBUbEk38McWv0qVEZwOpVJBi7ugK5qVNgeuYjGNQxrrnoM/5+TI/BPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.39.1", + "@typescript-eslint/tsconfig-utils": "8.39.1", + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/visitor-keys": "8.39.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.39.1.tgz", + "integrity": "sha512-W8FQi6kEh2e8zVhQ0eeRnxdvIoOkAp/CPAahcNio6nO9dsIwb9b34z90KOlheoyuVf6LSOEdjlkxSkapNEc+4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.39.1", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, "node_modules/@typescript-eslint/project-service": { "version": "8.40.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.40.0.tgz", @@ -5847,15 +6172,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.40.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.40.0.tgz", - "integrity": "sha512-eE60cK4KzAc6ZrzlJnflXdrMqOBaugeukWICO2rB0KNvwdIMaEaYiywwHMzA1qFpTxrLhN9Lp4E/00EgWcD3Ow==", + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.39.1.tgz", + "integrity": "sha512-gu9/ahyatyAdQbKeHnhT4R+y3YLtqqHyvkfDxaBYk97EcbfChSJXyaJnIL3ygUv7OuZatePHmQvuH5ru0lnVeA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.40.0", - "@typescript-eslint/typescript-estree": "8.40.0", - "@typescript-eslint/utils": "8.40.0", + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/typescript-estree": "8.39.1", + "@typescript-eslint/utils": "8.39.1", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -5871,6 +6196,174 @@ "typescript": ">=4.8.4 <6.0.0" } }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/project-service": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.39.1.tgz", + "integrity": "sha512-8fZxek3ONTwBu9ptw5nCKqZOSkXshZB7uAxuFF0J/wTMkKydjXCzqqga7MlFMpHi9DoG4BadhmTkITBcg8Aybw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.39.1", + "@typescript-eslint/types": "^8.39.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/scope-manager": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.39.1.tgz", + "integrity": "sha512-RkBKGBrjgskFGWuyUGz/EtD8AF/GW49S21J8dvMzpJitOF1slLEbbHnNEtAHtnDAnx8qDEdRrULRnWVx27wGBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/visitor-keys": "8.39.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.39.1.tgz", + "integrity": "sha512-ePUPGVtTMR8XMU2Hee8kD0Pu4NDE1CN9Q1sxGSGd/mbOtGZDM7pnhXNJnzW63zk/q+Z54zVzj44HtwXln5CvHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.1.tgz", + "integrity": "sha512-7sPDKQQp+S11laqTrhHqeAbsCfMkwJMrV7oTDvtDds4mEofJYir414bYKUEb8YPUm9QL3U+8f6L6YExSoAGdQw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.39.1.tgz", + "integrity": "sha512-EKkpcPuIux48dddVDXyQBlKdeTPMmALqBUbEk38McWv0qVEZwOpVJBi7ugK5qVNgeuYjGNQxrrnoM/5+TI/BPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.39.1", + "@typescript-eslint/tsconfig-utils": "8.39.1", + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/visitor-keys": "8.39.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.39.1.tgz", + "integrity": "sha512-VF5tZ2XnUSTuiqZFXCZfZs1cgkdd3O/sSYmdo2EpSyDlC86UM/8YytTmKnehOW3TGAlivqTDT6bS87B/GQ/jyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.39.1", + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/typescript-estree": "8.39.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.39.1.tgz", + "integrity": "sha512-W8FQi6kEh2e8zVhQ0eeRnxdvIoOkAp/CPAahcNio6nO9dsIwb9b34z90KOlheoyuVf6LSOEdjlkxSkapNEc+4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.39.1", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@typescript-eslint/type-utils/node_modules/ts-api-utils": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", @@ -13730,9 +14223,9 @@ "license": "MIT" }, "node_modules/msw": { - "version": "2.10.5", - "resolved": "https://registry.npmjs.org/msw/-/msw-2.10.5.tgz", - "integrity": "sha512-0EsQCrCI1HbhpBWd89DvmxY6plmvrM96b0sCIztnvcNHQbXn5vqwm1KlXslo6u4wN9LFGLC1WFjjgljcQhe40A==", + "version": "2.10.4", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.10.4.tgz", + "integrity": "sha512-6R1or/qyele7q3RyPwNuvc0IxO8L8/Aim6Sz5ncXEgcWUNxSKE+udriTOWHtpMwmfkLYlacA2y7TIx4cL5lgHA==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -18769,6 +19262,109 @@ "typescript": ">=4.8.4 <6.0.0" } }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.40.0.tgz", + "integrity": "sha512-w/EboPlBwnmOBtRbiOvzjD+wdiZdgFeo17lkltrtn7X37vagKKWJABvyfsJXTlHe6XBzugmYgd4A4nW+k8Mixw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.40.0", + "@typescript-eslint/type-utils": "8.40.0", + "@typescript-eslint/utils": "8.40.0", + "@typescript-eslint/visitor-keys": "8.40.0", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.40.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": { + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.40.0.tgz", + "integrity": "sha512-jCNyAuXx8dr5KJMkecGmZ8KI61KBUhkCob+SD+C+I5+Y1FWI2Y3QmY4/cxMCC5WAsZqoEtEETVhUiUMIGCf6Bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.40.0", + "@typescript-eslint/types": "8.40.0", + "@typescript-eslint/typescript-estree": "8.40.0", + "@typescript-eslint/visitor-keys": "8.40.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/type-utils": { + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.40.0.tgz", + "integrity": "sha512-eE60cK4KzAc6ZrzlJnflXdrMqOBaugeukWICO2rB0KNvwdIMaEaYiywwHMzA1qFpTxrLhN9Lp4E/00EgWcD3Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.40.0", + "@typescript-eslint/typescript-estree": "8.40.0", + "@typescript-eslint/utils": "8.40.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/typescript-eslint/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/typescript-eslint/node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, "node_modules/ufo": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", @@ -18794,9 +19390,9 @@ } }, "node_modules/undici-types": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", - "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { @@ -23381,11 +23977,11 @@ "dev": true }, "@types/node": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz", - "integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==", + "version": "24.1.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.1.0.tgz", + "integrity": "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==", "requires": { - "undici-types": "~7.10.0" + "undici-types": "~7.8.0" } }, "@types/node-forge": { @@ -23499,28 +24095,114 @@ } }, "@typescript-eslint/eslint-plugin": { - "version": "8.40.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.40.0.tgz", - "integrity": "sha512-w/EboPlBwnmOBtRbiOvzjD+wdiZdgFeo17lkltrtn7X37vagKKWJABvyfsJXTlHe6XBzugmYgd4A4nW+k8Mixw==", + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.39.1.tgz", + "integrity": "sha512-yYegZ5n3Yr6eOcqgj2nJH8cH/ZZgF+l0YIdKILSDjYFRjgYQMgv/lRjV5Z7Up04b9VYUondt8EPMqg7kTWgJ2g==", "dev": true, "requires": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.40.0", - "@typescript-eslint/type-utils": "8.40.0", - "@typescript-eslint/utils": "8.40.0", - "@typescript-eslint/visitor-keys": "8.40.0", + "@typescript-eslint/scope-manager": "8.39.1", + "@typescript-eslint/type-utils": "8.39.1", + "@typescript-eslint/utils": "8.39.1", + "@typescript-eslint/visitor-keys": "8.39.1", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "dependencies": { + "@typescript-eslint/project-service": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.39.1.tgz", + "integrity": "sha512-8fZxek3ONTwBu9ptw5nCKqZOSkXshZB7uAxuFF0J/wTMkKydjXCzqqga7MlFMpHi9DoG4BadhmTkITBcg8Aybw==", + "dev": true, + "requires": { + "@typescript-eslint/tsconfig-utils": "^8.39.1", + "@typescript-eslint/types": "^8.39.1", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/scope-manager": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.39.1.tgz", + "integrity": "sha512-RkBKGBrjgskFGWuyUGz/EtD8AF/GW49S21J8dvMzpJitOF1slLEbbHnNEtAHtnDAnx8qDEdRrULRnWVx27wGBw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/visitor-keys": "8.39.1" + } + }, + "@typescript-eslint/tsconfig-utils": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.39.1.tgz", + "integrity": "sha512-ePUPGVtTMR8XMU2Hee8kD0Pu4NDE1CN9Q1sxGSGd/mbOtGZDM7pnhXNJnzW63zk/q+Z54zVzj44HtwXln5CvHA==", + "dev": true, + "requires": {} + }, + "@typescript-eslint/types": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.1.tgz", + "integrity": "sha512-7sPDKQQp+S11laqTrhHqeAbsCfMkwJMrV7oTDvtDds4mEofJYir414bYKUEb8YPUm9QL3U+8f6L6YExSoAGdQw==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.39.1.tgz", + "integrity": "sha512-EKkpcPuIux48dddVDXyQBlKdeTPMmALqBUbEk38McWv0qVEZwOpVJBi7ugK5qVNgeuYjGNQxrrnoM/5+TI/BPw==", + "dev": true, + "requires": { + "@typescript-eslint/project-service": "8.39.1", + "@typescript-eslint/tsconfig-utils": "8.39.1", + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/visitor-keys": "8.39.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + } + }, + "@typescript-eslint/utils": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.39.1.tgz", + "integrity": "sha512-VF5tZ2XnUSTuiqZFXCZfZs1cgkdd3O/sSYmdo2EpSyDlC86UM/8YytTmKnehOW3TGAlivqTDT6bS87B/GQ/jyg==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.39.1", + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/typescript-estree": "8.39.1" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.39.1.tgz", + "integrity": "sha512-W8FQi6kEh2e8zVhQ0eeRnxdvIoOkAp/CPAahcNio6nO9dsIwb9b34z90KOlheoyuVf6LSOEdjlkxSkapNEc+4A==", + "dev": true, + "requires": { + "@typescript-eslint/types": "8.39.1", + "eslint-visitor-keys": "^4.2.1" + } + }, + "eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true + }, "ignore": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.4.tgz", "integrity": "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==", "dev": true }, + "semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true + }, "ts-api-utils": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", @@ -23531,16 +24213,99 @@ } }, "@typescript-eslint/parser": { - "version": "8.40.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.40.0.tgz", - "integrity": "sha512-jCNyAuXx8dr5KJMkecGmZ8KI61KBUhkCob+SD+C+I5+Y1FWI2Y3QmY4/cxMCC5WAsZqoEtEETVhUiUMIGCf6Bw==", + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.39.1.tgz", + "integrity": "sha512-pUXGCuHnnKw6PyYq93lLRiZm3vjuslIy7tus1lIQTYVK9bL8XBgJnCWm8a0KcTtHC84Yya1Q6rtll+duSMj0dg==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "8.40.0", - "@typescript-eslint/types": "8.40.0", - "@typescript-eslint/typescript-estree": "8.40.0", - "@typescript-eslint/visitor-keys": "8.40.0", + "@typescript-eslint/scope-manager": "8.39.1", + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/typescript-estree": "8.39.1", + "@typescript-eslint/visitor-keys": "8.39.1", "debug": "^4.3.4" + }, + "dependencies": { + "@typescript-eslint/project-service": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.39.1.tgz", + "integrity": "sha512-8fZxek3ONTwBu9ptw5nCKqZOSkXshZB7uAxuFF0J/wTMkKydjXCzqqga7MlFMpHi9DoG4BadhmTkITBcg8Aybw==", + "dev": true, + "requires": { + "@typescript-eslint/tsconfig-utils": "^8.39.1", + "@typescript-eslint/types": "^8.39.1", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/scope-manager": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.39.1.tgz", + "integrity": "sha512-RkBKGBrjgskFGWuyUGz/EtD8AF/GW49S21J8dvMzpJitOF1slLEbbHnNEtAHtnDAnx8qDEdRrULRnWVx27wGBw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/visitor-keys": "8.39.1" + } + }, + "@typescript-eslint/tsconfig-utils": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.39.1.tgz", + "integrity": "sha512-ePUPGVtTMR8XMU2Hee8kD0Pu4NDE1CN9Q1sxGSGd/mbOtGZDM7pnhXNJnzW63zk/q+Z54zVzj44HtwXln5CvHA==", + "dev": true, + "requires": {} + }, + "@typescript-eslint/types": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.1.tgz", + "integrity": "sha512-7sPDKQQp+S11laqTrhHqeAbsCfMkwJMrV7oTDvtDds4mEofJYir414bYKUEb8YPUm9QL3U+8f6L6YExSoAGdQw==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.39.1.tgz", + "integrity": "sha512-EKkpcPuIux48dddVDXyQBlKdeTPMmALqBUbEk38McWv0qVEZwOpVJBi7ugK5qVNgeuYjGNQxrrnoM/5+TI/BPw==", + "dev": true, + "requires": { + "@typescript-eslint/project-service": "8.39.1", + "@typescript-eslint/tsconfig-utils": "8.39.1", + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/visitor-keys": "8.39.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.39.1.tgz", + "integrity": "sha512-W8FQi6kEh2e8zVhQ0eeRnxdvIoOkAp/CPAahcNio6nO9dsIwb9b34z90KOlheoyuVf6LSOEdjlkxSkapNEc+4A==", + "dev": true, + "requires": { + "@typescript-eslint/types": "8.39.1", + "eslint-visitor-keys": "^4.2.1" + } + }, + "eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true + }, + "semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true + }, + "ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "requires": {} + } } }, "@typescript-eslint/project-service": { @@ -23572,18 +24337,104 @@ "requires": {} }, "@typescript-eslint/type-utils": { - "version": "8.40.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.40.0.tgz", - "integrity": "sha512-eE60cK4KzAc6ZrzlJnflXdrMqOBaugeukWICO2rB0KNvwdIMaEaYiywwHMzA1qFpTxrLhN9Lp4E/00EgWcD3Ow==", + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.39.1.tgz", + "integrity": "sha512-gu9/ahyatyAdQbKeHnhT4R+y3YLtqqHyvkfDxaBYk97EcbfChSJXyaJnIL3ygUv7OuZatePHmQvuH5ru0lnVeA==", "dev": true, "requires": { - "@typescript-eslint/types": "8.40.0", - "@typescript-eslint/typescript-estree": "8.40.0", - "@typescript-eslint/utils": "8.40.0", + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/typescript-estree": "8.39.1", + "@typescript-eslint/utils": "8.39.1", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "dependencies": { + "@typescript-eslint/project-service": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.39.1.tgz", + "integrity": "sha512-8fZxek3ONTwBu9ptw5nCKqZOSkXshZB7uAxuFF0J/wTMkKydjXCzqqga7MlFMpHi9DoG4BadhmTkITBcg8Aybw==", + "dev": true, + "requires": { + "@typescript-eslint/tsconfig-utils": "^8.39.1", + "@typescript-eslint/types": "^8.39.1", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/scope-manager": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.39.1.tgz", + "integrity": "sha512-RkBKGBrjgskFGWuyUGz/EtD8AF/GW49S21J8dvMzpJitOF1slLEbbHnNEtAHtnDAnx8qDEdRrULRnWVx27wGBw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/visitor-keys": "8.39.1" + } + }, + "@typescript-eslint/tsconfig-utils": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.39.1.tgz", + "integrity": "sha512-ePUPGVtTMR8XMU2Hee8kD0Pu4NDE1CN9Q1sxGSGd/mbOtGZDM7pnhXNJnzW63zk/q+Z54zVzj44HtwXln5CvHA==", + "dev": true, + "requires": {} + }, + "@typescript-eslint/types": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.1.tgz", + "integrity": "sha512-7sPDKQQp+S11laqTrhHqeAbsCfMkwJMrV7oTDvtDds4mEofJYir414bYKUEb8YPUm9QL3U+8f6L6YExSoAGdQw==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.39.1.tgz", + "integrity": "sha512-EKkpcPuIux48dddVDXyQBlKdeTPMmALqBUbEk38McWv0qVEZwOpVJBi7ugK5qVNgeuYjGNQxrrnoM/5+TI/BPw==", + "dev": true, + "requires": { + "@typescript-eslint/project-service": "8.39.1", + "@typescript-eslint/tsconfig-utils": "8.39.1", + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/visitor-keys": "8.39.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + } + }, + "@typescript-eslint/utils": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.39.1.tgz", + "integrity": "sha512-VF5tZ2XnUSTuiqZFXCZfZs1cgkdd3O/sSYmdo2EpSyDlC86UM/8YytTmKnehOW3TGAlivqTDT6bS87B/GQ/jyg==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.39.1", + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/typescript-estree": "8.39.1" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.39.1.tgz", + "integrity": "sha512-W8FQi6kEh2e8zVhQ0eeRnxdvIoOkAp/CPAahcNio6nO9dsIwb9b34z90KOlheoyuVf6LSOEdjlkxSkapNEc+4A==", + "dev": true, + "requires": { + "@typescript-eslint/types": "8.39.1", + "eslint-visitor-keys": "^4.2.1" + } + }, + "eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true + }, + "semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true + }, "ts-api-utils": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", @@ -28622,9 +29473,9 @@ "version": "2.1.3" }, "msw": { - "version": "2.10.5", - "resolved": "https://registry.npmjs.org/msw/-/msw-2.10.5.tgz", - "integrity": "sha512-0EsQCrCI1HbhpBWd89DvmxY6plmvrM96b0sCIztnvcNHQbXn5vqwm1KlXslo6u4wN9LFGLC1WFjjgljcQhe40A==", + "version": "2.10.4", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.10.4.tgz", + "integrity": "sha512-6R1or/qyele7q3RyPwNuvc0IxO8L8/Aim6Sz5ncXEgcWUNxSKE+udriTOWHtpMwmfkLYlacA2y7TIx4cL5lgHA==", "dev": true, "requires": { "@bundled-es-modules/cookie": "^2.0.1", @@ -31820,6 +32671,64 @@ "@typescript-eslint/parser": "8.40.0", "@typescript-eslint/typescript-estree": "8.40.0", "@typescript-eslint/utils": "8.40.0" + }, + "dependencies": { + "@typescript-eslint/eslint-plugin": { + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.40.0.tgz", + "integrity": "sha512-w/EboPlBwnmOBtRbiOvzjD+wdiZdgFeo17lkltrtn7X37vagKKWJABvyfsJXTlHe6XBzugmYgd4A4nW+k8Mixw==", + "dev": true, + "requires": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.40.0", + "@typescript-eslint/type-utils": "8.40.0", + "@typescript-eslint/utils": "8.40.0", + "@typescript-eslint/visitor-keys": "8.40.0", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + } + }, + "@typescript-eslint/parser": { + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.40.0.tgz", + "integrity": "sha512-jCNyAuXx8dr5KJMkecGmZ8KI61KBUhkCob+SD+C+I5+Y1FWI2Y3QmY4/cxMCC5WAsZqoEtEETVhUiUMIGCf6Bw==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "8.40.0", + "@typescript-eslint/types": "8.40.0", + "@typescript-eslint/typescript-estree": "8.40.0", + "@typescript-eslint/visitor-keys": "8.40.0", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/type-utils": { + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.40.0.tgz", + "integrity": "sha512-eE60cK4KzAc6ZrzlJnflXdrMqOBaugeukWICO2rB0KNvwdIMaEaYiywwHMzA1qFpTxrLhN9Lp4E/00EgWcD3Ow==", + "dev": true, + "requires": { + "@typescript-eslint/types": "8.40.0", + "@typescript-eslint/typescript-estree": "8.40.0", + "@typescript-eslint/utils": "8.40.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + } + }, + "ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true + }, + "ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "requires": {} + } } }, "ufo": { @@ -31839,9 +32748,9 @@ } }, "undici-types": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", - "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==" + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==" }, "unicode-canonical-property-names-ecmascript": { "version": "2.0.1", diff --git a/package.json b/package.json index a262b49f..171dba95 100644 --- a/package.json +++ b/package.json @@ -47,13 +47,13 @@ "@testing-library/jest-dom": "6.6.4", "@testing-library/react": "16.3.0", "@testing-library/user-event": "14.6.1", - "@types/node": "24.3.0", + "@types/node": "24.1.0", "@types/react": "18.3.12", "@types/react-dom": "18.3.1", "@types/react-redux": "7.1.34", "@types/uuid": "10.0.0", - "@typescript-eslint/eslint-plugin": "8.40.0", - "@typescript-eslint/parser": "8.40.0", + "@typescript-eslint/eslint-plugin": "8.39.1", + "@typescript-eslint/parser": "8.39.1", "@vitejs/plugin-react": "4.7.0", "@vitest/coverage-v8": "3.2.4", "babel-loader": "10.0.0", @@ -81,7 +81,7 @@ "madge": "8.0.0", "mini-css-extract-plugin": "2.9.2", "moment": "2.30.1", - "msw": "2.10.5", + "msw": "2.10.4", "npm-run-all": "4.1.5", "path-browserify": "1.0.1", "postcss-scss": "4.0.9", diff --git a/packit.yaml b/packit.yaml index 2cbf432b..bd019940 100644 --- a/packit.yaml +++ b/packit.yaml @@ -16,15 +16,6 @@ srpm_build_deps: - npm jobs: - - job: tests - identifier: self - trigger: pull_request - tmt_plan: /plans/all/main - targets: - - centos-stream-10 - - fedora-41 - - fedora-42 - - job: copr_build trigger: pull_request targets: &build_targets @@ -33,6 +24,7 @@ jobs: - centos-stream-10 - centos-stream-10-aarch64 - fedora-all + - fedora-all-aarch64 - job: copr_build trigger: commit diff --git a/plans/all.fmf b/plans/all.fmf deleted file mode 100644 index 66deae89..00000000 --- a/plans/all.fmf +++ /dev/null @@ -1,14 +0,0 @@ -summary: cockpit-image-builder playwright tests -prepare: - how: install - package: - - cockpit-image-builder -discover: - how: fmf -execute: - how: tmt - -/main: - summary: playwright tests - discover+: - test: /schutzbot/playwright diff --git a/playwright/helpers/helpers.ts b/playwright/helpers/helpers.ts index 9b086de7..33291ac0 100644 --- a/playwright/helpers/helpers.ts +++ b/playwright/helpers/helpers.ts @@ -1,6 +1,3 @@ -import { execSync } from 'child_process'; -import { readFileSync } from 'node:fs'; - import { expect, type Page } from '@playwright/test'; export const togglePreview = async (page: Page) => { @@ -45,43 +42,3 @@ export const closePopupsIfExist = async (page: Page) => { }); } }; - -// copied over from constants -const ON_PREM_RELEASES = new Map([ - ['centos-10', 'CentOS Stream 10'], - ['fedora-41', 'Fedora Linux 41'], - ['fedora-42', 'Fedora Linux 42'], - ['rhel-10', 'Red Hat Enterprise Linux (RHEL) 10'], -]); - -/* eslint-disable @typescript-eslint/no-explicit-any */ -export const getHostDistroName = (): string => { - const osRelData = readFileSync('/etc/os-release'); - const lines = osRelData - .toString('utf-8') - .split('\n') - .filter((l) => l !== ''); - const osRel = {}; - - for (const l of lines) { - const lineData = l.split('='); - (osRel as any)[lineData[0]] = lineData[1].replace(/"/g, ''); - } - - // strip minor version from rhel - const distro = ON_PREM_RELEASES.get( - `${(osRel as any)['ID']}-${(osRel as any)['VERSION_ID'].split('.')[0]}`, - ); - - if (distro === undefined) { - /* eslint-disable no-console */ - console.error('getHostDistroName failed, os-release config:', osRel); - throw new Error('getHostDistroName failed, distro undefined'); - } - - return distro; -}; - -export const getHostArch = (): string => { - return execSync('uname -m').toString('utf-8').replace(/\s/g, ''); -}; diff --git a/playwright/helpers/navHelpers.ts b/playwright/helpers/navHelpers.ts index 0fa6fe1d..40c1007c 100644 --- a/playwright/helpers/navHelpers.ts +++ b/playwright/helpers/navHelpers.ts @@ -1,6 +1,6 @@ -import { expect, FrameLocator, Page } from '@playwright/test'; +import type { FrameLocator, Page } from '@playwright/test'; -import { getHostArch, getHostDistroName, isHosted } from './helpers'; +import { isHosted } from './helpers'; /** * Opens the wizard, fills out the "Image Output" step, and navigates to the optional steps @@ -8,13 +8,6 @@ import { getHostArch, getHostDistroName, isHosted } from './helpers'; */ export const navigateToOptionalSteps = async (page: Page | FrameLocator) => { await page.getByRole('button', { name: 'Create image blueprint' }).click(); - if (!isHosted()) { - // wait until the distro and architecture aligns with the host - await expect(page.getByTestId('release_select')).toHaveText( - getHostDistroName(), - ); - await expect(page.getByTestId('arch_select')).toHaveText(getHostArch()); - } await page.getByRole('checkbox', { name: 'Virtualization' }).click(); await page.getByRole('button', { name: 'Next' }).click(); }; diff --git a/playwright/test.spec.ts b/playwright/test.spec.ts index 593247ae..6538c09e 100644 --- a/playwright/test.spec.ts +++ b/playwright/test.spec.ts @@ -92,12 +92,7 @@ test.describe.serial('test', () => { await frame.getByRole('button', { name: 'Create blueprint' }).click(); await expect( - frame.locator('.pf-v6-c-card__title-text').getByText( - // if the name is too long, the blueprint card will have a truncated name. - blueprintName.length > 24 - ? blueprintName.slice(0, 24) + '...' - : blueprintName, - ), + frame.locator('.pf-v6-c-card__title-text').getByText(blueprintName), ).toBeVisible(); }); diff --git a/schutzbot/playwright.fmf b/schutzbot/playwright.fmf deleted file mode 100644 index bbc5721a..00000000 --- a/schutzbot/playwright.fmf +++ /dev/null @@ -1,8 +0,0 @@ -summary: run playwright tests -test: ./playwright_tests.sh -require: - - cockpit-image-builder - - podman - - nodejs - - nodejs-npm -duration: 30m diff --git a/schutzbot/playwright_tests.sh b/schutzbot/playwright_tests.sh index 052faed2..d6a5a87f 100755 --- a/schutzbot/playwright_tests.sh +++ b/schutzbot/playwright_tests.sh @@ -1,16 +1,16 @@ #!/bin/bash set -euo pipefail -TMT_SOURCE_DIR=${TMT_SOURCE_DIR:-} -if [ -n "$TMT_SOURCE_DIR" ]; then - # Move to the directory with sources - cd "${TMT_SOURCE_DIR}/cockpit-image-builder" - npm ci -elif [ "${CI:-}" != "true" ]; then - # packit drops us into the schutzbot directory - cd ../ - npm ci -fi +# As playwright isn't supported on fedora/el, install dependencies +# beforehand. +sudo dnf install -y \ + alsa-lib \ + libXrandr-devel \ + libXdamage-devel \ + libXcomposite-devel \ + at-spi2-atk-devel \ + cups \ + atk sudo systemctl enable --now cockpit.socket @@ -19,13 +19,10 @@ sudo usermod -aG wheel admin echo "admin ALL=(ALL:ALL) NOPASSWD: ALL" | sudo tee "/etc/sudoers.d/admin-nopasswd" function upload_artifacts { - if [ -n "${TMT_TEST_DATA:-}" ]; then - mv playwright-report "$TMT_TEST_DATA"/playwright-report - else - USER="$(whoami)" - sudo chown -R "$USER:$USER" playwright-report - mv playwright-report /tmp/artifacts/ - fi + mkdir -p /tmp/artifacts/extra-screenshots + USER="$(whoami)" + sudo chown -R "$USER:$USER" playwright-report + mv playwright-report /tmp/artifacts/ } trap upload_artifacts EXIT @@ -76,12 +73,11 @@ sudo podman run \ -e "CI=true" \ -e "PLAYWRIGHT_USER=admin" \ -e "PLAYWRIGHT_PASSWORD=foobar" \ - -e "CURRENTS_PROJECT_ID=${CURRENTS_PROJECT_ID:-}" \ - -e "CURRENTS_RECORD_KEY=${CURRENTS_RECORD_KEY:-}" \ + -e "CURRENTS_PROJECT_ID=$CURRENTS_PROJECT_ID" \ + -e "CURRENTS_RECORD_KEY=$CURRENTS_RECORD_KEY" \ --net=host \ -v "$PWD:/tests" \ -v '/etc:/etc' \ - -v '/etc/os-release:/etc/os-release' \ --privileged \ --rm \ --init \ diff --git a/schutzbot/terraform b/schutzbot/terraform index a3ddc921..9a64fd4c 100644 --- a/schutzbot/terraform +++ b/schutzbot/terraform @@ -1 +1 @@ -cf0a810fd3b75fa27139746c4dfe72222e13dcba +7b4735d287dd0950e0a6f47dde65b62b0f239da1 diff --git a/src/Components/Blueprints/BlueprintCard.tsx b/src/Components/Blueprints/BlueprintCard.tsx index 8858398c..7bb47aad 100644 --- a/src/Components/Blueprints/BlueprintCard.tsx +++ b/src/Components/Blueprints/BlueprintCard.tsx @@ -8,6 +8,7 @@ import { CardHeader, CardTitle, Spinner, + Truncate, } from '@patternfly/react-core'; import { useDeleteBPWithNotification as useDeleteBlueprintMutation } from '../../Hooks'; @@ -50,21 +51,11 @@ const BlueprintCard = ({ blueprint }: blueprintProps) => { onChange: () => dispatch(setBlueprintId(blueprint.id)), }} > - + {isLoading && blueprint.id === selectedBlueprintId && ( )} - { - // NOTE: This might be an issue with the pf6 truncate component. - // Since we're not really using the popover, we can just - // use vanilla js to truncate the string rather than use the - // Truncate component. We can match the behaviour of the component - // by also splitting on 24 characters. - // https://github.com/patternfly/patternfly-react/issues/11964 - blueprint.name && blueprint.name.length > 24 - ? blueprint.name.slice(0, 24) + '...' - : blueprint.name - } + {blueprint.description} diff --git a/src/Components/Blueprints/BlueprintsSideBar.tsx b/src/Components/Blueprints/BlueprintsSideBar.tsx index 9422f8e3..6c1dd852 100644 --- a/src/Components/Blueprints/BlueprintsSideBar.tsx +++ b/src/Components/Blueprints/BlueprintsSideBar.tsx @@ -1,4 +1,4 @@ -import React, { useCallback } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import { Bullseye, @@ -17,6 +17,7 @@ import { import { PlusCircleIcon, SearchIcon } from '@patternfly/react-icons'; import { SVGIconProps } from '@patternfly/react-icons/dist/esm/createIcon'; import useChrome from '@redhat-cloud-services/frontend-components/useChrome'; +import { ChromeUser } from '@redhat-cloud-services/types'; import debounce from 'lodash/debounce'; import { Link } from 'react-router-dom'; @@ -28,7 +29,6 @@ import { PAGINATION_LIMIT, PAGINATION_OFFSET, } from '../../constants'; -import { useGetUser } from '../../Hooks'; import { useGetBlueprintsQuery } from '../../store/backendApi'; import { selectBlueprintSearchInput, @@ -60,8 +60,8 @@ type emptyBlueprintStateProps = { }; const BlueprintsSidebar = () => { + const [userData, setUserData] = useState(undefined); const { analytics, auth } = useChrome(); - const { userData } = useGetUser(auth); const selectedBlueprintId = useAppSelector(selectSelectedBlueprintId); const blueprintSearchInput = useAppSelector(selectBlueprintSearchInput); @@ -73,6 +73,16 @@ const BlueprintsSidebar = () => { offset: blueprintsOffset, }; + useEffect(() => { + (async () => { + const data = await auth.getUser(); + setUserData(data); + })(); + // This useEffect hook should run *only* on mount and therefore has an empty + // dependency array. eslint's exhaustive-deps rule does not support this use. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + if (blueprintSearchInput) { searchParams.search = blueprintSearchInput; } diff --git a/src/Components/Blueprints/BuildImagesButton.tsx b/src/Components/Blueprints/BuildImagesButton.tsx index 431b765b..6304d98a 100644 --- a/src/Components/Blueprints/BuildImagesButton.tsx +++ b/src/Components/Blueprints/BuildImagesButton.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { Button, @@ -16,13 +16,11 @@ import { } from '@patternfly/react-core'; import { MenuToggleElement } from '@patternfly/react-core/dist/esm/components/MenuToggle/MenuToggle'; import useChrome from '@redhat-cloud-services/frontend-components/useChrome'; +import { ChromeUser } from '@redhat-cloud-services/types'; import { skipToken } from '@reduxjs/toolkit/query'; import { AMPLITUDE_MODULE_NAME, targetOptions } from '../../constants'; -import { - useComposeBPWithNotification as useComposeBlueprintMutation, - useGetUser, -} from '../../Hooks'; +import { useComposeBPWithNotification as useComposeBlueprintMutation } from '../../Hooks'; import { useGetBlueprintQuery } from '../../store/backendApi'; import { selectSelectedBlueprintId } from '../../store/BlueprintSlice'; import { useAppSelector } from '../../store/hooks'; @@ -39,7 +37,18 @@ export const BuildImagesButton = ({ children }: BuildImagesButtonPropTypes) => { const { trigger: buildBlueprint, isLoading: imageBuildLoading } = useComposeBlueprintMutation(); const { analytics, auth } = useChrome(); - const { userData } = useGetUser(auth); + + const [userData, setUserData] = useState(undefined); + + useEffect(() => { + (async () => { + const data = await auth.getUser(); + setUserData(data); + })(); + // This useEffect hook should run *only* on mount and therefore has an empty + // dependency array. eslint's exhaustive-deps rule does not support this use. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); const onBuildHandler = async () => { if (selectedBlueprintId) { diff --git a/src/Components/Blueprints/DeleteBlueprintModal.tsx b/src/Components/Blueprints/DeleteBlueprintModal.tsx index 230deeed..afd209d5 100644 --- a/src/Components/Blueprints/DeleteBlueprintModal.tsx +++ b/src/Components/Blueprints/DeleteBlueprintModal.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { Button, @@ -9,16 +9,14 @@ import { ModalVariant, } from '@patternfly/react-core'; import useChrome from '@redhat-cloud-services/frontend-components/useChrome'; +import { ChromeUser } from '@redhat-cloud-services/types'; import { AMPLITUDE_MODULE_NAME, PAGINATION_LIMIT, PAGINATION_OFFSET, } from '../../constants'; -import { - useDeleteBPWithNotification as useDeleteBlueprintMutation, - useGetUser, -} from '../../Hooks'; +import { useDeleteBPWithNotification as useDeleteBlueprintMutation } from '../../Hooks'; import { backendApi, useGetBlueprintsQuery } from '../../store/backendApi'; import { selectBlueprintSearchInput, @@ -44,7 +42,17 @@ export const DeleteBlueprintModal: React.FunctionComponent< const blueprintsLimit = useAppSelector(selectLimit) || PAGINATION_LIMIT; const dispatch = useAppDispatch(); const { analytics, auth } = useChrome(); - const { userData } = useGetUser(auth); + const [userData, setUserData] = useState(undefined); + + useEffect(() => { + (async () => { + const data = await auth.getUser(); + setUserData(data); + })(); + // This useEffect hook should run *only* on mount and therefore has an empty + // dependency array. eslint's exhaustive-deps rule does not support this use. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); const searchParams: GetBlueprintsApiArg = { limit: blueprintsLimit, diff --git a/src/Components/CreateImageWizard/steps/Packages/Packages.tsx b/src/Components/CreateImageWizard/steps/Packages/Packages.tsx index 8106fd4b..f067d16c 100644 --- a/src/Components/CreateImageWizard/steps/Packages/Packages.tsx +++ b/src/Components/CreateImageWizard/steps/Packages/Packages.tsx @@ -50,7 +50,6 @@ import { Thead, Tr, } from '@patternfly/react-table'; -import { orderBy } from 'lodash'; import { useDispatch } from 'react-redux'; import CustomHelperText from './components/CustomHelperText'; @@ -67,6 +66,7 @@ import { } from '../../../../constants'; import { useGetArchitecturesQuery } from '../../../../store/backendApi'; import { + ApiPackageSourcesResponse, ApiRepositoryResponseRead, ApiSearchRpmResponse, useCreateRepositoryMutation, @@ -700,7 +700,7 @@ const Packages = () => { ); } - let unpackedData: IBPackageWithRepositoryInfo[] = + const unpackedData: IBPackageWithRepositoryInfo[] = combinedPackageData.flatMap((item) => { // Spread modules into separate rows by application stream if (item.sources) { @@ -724,16 +724,13 @@ const Packages = () => { }); // group by name, but sort by application stream in descending order - unpackedData = orderBy( - unpackedData, - [ - 'name', - (pkg) => pkg.stream || '', - (pkg) => pkg.repository || '', - (pkg) => pkg.module_name || '', - ], - ['asc', 'desc', 'asc', 'asc'], - ); + unpackedData.sort((a, b) => { + if (a.name === b.name) { + return (b.stream ?? '').localeCompare(a.stream ?? ''); + } else { + return a.name.localeCompare(b.name); + } + }); if (toggleSelected === 'toggle-available') { if (activeTabKey === Repos.INCLUDED) { @@ -869,6 +866,8 @@ const Packages = () => { dispatch(addPackage(pkg)); if (pkg.type === 'module') { setActiveStream(pkg.stream || ''); + setActiveSortIndex(2); + setPage(1); dispatch( addModule({ name: pkg.module_name || '', @@ -994,18 +993,7 @@ const Packages = () => { } }; - const getPackageUniqueKey = (pkg: IBPackageWithRepositoryInfo): string => { - try { - if (!pkg || !pkg.name) { - return `invalid_${Date.now()}`; - } - return `${pkg.name}_${pkg.stream || 'none'}_${pkg.module_name || 'none'}_${pkg.repository || 'unknown'}`; - } catch { - return `error_${Date.now()}`; - } - }; - - const initialExpandedPkgs: string[] = []; + const initialExpandedPkgs: IBPackageWithRepositoryInfo[] = []; const [expandedPkgs, setExpandedPkgs] = useState(initialExpandedPkgs); const setPkgExpanded = ( @@ -1013,13 +1001,12 @@ const Packages = () => { isExpanding: boolean, ) => setExpandedPkgs((prevExpanded) => { - const pkgKey = getPackageUniqueKey(pkg); - const otherExpandedPkgs = prevExpanded.filter((key) => key !== pkgKey); - return isExpanding ? [...otherExpandedPkgs, pkgKey] : otherExpandedPkgs; + const otherExpandedPkgs = prevExpanded.filter((p) => p.name !== pkg.name); + return isExpanding ? [...otherExpandedPkgs, pkg] : otherExpandedPkgs; }); const isPkgExpanded = (pkg: IBPackageWithRepositoryInfo) => - expandedPkgs.includes(getPackageUniqueKey(pkg)); + expandedPkgs.includes(pkg); const initialExpandedGroups: GroupWithRepositoryInfo['name'][] = []; const [expandedGroups, setExpandedGroups] = useState(initialExpandedGroups); @@ -1043,37 +1030,51 @@ const Packages = () => { 'asc' | 'desc' >('asc'); - const sortedPackages = useMemo(() => { - if (!transformedPackages || !Array.isArray(transformedPackages)) { - return []; - } + const getSortableRowValues = ( + pkg: IBPackageWithRepositoryInfo, + ): (string | number | ApiPackageSourcesResponse[] | undefined)[] => { + return [pkg.name, pkg.summary, pkg.stream, pkg.end_date, pkg.repository]; + }; - return orderBy( - transformedPackages, - [ - // Active stream packages first (if activeStream is set) - (pkg) => (activeStream && pkg.stream === activeStream ? 0 : 1), - // Then by name - 'name', - // Then by stream version (descending) - (pkg) => { - if (!pkg.stream) return ''; - const parts = pkg.stream - .split('.') - .map((part) => parseInt(part, 10) || 0); - // Convert to string with zero-padding for proper sorting - return parts.map((p) => p.toString().padStart(10, '0')).join('.'); - }, - // Then by end date (nulls last) - (pkg) => pkg.end_date || '9999-12-31', - // Then by repository - (pkg) => pkg.repository || '', - // Finally by module name - (pkg) => pkg.module_name || '', - ], - ['asc', 'asc', 'desc', 'asc', 'asc', 'asc'], - ); - }, [transformedPackages, activeStream]); + let sortedPackages = transformedPackages; + sortedPackages = transformedPackages.sort((a, b) => { + const aValue = getSortableRowValues(a)[activeSortIndex]; + const bValue = getSortableRowValues(b)[activeSortIndex]; + if (typeof aValue === 'number') { + // Numeric sort + if (activeSortDirection === 'asc') { + return (aValue as number) - (bValue as number); + } + return (bValue as number) - (aValue as number); + } + // String sort + // if active stream is set, sort it to the top + if (aValue === activeStream) { + return -1; + } + if (bValue === activeStream) { + return 1; + } + if (activeSortDirection === 'asc') { + // handle packages with undefined stream + if (!aValue) { + return -1; + } + if (!bValue) { + return 1; + } + return (aValue as string).localeCompare(bValue as string); + } else { + // handle packages with undefined stream + if (!aValue) { + return 1; + } + if (!bValue) { + return -1; + } + return (bValue as string).localeCompare(aValue as string); + } + }); const getSortParams = (columnIndex: number) => ({ sortBy: { @@ -1099,14 +1100,14 @@ const Packages = () => { (module) => module.name === pkg.name, ); isSelected = - packages.some((p) => p.name === pkg.name && p.stream === pkg.stream) && - !isModuleWithSameName; + packages.some((p) => p.name === pkg.name) && !isModuleWithSameName; } if (pkg.type === 'module') { - // the package is selected if its module stream matches one in enabled_modules + // the package is selected if it's added to the packages state + // and its module stream matches one in enabled_modules isSelected = - packages.some((p) => p.name === pkg.name && p.stream === pkg.stream) && + packages.some((p) => p.name === pkg.name) && modules.some( (m) => m.name === pkg.module_name && m.stream === pkg.stream, ); @@ -1207,7 +1208,7 @@ const Packages = () => { .slice(computeStart(), computeEnd()) .map((grp, rowIndex) => ( @@ -1307,7 +1308,7 @@ const Packages = () => { .slice(computeStart(), computeEnd()) .map((pkg, rowIndex) => ( diff --git a/src/Components/CreateImageWizard/steps/Registration/index.tsx b/src/Components/CreateImageWizard/steps/Registration/index.tsx index 081fd54e..bd96d505 100644 --- a/src/Components/CreateImageWizard/steps/Registration/index.tsx +++ b/src/Components/CreateImageWizard/steps/Registration/index.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { ClipboardCopy, @@ -16,7 +16,6 @@ import ActivationKeysList from './components/ActivationKeysList'; import Registration from './components/Registration'; import SatelliteRegistration from './components/SatelliteRegistration'; -import { useGetUser } from '../../../../Hooks'; import { useAppSelector } from '../../../../store/hooks'; import { selectActivationKey, @@ -25,7 +24,18 @@ import { const RegistrationStep = () => { const { auth } = useChrome(); - const { orgId } = useGetUser(auth); + const [orgId, setOrgId] = useState(undefined); + + useEffect(() => { + (async () => { + const userData = await auth.getUser(); + const id = userData?.identity?.internal?.org_id; + setOrgId(id); + })(); + // This useEffect hook should run *only* on mount and therefore has an empty + // dependency array. eslint's exhaustive-deps rule does not support this use. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); const activationKey = useAppSelector(selectActivationKey); const registrationType = useAppSelector(selectRegistrationType); diff --git a/src/Components/CreateImageWizard/steps/Review/Footer/CreateDropdown.tsx b/src/Components/CreateImageWizard/steps/Review/Footer/CreateDropdown.tsx index ba26cfda..f05d4668 100644 --- a/src/Components/CreateImageWizard/steps/Review/Footer/CreateDropdown.tsx +++ b/src/Components/CreateImageWizard/steps/Review/Footer/CreateDropdown.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { Button, @@ -14,12 +14,12 @@ import { Spinner, } from '@patternfly/react-core'; import useChrome from '@redhat-cloud-services/frontend-components/useChrome'; +import { ChromeUser } from '@redhat-cloud-services/types'; import { AMPLITUDE_MODULE_NAME } from '../../../../../constants'; import { useComposeBPWithNotification as useComposeBlueprintMutation, useCreateBPWithNotification as useCreateBlueprintMutation, - useGetUser, } from '../../../../../Hooks'; import { setBlueprintId } from '../../../../../store/BlueprintSlice'; import { CockpitCreateBlueprintRequest } from '../../../../../store/cockpit/types'; @@ -44,8 +44,19 @@ export const CreateSaveAndBuildBtn = ({ setIsOpen, isDisabled, }: CreateDropdownProps) => { + const [userData, setUserData] = useState(undefined); + const { analytics, auth, isBeta } = useChrome(); - const { userData } = useGetUser(auth); + + useEffect(() => { + (async () => { + const data = await auth.getUser(); + setUserData(data); + })(); + // This useEffect hook should run *only* on mount and therefore has an empty + // dependency array. eslint's exhaustive-deps rule does not support this use. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); const packages = useAppSelector(selectPackages); @@ -102,7 +113,17 @@ export const CreateSaveButton = ({ isDisabled, }: CreateDropdownProps) => { const { analytics, auth, isBeta } = useChrome(); - const { userData } = useGetUser(auth); + const [userData, setUserData] = useState(undefined); + + useEffect(() => { + (async () => { + const data = await auth.getUser(); + setUserData(data); + })(); + // This useEffect hook should run *only* on mount and therefore has an empty + // dependency array. eslint's exhaustive-deps rule does not support this use. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); const packages = useAppSelector(selectPackages); diff --git a/src/Components/CreateImageWizard/steps/Review/Footer/EditDropdown.tsx b/src/Components/CreateImageWizard/steps/Review/Footer/EditDropdown.tsx index 6cc19cc3..cc025a9f 100644 --- a/src/Components/CreateImageWizard/steps/Review/Footer/EditDropdown.tsx +++ b/src/Components/CreateImageWizard/steps/Review/Footer/EditDropdown.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { DropdownItem, @@ -9,11 +9,11 @@ import { Spinner, } from '@patternfly/react-core'; import useChrome from '@redhat-cloud-services/frontend-components/useChrome'; +import { ChromeUser } from '@redhat-cloud-services/types'; import { AMPLITUDE_MODULE_NAME } from '../../../../../constants'; import { useComposeBPWithNotification as useComposeBlueprintMutation, - useGetUser, useUpdateBPWithNotification as useUpdateBlueprintMutation, } from '../../../../../Hooks'; import { CockpitCreateBlueprintRequest } from '../../../../../store/cockpit/types'; @@ -37,8 +37,19 @@ export const EditSaveAndBuildBtn = ({ blueprintId, isDisabled, }: EditDropdownProps) => { + const [userData, setUserData] = useState(undefined); + const { analytics, auth, isBeta } = useChrome(); - const { userData } = useGetUser(auth); + + useEffect(() => { + (async () => { + const data = await auth.getUser(); + setUserData(data); + })(); + // This useEffect hook should run *only* on mount and therefore has an empty + // dependency array. eslint's exhaustive-deps rule does not support this use. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); const { trigger: buildBlueprint } = useComposeBlueprintMutation(); const packages = useAppSelector(selectPackages); @@ -94,8 +105,19 @@ export const EditSaveButton = ({ blueprintId, isDisabled, }: EditDropdownProps) => { + const [userData, setUserData] = useState(undefined); + const { analytics, auth, isBeta } = useChrome(); - const { userData } = useGetUser(auth); + + useEffect(() => { + (async () => { + const data = await auth.getUser(); + setUserData(data); + })(); + // This useEffect hook should run *only* on mount and therefore has an empty + // dependency array. eslint's exhaustive-deps rule does not support this use. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); const packages = useAppSelector(selectPackages); diff --git a/src/Components/CreateImageWizard/steps/Review/Footer/Footer.tsx b/src/Components/CreateImageWizard/steps/Review/Footer/Footer.tsx index f6a35745..c12d590f 100644 --- a/src/Components/CreateImageWizard/steps/Review/Footer/Footer.tsx +++ b/src/Components/CreateImageWizard/steps/Review/Footer/Footer.tsx @@ -18,7 +18,6 @@ import { EditSaveAndBuildBtn, EditSaveButton } from './EditDropdown'; import { useCreateBPWithNotification as useCreateBlueprintMutation, - useGetUser, useUpdateBPWithNotification as useUpdateBlueprintMutation, } from '../../../../../Hooks'; import { resolveRelPath } from '../../../../../Utilities/path'; @@ -34,7 +33,6 @@ const ReviewWizardFooter = () => { const { isSuccess: isUpdateSuccess, reset: resetUpdate } = useUpdateBlueprintMutation({ fixedCacheKey: 'updateBlueprintKey' }); const { auth } = useChrome(); - const { orgId } = useGetUser(auth); const { composeId } = useParams(); const [isOpen, setIsOpen] = useState(false); const store = useStore(); @@ -54,12 +52,14 @@ const ReviewWizardFooter = () => { const getBlueprintPayload = async () => { if (!process.env.IS_ON_PREMISE) { + const userData = await auth.getUser(); + const orgId = userData?.identity?.internal?.org_id; const requestBody = orgId && mapRequestFromState(store, orgId); return requestBody; } - // NOTE: This is fine for on prem because we save the org id - // to state through a form field in the registration step + // NOTE: This should be fine on-prem, we should + // be able to ignore the `org-id` return mapRequestFromState(store, ''); }; diff --git a/src/Components/CreateImageWizard/utilities/requestMapper.ts b/src/Components/CreateImageWizard/utilities/requestMapper.ts index ae2ee53a..bdb4e589 100644 --- a/src/Components/CreateImageWizard/utilities/requestMapper.ts +++ b/src/Components/CreateImageWizard/utilities/requestMapper.ts @@ -211,9 +211,8 @@ function commonRequestToState( snapshot_date = ''; } - // we need to check for the region for on-prem const awsUploadOptions = aws?.upload_request - .options as AwsUploadRequestOptions & { region?: string | undefined }; + .options as AwsUploadRequestOptions; const gcpUploadOptions = gcp?.upload_request .options as GcpUploadRequestOptions; const azureUploadOptions = azure?.upload_request @@ -316,7 +315,6 @@ function commonRequestToState( : 'manual') as AwsShareMethod, source: { id: awsUploadOptions?.share_with_sources?.[0] }, sourceId: awsUploadOptions?.share_with_sources?.[0], - region: awsUploadOptions?.region, }, snapshotting: { useLatest: !snapshot_date && !request.image_requests[0]?.content_template, diff --git a/src/Components/ImagesTable/ImageDetails.tsx b/src/Components/ImagesTable/ImageDetails.tsx index 21b8a0db..809b0394 100644 --- a/src/Components/ImagesTable/ImageDetails.tsx +++ b/src/Components/ImagesTable/ImageDetails.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { Alert, @@ -13,11 +13,11 @@ import { } from '@patternfly/react-core'; import { ExternalLinkAltIcon } from '@patternfly/react-icons'; import useChrome from '@redhat-cloud-services/frontend-components/useChrome'; +import { ChromeUser } from '@redhat-cloud-services/types'; import ClonesTable from './ClonesTable'; import { AMPLITUDE_MODULE_NAME } from '../../constants'; -import { useGetUser } from '../../Hooks'; import { useGetComposeStatusQuery } from '../../store/backendApi'; import { extractProvisioningList } from '../../store/helpers'; import { @@ -134,9 +134,19 @@ type AwsDetailsPropTypes = { export const AwsDetails = ({ compose }: AwsDetailsPropTypes) => { const options = compose.request.image_requests[0].upload_request.options; + const [userData, setUserData] = useState(undefined); const { analytics, auth } = useChrome(); - const { userData } = useGetUser(auth); + + useEffect(() => { + (async () => { + const data = await auth.getUser(); + setUserData(data); + })(); + // This useEffect hook should run *only* on mount and therefore has an empty + // dependency array. eslint's exhaustive-deps rule does not support this use. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); if (!isAwsUploadRequestOptions(options)) { throw TypeError( diff --git a/src/Components/ImagesTable/ImagesTable.tsx b/src/Components/ImagesTable/ImagesTable.tsx index 69af395c..32782d3a 100644 --- a/src/Components/ImagesTable/ImagesTable.tsx +++ b/src/Components/ImagesTable/ImagesTable.tsx @@ -25,7 +25,7 @@ import { Tr, } from '@patternfly/react-table'; import useChrome from '@redhat-cloud-services/frontend-components/useChrome'; -import { useFlag } from '@unleash/proxy-client-react'; +import { ChromeUser } from '@redhat-cloud-services/types'; import { useDispatch } from 'react-redux'; import { NavigateFunction, useNavigate } from 'react-router-dom'; @@ -58,7 +58,6 @@ import { SEARCH_INPUT, STATUS_POLLING_INTERVAL, } from '../../constants'; -import { useGetUser } from '../../Hooks'; import { useGetBlueprintComposesQuery, useGetBlueprintsQuery, @@ -88,12 +87,11 @@ import { timestampToDisplayString, timestampToDisplayStringDetailed, } from '../../Utilities/time'; -import { AzureLaunchModal } from '../Launch/AzureLaunchModal'; -import { OciLaunchModal } from '../Launch/OciLaunchModal'; const ImagesTable = () => { const [page, setPage] = useState(1); const [perPage, setPerPage] = useState(10); + const [userData, setUserData] = useState(undefined); const selectedBlueprintId = useAppSelector(selectSelectedBlueprintId); const blueprintSearchInput = @@ -106,7 +104,16 @@ const ImagesTable = () => { const blueprintsLimit = useAppSelector(selectLimit) || PAGINATION_LIMIT; const { analytics, auth } = useChrome(); - const { userData } = useGetUser(auth); + + useEffect(() => { + (async () => { + const data = await auth.getUser(); + setUserData(data); + })(); + // This useEffect hook should run *only* on mount and therefore has an empty + // dependency array. eslint's exhaustive-deps rule does not support this use. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); const searchParamsGetBlueprints: GetBlueprintsApiArg = { limit: blueprintsLimit, @@ -375,14 +382,8 @@ type AzureRowPropTypes = { }; const AzureRow = ({ compose, rowIndex }: AzureRowPropTypes) => { - const launchEofFlag = useFlag('image-builder.launcheof'); - const details = ; - const instance = launchEofFlag ? ( - - ) : ( - - ); + const instance = ; const status = ; return ( @@ -402,18 +403,13 @@ type OciRowPropTypes = { }; const OciRow = ({ compose, rowIndex }: OciRowPropTypes) => { - const launchEofFlag = useFlag('image-builder.launcheof'); const daysToExpiration = Math.floor( computeHoursToExpiration(compose.created_at) / 24, ); const isExpired = daysToExpiration >= OCI_STORAGE_EXPIRATION_TIME_IN_DAYS; const details = ; - const instance = launchEofFlag ? ( - - ) : ( - - ); + const instance = ; const status = ( { const navigate = useNavigate(); + const [userData, setUserData] = useState(undefined); const { analytics, auth } = useChrome(); - const { userData } = useGetUser(auth); + + useEffect(() => { + (async () => { + const data = await auth.getUser(); + setUserData(data); + })(); + // This useEffect hook should run *only* on mount and therefore has an empty + // dependency array. eslint's exhaustive-deps rule does not support this use. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); const target = ; const status = ; @@ -547,8 +553,18 @@ const Row = ({ details, instance, }: RowPropTypes) => { + const [userData, setUserData] = useState(undefined); const { analytics, auth } = useChrome(); - const { userData } = useGetUser(auth); + + useEffect(() => { + (async () => { + const data = await auth.getUser(); + setUserData(data); + })(); + // This useEffect hook should run *only* on mount and therefore has an empty + // dependency array. eslint's exhaustive-deps rule does not support this use. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); const [isExpanded, setIsExpanded] = useState(false); const handleToggle = () => setIsExpanded(!isExpanded); diff --git a/src/Components/ImagesTable/Instance.tsx b/src/Components/ImagesTable/Instance.tsx index 254f0b05..4af6f5e9 100644 --- a/src/Components/ImagesTable/Instance.tsx +++ b/src/Components/ImagesTable/Instance.tsx @@ -1,4 +1,4 @@ -import React, { Suspense, useState } from 'react'; +import React, { Suspense, useEffect, useState } from 'react'; import path from 'path'; @@ -20,6 +20,7 @@ import { } from '@patternfly/react-core/dist/esm/components/List/List'; import { ExternalLinkAltIcon } from '@patternfly/react-icons'; import useChrome from '@redhat-cloud-services/frontend-components/useChrome'; +import { ChromeUser } from '@redhat-cloud-services/types'; import { useLoadModule, useScalprum } from '@scalprum/react-core'; import cockpit from 'cockpit'; import { useNavigate } from 'react-router-dom'; @@ -30,7 +31,6 @@ import { MODAL_ANCHOR, SEARCH_INPUT, } from '../../constants'; -import { useGetUser } from '../../Hooks'; import { useGetBlueprintsQuery, useGetComposeStatusQuery, @@ -101,9 +101,19 @@ const ProvisioningLink = ({ composeStatus, }: ProvisioningLinkPropTypes) => { const launchEofFlag = useFlag('image-builder.launcheof'); + const [userData, setUserData] = useState(undefined); const { analytics, auth } = useChrome(); - const { userData } = useGetUser(auth); + + useEffect(() => { + (async () => { + const data = await auth.getUser(); + setUserData(data); + })(); + // This useEffect hook should run *only* on mount and therefore has an empty + // dependency array. eslint's exhaustive-deps rule does not support this use. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); const [isModalOpen, setIsModalOpen] = useState(false); const [exposedScalprumModule, error] = useLoadModule( diff --git a/src/Components/ImagesTable/Status.tsx b/src/Components/ImagesTable/Status.tsx index fbe08ceb..b3c4ca8a 100644 --- a/src/Components/ImagesTable/Status.tsx +++ b/src/Components/ImagesTable/Status.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import './ImageBuildStatus.scss'; import { @@ -24,13 +24,13 @@ import { PendingIcon, } from '@patternfly/react-icons'; import useChrome from '@redhat-cloud-services/frontend-components/useChrome'; +import { ChromeUser } from '@redhat-cloud-services/types'; import { AMPLITUDE_MODULE_NAME, AWS_S3_EXPIRATION_TIME_IN_HOURS, OCI_STORAGE_EXPIRATION_TIME_IN_DAYS, } from '../../constants'; -import { useGetUser } from '../../Hooks'; import { useGetComposeStatusQuery } from '../../store/backendApi'; import { CockpitComposesResponseItem } from '../../store/cockpit/types'; import { @@ -122,8 +122,18 @@ export const CloudStatus = ({ compose }: CloudStatusPropTypes) => { const { data, isSuccess } = useGetComposeStatusQuery({ composeId: compose.id, }); + const [userData, setUserData] = useState(undefined); const { analytics, auth } = useChrome(); - const { userData } = useGetUser(auth); + + useEffect(() => { + (async () => { + const data = await auth.getUser(); + setUserData(data); + })(); + // This useEffect hook should run *only* on mount and therefore has an empty + // dependency array. eslint's exhaustive-deps rule does not support this use. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); if (!isSuccess) { return ; diff --git a/src/Components/Launch/AzureLaunchModal.tsx b/src/Components/Launch/AzureLaunchModal.tsx deleted file mode 100644 index 8cfba072..00000000 --- a/src/Components/Launch/AzureLaunchModal.tsx +++ /dev/null @@ -1,116 +0,0 @@ -import React, { Fragment, useState } from 'react'; - -import { - Button, - List, - ListComponent, - ListItem, - Modal, - ModalBody, - ModalFooter, - ModalHeader, - ModalVariant, - OrderType, - Skeleton, -} from '@patternfly/react-core'; -import { ExternalLinkAltIcon } from '@patternfly/react-icons'; - -import { - ComposesResponseItem, - useGetComposeStatusQuery, -} from '../../store/imageBuilderApi'; -import { isAzureUploadStatus } from '../../store/typeGuards'; - -type LaunchProps = { - compose: ComposesResponseItem; -}; - -export const AzureLaunchModal = ({ compose }: LaunchProps) => { - const [isModalOpen, setIsModalOpen] = useState(false); - const { data, isSuccess, isFetching } = useGetComposeStatusQuery({ - composeId: compose.id, - }); - - if (!isSuccess) { - return ; - } - - const options = data?.image_status.upload_status?.options; - - if (options && !isAzureUploadStatus(options)) { - throw TypeError( - `Error: options must be of type AzureUploadStatus, not ${typeof options}.`, - ); - } - - const handleModalToggle = (_event: KeyboardEvent | React.MouseEvent) => { - setIsModalOpen(!isModalOpen); - }; - - return ( - - - - - - - - - Locate{' '} - {!isFetching && ( - - {options?.image_name}{' '} - - )} - {isFetching && } - in the{' '} - - . - - - Create a Virtual Machine (VM) by using the image. -
- Note: Review the{' '} - - Availability Zone - {' '} - and the Size to - meet your requirements. Adjust these settings as needed. -
-
-
- - - -
-
- ); -}; diff --git a/src/Components/Launch/OciLaunchModal.tsx b/src/Components/Launch/OciLaunchModal.tsx deleted file mode 100644 index fa129be3..00000000 --- a/src/Components/Launch/OciLaunchModal.tsx +++ /dev/null @@ -1,139 +0,0 @@ -import React, { Fragment, useState } from 'react'; - -import { - Button, - ClipboardCopy, - ClipboardCopyVariant, - List, - ListComponent, - ListItem, - Modal, - ModalBody, - ModalFooter, - ModalHeader, - ModalVariant, - OrderType, - Skeleton, -} from '@patternfly/react-core'; -import { ExternalLinkAltIcon } from '@patternfly/react-icons'; -import { useNavigate } from 'react-router-dom'; - -import { - ComposesResponseItem, - useGetComposeStatusQuery, -} from '../../store/imageBuilderApi'; -import { isOciUploadStatus } from '../../store/typeGuards'; -import { resolveRelPath } from '../../Utilities/path'; - -type LaunchProps = { - isExpired: boolean; - compose: ComposesResponseItem; -}; - -export const OciLaunchModal = ({ isExpired, compose }: LaunchProps) => { - const [isModalOpen, setIsModalOpen] = useState(false); - const { data, isSuccess, isFetching } = useGetComposeStatusQuery({ - composeId: compose.id, - }); - - const navigate = useNavigate(); - if (!isSuccess) { - return ; - } - - const options = data?.image_status.upload_status?.options; - - if (options && !isOciUploadStatus(options)) { - throw TypeError( - `Error: options must be of type OciUploadStatus, not ${typeof options}.`, - ); - } - - if (isExpired) { - return ( - - ); - } - - const handleModalToggle = () => { - setIsModalOpen(!isModalOpen); - }; - - return ( - - - - - - - - Navigate to the{' '} - {' '} - page. - - - Select{' '} - Import image, - and enter the Object Storage URL of the image. - {!isFetching && ( - - {options?.url || ''} - - )} - {isFetching && } - - - After the image is available, click on{' '} - Create instance. - - - - - - - - - ); -}; diff --git a/src/Components/ShareImageModal/RegionsSelect.tsx b/src/Components/ShareImageModal/RegionsSelect.tsx index abd20390..13ca0dcc 100644 --- a/src/Components/ShareImageModal/RegionsSelect.tsx +++ b/src/Components/ShareImageModal/RegionsSelect.tsx @@ -156,7 +156,7 @@ const RegionsSelect = ({ composeId, handleClose }: RegionsSelectPropTypes) => { diff --git a/src/Hooks/index.tsx b/src/Hooks/index.tsx index cb2f6aef..df6abc94 100644 --- a/src/Hooks/index.tsx +++ b/src/Hooks/index.tsx @@ -4,4 +4,3 @@ export { useDeleteBPWithNotification } from './MutationNotifications/useDeleteBP export { useFixupBPWithNotification } from './MutationNotifications/useFixupBPWithNotification'; export { useComposeBPWithNotification } from './MutationNotifications/useComposeBPWithNotification'; export { useCloneComposeWithNotification } from './MutationNotifications/useCloneComposeWithNotification'; -export { useGetUser } from './useGetUser'; diff --git a/src/Hooks/useGetUser.tsx b/src/Hooks/useGetUser.tsx deleted file mode 100644 index 445bdf22..00000000 --- a/src/Hooks/useGetUser.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { useEffect, useState } from 'react'; - -import { ChromeUser } from '@redhat-cloud-services/types'; - -export const useGetUser = (auth: { getUser(): Promise }) => { - const [userData, setUserData] = useState(undefined); - const [orgId, setOrgId] = useState(undefined); - - useEffect(() => { - (async () => { - if (!process.env.IS_ON_PREMISE) { - const data = await auth.getUser(); - const id = data?.identity.internal?.org_id; - setUserData(data); - setOrgId(id); - } - })(); - // This useEffect hook should run *only* on mount and therefore has an empty - // dependency array. eslint's exhaustive-deps rule does not support this use. - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - return { userData, orgId }; -}; diff --git a/src/mockServiceWorker.js b/src/mockServiceWorker.js index 723b0714..be4527c7 100644 --- a/src/mockServiceWorker.js +++ b/src/mockServiceWorker.js @@ -7,7 +7,7 @@ * - Please do NOT modify this file. */ -const PACKAGE_VERSION = '2.10.5' +const PACKAGE_VERSION = '2.10.4' const INTEGRITY_CHECKSUM = 'f5825c521429caf22a4dd13b66e243af' const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') const activeClientIds = new Set() diff --git a/src/test/Components/CreateImageWizard/steps/Packages/Packages.test.tsx b/src/test/Components/CreateImageWizard/steps/Packages/Packages.test.tsx index 42fecc29..85a2a12e 100644 --- a/src/test/Components/CreateImageWizard/steps/Packages/Packages.test.tsx +++ b/src/test/Components/CreateImageWizard/steps/Packages/Packages.test.tsx @@ -513,123 +513,6 @@ describe('Step Packages', () => { expect(secondAppStreamRow).toBeDisabled(); expect(secondAppStreamRow).not.toBeChecked(); }); - - test('module selection sorts selected stream to top while maintaining alphabetical order', async () => { - const user = userEvent.setup(); - - await renderCreateMode(); - await goToPackagesStep(); - await typeIntoSearchBox('sortingTest'); - - await screen.findAllByText('alphaModule'); - await screen.findAllByText('betaModule'); - await screen.findAllByText('gammaModule'); - - let rows = await screen.findAllByRole('row'); - rows.shift(); - expect(rows).toHaveLength(6); - - expect(rows[0]).toHaveTextContent('alphaModule'); - expect(rows[0]).toHaveTextContent('3.0'); - expect(rows[1]).toHaveTextContent('alphaModule'); - expect(rows[1]).toHaveTextContent('2.0'); - expect(rows[2]).toHaveTextContent('betaModule'); - expect(rows[2]).toHaveTextContent('4.0'); - expect(rows[3]).toHaveTextContent('betaModule'); - expect(rows[3]).toHaveTextContent('2.0'); - - // Select betaModule with stream 2.0 (row index 3) - const betaModule20Checkbox = await screen.findByRole('checkbox', { - name: /select row 3/i, - }); - - await waitFor(() => user.click(betaModule20Checkbox)); - expect(betaModule20Checkbox).toBeChecked(); - - // After selection, the active stream (2.0) should be prioritized - // All modules with stream 2.0 should move to the top, maintaining alphabetical order - rows = await screen.findAllByRole('row'); - rows.shift(); - expect(rows[0]).toHaveTextContent('alphaModule'); - expect(rows[0]).toHaveTextContent('2.0'); - expect(rows[1]).toHaveTextContent('betaModule'); - expect(rows[1]).toHaveTextContent('2.0'); - expect(rows[2]).toHaveTextContent('gammaModule'); - expect(rows[2]).toHaveTextContent('2.0'); - expect(rows[3]).toHaveTextContent('alphaModule'); - expect(rows[3]).toHaveTextContent('3.0'); - expect(rows[4]).toHaveTextContent('betaModule'); - expect(rows[4]).toHaveTextContent('4.0'); - expect(rows[5]).toHaveTextContent('gammaModule'); - expect(rows[5]).toHaveTextContent('1.5'); - - // Verify that only the selected module is checked - const updatedBetaModule20Checkbox = await screen.findByRole('checkbox', { - name: /select row 1/i, // betaModule 2.0 is now at position 1 - }); - expect(updatedBetaModule20Checkbox).toBeChecked(); - - // Verify that only one checkbox is checked - const allCheckboxes = await screen.findAllByRole('checkbox', { - name: /select row [0-9]/i, - }); - const checkedCheckboxes = allCheckboxes.filter( - (cb) => (cb as HTMLInputElement).checked, - ); - expect(checkedCheckboxes).toHaveLength(1); - expect(checkedCheckboxes[0]).toBe(updatedBetaModule20Checkbox); - }); - - test('unselecting a module does not cause jumping but may reset sort to default', async () => { - const user = userEvent.setup(); - - await renderCreateMode(); - await goToPackagesStep(); - await selectCustomRepo(); - await typeIntoSearchBox('sortingTest'); - await screen.findAllByText('betaModule'); - const betaModule20Checkbox = await screen.findByRole('checkbox', { - name: /select row 3/i, - }); - await waitFor(() => user.click(betaModule20Checkbox)); - expect(betaModule20Checkbox).toBeChecked(); - let rows = await screen.findAllByRole('row'); - rows.shift(); - expect(rows[0]).toHaveTextContent('alphaModule'); - expect(rows[0]).toHaveTextContent('2.0'); - expect(rows[1]).toHaveTextContent('betaModule'); - expect(rows[1]).toHaveTextContent('2.0'); - - const updatedBetaModule20Checkbox = await screen.findByRole('checkbox', { - name: /select row 1/i, - }); - await waitFor(() => user.click(updatedBetaModule20Checkbox)); - expect(updatedBetaModule20Checkbox).not.toBeChecked(); - - // After unselection, the sort may reset to default or stay the same - // The important thing is that we don't get jumping/reordering during the interaction - rows = await screen.findAllByRole('row'); - rows.shift(); // Remove header row - const allCheckboxes = await screen.findAllByRole('checkbox', { - name: /select row [0-9]/i, - }); - const checkedCheckboxes = allCheckboxes.filter( - (cb) => (cb as HTMLInputElement).checked, - ); - expect(checkedCheckboxes).toHaveLength(0); - - // The key test: the table should have a consistent, predictable order - // Either the original alphabetical order OR the stream-sorted order - // What we don't want is jumping around during the selection/unselection process - expect(rows).toHaveLength(6); // Still have all 6 modules - const moduleNames = rows.map((row) => { - const match = row.textContent?.match(/(\w+Module)/); - return match ? match[1] : ''; - }); - expect(moduleNames).toContain('alphaModule'); - expect(moduleNames).toContain('betaModule'); - expect(moduleNames).toContain('gammaModule'); - }); }); }); diff --git a/src/test/fixtures/packages.ts b/src/test/fixtures/packages.ts index 08aefb20..0f494415 100644 --- a/src/test/fixtures/packages.ts +++ b/src/test/fixtures/packages.ts @@ -75,64 +75,6 @@ export const mockSourcesPackagesResults = ( }, ]; } - if (search === 'sortingTest') { - return [ - { - package_name: 'alphaModule', - summary: 'Alpha module for sorting tests', - package_sources: [ - { - name: 'alphaModule', - type: 'module', - stream: '2.0', - end_date: '2025-12-01', - }, - { - name: 'alphaModule', - type: 'module', - stream: '3.0', - end_date: '2027-12-01', - }, - ], - }, - { - package_name: 'betaModule', - summary: 'Beta module for sorting tests', - package_sources: [ - { - name: 'betaModule', - type: 'module', - stream: '2.0', - end_date: '2025-06-01', - }, - { - name: 'betaModule', - type: 'module', - stream: '4.0', - end_date: '2028-06-01', - }, - ], - }, - { - package_name: 'gammaModule', - summary: 'Gamma module for sorting tests', - package_sources: [ - { - name: 'gammaModule', - type: 'module', - stream: '2.0', - end_date: '2025-08-01', - }, - { - name: 'gammaModule', - type: 'module', - stream: '1.5', - end_date: '2026-08-01', - }, - ], - }, - ]; - } if (search === 'mock') { return [ {