diff --git a/.fmf/version b/.fmf/version new file mode 100644 index 00000000..d00491fd --- /dev/null +++ b/.fmf/version @@ -0,0 +1 @@ +1 diff --git a/.forgejo/workflows/ci.yml b/.forgejo/workflows/ci.yml new file mode 100644 index 00000000..b6742f80 --- /dev/null +++ b/.forgejo/workflows/ci.yml @@ -0,0 +1,257 @@ +--- +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 new file mode 100644 index 00000000..b6742f80 --- /dev/null +++ b/.forgejo/workflows/ci.yml.disabled @@ -0,0 +1,257 @@ +--- +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 0948c85c..9cbc9f7a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -32,8 +32,7 @@ test: - RUNNER: - aws/fedora-41-x86_64 - aws/fedora-42-x86_64 - - aws/rhel-9.6-nightly-x86_64 - - aws/rhel-10.0-nightly-x86_64 + - aws/rhel-10.1-nightly-x86_64 INTERNAL_NETWORK: ["true"] finish: diff --git a/.tekton/image-builder-frontend-pull-request.yaml b/.tekton/image-builder-frontend-pull-request.yaml index 2f7c1683..111fde6d 100644 --- a/.tekton/image-builder-frontend-pull-request.yaml +++ b/.tekton/image-builder-frontend-pull-request.yaml @@ -194,7 +194,7 @@ spec: - name: name value: prefetch-dependencies - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-prefetch-dependencies:0.2@sha256:76bc23ca84c6b31251fee267f417ed262d9ea49655e942d1978149e137295bb0 + value: quay.io/konflux-ci/tekton-catalog/task-prefetch-dependencies:0.2@sha256:ce5f2485d759221444357fe38276be876fc54531651e50dcfc0f84b34909d760 - name: kind value: task resolver: bundles @@ -238,7 +238,7 @@ spec: - name: name value: buildah - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-buildah:0.4@sha256:09ca1ce263bb686b08187a3b836f6a5bd54875e8596105e4d7d816d57caf55a0 + value: quay.io/konflux-ci/tekton-catalog/task-buildah:0.4@sha256:7782cb7462130de8e8839a58dd15ed78e50938d718b51375267679c6044b4367 - name: kind value: task resolver: bundles @@ -413,7 +413,7 @@ spec: - name: name value: ecosystem-cert-preflight-checks - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-ecosystem-cert-preflight-checks:0.2@sha256:4bafcaab0f0c998a89a1cc33bdbbf74f39eea52e6c0e43013c356a322f94940f + value: quay.io/konflux-ci/tekton-catalog/task-ecosystem-cert-preflight-checks:0.2@sha256:1f151e00f7fc427654b7b76045a426bb02fe650d192ffe147a304d2184787e38 - name: kind value: task resolver: bundles @@ -503,7 +503,7 @@ spec: - name: name value: push-dockerfile - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-push-dockerfile:0.1@sha256:5446102f233991bdc73451201adf361766d45c638f3d89f19121ae1c2ba8bf17 + value: quay.io/konflux-ci/tekton-catalog/task-push-dockerfile:0.1@sha256:d5cb22a833be51dd72a872cac8bfbe149e8ad34da7cb48a643a1e613447a1f9d - name: kind value: task resolver: bundles diff --git a/.tekton/image-builder-frontend-push.yaml b/.tekton/image-builder-frontend-push.yaml index 81f634d3..ff7ad971 100644 --- a/.tekton/image-builder-frontend-push.yaml +++ b/.tekton/image-builder-frontend-push.yaml @@ -191,7 +191,7 @@ spec: - name: name value: prefetch-dependencies - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-prefetch-dependencies:0.2@sha256:76bc23ca84c6b31251fee267f417ed262d9ea49655e942d1978149e137295bb0 + value: quay.io/konflux-ci/tekton-catalog/task-prefetch-dependencies:0.2@sha256:ce5f2485d759221444357fe38276be876fc54531651e50dcfc0f84b34909d760 - name: kind value: task resolver: bundles @@ -235,7 +235,7 @@ spec: - name: name value: buildah - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-buildah:0.4@sha256:09ca1ce263bb686b08187a3b836f6a5bd54875e8596105e4d7d816d57caf55a0 + value: quay.io/konflux-ci/tekton-catalog/task-buildah:0.4@sha256:7782cb7462130de8e8839a58dd15ed78e50938d718b51375267679c6044b4367 - name: kind value: task resolver: bundles @@ -410,7 +410,7 @@ spec: - name: name value: ecosystem-cert-preflight-checks - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-ecosystem-cert-preflight-checks:0.2@sha256:4bafcaab0f0c998a89a1cc33bdbbf74f39eea52e6c0e43013c356a322f94940f + value: quay.io/konflux-ci/tekton-catalog/task-ecosystem-cert-preflight-checks:0.2@sha256:1f151e00f7fc427654b7b76045a426bb02fe650d192ffe147a304d2184787e38 - name: kind value: task resolver: bundles @@ -500,7 +500,7 @@ spec: - name: name value: push-dockerfile - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-push-dockerfile:0.1@sha256:5446102f233991bdc73451201adf361766d45c638f3d89f19121ae1c2ba8bf17 + value: quay.io/konflux-ci/tekton-catalog/task-push-dockerfile:0.1@sha256:d5cb22a833be51dd72a872cac8bfbe149e8ad34da7cb48a643a1e613447a1f9d - name: kind value: task resolver: bundles diff --git a/cockpit/cockpit-image-builder.spec b/cockpit/cockpit-image-builder.spec index 213d9af1..2c739e91 100644 --- a/cockpit/cockpit-image-builder.spec +++ b/cockpit/cockpit-image-builder.spec @@ -1,5 +1,5 @@ Name: cockpit-image-builder -Version: 74 +Version: 76 Release: 1%{?dist} Summary: Image builder plugin for Cockpit diff --git a/package-lock.json b/package-lock.json index 8aa706ba..c8b41f90 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,16 +10,17 @@ "hasInstallScript": true, "dependencies": { "@ltd/j-toml": "1.38.0", - "@patternfly/patternfly": "6.3.0", + "@patternfly/patternfly": "6.3.1", "@patternfly/react-code-editor": "6.3.1", - "@patternfly/react-core": "6.3.0", + "@patternfly/react-core": "6.3.1", "@patternfly/react-table": "6.3.1", "@redhat-cloud-services/frontend-components": "7.0.3", - "@redhat-cloud-services/frontend-components-notifications": "6.1.3", + "@redhat-cloud-services/frontend-components-notifications": "6.1.5", "@redhat-cloud-services/frontend-components-utilities": "7.0.3", + "@redhat-cloud-services/types": "3.0.1", "@reduxjs/toolkit": "2.8.2", "@scalprum/react-core": "0.9.5", - "@sentry/webpack-plugin": "4.1.0", + "@sentry/webpack-plugin": "4.1.1", "@unleash/proxy-client-react": "5.0.1", "classnames": "2.5.1", "jwt-decode": "4.0.0", @@ -48,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.1.0", + "@types/node": "24.3.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.39.0", - "@typescript-eslint/parser": "8.39.0", + "@typescript-eslint/eslint-plugin": "8.40.0", + "@typescript-eslint/parser": "8.40.0", "@vitejs/plugin-react": "4.7.0", "@vitest/coverage-v8": "3.2.4", "babel-loader": "10.0.0", @@ -63,13 +64,13 @@ "chartjs-plugin-annotation": "3.1.0", "copy-webpack-plugin": "13.0.0", "css-loader": "7.1.2", - "eslint": "9.32.0", + "eslint": "9.33.0", "eslint-plugin-disable-autofix": "5.0.1", "eslint-plugin-import": "2.32.0", "eslint-plugin-jest-dom": "5.5.0", "eslint-plugin-jsx-a11y": "6.10.2", "eslint-plugin-playwright": "2.2.2", - "eslint-plugin-prettier": "5.5.3", + "eslint-plugin-prettier": "5.5.4", "eslint-plugin-react": "7.37.5", "eslint-plugin-react-hooks": "5.2.0", "eslint-plugin-react-redux": "4.2.2", @@ -82,7 +83,7 @@ "madge": "8.0.0", "mini-css-extract-plugin": "2.9.2", "moment": "2.30.1", - "msw": "2.10.4", + "msw": "2.10.5", "npm-run-all": "4.1.5", "path-browserify": "1.0.1", "postcss-scss": "4.0.9", @@ -90,12 +91,12 @@ "redux-mock-store": "1.5.5", "sass": "1.90.0", "sass-loader": "16.0.5", - "stylelint": "16.23.0", - "stylelint-config-recommended-scss": "15.0.1", + "stylelint": "16.23.1", + "stylelint-config-recommended-scss": "16.0.0", "ts-node": "10.9.2", "ts-patch": "3.3.0", "typescript": "5.8.3", - "typescript-eslint": "8.38.0", + "typescript-eslint": "8.40.0", "uuid": "11.1.0", "vitest": "3.2.4", "vitest-canvas-mock": "0.3.3", @@ -3016,9 +3017,9 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", - "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -3026,9 +3027,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", - "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3147,13 +3148,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", - "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.15.1", + "@eslint/core": "^0.15.2", "levn": "^0.4.1" }, "engines": { @@ -3765,9 +3766,9 @@ } }, "node_modules/@patternfly/patternfly": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-6.3.0.tgz", - "integrity": "sha512-I/Z0uuaVYfv9QqNAP2/iCGUGhUb0idatNTfFLQk0VnUi2hSMgznDogvAjmlVtZu2DW0P0FymS0oo5EjL+uW6Dg==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-6.3.1.tgz", + "integrity": "sha512-O/lTo5EHKzer/HNzqMQOQEAMG7izDDkEHpAeJ5+sGaeQ/maB3RK7sQsOPS4DjrnMxt4/cC6LogK2mowlbf1j5Q==", "license": "MIT" }, "node_modules/@patternfly/react-code-editor": { @@ -3777,7 +3778,7 @@ "license": "MIT", "dependencies": { "@monaco-editor/react": "^4.6.0", - "@patternfly/react-core": "^6.3.1", + "@patternfly/react-core": "6.3.1", "@patternfly/react-icons": "^6.3.1", "@patternfly/react-styles": "^6.3.1", "react-dropzone": "14.3.5", @@ -3788,24 +3789,6 @@ "react-dom": "^17 || ^18 || ^19" } }, - "node_modules/@patternfly/react-code-editor/node_modules/@patternfly/react-core": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/@patternfly/react-core/-/react-core-6.3.1.tgz", - "integrity": "sha512-1qV20nU4M6PA28qnikH9fPLQlkteaZZToFlATjBNBw7aUI6zIvj7U0akkHz8raWcfHAI+tAzGV7dfKjiv035/g==", - "license": "MIT", - "dependencies": { - "@patternfly/react-icons": "^6.3.1", - "@patternfly/react-styles": "^6.3.1", - "@patternfly/react-tokens": "^6.3.1", - "focus-trap": "7.6.4", - "react-dropzone": "^14.3.5", - "tslib": "^2.8.1" - }, - "peerDependencies": { - "react": "^17 || ^18 || ^19", - "react-dom": "^17 || ^18 || ^19" - } - }, "node_modules/@patternfly/react-component-groups": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@patternfly/react-component-groups/-/react-component-groups-6.1.0.tgz", @@ -3824,14 +3807,14 @@ } }, "node_modules/@patternfly/react-core": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@patternfly/react-core/-/react-core-6.3.0.tgz", - "integrity": "sha512-TM+pLwLd5DzaDlOQhqeju9H9QUFQypQiNwXQLNIxOV5r3fmKh4NTp2Av/8WmFkpCj8mejDOfp4TNxoU1zdjCkQ==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@patternfly/react-core/-/react-core-6.3.1.tgz", + "integrity": "sha512-1qV20nU4M6PA28qnikH9fPLQlkteaZZToFlATjBNBw7aUI6zIvj7U0akkHz8raWcfHAI+tAzGV7dfKjiv035/g==", "license": "MIT", "dependencies": { - "@patternfly/react-icons": "^6.3.0", - "@patternfly/react-styles": "^6.3.0", - "@patternfly/react-tokens": "^6.3.0", + "@patternfly/react-icons": "^6.3.1", + "@patternfly/react-styles": "^6.3.1", + "@patternfly/react-tokens": "^6.3.1", "focus-trap": "7.6.4", "react-dropzone": "^14.3.5", "tslib": "^2.8.1" @@ -3863,7 +3846,7 @@ "integrity": "sha512-ZndBbPcMr/vInP5eELRe9m7MWzRoejRAhWx+25xOdjVAd31/CmMK1nBgZk4QAXaWjH1P+uZaZYsTgr/FMTte2g==", "license": "MIT", "dependencies": { - "@patternfly/react-core": "^6.3.1", + "@patternfly/react-core": "6.3.1", "@patternfly/react-icons": "6.3.1", "@patternfly/react-styles": "^6.3.1", "@patternfly/react-tokens": "^6.3.1", @@ -3875,24 +3858,6 @@ "react-dom": "^17 || ^18 || ^19" } }, - "node_modules/@patternfly/react-table/node_modules/@patternfly/react-core": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/@patternfly/react-core/-/react-core-6.3.1.tgz", - "integrity": "sha512-1qV20nU4M6PA28qnikH9fPLQlkteaZZToFlATjBNBw7aUI6zIvj7U0akkHz8raWcfHAI+tAzGV7dfKjiv035/g==", - "license": "MIT", - "dependencies": { - "@patternfly/react-icons": "6.3.1", - "@patternfly/react-styles": "^6.3.1", - "@patternfly/react-tokens": "^6.3.1", - "focus-trap": "7.6.4", - "react-dropzone": "^14.3.5", - "tslib": "^2.8.1" - }, - "peerDependencies": { - "react": "^17 || ^18 || ^19", - "react-dom": "^17 || ^18 || ^19" - } - }, "node_modules/@patternfly/react-tokens": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-6.3.1.tgz", @@ -4187,13 +4152,13 @@ } }, "node_modules/@redhat-cloud-services/frontend-components-notifications": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/@redhat-cloud-services/frontend-components-notifications/-/frontend-components-notifications-6.1.3.tgz", - "integrity": "sha512-GeLVfH+e6M8CLehk4U3LVoUzQ7G2pyw3SY6I8R/WBaYfBuwqBhWRHiHt/bTE+VzZo5yfBsnbKQ59JWNpYniDvA==", + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/@redhat-cloud-services/frontend-components-notifications/-/frontend-components-notifications-6.1.5.tgz", + "integrity": "sha512-CanlpBYpIKzLQZCD0Q3J5KezN0bCEeNW/rFclBPJ2CXuiyIwB0o0KRg6yfoF33V95LrDg1J3Q2DNHl8GhS+/mg==", "license": "Apache-2.0", "dependencies": { - "@redhat-cloud-services/frontend-components": "^6.0.0", - "@redhat-cloud-services/frontend-components-utilities": "^6.0.0" + "@redhat-cloud-services/frontend-components": "^7.0.0", + "@redhat-cloud-services/frontend-components-utilities": "^7.0.0" }, "peerDependencies": { "@patternfly/react-core": "^6.0.0", @@ -4202,82 +4167,6 @@ "react-dom": "^18.2.0" } }, - "node_modules/@redhat-cloud-services/frontend-components-notifications/node_modules/@redhat-cloud-services/frontend-components": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@redhat-cloud-services/frontend-components/-/frontend-components-6.1.1.tgz", - "integrity": "sha512-p6nqk6pLL8nnxxIzfyQd5K6yW4EBqNZlAlgWmgSttwuuPAzHTDHFtutTvtCMK/hzlyfTnyGgjRMe2TL+wXrEcQ==", - "license": "Apache-2.0", - "dependencies": { - "@patternfly/react-component-groups": "^6.0.0", - "@redhat-cloud-services/frontend-components-utilities": "^6.0.0", - "@redhat-cloud-services/types": "^2.0.0", - "@scalprum/core": "^0.8.1", - "@scalprum/react-core": "^0.9.1", - "classnames": "^2.2.5", - "sanitize-html": "^2.13.1" - }, - "peerDependencies": { - "@patternfly/react-core": "^6.0.0", - "@patternfly/react-icons": "^6.0.0", - "@patternfly/react-table": "^6.0.0", - "@patternfly/react-tokens": "^6.0.0", - "lodash": "^4.17.15", - "prop-types": "^15.6.2", - "react": "^18.2.0", - "react-content-loader": "^6.2.0", - "react-dom": "^18.2.0", - "react-redux": "^7.0.0 || ^8.0.0 || ^9.0.0", - "react-router-dom": "^5.0.0 || ^6.0.0" - } - }, - "node_modules/@redhat-cloud-services/frontend-components-notifications/node_modules/@redhat-cloud-services/frontend-components-utilities": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@redhat-cloud-services/frontend-components-utilities/-/frontend-components-utilities-6.1.1.tgz", - "integrity": "sha512-ETaL9dUOyxrqjJx+QcxI5pBmDOShC6voJINlU4afhelJBfam01oMQxQYB1aPFKs2JmBDbYgT2fgT/6s/1FJS0Q==", - "license": "Apache-2.0", - "dependencies": { - "@redhat-cloud-services/rbac-client": "^1.0.111 || 2.x", - "@redhat-cloud-services/types": "^2.0.0", - "@sentry/browser": "^7.119.1", - "awesome-debounce-promise": "^2.1.0", - "axios": "^0.28.1 || ^1.7.0", - "commander": "^2.20.3", - "mkdirp": "^1.0.4", - "p-map": "^7.0.2", - "react-content-loader": "^6.2.0" - }, - "peerDependencies": { - "@patternfly/react-core": "^6.0.0", - "@patternfly/react-table": "^6.0.0", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-redux": "^7.0.0 || ^8.0.0 || ^9.0.0", - "react-router-dom": "^5.0.0 || ^6.0.0" - } - }, - "node_modules/@redhat-cloud-services/frontend-components-notifications/node_modules/@redhat-cloud-services/rbac-client": { - "version": "2.2.11", - "resolved": "https://registry.npmjs.org/@redhat-cloud-services/rbac-client/-/rbac-client-2.2.11.tgz", - "integrity": "sha512-3LLW5XtBMqcdlYJ4lAFo7MaGnHXUBP01O3kty4ocU69EPjiip6/Gtj83vAx5hkgAWX9h6ZEntTDeDuU/ZfGdKQ==", - "license": "Apache-2.0", - "dependencies": { - "@redhat-cloud-services/javascript-clients-shared": "^1.2.4", - "axios": "^1.7.2", - "tslib": "^2.6.2" - } - }, - "node_modules/@redhat-cloud-services/frontend-components-notifications/node_modules/p-map": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz", - "integrity": "sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@redhat-cloud-services/frontend-components-utilities": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/@redhat-cloud-services/frontend-components-utilities/-/frontend-components-utilities-7.0.3.tgz", @@ -4303,12 +4192,6 @@ "react-router-dom": "^5.0.0 || ^6.0.0" } }, - "node_modules/@redhat-cloud-services/frontend-components-utilities/node_modules/@redhat-cloud-services/types": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@redhat-cloud-services/types/-/types-3.0.1.tgz", - "integrity": "sha512-GXw72DDOtKCkl2NlqkjGYVWjfm81LSA75jzbdMqKuefkxMvV4SsA0uNcauGiqZKUhq20RlNz3xBlKF3lvsfD1Q==", - "license": "Apache-2.0" - }, "node_modules/@redhat-cloud-services/frontend-components-utilities/node_modules/p-map": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz", @@ -4321,12 +4204,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@redhat-cloud-services/frontend-components/node_modules/@redhat-cloud-services/types": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@redhat-cloud-services/types/-/types-3.0.1.tgz", - "integrity": "sha512-GXw72DDOtKCkl2NlqkjGYVWjfm81LSA75jzbdMqKuefkxMvV4SsA0uNcauGiqZKUhq20RlNz3xBlKF3lvsfD1Q==", - "license": "Apache-2.0" - }, "node_modules/@redhat-cloud-services/javascript-clients-shared": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@redhat-cloud-services/javascript-clients-shared/-/javascript-clients-shared-1.2.7.tgz", @@ -4382,9 +4259,9 @@ } }, "node_modules/@redhat-cloud-services/types": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@redhat-cloud-services/types/-/types-2.0.0.tgz", - "integrity": "sha512-T9KvjntxMtRkNcyYCKB9OktIy7hhy24Y4n1nDZgGsysSnfWOrnmsrLUI7pyrwqsRVzjIjI3oYf7qH7DE5LcFag==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@redhat-cloud-services/types/-/types-3.0.1.tgz", + "integrity": "sha512-GXw72DDOtKCkl2NlqkjGYVWjfm81LSA75jzbdMqKuefkxMvV4SsA0uNcauGiqZKUhq20RlNz3xBlKF3lvsfD1Q==", "license": "Apache-2.0" }, "node_modules/@reduxjs/toolkit": { @@ -4825,9 +4702,9 @@ } }, "node_modules/@sentry/babel-plugin-component-annotate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-4.1.0.tgz", - "integrity": "sha512-UkcnqC7Bp9ODyoBN7BKcRotd1jz/I2vyruE/qjNfRC7UnP+jIRItUWYaXxQPON1fTw+N+egKdByk0M1y2OPv/Q==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-4.1.1.tgz", + "integrity": "sha512-HUpqrCK7zDVojTV6KL6BO9ZZiYrEYQqvYQrscyMsq04z+WCupXaH6YEliiNRvreR8DBJgdsG3lBRpebhUGmvfA==", "license": "MIT", "engines": { "node": ">= 14" @@ -4853,13 +4730,13 @@ } }, "node_modules/@sentry/bundler-plugin-core": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@sentry/bundler-plugin-core/-/bundler-plugin-core-4.1.0.tgz", - "integrity": "sha512-/5XBtCF6M+9frEXrrvfSWOdOC2q6I1L7oY7qbUVegNkp3kYVGihNZZnJIXGzo9rmwnA0IV7jI3o0pF/HDRqPeA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@sentry/bundler-plugin-core/-/bundler-plugin-core-4.1.1.tgz", + "integrity": "sha512-Hx9RgXaD1HEYmL5aYoWwCKkVvPp4iklwfD9mvmdpQtcwLg6b6oLnPVDQaOry1ak6Pxt8smlrWcKy4IiKASlvig==", "license": "MIT", "dependencies": { "@babel/core": "^7.18.5", - "@sentry/babel-plugin-component-annotate": "4.1.0", + "@sentry/babel-plugin-component-annotate": "4.1.1", "@sentry/cli": "^2.51.0", "dotenv": "^16.3.1", "find-up": "^5.0.0", @@ -4914,9 +4791,9 @@ } }, "node_modules/@sentry/cli": { - "version": "2.51.1", - "resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-2.51.1.tgz", - "integrity": "sha512-FU+54kNcKJABU0+ekvtnoXHM9zVrDe1zXVFbQT7mS0On0m1P0zFRGdzbnWe2XzpzuEAJXtK6aog/W+esRU9AIA==", + "version": "2.52.0", + "resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-2.52.0.tgz", + "integrity": "sha512-PXyo7Yv7+rVMSBGZfI/eFEzzhiKedTs25sDCjz4a3goAZ/F5R5tn3MKq30pnze5wNnoQmLujAa0uUjfNcWP+uQ==", "hasInstallScript": true, "license": "BSD-3-Clause", "dependencies": { @@ -4933,20 +4810,20 @@ "node": ">= 10" }, "optionalDependencies": { - "@sentry/cli-darwin": "2.51.1", - "@sentry/cli-linux-arm": "2.51.1", - "@sentry/cli-linux-arm64": "2.51.1", - "@sentry/cli-linux-i686": "2.51.1", - "@sentry/cli-linux-x64": "2.51.1", - "@sentry/cli-win32-arm64": "2.51.1", - "@sentry/cli-win32-i686": "2.51.1", - "@sentry/cli-win32-x64": "2.51.1" + "@sentry/cli-darwin": "2.52.0", + "@sentry/cli-linux-arm": "2.52.0", + "@sentry/cli-linux-arm64": "2.52.0", + "@sentry/cli-linux-i686": "2.52.0", + "@sentry/cli-linux-x64": "2.52.0", + "@sentry/cli-win32-arm64": "2.52.0", + "@sentry/cli-win32-i686": "2.52.0", + "@sentry/cli-win32-x64": "2.52.0" } }, "node_modules/@sentry/cli-darwin": { - "version": "2.51.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-darwin/-/cli-darwin-2.51.1.tgz", - "integrity": "sha512-R1u8IQdn/7Rr8sf6bVVr0vJT4OqwCFdYsS44Y3OoWGVJW2aAQTWRJOTlV4ueclVLAyUQzmgBjfR8AtiUhd/M5w==", + "version": "2.52.0", + "resolved": "https://registry.npmjs.org/@sentry/cli-darwin/-/cli-darwin-2.52.0.tgz", + "integrity": "sha512-ieQs/p4yTHT27nBzy0wtAb8BSISfWlpXdgsACcwXimYa36NJRwyCqgOXUaH/BYiTdwWSHpuANbUHGJW6zljzxw==", "license": "BSD-3-Clause", "optional": true, "os": [ @@ -4957,9 +4834,9 @@ } }, "node_modules/@sentry/cli-linux-arm": { - "version": "2.51.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm/-/cli-linux-arm-2.51.1.tgz", - "integrity": "sha512-Klro17OmSSKOOSaxVKBBNPXet2+HrIDZUTSp8NRl4LQsIubdc1S/aQ79cH/g52Muwzpl3aFwPxyXw+46isfEgA==", + "version": "2.52.0", + "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm/-/cli-linux-arm-2.52.0.tgz", + "integrity": "sha512-tWMLU+hj+iip5Akx+S76biAOE1eMMWTDq8c0MqMv/ahHgb6/HiVngMcUsp59Oz3EczJGbTkcnS3vRTDodEcMDw==", "cpu": [ "arm" ], @@ -4975,9 +4852,9 @@ } }, "node_modules/@sentry/cli-linux-arm64": { - "version": "2.51.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.51.1.tgz", - "integrity": "sha512-nvA/hdhsw4bKLhslgbBqqvETjXwN1FVmwHLOrRvRcejDO6zeIKUElDiL5UOjGG0NC+62AxyNw5ri8Wzp/7rg9Q==", + "version": "2.52.0", + "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.52.0.tgz", + "integrity": "sha512-RxT5uzxjCkcvplmx0bavJIEYerRex2Rg/2RAVBdVvWLKFOcmeerTn/VVxPZVuDIVMVyjlZsteWPYwfUm+Ia3wQ==", "cpu": [ "arm64" ], @@ -4993,9 +4870,9 @@ } }, "node_modules/@sentry/cli-linux-i686": { - "version": "2.51.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-linux-i686/-/cli-linux-i686-2.51.1.tgz", - "integrity": "sha512-jp4TmR8VXBdT9dLo6mHniQHN0xKnmJoPGVz9h9VDvO2Vp/8o96rBc555D4Am5wJOXmfuPlyjGcmwHlB3+kQRWw==", + "version": "2.52.0", + "resolved": "https://registry.npmjs.org/@sentry/cli-linux-i686/-/cli-linux-i686-2.52.0.tgz", + "integrity": "sha512-sKcJmIg7QWFtlNU5Bs5OZprwdIzzyYMRpFkWioPZ4TE82yvP1+2SAX31VPUlTx+7NLU6YVEWNwvSxh8LWb7iOw==", "cpu": [ "x86", "ia32" @@ -5012,9 +4889,9 @@ } }, "node_modules/@sentry/cli-linux-x64": { - "version": "2.51.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-linux-x64/-/cli-linux-x64-2.51.1.tgz", - "integrity": "sha512-JuLt0MXM2KHNFmjqXjv23sly56mJmUQzGBWktkpY3r+jE08f5NLKPd5wQ6W/SoLXGIOKnwLz0WoUg7aBVyQdeQ==", + "version": "2.52.0", + "resolved": "https://registry.npmjs.org/@sentry/cli-linux-x64/-/cli-linux-x64-2.52.0.tgz", + "integrity": "sha512-aPZ7bP02zGkuEqTiOAm4np/ggfgtzrq4ti1Xze96Csi/DV3820SCfLrPlsvcvnqq7x69IL9cI3kXjdEpgrfGxw==", "cpu": [ "x64" ], @@ -5030,9 +4907,9 @@ } }, "node_modules/@sentry/cli-win32-arm64": { - "version": "2.51.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-win32-arm64/-/cli-win32-arm64-2.51.1.tgz", - "integrity": "sha512-PiwjTdIFDazTQCTyDCutiSkt4omggYSKnO3HE1+LDjElsFrWY9pJs4fU3D40WAyE2oKu0MarjNH/WxYGdqEAlg==", + "version": "2.52.0", + "resolved": "https://registry.npmjs.org/@sentry/cli-win32-arm64/-/cli-win32-arm64-2.52.0.tgz", + "integrity": "sha512-90hrB5XdwJVhRpCmVrEcYoKW8nl5/V9OfVvOGeKUPvUkApLzvsInK74FYBZEVyAn1i/NdUv+Xk9q2zqUGK1aLQ==", "cpu": [ "arm64" ], @@ -5046,9 +4923,9 @@ } }, "node_modules/@sentry/cli-win32-i686": { - "version": "2.51.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-win32-i686/-/cli-win32-i686-2.51.1.tgz", - "integrity": "sha512-TMvZZpeiI2HmrDFNVQ0uOiTuYKvjEGOZdmUxe3WlhZW82A/2Oka7sQ24ljcOovbmBOj5+fjCHRUMYvLMCWiysA==", + "version": "2.52.0", + "resolved": "https://registry.npmjs.org/@sentry/cli-win32-i686/-/cli-win32-i686-2.52.0.tgz", + "integrity": "sha512-HXlSE4CaLylNrELx4KVmOQjV5bURCNuky6sjCWiTH7HyDqHEak2Rk8iLE0JNLj5RETWMvmaZnZZFfmyGlY1opg==", "cpu": [ "x86", "ia32" @@ -5063,9 +4940,9 @@ } }, "node_modules/@sentry/cli-win32-x64": { - "version": "2.51.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-win32-x64/-/cli-win32-x64-2.51.1.tgz", - "integrity": "sha512-v2hreYUPPTNK1/N7+DeX7XBN/zb7p539k+2Osf0HFyVBaoUC3Y3+KBwSf4ASsnmgTAK7HCGR+X0NH1vP+icw4w==", + "version": "2.52.0", + "resolved": "https://registry.npmjs.org/@sentry/cli-win32-x64/-/cli-win32-x64-2.52.0.tgz", + "integrity": "sha512-hJT0C3FwHk1Mt9oFqcci88wbO1D+yAWUL8J29HEGM5ZAqlhdh7sAtPDIC3P2LceUJOjnXihow47Bkj62juatIQ==", "cpu": [ "x64" ], @@ -5143,12 +5020,12 @@ } }, "node_modules/@sentry/webpack-plugin": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@sentry/webpack-plugin/-/webpack-plugin-4.1.0.tgz", - "integrity": "sha512-YqfDfyGAuT/9YW1kgAPfD7kGUKQCh1E5co+qMdToxi/Mz4xsWJY02rFS5GrJixYktYJfSMze8NiRr89yJMxYHw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@sentry/webpack-plugin/-/webpack-plugin-4.1.1.tgz", + "integrity": "sha512-2gFWcQMW1HdJDo/7rADeFs9crkH02l+mW4O1ORbxSjuegauyp1W8SBe7EfPoXbUmLdA3zwnpIxEXjjQpP5Etzg==", "license": "MIT", "dependencies": { - "@sentry/bundler-plugin-core": "4.1.0", + "@sentry/bundler-plugin-core": "4.1.1", "unplugin": "1.0.1", "uuid": "^9.0.0" }, @@ -5700,12 +5577,12 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.1.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.1.0.tgz", - "integrity": "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==", + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz", + "integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==", "license": "MIT", "dependencies": { - "undici-types": "~7.8.0" + "undici-types": "~7.10.0" } }, "node_modules/@types/node-forge": { @@ -5835,17 +5712,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.39.0.tgz", - "integrity": "sha512-bhEz6OZeUR+O/6yx9Jk6ohX6H9JSFTaiY0v9/PuKT3oGK0rn0jNplLmyFUGV+a9gfYnVNwGDwS/UkLIuXNb2Rw==", + "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.39.0", - "@typescript-eslint/type-utils": "8.39.0", - "@typescript-eslint/utils": "8.39.0", - "@typescript-eslint/visitor-keys": "8.39.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", @@ -5859,166 +5736,11 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.39.0", + "@typescript-eslint/parser": "^8.40.0", "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.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.39.0.tgz", - "integrity": "sha512-CTzJqaSq30V/Z2Og9jogzZt8lJRR5TKlAdXmWgdu4hgcC9Kww5flQ+xFvMxIBWVNdxJO7OifgdOK4PokMIWPew==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.39.0", - "@typescript-eslint/types": "^8.39.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": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.39.0.tgz", - "integrity": "sha512-8QOzff9UKxOh6npZQ/4FQu4mjdOCGSdO3p44ww0hk8Vu+IGbg0tB/H1LcTARRDzGCC8pDGbh2rissBuuoPgH8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.39.0", - "@typescript-eslint/visitor-keys": "8.39.0" - }, - "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.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.39.0.tgz", - "integrity": "sha512-Fd3/QjmFV2sKmvv3Mrj8r6N8CryYiCS8Wdb/6/rgOXAWGcFuc+VkQuG28uk/4kVNVZBQuuDHEDUpo/pQ32zsIQ==", - "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.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.0.tgz", - "integrity": "sha512-ArDdaOllnCj3yn/lzKn9s0pBQYmmyme/v1HbGIGB0GB/knFI3fWMHloC+oYTJW46tVbYnGKTMDK4ah1sC2v0Kg==", - "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.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.39.0.tgz", - "integrity": "sha512-ndWdiflRMvfIgQRpckQQLiB5qAKQ7w++V4LlCHwp62eym1HLB/kw7D9f2e8ytONls/jt89TEasgvb+VwnRprsw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.39.0", - "@typescript-eslint/tsconfig-utils": "8.39.0", - "@typescript-eslint/types": "8.39.0", - "@typescript-eslint/visitor-keys": "8.39.0", - "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.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.39.0.tgz", - "integrity": "sha512-4GVSvNA0Vx1Ktwvf4sFE+exxJ3QGUorQG1/A5mRfRNZtkBT2xrA/BCO2H0eALx/PnvCS6/vmYwRdDA41EoffkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.39.0", - "@typescript-eslint/types": "8.39.0", - "@typescript-eslint/typescript-estree": "8.39.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/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.39.0.tgz", - "integrity": "sha512-ldgiJ+VAhQCfIjeOgu8Kj5nSxds0ktPOSO9p4+0VDH2R2pLvQraaM5Oen2d7NxzMCm+Sn/vJT+mv2H5u6b/3fA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.39.0", - "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", @@ -6029,19 +5751,6 @@ "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", @@ -6056,16 +5765,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.39.0.tgz", - "integrity": "sha512-g3WpVQHngx0aLXn6kfIYCZxM6rRJlWzEkVpqEFLT3SgEDsp9cpCbxxgwnE504q4H+ruSDh/VGS6nqZIDynP+vg==", + "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.39.0", - "@typescript-eslint/types": "8.39.0", - "@typescript-eslint/typescript-estree": "8.39.0", - "@typescript-eslint/visitor-keys": "8.39.0", + "@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": { @@ -6080,172 +5789,15 @@ "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/project-service": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.39.0.tgz", - "integrity": "sha512-CTzJqaSq30V/Z2Og9jogzZt8lJRR5TKlAdXmWgdu4hgcC9Kww5flQ+xFvMxIBWVNdxJO7OifgdOK4PokMIWPew==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.39.0", - "@typescript-eslint/types": "^8.39.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": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.39.0.tgz", - "integrity": "sha512-8QOzff9UKxOh6npZQ/4FQu4mjdOCGSdO3p44ww0hk8Vu+IGbg0tB/H1LcTARRDzGCC8pDGbh2rissBuuoPgH8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.39.0", - "@typescript-eslint/visitor-keys": "8.39.0" - }, - "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.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.39.0.tgz", - "integrity": "sha512-Fd3/QjmFV2sKmvv3Mrj8r6N8CryYiCS8Wdb/6/rgOXAWGcFuc+VkQuG28uk/4kVNVZBQuuDHEDUpo/pQ32zsIQ==", - "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.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.0.tgz", - "integrity": "sha512-ArDdaOllnCj3yn/lzKn9s0pBQYmmyme/v1HbGIGB0GB/knFI3fWMHloC+oYTJW46tVbYnGKTMDK4ah1sC2v0Kg==", - "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.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.39.0.tgz", - "integrity": "sha512-ndWdiflRMvfIgQRpckQQLiB5qAKQ7w++V4LlCHwp62eym1HLB/kw7D9f2e8ytONls/jt89TEasgvb+VwnRprsw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.39.0", - "@typescript-eslint/tsconfig-utils": "8.39.0", - "@typescript-eslint/types": "8.39.0", - "@typescript-eslint/visitor-keys": "8.39.0", - "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.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.39.0.tgz", - "integrity": "sha512-ldgiJ+VAhQCfIjeOgu8Kj5nSxds0ktPOSO9p4+0VDH2R2pLvQraaM5Oen2d7NxzMCm+Sn/vJT+mv2H5u6b/3fA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.39.0", - "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.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz", - "integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.40.0.tgz", + "integrity": "sha512-/A89vz7Wf5DEXsGVvcGdYKbVM9F7DyFXj52lNYUDS1L9yJfqjW/fIp5PgMuEJL/KeqVTe2QSbXAGUZljDUpArw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.38.0", - "@typescript-eslint/types": "^8.38.0", + "@typescript-eslint/tsconfig-utils": "^8.40.0", + "@typescript-eslint/types": "^8.40.0", "debug": "^4.3.4" }, "engines": { @@ -6256,18 +5808,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz", - "integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.40.0.tgz", + "integrity": "sha512-y9ObStCcdCiZKzwqsE8CcpyuVMwRouJbbSrNuThDpv16dFAj429IkM6LNb1dZ2m7hK5fHyzNcErZf7CEeKXR4w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/visitor-keys": "8.38.0" + "@typescript-eslint/types": "8.40.0", + "@typescript-eslint/visitor-keys": "8.40.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6278,9 +5830,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz", - "integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.40.0.tgz", + "integrity": "sha512-jtMytmUaG9d/9kqSl/W3E3xaWESo4hFDxAIHGVW/WKKtQhesnRIJSAJO6XckluuJ6KDB5woD1EiqknriCtAmcw==", "dev": true, "license": "MIT", "engines": { @@ -6291,19 +5843,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.39.0.tgz", - "integrity": "sha512-6B3z0c1DXVT2vYA9+z9axjtc09rqKUPRmijD5m9iv8iQpHBRYRMBcgxSiKTZKm6FwWw1/cI4v6em35OsKCiN5Q==", + "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.39.0", - "@typescript-eslint/typescript-estree": "8.39.0", - "@typescript-eslint/utils": "8.39.0", + "@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" }, @@ -6319,174 +5871,6 @@ "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/project-service": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.39.0.tgz", - "integrity": "sha512-CTzJqaSq30V/Z2Og9jogzZt8lJRR5TKlAdXmWgdu4hgcC9Kww5flQ+xFvMxIBWVNdxJO7OifgdOK4PokMIWPew==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.39.0", - "@typescript-eslint/types": "^8.39.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": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/scope-manager": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.39.0.tgz", - "integrity": "sha512-8QOzff9UKxOh6npZQ/4FQu4mjdOCGSdO3p44ww0hk8Vu+IGbg0tB/H1LcTARRDzGCC8pDGbh2rissBuuoPgH8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.39.0", - "@typescript-eslint/visitor-keys": "8.39.0" - }, - "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.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.39.0.tgz", - "integrity": "sha512-Fd3/QjmFV2sKmvv3Mrj8r6N8CryYiCS8Wdb/6/rgOXAWGcFuc+VkQuG28uk/4kVNVZBQuuDHEDUpo/pQ32zsIQ==", - "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.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.0.tgz", - "integrity": "sha512-ArDdaOllnCj3yn/lzKn9s0pBQYmmyme/v1HbGIGB0GB/knFI3fWMHloC+oYTJW46tVbYnGKTMDK4ah1sC2v0Kg==", - "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.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.39.0.tgz", - "integrity": "sha512-ndWdiflRMvfIgQRpckQQLiB5qAKQ7w++V4LlCHwp62eym1HLB/kw7D9f2e8ytONls/jt89TEasgvb+VwnRprsw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.39.0", - "@typescript-eslint/tsconfig-utils": "8.39.0", - "@typescript-eslint/types": "8.39.0", - "@typescript-eslint/visitor-keys": "8.39.0", - "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.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.39.0.tgz", - "integrity": "sha512-4GVSvNA0Vx1Ktwvf4sFE+exxJ3QGUorQG1/A5mRfRNZtkBT2xrA/BCO2H0eALx/PnvCS6/vmYwRdDA41EoffkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.39.0", - "@typescript-eslint/types": "8.39.0", - "@typescript-eslint/typescript-estree": "8.39.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/type-utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.39.0.tgz", - "integrity": "sha512-ldgiJ+VAhQCfIjeOgu8Kj5nSxds0ktPOSO9p4+0VDH2R2pLvQraaM5Oen2d7NxzMCm+Sn/vJT+mv2H5u6b/3fA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.39.0", - "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", @@ -6501,9 +5885,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz", - "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.40.0.tgz", + "integrity": "sha512-ETdbFlgbAmXHyFPwqUIYrfc12ArvpBhEVgGAxVYSwli26dn8Ko+lIo4Su9vI9ykTZdJn+vJprs/0eZU0YMAEQg==", "dev": true, "license": "MIT", "engines": { @@ -6515,16 +5899,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz", - "integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.40.0.tgz", + "integrity": "sha512-k1z9+GJReVVOkc1WfVKs1vBrR5MIKKbdAjDTPvIK3L8De6KbFfPFt6BKpdkdk7rZS2GtC/m6yI5MYX+UsuvVYQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.38.0", - "@typescript-eslint/tsconfig-utils": "8.38.0", - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/visitor-keys": "8.38.0", + "@typescript-eslint/project-service": "8.40.0", + "@typescript-eslint/tsconfig-utils": "8.40.0", + "@typescript-eslint/types": "8.40.0", + "@typescript-eslint/visitor-keys": "8.40.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -6540,7 +5924,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { @@ -6570,16 +5954,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz", - "integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.40.0.tgz", + "integrity": "sha512-Cgzi2MXSZyAUOY+BFwGs17s7ad/7L+gKt6Y8rAVVWS+7o6wrjeFN4nVfTpbE25MNcxyJ+iYUXflbs2xR9h4UBg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.38.0", - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/typescript-estree": "8.38.0" + "@typescript-eslint/scope-manager": "8.40.0", + "@typescript-eslint/types": "8.40.0", + "@typescript-eslint/typescript-estree": "8.40.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6590,17 +5974,17 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz", - "integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.40.0.tgz", + "integrity": "sha512-8CZ47QwalyRjsypfwnbI3hKy5gJDPmrkLjkgMxhi0+DZZ2QNx2naS6/hWoVYUHU7LU2zleF68V9miaVZvhFfTA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/types": "8.40.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -9991,20 +9375,20 @@ } }, "node_modules/eslint": { - "version": "9.32.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.32.0.tgz", - "integrity": "sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==", + "version": "9.33.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.33.0.tgz", + "integrity": "sha512-TS9bTNIryDzStCpJN93aC5VRSW3uTx9sClUn4B87pwiCaJh220otoI0X8mJKr+VcPtniMdN8GKjlwgWGUv5ZKA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", - "@eslint/config-helpers": "^0.3.0", - "@eslint/core": "^0.15.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.32.0", - "@eslint/plugin-kit": "^0.3.4", + "@eslint/js": "9.33.0", + "@eslint/plugin-kit": "^0.3.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -10326,9 +9710,9 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.3.tgz", - "integrity": "sha512-NAdMYww51ehKfDyDhv59/eIItUVzU0Io9H2E8nHNGKEeeqlnci+1gCvrHib6EmZdf6GxF+LCV5K7UC65Ezvw7w==", + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz", + "integrity": "sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==", "dev": true, "license": "MIT", "dependencies": { @@ -10531,6 +9915,19 @@ "node": ">=10" } }, + "node_modules/eslint/node_modules/@eslint/js": { + "version": "9.33.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.33.0.tgz", + "integrity": "sha512-5K1/mKhWaMfreBGJTwval43JJmkip0RmM+3+IuqupeSKNC/Th2Kc7ucaq5ovTSra/OOKB9c58CGSz3QMVbWt0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, "node_modules/eslint/node_modules/ajv": { "version": "6.12.6", "dev": true, @@ -14333,9 +13730,9 @@ "license": "MIT" }, "node_modules/msw": { - "version": "2.10.4", - "resolved": "https://registry.npmjs.org/msw/-/msw-2.10.4.tgz", - "integrity": "sha512-6R1or/qyele7q3RyPwNuvc0IxO8L8/Aim6Sz5ncXEgcWUNxSKE+udriTOWHtpMwmfkLYlacA2y7TIx4cL5lgHA==", + "version": "2.10.5", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.10.5.tgz", + "integrity": "sha512-0EsQCrCI1HbhpBWd89DvmxY6plmvrM96b0sCIztnvcNHQbXn5vqwm1KlXslo6u4wN9LFGLC1WFjjgljcQhe40A==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -18178,9 +17575,9 @@ "license": "MIT" }, "node_modules/stylelint": { - "version": "16.23.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.23.0.tgz", - "integrity": "sha512-69T5aS2LUY306ekt1Q1oaSPwz/jaG9HjyMix3UMrai1iEbuOafBe2Dh8xlyczrxFAy89qcKyZWWtc42XLx3Bbw==", + "version": "16.23.1", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.23.1.tgz", + "integrity": "sha512-dNvDTsKV1U2YtiUDfe9d2gp902veFeo3ecCWdGlmLm2WFrAV0+L5LoOj/qHSBABQwMsZPJwfC4bf39mQm1S5zw==", "dev": true, "funding": [ { @@ -18241,9 +17638,9 @@ } }, "node_modules/stylelint-config-recommended": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-16.0.0.tgz", - "integrity": "sha512-4RSmPjQegF34wNcK1e1O3Uz91HN8P1aFdFzio90wNK9mjgAI19u5vsU868cVZboKzCaa5XbpvtTzAAGQAxpcXA==", + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-17.0.0.tgz", + "integrity": "sha512-WaMSdEiPfZTSFVoYmJbxorJfA610O0tlYuU2aEwY33UQhSPgFbClrVJYWvy3jGJx+XW37O+LyNLiZOEXhKhJmA==", "dev": true, "funding": [ { @@ -18260,26 +17657,26 @@ "node": ">=18.12.0" }, "peerDependencies": { - "stylelint": "^16.16.0" + "stylelint": "^16.23.0" } }, "node_modules/stylelint-config-recommended-scss": { - "version": "15.0.1", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-15.0.1.tgz", - "integrity": "sha512-V24bxkNkFGggqPVJlP9iXaBabwSGEG7QTz+PyxrRtjPkcF+/NsWtB3tKYvFYEmczRkWiIEfuFMhGpJFj9Fxe6Q==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-16.0.0.tgz", + "integrity": "sha512-Vh09MlGgKUwgHwuTQXPI6CbwMgXEUpjv+6EpVhZtHkGUJY7yFIoJby3Wcjd12rvdp6xOMdMIKU48Qu6KwZK+mw==", "dev": true, "license": "MIT", "dependencies": { "postcss-scss": "^4.0.9", - "stylelint-config-recommended": "^16.0.0", - "stylelint-scss": "^6.12.0" + "stylelint-config-recommended": "^17.0.0", + "stylelint-scss": "^6.12.1" }, "engines": { "node": ">=20" }, "peerDependencies": { "postcss": "^8.3.3", - "stylelint": "^16.16.0" + "stylelint": "^16.23.1" }, "peerDependenciesMeta": { "postcss": { @@ -19349,16 +18746,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.38.0.tgz", - "integrity": "sha512-FsZlrYK6bPDGoLeZRuvx2v6qrM03I0U0SnfCLPs/XCCPCFD80xU9Pg09H/K+XFa68uJuZo7l/Xhs+eDRg2l3hg==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.40.0.tgz", + "integrity": "sha512-Xvd2l+ZmFDPEt4oj1QEXzA4A2uUK6opvKu3eGN9aGjB8au02lIVcLyi375w94hHyejTOmzIU77L8ol2sRg9n7Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.38.0", - "@typescript-eslint/parser": "8.38.0", - "@typescript-eslint/typescript-estree": "8.38.0", - "@typescript-eslint/utils": "8.38.0" + "@typescript-eslint/eslint-plugin": "8.40.0", + "@typescript-eslint/parser": "8.40.0", + "@typescript-eslint/typescript-estree": "8.40.0", + "@typescript-eslint/utils": "8.40.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -19369,110 +18766,7 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz", - "integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.38.0", - "@typescript-eslint/type-utils": "8.38.0", - "@typescript-eslint/utils": "8.38.0", - "@typescript-eslint/visitor-keys": "8.38.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.38.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz", - "integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.38.0", - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/typescript-estree": "8.38.0", - "@typescript-eslint/visitor-keys": "8.38.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 <5.9.0" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/type-utils": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz", - "integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/typescript-estree": "8.38.0", - "@typescript-eslint/utils": "8.38.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 <5.9.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" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/ufo": { @@ -19500,9 +18794,9 @@ } }, "node_modules/undici-types": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", - "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", + "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { @@ -22512,15 +21806,15 @@ } }, "@eslint/config-helpers": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", - "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", "dev": true }, "@eslint/core": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", - "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", "dev": true, "requires": { "@types/json-schema": "^7.0.15" @@ -22601,12 +21895,12 @@ "dev": true }, "@eslint/plugin-kit": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", - "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", "dev": true, "requires": { - "@eslint/core": "^0.15.1", + "@eslint/core": "^0.15.2", "levn": "^0.4.1" } }, @@ -22975,9 +22269,9 @@ "optional": true }, "@patternfly/patternfly": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-6.3.0.tgz", - "integrity": "sha512-I/Z0uuaVYfv9QqNAP2/iCGUGhUb0idatNTfFLQk0VnUi2hSMgznDogvAjmlVtZu2DW0P0FymS0oo5EjL+uW6Dg==" + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-6.3.1.tgz", + "integrity": "sha512-O/lTo5EHKzer/HNzqMQOQEAMG7izDDkEHpAeJ5+sGaeQ/maB3RK7sQsOPS4DjrnMxt4/cC6LogK2mowlbf1j5Q==" }, "@patternfly/react-code-editor": { "version": "6.3.1", @@ -22985,26 +22279,11 @@ "integrity": "sha512-lzrION96CR2G3ASjE++dX/dExH08HVcCLXbHdmiiTL4eHfbqXt4edDc+UX619XrbaccJBE+BxNNGKyO8bgpKRg==", "requires": { "@monaco-editor/react": "^4.6.0", - "@patternfly/react-core": "^6.3.1", + "@patternfly/react-core": "6.3.1", "@patternfly/react-icons": "^6.3.1", "@patternfly/react-styles": "^6.3.1", "react-dropzone": "14.3.5", "tslib": "^2.8.1" - }, - "dependencies": { - "@patternfly/react-core": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/@patternfly/react-core/-/react-core-6.3.1.tgz", - "integrity": "sha512-1qV20nU4M6PA28qnikH9fPLQlkteaZZToFlATjBNBw7aUI6zIvj7U0akkHz8raWcfHAI+tAzGV7dfKjiv035/g==", - "requires": { - "@patternfly/react-icons": "^6.3.1", - "@patternfly/react-styles": "^6.3.1", - "@patternfly/react-tokens": "^6.3.1", - "focus-trap": "7.6.4", - "react-dropzone": "^14.3.5", - "tslib": "^2.8.1" - } - } } }, "@patternfly/react-component-groups": { @@ -23020,13 +22299,13 @@ } }, "@patternfly/react-core": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@patternfly/react-core/-/react-core-6.3.0.tgz", - "integrity": "sha512-TM+pLwLd5DzaDlOQhqeju9H9QUFQypQiNwXQLNIxOV5r3fmKh4NTp2Av/8WmFkpCj8mejDOfp4TNxoU1zdjCkQ==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@patternfly/react-core/-/react-core-6.3.1.tgz", + "integrity": "sha512-1qV20nU4M6PA28qnikH9fPLQlkteaZZToFlATjBNBw7aUI6zIvj7U0akkHz8raWcfHAI+tAzGV7dfKjiv035/g==", "requires": { - "@patternfly/react-icons": "^6.3.0", - "@patternfly/react-styles": "^6.3.0", - "@patternfly/react-tokens": "^6.3.0", + "@patternfly/react-icons": "^6.3.1", + "@patternfly/react-styles": "^6.3.1", + "@patternfly/react-tokens": "^6.3.1", "focus-trap": "7.6.4", "react-dropzone": "^14.3.5", "tslib": "^2.8.1" @@ -23048,27 +22327,12 @@ "resolved": "https://registry.npmjs.org/@patternfly/react-table/-/react-table-6.3.1.tgz", "integrity": "sha512-ZndBbPcMr/vInP5eELRe9m7MWzRoejRAhWx+25xOdjVAd31/CmMK1nBgZk4QAXaWjH1P+uZaZYsTgr/FMTte2g==", "requires": { - "@patternfly/react-core": "^6.3.1", + "@patternfly/react-core": "6.3.1", "@patternfly/react-icons": "6.3.1", "@patternfly/react-styles": "^6.3.1", "@patternfly/react-tokens": "^6.3.1", "lodash": "^4.17.21", "tslib": "^2.8.1" - }, - "dependencies": { - "@patternfly/react-core": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/@patternfly/react-core/-/react-core-6.3.1.tgz", - "integrity": "sha512-1qV20nU4M6PA28qnikH9fPLQlkteaZZToFlATjBNBw7aUI6zIvj7U0akkHz8raWcfHAI+tAzGV7dfKjiv035/g==", - "requires": { - "@patternfly/react-icons": "6.3.1", - "@patternfly/react-styles": "^6.3.1", - "@patternfly/react-tokens": "^6.3.1", - "focus-trap": "7.6.4", - "react-dropzone": "^14.3.5", - "tslib": "^2.8.1" - } - } } }, "@patternfly/react-tokens": { @@ -23141,13 +22405,6 @@ "@scalprum/react-core": "^0.9.1", "classnames": "^2.2.5", "sanitize-html": "^2.13.1" - }, - "dependencies": { - "@redhat-cloud-services/types": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@redhat-cloud-services/types/-/types-3.0.1.tgz", - "integrity": "sha512-GXw72DDOtKCkl2NlqkjGYVWjfm81LSA75jzbdMqKuefkxMvV4SsA0uNcauGiqZKUhq20RlNz3xBlKF3lvsfD1Q==" - } } }, "@redhat-cloud-services/frontend-components-config": { @@ -23258,59 +22515,12 @@ } }, "@redhat-cloud-services/frontend-components-notifications": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/@redhat-cloud-services/frontend-components-notifications/-/frontend-components-notifications-6.1.3.tgz", - "integrity": "sha512-GeLVfH+e6M8CLehk4U3LVoUzQ7G2pyw3SY6I8R/WBaYfBuwqBhWRHiHt/bTE+VzZo5yfBsnbKQ59JWNpYniDvA==", + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/@redhat-cloud-services/frontend-components-notifications/-/frontend-components-notifications-6.1.5.tgz", + "integrity": "sha512-CanlpBYpIKzLQZCD0Q3J5KezN0bCEeNW/rFclBPJ2CXuiyIwB0o0KRg6yfoF33V95LrDg1J3Q2DNHl8GhS+/mg==", "requires": { - "@redhat-cloud-services/frontend-components": "^6.0.0", - "@redhat-cloud-services/frontend-components-utilities": "^6.0.0" - }, - "dependencies": { - "@redhat-cloud-services/frontend-components": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@redhat-cloud-services/frontend-components/-/frontend-components-6.1.1.tgz", - "integrity": "sha512-p6nqk6pLL8nnxxIzfyQd5K6yW4EBqNZlAlgWmgSttwuuPAzHTDHFtutTvtCMK/hzlyfTnyGgjRMe2TL+wXrEcQ==", - "requires": { - "@patternfly/react-component-groups": "^6.0.0", - "@redhat-cloud-services/frontend-components-utilities": "^6.0.0", - "@redhat-cloud-services/types": "^2.0.0", - "@scalprum/core": "^0.8.1", - "@scalprum/react-core": "^0.9.1", - "classnames": "^2.2.5", - "sanitize-html": "^2.13.1" - } - }, - "@redhat-cloud-services/frontend-components-utilities": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@redhat-cloud-services/frontend-components-utilities/-/frontend-components-utilities-6.1.1.tgz", - "integrity": "sha512-ETaL9dUOyxrqjJx+QcxI5pBmDOShC6voJINlU4afhelJBfam01oMQxQYB1aPFKs2JmBDbYgT2fgT/6s/1FJS0Q==", - "requires": { - "@redhat-cloud-services/rbac-client": "^1.0.111 || 2.x", - "@redhat-cloud-services/types": "^2.0.0", - "@sentry/browser": "^7.119.1", - "awesome-debounce-promise": "^2.1.0", - "axios": "^0.28.1 || ^1.7.0", - "commander": "^2.20.3", - "mkdirp": "^1.0.4", - "p-map": "^7.0.2", - "react-content-loader": "^6.2.0" - } - }, - "@redhat-cloud-services/rbac-client": { - "version": "2.2.11", - "resolved": "https://registry.npmjs.org/@redhat-cloud-services/rbac-client/-/rbac-client-2.2.11.tgz", - "integrity": "sha512-3LLW5XtBMqcdlYJ4lAFo7MaGnHXUBP01O3kty4ocU69EPjiip6/Gtj83vAx5hkgAWX9h6ZEntTDeDuU/ZfGdKQ==", - "requires": { - "@redhat-cloud-services/javascript-clients-shared": "^1.2.4", - "axios": "^1.7.2", - "tslib": "^2.6.2" - } - }, - "p-map": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz", - "integrity": "sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==" - } + "@redhat-cloud-services/frontend-components": "^7.0.0", + "@redhat-cloud-services/frontend-components-utilities": "^7.0.0" } }, "@redhat-cloud-services/frontend-components-utilities": { @@ -23329,11 +22539,6 @@ "react-content-loader": "^6.2.0" }, "dependencies": { - "@redhat-cloud-services/types": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@redhat-cloud-services/types/-/types-3.0.1.tgz", - "integrity": "sha512-GXw72DDOtKCkl2NlqkjGYVWjfm81LSA75jzbdMqKuefkxMvV4SsA0uNcauGiqZKUhq20RlNz3xBlKF3lvsfD1Q==" - }, "p-map": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz", @@ -23383,9 +22588,9 @@ } }, "@redhat-cloud-services/types": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@redhat-cloud-services/types/-/types-2.0.0.tgz", - "integrity": "sha512-T9KvjntxMtRkNcyYCKB9OktIy7hhy24Y4n1nDZgGsysSnfWOrnmsrLUI7pyrwqsRVzjIjI3oYf7qH7DE5LcFag==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@redhat-cloud-services/types/-/types-3.0.1.tgz", + "integrity": "sha512-GXw72DDOtKCkl2NlqkjGYVWjfm81LSA75jzbdMqKuefkxMvV4SsA0uNcauGiqZKUhq20RlNz3xBlKF3lvsfD1Q==" }, "@reduxjs/toolkit": { "version": "2.8.2", @@ -23634,9 +22839,9 @@ } }, "@sentry/babel-plugin-component-annotate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-4.1.0.tgz", - "integrity": "sha512-UkcnqC7Bp9ODyoBN7BKcRotd1jz/I2vyruE/qjNfRC7UnP+jIRItUWYaXxQPON1fTw+N+egKdByk0M1y2OPv/Q==" + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-4.1.1.tgz", + "integrity": "sha512-HUpqrCK7zDVojTV6KL6BO9ZZiYrEYQqvYQrscyMsq04z+WCupXaH6YEliiNRvreR8DBJgdsG3lBRpebhUGmvfA==" }, "@sentry/browser": { "version": "7.120.3", @@ -23654,12 +22859,12 @@ } }, "@sentry/bundler-plugin-core": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@sentry/bundler-plugin-core/-/bundler-plugin-core-4.1.0.tgz", - "integrity": "sha512-/5XBtCF6M+9frEXrrvfSWOdOC2q6I1L7oY7qbUVegNkp3kYVGihNZZnJIXGzo9rmwnA0IV7jI3o0pF/HDRqPeA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@sentry/bundler-plugin-core/-/bundler-plugin-core-4.1.1.tgz", + "integrity": "sha512-Hx9RgXaD1HEYmL5aYoWwCKkVvPp4iklwfD9mvmdpQtcwLg6b6oLnPVDQaOry1ak6Pxt8smlrWcKy4IiKASlvig==", "requires": { "@babel/core": "^7.18.5", - "@sentry/babel-plugin-component-annotate": "4.1.0", + "@sentry/babel-plugin-component-annotate": "4.1.1", "@sentry/cli": "^2.51.0", "dotenv": "^16.3.1", "find-up": "^5.0.0", @@ -23695,18 +22900,18 @@ } }, "@sentry/cli": { - "version": "2.51.1", - "resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-2.51.1.tgz", - "integrity": "sha512-FU+54kNcKJABU0+ekvtnoXHM9zVrDe1zXVFbQT7mS0On0m1P0zFRGdzbnWe2XzpzuEAJXtK6aog/W+esRU9AIA==", + "version": "2.52.0", + "resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-2.52.0.tgz", + "integrity": "sha512-PXyo7Yv7+rVMSBGZfI/eFEzzhiKedTs25sDCjz4a3goAZ/F5R5tn3MKq30pnze5wNnoQmLujAa0uUjfNcWP+uQ==", "requires": { - "@sentry/cli-darwin": "2.51.1", - "@sentry/cli-linux-arm": "2.51.1", - "@sentry/cli-linux-arm64": "2.51.1", - "@sentry/cli-linux-i686": "2.51.1", - "@sentry/cli-linux-x64": "2.51.1", - "@sentry/cli-win32-arm64": "2.51.1", - "@sentry/cli-win32-i686": "2.51.1", - "@sentry/cli-win32-x64": "2.51.1", + "@sentry/cli-darwin": "2.52.0", + "@sentry/cli-linux-arm": "2.52.0", + "@sentry/cli-linux-arm64": "2.52.0", + "@sentry/cli-linux-i686": "2.52.0", + "@sentry/cli-linux-x64": "2.52.0", + "@sentry/cli-win32-arm64": "2.52.0", + "@sentry/cli-win32-i686": "2.52.0", + "@sentry/cli-win32-x64": "2.52.0", "https-proxy-agent": "^5.0.0", "node-fetch": "^2.6.7", "progress": "^2.0.3", @@ -23715,51 +22920,51 @@ } }, "@sentry/cli-darwin": { - "version": "2.51.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-darwin/-/cli-darwin-2.51.1.tgz", - "integrity": "sha512-R1u8IQdn/7Rr8sf6bVVr0vJT4OqwCFdYsS44Y3OoWGVJW2aAQTWRJOTlV4ueclVLAyUQzmgBjfR8AtiUhd/M5w==", + "version": "2.52.0", + "resolved": "https://registry.npmjs.org/@sentry/cli-darwin/-/cli-darwin-2.52.0.tgz", + "integrity": "sha512-ieQs/p4yTHT27nBzy0wtAb8BSISfWlpXdgsACcwXimYa36NJRwyCqgOXUaH/BYiTdwWSHpuANbUHGJW6zljzxw==", "optional": true }, "@sentry/cli-linux-arm": { - "version": "2.51.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm/-/cli-linux-arm-2.51.1.tgz", - "integrity": "sha512-Klro17OmSSKOOSaxVKBBNPXet2+HrIDZUTSp8NRl4LQsIubdc1S/aQ79cH/g52Muwzpl3aFwPxyXw+46isfEgA==", + "version": "2.52.0", + "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm/-/cli-linux-arm-2.52.0.tgz", + "integrity": "sha512-tWMLU+hj+iip5Akx+S76biAOE1eMMWTDq8c0MqMv/ahHgb6/HiVngMcUsp59Oz3EczJGbTkcnS3vRTDodEcMDw==", "optional": true }, "@sentry/cli-linux-arm64": { - "version": "2.51.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.51.1.tgz", - "integrity": "sha512-nvA/hdhsw4bKLhslgbBqqvETjXwN1FVmwHLOrRvRcejDO6zeIKUElDiL5UOjGG0NC+62AxyNw5ri8Wzp/7rg9Q==", + "version": "2.52.0", + "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.52.0.tgz", + "integrity": "sha512-RxT5uzxjCkcvplmx0bavJIEYerRex2Rg/2RAVBdVvWLKFOcmeerTn/VVxPZVuDIVMVyjlZsteWPYwfUm+Ia3wQ==", "optional": true }, "@sentry/cli-linux-i686": { - "version": "2.51.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-linux-i686/-/cli-linux-i686-2.51.1.tgz", - "integrity": "sha512-jp4TmR8VXBdT9dLo6mHniQHN0xKnmJoPGVz9h9VDvO2Vp/8o96rBc555D4Am5wJOXmfuPlyjGcmwHlB3+kQRWw==", + "version": "2.52.0", + "resolved": "https://registry.npmjs.org/@sentry/cli-linux-i686/-/cli-linux-i686-2.52.0.tgz", + "integrity": "sha512-sKcJmIg7QWFtlNU5Bs5OZprwdIzzyYMRpFkWioPZ4TE82yvP1+2SAX31VPUlTx+7NLU6YVEWNwvSxh8LWb7iOw==", "optional": true }, "@sentry/cli-linux-x64": { - "version": "2.51.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-linux-x64/-/cli-linux-x64-2.51.1.tgz", - "integrity": "sha512-JuLt0MXM2KHNFmjqXjv23sly56mJmUQzGBWktkpY3r+jE08f5NLKPd5wQ6W/SoLXGIOKnwLz0WoUg7aBVyQdeQ==", + "version": "2.52.0", + "resolved": "https://registry.npmjs.org/@sentry/cli-linux-x64/-/cli-linux-x64-2.52.0.tgz", + "integrity": "sha512-aPZ7bP02zGkuEqTiOAm4np/ggfgtzrq4ti1Xze96Csi/DV3820SCfLrPlsvcvnqq7x69IL9cI3kXjdEpgrfGxw==", "optional": true }, "@sentry/cli-win32-arm64": { - "version": "2.51.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-win32-arm64/-/cli-win32-arm64-2.51.1.tgz", - "integrity": "sha512-PiwjTdIFDazTQCTyDCutiSkt4omggYSKnO3HE1+LDjElsFrWY9pJs4fU3D40WAyE2oKu0MarjNH/WxYGdqEAlg==", + "version": "2.52.0", + "resolved": "https://registry.npmjs.org/@sentry/cli-win32-arm64/-/cli-win32-arm64-2.52.0.tgz", + "integrity": "sha512-90hrB5XdwJVhRpCmVrEcYoKW8nl5/V9OfVvOGeKUPvUkApLzvsInK74FYBZEVyAn1i/NdUv+Xk9q2zqUGK1aLQ==", "optional": true }, "@sentry/cli-win32-i686": { - "version": "2.51.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-win32-i686/-/cli-win32-i686-2.51.1.tgz", - "integrity": "sha512-TMvZZpeiI2HmrDFNVQ0uOiTuYKvjEGOZdmUxe3WlhZW82A/2Oka7sQ24ljcOovbmBOj5+fjCHRUMYvLMCWiysA==", + "version": "2.52.0", + "resolved": "https://registry.npmjs.org/@sentry/cli-win32-i686/-/cli-win32-i686-2.52.0.tgz", + "integrity": "sha512-HXlSE4CaLylNrELx4KVmOQjV5bURCNuky6sjCWiTH7HyDqHEak2Rk8iLE0JNLj5RETWMvmaZnZZFfmyGlY1opg==", "optional": true }, "@sentry/cli-win32-x64": { - "version": "2.51.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-win32-x64/-/cli-win32-x64-2.51.1.tgz", - "integrity": "sha512-v2hreYUPPTNK1/N7+DeX7XBN/zb7p539k+2Osf0HFyVBaoUC3Y3+KBwSf4ASsnmgTAK7HCGR+X0NH1vP+icw4w==", + "version": "2.52.0", + "resolved": "https://registry.npmjs.org/@sentry/cli-win32-x64/-/cli-win32-x64-2.52.0.tgz", + "integrity": "sha512-hJT0C3FwHk1Mt9oFqcci88wbO1D+yAWUL8J29HEGM5ZAqlhdh7sAtPDIC3P2LceUJOjnXihow47Bkj62juatIQ==", "optional": true }, "@sentry/core": { @@ -23807,11 +23012,11 @@ } }, "@sentry/webpack-plugin": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@sentry/webpack-plugin/-/webpack-plugin-4.1.0.tgz", - "integrity": "sha512-YqfDfyGAuT/9YW1kgAPfD7kGUKQCh1E5co+qMdToxi/Mz4xsWJY02rFS5GrJixYktYJfSMze8NiRr89yJMxYHw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@sentry/webpack-plugin/-/webpack-plugin-4.1.1.tgz", + "integrity": "sha512-2gFWcQMW1HdJDo/7rADeFs9crkH02l+mW4O1ORbxSjuegauyp1W8SBe7EfPoXbUmLdA3zwnpIxEXjjQpP5Etzg==", "requires": { - "@sentry/bundler-plugin-core": "4.1.0", + "@sentry/bundler-plugin-core": "4.1.1", "unplugin": "1.0.1", "uuid": "^9.0.0" }, @@ -24176,11 +23381,11 @@ "dev": true }, "@types/node": { - "version": "24.1.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.1.0.tgz", - "integrity": "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==", + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz", + "integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==", "requires": { - "undici-types": "~7.8.0" + "undici-types": "~7.10.0" } }, "@types/node-forge": { @@ -24294,114 +23499,28 @@ } }, "@typescript-eslint/eslint-plugin": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.39.0.tgz", - "integrity": "sha512-bhEz6OZeUR+O/6yx9Jk6ohX6H9JSFTaiY0v9/PuKT3oGK0rn0jNplLmyFUGV+a9gfYnVNwGDwS/UkLIuXNb2Rw==", + "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.39.0", - "@typescript-eslint/type-utils": "8.39.0", - "@typescript-eslint/utils": "8.39.0", - "@typescript-eslint/visitor-keys": "8.39.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" }, "dependencies": { - "@typescript-eslint/project-service": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.39.0.tgz", - "integrity": "sha512-CTzJqaSq30V/Z2Og9jogzZt8lJRR5TKlAdXmWgdu4hgcC9Kww5flQ+xFvMxIBWVNdxJO7OifgdOK4PokMIWPew==", - "dev": true, - "requires": { - "@typescript-eslint/tsconfig-utils": "^8.39.0", - "@typescript-eslint/types": "^8.39.0", - "debug": "^4.3.4" - } - }, - "@typescript-eslint/scope-manager": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.39.0.tgz", - "integrity": "sha512-8QOzff9UKxOh6npZQ/4FQu4mjdOCGSdO3p44ww0hk8Vu+IGbg0tB/H1LcTARRDzGCC8pDGbh2rissBuuoPgH8A==", - "dev": true, - "requires": { - "@typescript-eslint/types": "8.39.0", - "@typescript-eslint/visitor-keys": "8.39.0" - } - }, - "@typescript-eslint/tsconfig-utils": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.39.0.tgz", - "integrity": "sha512-Fd3/QjmFV2sKmvv3Mrj8r6N8CryYiCS8Wdb/6/rgOXAWGcFuc+VkQuG28uk/4kVNVZBQuuDHEDUpo/pQ32zsIQ==", - "dev": true, - "requires": {} - }, - "@typescript-eslint/types": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.0.tgz", - "integrity": "sha512-ArDdaOllnCj3yn/lzKn9s0pBQYmmyme/v1HbGIGB0GB/knFI3fWMHloC+oYTJW46tVbYnGKTMDK4ah1sC2v0Kg==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.39.0.tgz", - "integrity": "sha512-ndWdiflRMvfIgQRpckQQLiB5qAKQ7w++V4LlCHwp62eym1HLB/kw7D9f2e8ytONls/jt89TEasgvb+VwnRprsw==", - "dev": true, - "requires": { - "@typescript-eslint/project-service": "8.39.0", - "@typescript-eslint/tsconfig-utils": "8.39.0", - "@typescript-eslint/types": "8.39.0", - "@typescript-eslint/visitor-keys": "8.39.0", - "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.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.39.0.tgz", - "integrity": "sha512-4GVSvNA0Vx1Ktwvf4sFE+exxJ3QGUorQG1/A5mRfRNZtkBT2xrA/BCO2H0eALx/PnvCS6/vmYwRdDA41EoffkQ==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.39.0", - "@typescript-eslint/types": "8.39.0", - "@typescript-eslint/typescript-estree": "8.39.0" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.39.0.tgz", - "integrity": "sha512-ldgiJ+VAhQCfIjeOgu8Kj5nSxds0ktPOSO9p4+0VDH2R2pLvQraaM5Oen2d7NxzMCm+Sn/vJT+mv2H5u6b/3fA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "8.39.0", - "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", @@ -24412,228 +23531,59 @@ } }, "@typescript-eslint/parser": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.39.0.tgz", - "integrity": "sha512-g3WpVQHngx0aLXn6kfIYCZxM6rRJlWzEkVpqEFLT3SgEDsp9cpCbxxgwnE504q4H+ruSDh/VGS6nqZIDynP+vg==", + "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.39.0", - "@typescript-eslint/types": "8.39.0", - "@typescript-eslint/typescript-estree": "8.39.0", - "@typescript-eslint/visitor-keys": "8.39.0", + "@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" - }, - "dependencies": { - "@typescript-eslint/project-service": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.39.0.tgz", - "integrity": "sha512-CTzJqaSq30V/Z2Og9jogzZt8lJRR5TKlAdXmWgdu4hgcC9Kww5flQ+xFvMxIBWVNdxJO7OifgdOK4PokMIWPew==", - "dev": true, - "requires": { - "@typescript-eslint/tsconfig-utils": "^8.39.0", - "@typescript-eslint/types": "^8.39.0", - "debug": "^4.3.4" - } - }, - "@typescript-eslint/scope-manager": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.39.0.tgz", - "integrity": "sha512-8QOzff9UKxOh6npZQ/4FQu4mjdOCGSdO3p44ww0hk8Vu+IGbg0tB/H1LcTARRDzGCC8pDGbh2rissBuuoPgH8A==", - "dev": true, - "requires": { - "@typescript-eslint/types": "8.39.0", - "@typescript-eslint/visitor-keys": "8.39.0" - } - }, - "@typescript-eslint/tsconfig-utils": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.39.0.tgz", - "integrity": "sha512-Fd3/QjmFV2sKmvv3Mrj8r6N8CryYiCS8Wdb/6/rgOXAWGcFuc+VkQuG28uk/4kVNVZBQuuDHEDUpo/pQ32zsIQ==", - "dev": true, - "requires": {} - }, - "@typescript-eslint/types": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.0.tgz", - "integrity": "sha512-ArDdaOllnCj3yn/lzKn9s0pBQYmmyme/v1HbGIGB0GB/knFI3fWMHloC+oYTJW46tVbYnGKTMDK4ah1sC2v0Kg==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.39.0.tgz", - "integrity": "sha512-ndWdiflRMvfIgQRpckQQLiB5qAKQ7w++V4LlCHwp62eym1HLB/kw7D9f2e8ytONls/jt89TEasgvb+VwnRprsw==", - "dev": true, - "requires": { - "@typescript-eslint/project-service": "8.39.0", - "@typescript-eslint/tsconfig-utils": "8.39.0", - "@typescript-eslint/types": "8.39.0", - "@typescript-eslint/visitor-keys": "8.39.0", - "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.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.39.0.tgz", - "integrity": "sha512-ldgiJ+VAhQCfIjeOgu8Kj5nSxds0ktPOSO9p4+0VDH2R2pLvQraaM5Oen2d7NxzMCm+Sn/vJT+mv2H5u6b/3fA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "8.39.0", - "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": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz", - "integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.40.0.tgz", + "integrity": "sha512-/A89vz7Wf5DEXsGVvcGdYKbVM9F7DyFXj52lNYUDS1L9yJfqjW/fIp5PgMuEJL/KeqVTe2QSbXAGUZljDUpArw==", "dev": true, "requires": { - "@typescript-eslint/tsconfig-utils": "^8.38.0", - "@typescript-eslint/types": "^8.38.0", + "@typescript-eslint/tsconfig-utils": "^8.40.0", + "@typescript-eslint/types": "^8.40.0", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz", - "integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.40.0.tgz", + "integrity": "sha512-y9ObStCcdCiZKzwqsE8CcpyuVMwRouJbbSrNuThDpv16dFAj429IkM6LNb1dZ2m7hK5fHyzNcErZf7CEeKXR4w==", "dev": true, "requires": { - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/visitor-keys": "8.38.0" + "@typescript-eslint/types": "8.40.0", + "@typescript-eslint/visitor-keys": "8.40.0" } }, "@typescript-eslint/tsconfig-utils": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz", - "integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.40.0.tgz", + "integrity": "sha512-jtMytmUaG9d/9kqSl/W3E3xaWESo4hFDxAIHGVW/WKKtQhesnRIJSAJO6XckluuJ6KDB5woD1EiqknriCtAmcw==", "dev": true, "requires": {} }, "@typescript-eslint/type-utils": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.39.0.tgz", - "integrity": "sha512-6B3z0c1DXVT2vYA9+z9axjtc09rqKUPRmijD5m9iv8iQpHBRYRMBcgxSiKTZKm6FwWw1/cI4v6em35OsKCiN5Q==", + "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.39.0", - "@typescript-eslint/typescript-estree": "8.39.0", - "@typescript-eslint/utils": "8.39.0", + "@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" }, "dependencies": { - "@typescript-eslint/project-service": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.39.0.tgz", - "integrity": "sha512-CTzJqaSq30V/Z2Og9jogzZt8lJRR5TKlAdXmWgdu4hgcC9Kww5flQ+xFvMxIBWVNdxJO7OifgdOK4PokMIWPew==", - "dev": true, - "requires": { - "@typescript-eslint/tsconfig-utils": "^8.39.0", - "@typescript-eslint/types": "^8.39.0", - "debug": "^4.3.4" - } - }, - "@typescript-eslint/scope-manager": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.39.0.tgz", - "integrity": "sha512-8QOzff9UKxOh6npZQ/4FQu4mjdOCGSdO3p44ww0hk8Vu+IGbg0tB/H1LcTARRDzGCC8pDGbh2rissBuuoPgH8A==", - "dev": true, - "requires": { - "@typescript-eslint/types": "8.39.0", - "@typescript-eslint/visitor-keys": "8.39.0" - } - }, - "@typescript-eslint/tsconfig-utils": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.39.0.tgz", - "integrity": "sha512-Fd3/QjmFV2sKmvv3Mrj8r6N8CryYiCS8Wdb/6/rgOXAWGcFuc+VkQuG28uk/4kVNVZBQuuDHEDUpo/pQ32zsIQ==", - "dev": true, - "requires": {} - }, - "@typescript-eslint/types": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.0.tgz", - "integrity": "sha512-ArDdaOllnCj3yn/lzKn9s0pBQYmmyme/v1HbGIGB0GB/knFI3fWMHloC+oYTJW46tVbYnGKTMDK4ah1sC2v0Kg==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.39.0.tgz", - "integrity": "sha512-ndWdiflRMvfIgQRpckQQLiB5qAKQ7w++V4LlCHwp62eym1HLB/kw7D9f2e8ytONls/jt89TEasgvb+VwnRprsw==", - "dev": true, - "requires": { - "@typescript-eslint/project-service": "8.39.0", - "@typescript-eslint/tsconfig-utils": "8.39.0", - "@typescript-eslint/types": "8.39.0", - "@typescript-eslint/visitor-keys": "8.39.0", - "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.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.39.0.tgz", - "integrity": "sha512-4GVSvNA0Vx1Ktwvf4sFE+exxJ3QGUorQG1/A5mRfRNZtkBT2xrA/BCO2H0eALx/PnvCS6/vmYwRdDA41EoffkQ==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.39.0", - "@typescript-eslint/types": "8.39.0", - "@typescript-eslint/typescript-estree": "8.39.0" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.39.0.tgz", - "integrity": "sha512-ldgiJ+VAhQCfIjeOgu8Kj5nSxds0ktPOSO9p4+0VDH2R2pLvQraaM5Oen2d7NxzMCm+Sn/vJT+mv2H5u6b/3fA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "8.39.0", - "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", @@ -24644,21 +23594,21 @@ } }, "@typescript-eslint/types": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz", - "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.40.0.tgz", + "integrity": "sha512-ETdbFlgbAmXHyFPwqUIYrfc12ArvpBhEVgGAxVYSwli26dn8Ko+lIo4Su9vI9ykTZdJn+vJprs/0eZU0YMAEQg==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz", - "integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.40.0.tgz", + "integrity": "sha512-k1z9+GJReVVOkc1WfVKs1vBrR5MIKKbdAjDTPvIK3L8De6KbFfPFt6BKpdkdk7rZS2GtC/m6yI5MYX+UsuvVYQ==", "dev": true, "requires": { - "@typescript-eslint/project-service": "8.38.0", - "@typescript-eslint/tsconfig-utils": "8.38.0", - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/visitor-keys": "8.38.0", + "@typescript-eslint/project-service": "8.40.0", + "@typescript-eslint/tsconfig-utils": "8.40.0", + "@typescript-eslint/types": "8.40.0", + "@typescript-eslint/visitor-keys": "8.40.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -24683,24 +23633,24 @@ } }, "@typescript-eslint/utils": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz", - "integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.40.0.tgz", + "integrity": "sha512-Cgzi2MXSZyAUOY+BFwGs17s7ad/7L+gKt6Y8rAVVWS+7o6wrjeFN4nVfTpbE25MNcxyJ+iYUXflbs2xR9h4UBg==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.38.0", - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/typescript-estree": "8.38.0" + "@typescript-eslint/scope-manager": "8.40.0", + "@typescript-eslint/types": "8.40.0", + "@typescript-eslint/typescript-estree": "8.40.0" } }, "@typescript-eslint/visitor-keys": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz", - "integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.40.0.tgz", + "integrity": "sha512-8CZ47QwalyRjsypfwnbI3hKy5gJDPmrkLjkgMxhi0+DZZ2QNx2naS6/hWoVYUHU7LU2zleF68V9miaVZvhFfTA==", "dev": true, "requires": { - "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/types": "8.40.0", "eslint-visitor-keys": "^4.2.1" }, "dependencies": { @@ -26896,19 +25846,19 @@ } }, "eslint": { - "version": "9.32.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.32.0.tgz", - "integrity": "sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==", + "version": "9.33.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.33.0.tgz", + "integrity": "sha512-TS9bTNIryDzStCpJN93aC5VRSW3uTx9sClUn4B87pwiCaJh220otoI0X8mJKr+VcPtniMdN8GKjlwgWGUv5ZKA==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", - "@eslint/config-helpers": "^0.3.0", - "@eslint/core": "^0.15.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.32.0", - "@eslint/plugin-kit": "^0.3.4", + "@eslint/js": "9.33.0", + "@eslint/plugin-kit": "^0.3.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -26938,6 +25888,12 @@ "optionator": "^0.9.3" }, "dependencies": { + "@eslint/js": { + "version": "9.33.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.33.0.tgz", + "integrity": "sha512-5K1/mKhWaMfreBGJTwval43JJmkip0RmM+3+IuqupeSKNC/Th2Kc7ucaq5ovTSra/OOKB9c58CGSz3QMVbWt0A==", + "dev": true + }, "ajv": { "version": "6.12.6", "dev": true, @@ -27178,9 +26134,9 @@ } }, "eslint-plugin-prettier": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.3.tgz", - "integrity": "sha512-NAdMYww51ehKfDyDhv59/eIItUVzU0Io9H2E8nHNGKEeeqlnci+1gCvrHib6EmZdf6GxF+LCV5K7UC65Ezvw7w==", + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz", + "integrity": "sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==", "dev": true, "requires": { "prettier-linter-helpers": "^1.0.0", @@ -29666,9 +28622,9 @@ "version": "2.1.3" }, "msw": { - "version": "2.10.4", - "resolved": "https://registry.npmjs.org/msw/-/msw-2.10.4.tgz", - "integrity": "sha512-6R1or/qyele7q3RyPwNuvc0IxO8L8/Aim6Sz5ncXEgcWUNxSKE+udriTOWHtpMwmfkLYlacA2y7TIx4cL5lgHA==", + "version": "2.10.5", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.10.5.tgz", + "integrity": "sha512-0EsQCrCI1HbhpBWd89DvmxY6plmvrM96b0sCIztnvcNHQbXn5vqwm1KlXslo6u4wN9LFGLC1WFjjgljcQhe40A==", "dev": true, "requires": { "@bundled-es-modules/cookie": "^2.0.1", @@ -32129,9 +31085,9 @@ } }, "stylelint": { - "version": "16.23.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.23.0.tgz", - "integrity": "sha512-69T5aS2LUY306ekt1Q1oaSPwz/jaG9HjyMix3UMrai1iEbuOafBe2Dh8xlyczrxFAy89qcKyZWWtc42XLx3Bbw==", + "version": "16.23.1", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.23.1.tgz", + "integrity": "sha512-dNvDTsKV1U2YtiUDfe9d2gp902veFeo3ecCWdGlmLm2WFrAV0+L5LoOj/qHSBABQwMsZPJwfC4bf39mQm1S5zw==", "dev": true, "requires": { "@csstools/css-parser-algorithms": "^3.0.5", @@ -32221,21 +31177,21 @@ } }, "stylelint-config-recommended": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-16.0.0.tgz", - "integrity": "sha512-4RSmPjQegF34wNcK1e1O3Uz91HN8P1aFdFzio90wNK9mjgAI19u5vsU868cVZboKzCaa5XbpvtTzAAGQAxpcXA==", + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-17.0.0.tgz", + "integrity": "sha512-WaMSdEiPfZTSFVoYmJbxorJfA610O0tlYuU2aEwY33UQhSPgFbClrVJYWvy3jGJx+XW37O+LyNLiZOEXhKhJmA==", "dev": true, "requires": {} }, "stylelint-config-recommended-scss": { - "version": "15.0.1", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-15.0.1.tgz", - "integrity": "sha512-V24bxkNkFGggqPVJlP9iXaBabwSGEG7QTz+PyxrRtjPkcF+/NsWtB3tKYvFYEmczRkWiIEfuFMhGpJFj9Fxe6Q==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-16.0.0.tgz", + "integrity": "sha512-Vh09MlGgKUwgHwuTQXPI6CbwMgXEUpjv+6EpVhZtHkGUJY7yFIoJby3Wcjd12rvdp6xOMdMIKU48Qu6KwZK+mw==", "dev": true, "requires": { "postcss-scss": "^4.0.9", - "stylelint-config-recommended": "^16.0.0", - "stylelint-scss": "^6.12.0" + "stylelint-config-recommended": "^17.0.0", + "stylelint-scss": "^6.12.1" } }, "stylelint-scss": { @@ -32855,73 +31811,15 @@ "dev": true }, "typescript-eslint": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.38.0.tgz", - "integrity": "sha512-FsZlrYK6bPDGoLeZRuvx2v6qrM03I0U0SnfCLPs/XCCPCFD80xU9Pg09H/K+XFa68uJuZo7l/Xhs+eDRg2l3hg==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.40.0.tgz", + "integrity": "sha512-Xvd2l+ZmFDPEt4oj1QEXzA4A2uUK6opvKu3eGN9aGjB8au02lIVcLyi375w94hHyejTOmzIU77L8ol2sRg9n7Q==", "dev": true, "requires": { - "@typescript-eslint/eslint-plugin": "8.38.0", - "@typescript-eslint/parser": "8.38.0", - "@typescript-eslint/typescript-estree": "8.38.0", - "@typescript-eslint/utils": "8.38.0" - }, - "dependencies": { - "@typescript-eslint/eslint-plugin": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz", - "integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==", - "dev": true, - "requires": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.38.0", - "@typescript-eslint/type-utils": "8.38.0", - "@typescript-eslint/utils": "8.38.0", - "@typescript-eslint/visitor-keys": "8.38.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.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz", - "integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "8.38.0", - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/typescript-estree": "8.38.0", - "@typescript-eslint/visitor-keys": "8.38.0", - "debug": "^4.3.4" - } - }, - "@typescript-eslint/type-utils": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz", - "integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/typescript-estree": "8.38.0", - "@typescript-eslint/utils": "8.38.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": {} - } + "@typescript-eslint/eslint-plugin": "8.40.0", + "@typescript-eslint/parser": "8.40.0", + "@typescript-eslint/typescript-estree": "8.40.0", + "@typescript-eslint/utils": "8.40.0" } }, "ufo": { @@ -32941,9 +31839,9 @@ } }, "undici-types": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", - "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==" + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", + "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==" }, "unicode-canonical-property-names-ecmascript": { "version": "2.0.1", diff --git a/package.json b/package.json index 865bf33f..a262b49f 100644 --- a/package.json +++ b/package.json @@ -8,16 +8,17 @@ }, "dependencies": { "@ltd/j-toml": "1.38.0", - "@patternfly/patternfly": "6.3.0", + "@patternfly/patternfly": "6.3.1", "@patternfly/react-code-editor": "6.3.1", - "@patternfly/react-core": "6.3.0", + "@patternfly/react-core": "6.3.1", "@patternfly/react-table": "6.3.1", "@redhat-cloud-services/frontend-components": "7.0.3", - "@redhat-cloud-services/frontend-components-notifications": "6.1.3", + "@redhat-cloud-services/frontend-components-notifications": "6.1.5", "@redhat-cloud-services/frontend-components-utilities": "7.0.3", + "@redhat-cloud-services/types": "3.0.1", "@reduxjs/toolkit": "2.8.2", "@scalprum/react-core": "0.9.5", - "@sentry/webpack-plugin": "4.1.0", + "@sentry/webpack-plugin": "4.1.1", "@unleash/proxy-client-react": "5.0.1", "classnames": "2.5.1", "jwt-decode": "4.0.0", @@ -46,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.1.0", + "@types/node": "24.3.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.39.0", - "@typescript-eslint/parser": "8.39.0", + "@typescript-eslint/eslint-plugin": "8.40.0", + "@typescript-eslint/parser": "8.40.0", "@vitejs/plugin-react": "4.7.0", "@vitest/coverage-v8": "3.2.4", "babel-loader": "10.0.0", @@ -61,13 +62,13 @@ "chartjs-plugin-annotation": "3.1.0", "copy-webpack-plugin": "13.0.0", "css-loader": "7.1.2", - "eslint": "9.32.0", + "eslint": "9.33.0", "eslint-plugin-disable-autofix": "5.0.1", "eslint-plugin-import": "2.32.0", "eslint-plugin-jest-dom": "5.5.0", "eslint-plugin-jsx-a11y": "6.10.2", "eslint-plugin-playwright": "2.2.2", - "eslint-plugin-prettier": "5.5.3", + "eslint-plugin-prettier": "5.5.4", "eslint-plugin-react": "7.37.5", "eslint-plugin-react-hooks": "5.2.0", "eslint-plugin-react-redux": "4.2.2", @@ -80,7 +81,7 @@ "madge": "8.0.0", "mini-css-extract-plugin": "2.9.2", "moment": "2.30.1", - "msw": "2.10.4", + "msw": "2.10.5", "npm-run-all": "4.1.5", "path-browserify": "1.0.1", "postcss-scss": "4.0.9", @@ -88,12 +89,12 @@ "redux-mock-store": "1.5.5", "sass": "1.90.0", "sass-loader": "16.0.5", - "stylelint": "16.23.0", - "stylelint-config-recommended-scss": "15.0.1", + "stylelint": "16.23.1", + "stylelint-config-recommended-scss": "16.0.0", "ts-node": "10.9.2", "ts-patch": "3.3.0", "typescript": "5.8.3", - "typescript-eslint": "8.38.0", + "typescript-eslint": "8.40.0", "uuid": "11.1.0", "vitest": "3.2.4", "vitest-canvas-mock": "0.3.3", diff --git a/packit.yaml b/packit.yaml index bd019940..2cbf432b 100644 --- a/packit.yaml +++ b/packit.yaml @@ -16,6 +16,15 @@ 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 @@ -24,7 +33,6 @@ 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 new file mode 100644 index 00000000..66deae89 --- /dev/null +++ b/plans/all.fmf @@ -0,0 +1,14 @@ +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/Customizations/AAP.spec.ts b/playwright/Customizations/AAP.spec.ts new file mode 100644 index 00000000..f550257c --- /dev/null +++ b/playwright/Customizations/AAP.spec.ts @@ -0,0 +1,214 @@ +import { expect } from '@playwright/test'; +import { v4 as uuidv4 } from 'uuid'; + +import { test } from '../fixtures/customizations'; +import { isHosted } from '../helpers/helpers'; +import { ensureAuthenticated } from '../helpers/login'; +import { + ibFrame, + navigateToLandingPage, + navigateToOptionalSteps, +} from '../helpers/navHelpers'; +import { + createBlueprint, + deleteBlueprint, + exportBlueprint, + fillInDetails, + fillInImageOutputGuest, + importBlueprint, + registerLater, +} from '../helpers/wizardHelpers'; + +const validCallbackUrl = + 'https://controller.url/api/controller/v2/job_templates/9/callback/'; +const validHttpCallbackUrl = + 'http://controller.url/api/controller/v2/job_templates/9/callback/'; +const validHostConfigKey = 'hostconfigkey'; +const validCertificate = `-----BEGIN CERTIFICATE----- +MIIDXTCCAkWgAwIBAgIJAOEzx5ezZ9EIMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV +BAYTAklOMQswCQYDVQQIDAJLUjEMMAoGA1UEBwwDS1JHMRAwDgYDVQQKDAdUZXN0 +IENBMB4XDTI1MDUxNTEyMDAwMFoXDTI2MDUxNTEyMDAwMFowRTELMAkGA1UEBhMC +SU4xCzAJBgNVBAgMAktSMQwwCgYDVQQHDANSR0sxEDAOBgNVBAoMB1Rlc3QgQ0Ew +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC+R4gfN5pyJQo5qBTTtN+7 +eE9CSXZJ8SVVaE3U54IgqQoqsSoBY5QtExy7v5C6l6mW4E6dzK/JecmvTTO/BvlG +A5k2hxB6bOQxtxYwfgElH+RFWN9P4xxhtEiQgHoG1rDfnXuDJk1U3YEkCQELUebz +fF3EIDU1yR0Sz2bA+Sl2VXe8og1MEZfytq8VZUVltxtn2PfW7zI5gOllBR2sKeUc +K6h8HXN7qMgfEvsLIXxTw7fU/zA3ibcxfRCl3m6QhF8hwRh6F9Wtz2s8hCzGegV5 +z0M39nY7X8C3GZQ4Ly8v8DdY+FbEix7K3SSBRbWtdPfAHRFlX9Er2Wf8DAr7O2hH +AgMBAAGjUDBOMB0GA1UdDgQWBBTXXz2eIDgK+BhzDUAGzptn0OMcpDAfBgNVHSME +GDAWgBTXXz2eIDgK+BhzDUAGzptn0OMcpDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4IBAQAoUgY4jsuBMB3el9cc7JS2rcOhhJzn47Hj2UANfJq52g5lbjo7 +XDc7Wb3VDcV+1LzjdzayT1qO1WzHb6FDPW9L9f6h4s8lj6MvJ+xhOWgD11srdIt3 +vbQaQW4zDfeVRcKXzqbcUX8BLXAdzJPqVwZ+Z4EDjYrJ7lF9k+IqfZm0MsYX7el9 +kvdRHbLuF4Q0sZ05CXMFkhM0Ulhu4MZ+1FcsQa7nWfZzTmbjHOuWJPB4z5WwrB7z +U8YYvWJ3qxToWGbATqJxkRKGGqLrNrmwcfzgPqkpuCRYi0Kky6gJ1RvL+DRopY9x +uD+ckf3oH2wYAB6RpPRMkfVxe7lGMvq/yEZ6 +-----END CERTIFICATE-----`; +const invalidCertificate = `-----BEGIN CERTIFICATE----- +ThisIs*Not+Valid/Base64== +-----END CERTIFICATE-----`; + +test('Create a blueprint with AAP registration customization', async ({ + page, + cleanup, +}) => { + const blueprintName = 'test-' + uuidv4(); + + // Skip entirely in Cockpit/on-premise where AAP customization is unavailable + test.skip(!isHosted(), 'AAP customization is not available in the plugin'); + + // Delete the blueprint after the run fixture + await cleanup.add(() => deleteBlueprint(page, blueprintName)); + await ensureAuthenticated(page); + + // Navigate to IB landing page and get the frame + await navigateToLandingPage(page); + const frame = await ibFrame(page); + + await test.step('Navigate to optional steps in Wizard', async () => { + await navigateToOptionalSteps(frame); + await registerLater(frame); + }); + + await test.step('Select and fill the AAP step with valid configuration', async () => { + await frame + .getByRole('button', { name: 'Ansible Automation Platform' }) + .click(); + await frame + .getByRole('textbox', { name: 'ansible callback url' }) + .fill(validCallbackUrl); + await frame + .getByRole('textbox', { name: 'host config key' }) + .fill(validHostConfigKey); + await frame + .getByRole('textbox', { name: 'File upload' }) + .fill(validCertificate); + await expect(frame.getByRole('button', { name: 'Next' })).toBeEnabled(); + }); + + await test.step('Test TLS confirmation checkbox for HTTPS URLs', async () => { + // TLS confirmation checkbox should appear for HTTPS URLs + await expect( + frame.getByRole('checkbox', { + name: 'Insecure', + }), + ).toBeVisible(); + + // Check TLS confirmation and verify CA input is hidden + await frame + .getByRole('checkbox', { + name: 'Insecure', + }) + .check(); + await expect( + frame.getByRole('textbox', { name: 'File upload' }), + ).toBeHidden(); + + await frame + .getByRole('checkbox', { + name: 'Insecure', + }) + .uncheck(); + + await expect( + frame.getByRole('textbox', { name: 'File upload' }), + ).toBeVisible(); + }); + + await test.step('Test certificate validation', async () => { + await frame.getByRole('textbox', { name: 'File upload' }).clear(); + await frame + .getByRole('textbox', { name: 'File upload' }) + .fill(invalidCertificate); + await expect(frame.getByText(/Certificate.*is not valid/)).toBeVisible(); + + await frame.getByRole('textbox', { name: 'File upload' }).clear(); + await frame + .getByRole('textbox', { name: 'File upload' }) + .fill(validCertificate); + + await expect(frame.getByText('Certificate was uploaded')).toBeVisible(); + }); + + await test.step('Test HTTP URL behavior', async () => { + await frame.getByRole('textbox', { name: 'ansible callback url' }).clear(); + await frame + .getByRole('textbox', { name: 'ansible callback url' }) + .fill(validHttpCallbackUrl); + + // TLS confirmation checkbox should NOT appear for HTTP URLs + await expect( + frame.getByRole('checkbox', { + name: 'Insecure', + }), + ).toBeHidden(); + await expect( + frame.getByRole('textbox', { name: 'File upload' }), + ).toBeVisible(); + + await frame.getByRole('textbox', { name: 'ansible callback url' }).clear(); + await frame + .getByRole('textbox', { name: 'ansible callback url' }) + .fill(validCallbackUrl); + }); + + await test.step('Complete AAP configuration and proceed to review', async () => { + await frame.getByRole('button', { name: 'Review and finish' }).click(); + }); + + await test.step('Fill the BP details', async () => { + await fillInDetails(frame, blueprintName); + }); + + await test.step('Create BP', async () => { + await createBlueprint(frame, blueprintName); + }); + + await test.step('Edit BP and verify AAP configuration persists', async () => { + await frame.getByRole('button', { name: 'Edit blueprint' }).click(); + await frame.getByLabel('Revisit Ansible Automation Platform step').click(); + + await expect( + frame.getByRole('textbox', { name: 'ansible callback url' }), + ).toHaveValue(validCallbackUrl); + await expect( + frame.getByRole('textbox', { name: 'host config key' }), + ).toHaveValue(validHostConfigKey); + await expect( + frame.getByRole('textbox', { name: 'File upload' }), + ).toHaveValue(validCertificate); + + await frame.getByRole('button', { name: 'Review and finish' }).click(); + await frame + .getByRole('button', { name: 'Save changes to blueprint' }) + .click(); + }); + // This is for hosted service only as these features are not available in cockpit plugin + await test.step('Export BP', async (step) => { + step.skip(!isHosted(), 'Exporting is not available in the plugin'); + await exportBlueprint(page, blueprintName); + }); + + await test.step('Import BP', async (step) => { + step.skip(!isHosted(), 'Importing is not available in the plugin'); + await importBlueprint(page, blueprintName); + }); + + await test.step('Review imported BP', async (step) => { + step.skip(!isHosted(), 'Importing is not available in the plugin'); + await fillInImageOutputGuest(page); + await page + .getByRole('button', { name: 'Ansible Automation Platform' }) + .click(); + await expect( + page.getByRole('textbox', { name: 'ansible callback url' }), + ).toHaveValue(validCallbackUrl); + await expect( + page.getByRole('textbox', { name: 'host config key' }), + ).toBeEmpty(); + await expect( + page.getByRole('textbox', { name: 'File upload' }), + ).toHaveValue(validCertificate); + await page.getByRole('button', { name: 'Cancel' }).click(); + }); +}); diff --git a/playwright/helpers/helpers.ts b/playwright/helpers/helpers.ts index 33291ac0..9b086de7 100644 --- a/playwright/helpers/helpers.ts +++ b/playwright/helpers/helpers.ts @@ -1,3 +1,6 @@ +import { execSync } from 'child_process'; +import { readFileSync } from 'node:fs'; + import { expect, type Page } from '@playwright/test'; export const togglePreview = async (page: Page) => { @@ -42,3 +45,43 @@ 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 40c1007c..0fa6fe1d 100644 --- a/playwright/helpers/navHelpers.ts +++ b/playwright/helpers/navHelpers.ts @@ -1,6 +1,6 @@ -import type { FrameLocator, Page } from '@playwright/test'; +import { expect, FrameLocator, Page } from '@playwright/test'; -import { isHosted } from './helpers'; +import { getHostArch, getHostDistroName, isHosted } from './helpers'; /** * Opens the wizard, fills out the "Image Output" step, and navigates to the optional steps @@ -8,6 +8,13 @@ import { 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 cea02c9e..593247ae 100644 --- a/playwright/test.spec.ts +++ b/playwright/test.spec.ts @@ -72,6 +72,11 @@ test.describe.serial('test', () => { frame.getByRole('heading', { name: 'Systemd services' }); await frame.getByRole('button', { name: 'Next', exact: true }).click(); + if (isHosted()) { + frame.getByRole('heading', { name: 'Ansible Automation Platform' }); + await frame.getByRole('button', { name: 'Next', exact: true }).click(); + } + if (isHosted()) { frame.getByRole('heading', { name: 'First boot configuration' }); await frame.getByRole('button', { name: 'Next', exact: true }).click(); @@ -87,7 +92,12 @@ test.describe.serial('test', () => { await frame.getByRole('button', { name: 'Create blueprint' }).click(); await expect( - frame.locator('.pf-v6-c-card__title-text').getByText(blueprintName), + 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, + ), ).toBeVisible(); }); diff --git a/schutzbot/playwright.fmf b/schutzbot/playwright.fmf new file mode 100644 index 00000000..bbc5721a --- /dev/null +++ b/schutzbot/playwright.fmf @@ -0,0 +1,8 @@ +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 d6a5a87f..052faed2 100755 --- a/schutzbot/playwright_tests.sh +++ b/schutzbot/playwright_tests.sh @@ -1,16 +1,16 @@ #!/bin/bash set -euo pipefail -# 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 +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 sudo systemctl enable --now cockpit.socket @@ -19,10 +19,13 @@ sudo usermod -aG wheel admin echo "admin ALL=(ALL:ALL) NOPASSWD: ALL" | sudo tee "/etc/sudoers.d/admin-nopasswd" function upload_artifacts { - mkdir -p /tmp/artifacts/extra-screenshots - USER="$(whoami)" - sudo chown -R "$USER:$USER" playwright-report - mv playwright-report /tmp/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 } trap upload_artifacts EXIT @@ -73,11 +76,12 @@ 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 9a64fd4c..a3ddc921 100644 --- a/schutzbot/terraform +++ b/schutzbot/terraform @@ -1 +1 @@ -7b4735d287dd0950e0a6f47dde65b62b0f239da1 +cf0a810fd3b75fa27139746c4dfe72222e13dcba diff --git a/src/Components/Blueprints/BlueprintCard.tsx b/src/Components/Blueprints/BlueprintCard.tsx index 7a2ddb95..8858398c 100644 --- a/src/Components/Blueprints/BlueprintCard.tsx +++ b/src/Components/Blueprints/BlueprintCard.tsx @@ -50,11 +50,21 @@ const BlueprintCard = ({ blueprint }: blueprintProps) => { onChange: () => dispatch(setBlueprintId(blueprint.id)), }} > - + {isLoading && blueprint.id === selectedBlueprintId && ( )} - {blueprint.name} + { + // 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 6c1dd852..9422f8e3 100644 --- a/src/Components/Blueprints/BlueprintsSideBar.tsx +++ b/src/Components/Blueprints/BlueprintsSideBar.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useState } from 'react'; +import React, { useCallback } from 'react'; import { Bullseye, @@ -17,7 +17,6 @@ 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'; @@ -29,6 +28,7 @@ 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,16 +73,6 @@ 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 6304d98a..431b765b 100644 --- a/src/Components/Blueprints/BuildImagesButton.tsx +++ b/src/Components/Blueprints/BuildImagesButton.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useState } from 'react'; import { Button, @@ -16,11 +16,13 @@ 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 } from '../../Hooks'; +import { + useComposeBPWithNotification as useComposeBlueprintMutation, + useGetUser, +} from '../../Hooks'; import { useGetBlueprintQuery } from '../../store/backendApi'; import { selectSelectedBlueprintId } from '../../store/BlueprintSlice'; import { useAppSelector } from '../../store/hooks'; @@ -37,18 +39,7 @@ export const BuildImagesButton = ({ children }: BuildImagesButtonPropTypes) => { const { trigger: buildBlueprint, isLoading: imageBuildLoading } = useComposeBlueprintMutation(); const { analytics, auth } = useChrome(); - - 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 { userData } = useGetUser(auth); const onBuildHandler = async () => { if (selectedBlueprintId) { diff --git a/src/Components/Blueprints/DeleteBlueprintModal.tsx b/src/Components/Blueprints/DeleteBlueprintModal.tsx index afd209d5..230deeed 100644 --- a/src/Components/Blueprints/DeleteBlueprintModal.tsx +++ b/src/Components/Blueprints/DeleteBlueprintModal.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React from 'react'; import { Button, @@ -9,14 +9,16 @@ 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 } from '../../Hooks'; +import { + useDeleteBPWithNotification as useDeleteBlueprintMutation, + useGetUser, +} from '../../Hooks'; import { backendApi, useGetBlueprintsQuery } from '../../store/backendApi'; import { selectBlueprintSearchInput, @@ -42,17 +44,7 @@ export const DeleteBlueprintModal: React.FunctionComponent< const blueprintsLimit = useAppSelector(selectLimit) || PAGINATION_LIMIT; const dispatch = useAppDispatch(); const { analytics, auth } = useChrome(); - 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 { userData } = useGetUser(auth); const searchParams: GetBlueprintsApiArg = { limit: blueprintsLimit, diff --git a/src/Components/CreateImageWizard/CreateImageWizard.tsx b/src/Components/CreateImageWizard/CreateImageWizard.tsx index a2e529d3..ade9e016 100644 --- a/src/Components/CreateImageWizard/CreateImageWizard.tsx +++ b/src/Components/CreateImageWizard/CreateImageWizard.tsx @@ -15,6 +15,7 @@ import { WizardStepType } from '@patternfly/react-core/dist/esm/components/Wizar import useChrome from '@redhat-cloud-services/frontend-components/useChrome'; import { useNavigate, useSearchParams } from 'react-router-dom'; +import AAPStep from './steps/AAP'; import DetailsStep from './steps/Details'; import FileSystemStep from './steps/FileSystem'; import { FileSystemContext } from './steps/FileSystem/components/FileSystemTable'; @@ -40,6 +41,7 @@ import UsersStep from './steps/Users'; import { getHostArch, getHostDistro } from './utilities/getHostInfo'; import { useHasSpecificTargetOnly } from './utilities/hasSpecificTargetOnly'; import { + useAAPValidation, useDetailsValidation, useFilesystemValidation, useFirewallValidation, @@ -197,6 +199,7 @@ const CreateImageWizard = ({ isEdit }: CreateImageWizardProps) => { // Feature flags const complianceEnabled = useFlag('image-builder.compliance.enabled'); + const isAAPRegistrationEnabled = useFlag('image-builder.aap.enabled'); // IMPORTANT: Ensure the wizard starts with a fresh initial state useEffect(() => { @@ -283,6 +286,8 @@ const CreateImageWizard = ({ isEdit }: CreateImageWizardProps) => { const firewallValidation = useFirewallValidation(); // Services const servicesValidation = useServicesValidation(); + // AAP + const aapValidation = useAAPValidation(); // Firstboot const firstBootValidation = useFirstBootValidation(); // Details @@ -293,8 +298,10 @@ const CreateImageWizard = ({ isEdit }: CreateImageWizardProps) => { const hasWslTargetOnly = useHasSpecificTargetOnly('wsl'); let startIndex = 1; // default index + const JUMP_TO_REVIEW_STEP = 23; + if (isEdit) { - startIndex = 22; + startIndex = JUMP_TO_REVIEW_STEP; } const [wasRegisterVisited, setWasRegisterVisited] = useState(false); @@ -655,6 +662,22 @@ const CreateImageWizard = ({ isEdit }: CreateImageWizardProps) => { > , + + } + > + + , @@ -138,6 +138,7 @@ export const ValidatedInput = ({ value, placeholder, onChange, + ...props }: ValidatedTextInputPropTypes) => { const [isPristine, setIsPristine] = useState(!value ? true : false); @@ -164,6 +165,7 @@ export const ValidatedInput = ({ aria-label={ariaLabel || ''} onBlur={handleBlur} placeholder={placeholder || ''} + {...props} /> {!isPristine && !validator(value) && ( diff --git a/src/Components/CreateImageWizard/steps/AAP/components/AAPRegistration.tsx b/src/Components/CreateImageWizard/steps/AAP/components/AAPRegistration.tsx new file mode 100644 index 00000000..f4a78844 --- /dev/null +++ b/src/Components/CreateImageWizard/steps/AAP/components/AAPRegistration.tsx @@ -0,0 +1,178 @@ +import React from 'react'; + +import { + Checkbox, + DropEvent, + FileUpload, + FormGroup, + FormHelperText, + HelperText, + HelperTextItem, +} from '@patternfly/react-core'; + +import { useAppDispatch, useAppSelector } from '../../../../../store/hooks'; +import { + changeAapCallbackUrl, + changeAapHostConfigKey, + changeAapTlsCertificateAuthority, + changeAapTlsConfirmation, + selectAapCallbackUrl, + selectAapHostConfigKey, + selectAapTlsCertificateAuthority, + selectAapTlsConfirmation, +} from '../../../../../store/wizardSlice'; +import { useAAPValidation } from '../../../utilities/useValidation'; +import { ValidatedInputAndTextArea } from '../../../ValidatedInput'; +import { validateMultipleCertificates } from '../../../validators'; + +const AAPRegistration = () => { + const dispatch = useAppDispatch(); + const callbackUrl = useAppSelector(selectAapCallbackUrl); + const hostConfigKey = useAppSelector(selectAapHostConfigKey); + const tlsCertificateAuthority = useAppSelector( + selectAapTlsCertificateAuthority, + ); + const tlsConfirmation = useAppSelector(selectAapTlsConfirmation); + const [isRejected, setIsRejected] = React.useState(false); + const stepValidation = useAAPValidation(); + + const isHttpsUrl = callbackUrl?.toLowerCase().startsWith('https://') || false; + const shouldShowCaInput = !isHttpsUrl || (isHttpsUrl && !tlsConfirmation); + + const validated = stepValidation.errors['certificate'] + ? 'error' + : stepValidation.errors['certificate'] === undefined && + tlsCertificateAuthority && + validateMultipleCertificates(tlsCertificateAuthority).validCertificates + .length > 0 + ? 'success' + : 'default'; + + const handleCallbackUrlChange = (value: string) => { + dispatch(changeAapCallbackUrl(value)); + }; + + const handleHostConfigKeyChange = (value: string) => { + dispatch(changeAapHostConfigKey(value)); + }; + + const handleClear = () => { + dispatch(changeAapTlsCertificateAuthority('')); + }; + + const handleTextChange = ( + _event: React.ChangeEvent, + value: string, + ) => { + dispatch(changeAapTlsCertificateAuthority(value)); + setIsRejected(false); + }; + + const handleDataChange = (_: DropEvent, value: string) => { + dispatch(changeAapTlsCertificateAuthority(value)); + setIsRejected(false); + }; + + const handleFileRejected = () => { + dispatch(changeAapTlsCertificateAuthority('')); + setIsRejected(true); + }; + + const handleTlsConfirmationChange = (checked: boolean) => { + dispatch(changeAapTlsConfirmation(checked)); + }; + + return ( + <> + + handleCallbackUrlChange(value.trim())} + ariaLabel='ansible callback url' + isRequired + stepValidation={stepValidation} + fieldName='callbackUrl' + /> + + + + handleHostConfigKeyChange(value.trim())} + ariaLabel='host config key' + isRequired + stepValidation={stepValidation} + fieldName='hostConfigKey' + /> + + + {shouldShowCaInput && ( + + + + + + {isRejected + ? 'Must be a .PEM/.CER/.CRT file' + : validated === 'error' + ? stepValidation.errors['certificate'] + : validated === 'success' + ? 'Certificate was uploaded' + : 'Drag and drop a valid certificate file or upload one'} + + + + + )} + {isHttpsUrl && ( + + handleTlsConfirmationChange(checked)} + /> + {stepValidation.errors['tlsConfirmation'] && ( + + + + {stepValidation.errors['tlsConfirmation']} + + + + )} + + )} + + ); +}; + +export default AAPRegistration; diff --git a/src/Components/CreateImageWizard/steps/AAP/index.tsx b/src/Components/CreateImageWizard/steps/AAP/index.tsx new file mode 100644 index 00000000..7dbe0275 --- /dev/null +++ b/src/Components/CreateImageWizard/steps/AAP/index.tsx @@ -0,0 +1,18 @@ +import React from 'react'; + +import { Form, Title } from '@patternfly/react-core'; + +import AAPRegistration from './components/AAPRegistration'; + +const AAPStep = () => { + return ( +
+ + Ansible Automation Platform + + + + ); +}; + +export default AAPStep; diff --git a/src/Components/CreateImageWizard/steps/Oscap/components/OscapProfileInformation.tsx b/src/Components/CreateImageWizard/steps/Oscap/components/OscapProfileInformation.tsx index d8aa0102..a2d8a404 100644 --- a/src/Components/CreateImageWizard/steps/Oscap/components/OscapProfileInformation.tsx +++ b/src/Components/CreateImageWizard/steps/Oscap/components/OscapProfileInformation.tsx @@ -8,7 +8,10 @@ import { Spinner, } from '@patternfly/react-core'; -import { useGetOscapCustomizationsQuery } from '../../../../../store/backendApi'; +import { + useGetComplianceCustomizationsQuery, + useGetOscapCustomizationsQuery, +} from '../../../../../store/backendApi'; import { PolicyRead, usePolicyQuery } from '../../../../../store/complianceApi'; import { useAppDispatch, useAppSelector } from '../../../../../store/hooks'; import { OpenScapProfile } from '../../../../../store/imageBuilderApi'; @@ -16,6 +19,7 @@ import { changeCompliance, selectCompliancePolicyID, selectComplianceProfileID, + selectComplianceType, selectDistribution, selectFips, } from '../../../../../store/wizardSlice'; @@ -31,12 +35,29 @@ export const OscapProfileInformation = ({ const release = useAppSelector(selectDistribution); const compliancePolicyID = useAppSelector(selectCompliancePolicyID); const complianceProfileID = useAppSelector(selectComplianceProfileID); + const complianceType = useAppSelector(selectComplianceType); const fips = useAppSelector(selectFips); + const { + data: oscapPolicyInfo, + isFetching: isFetchingOscapPolicyInfo, + isSuccess: isSuccessOscapPolicyInfo, + error: policyError, + } = useGetComplianceCustomizationsQuery( + { + distribution: release, + policy: compliancePolicyID!, + }, + { + skip: !compliancePolicyID || !!process.env.IS_ON_PREMISE, + }, + ); + const { data: oscapProfileInfo, isFetching: isFetchingOscapProfileInfo, isSuccess: isSuccessOscapProfileInfo, + error: profileError, } = useGetOscapCustomizationsQuery( { distribution: release, @@ -48,6 +69,20 @@ export const OscapProfileInformation = ({ }, ); + const customizationData = + compliancePolicyID && oscapPolicyInfo ? oscapPolicyInfo : oscapProfileInfo; + const profileMetadata = oscapProfileInfo; + const isPolicyDataLoading = compliancePolicyID + ? isFetchingOscapPolicyInfo + : false; + const isFetchingOscapData = isPolicyDataLoading || isFetchingOscapProfileInfo; + const isPolicyDataSuccess = compliancePolicyID + ? isSuccessOscapPolicyInfo + : true; + const isSuccessOscapData = isPolicyDataSuccess && isSuccessOscapProfileInfo; + const hasCriticalError = profileError || (compliancePolicyID && policyError); + const shouldShowData = isSuccessOscapData && !hasCriticalError; + const { data: policyInfo, isFetching: isFetchingPolicyInfo, @@ -74,23 +109,28 @@ export const OscapProfileInformation = ({ policyTitle: pol.title, }), ); - }, [isSuccessPolicyInfo]); + }, [isSuccessPolicyInfo, dispatch, policyInfo]); - const oscapProfile = oscapProfileInfo?.openscap as OpenScapProfile; + const oscapProfile = profileMetadata?.openscap as OpenScapProfile | undefined; return ( <> - {(isFetchingOscapProfileInfo || isFetchingPolicyInfo) && ( - + {(isFetchingOscapData || isFetchingPolicyInfo) && } + {hasCriticalError && ( + + Unable to load compliance information. Please try again. + )} - {isSuccessOscapProfileInfo && ( + {shouldShowData && ( <> - Profile description + {complianceType === 'compliance' + ? 'Policy description' + : 'Profile description'} {oscapProfile?.profile_description} @@ -116,7 +156,7 @@ export const OscapProfileInformation = ({ - {(oscapProfileInfo?.packages ?? []).join(', ')} + {(customizationData?.packages ?? []).join(', ')} @@ -129,7 +169,7 @@ export const OscapProfileInformation = ({ - {oscapProfileInfo?.kernel?.append} + {customizationData?.kernel?.append} @@ -142,7 +182,7 @@ export const OscapProfileInformation = ({ - {(oscapProfileInfo?.services?.enabled ?? []).join(' ')} + {(customizationData?.services?.enabled ?? []).join(' ')} @@ -155,8 +195,8 @@ export const OscapProfileInformation = ({ - {(oscapProfileInfo?.services?.disabled ?? []) - .concat(oscapProfileInfo?.services?.masked ?? []) + {(customizationData?.services?.disabled ?? []) + .concat(customizationData?.services?.masked ?? []) .join(' ')} diff --git a/src/Components/CreateImageWizard/steps/Oscap/components/PolicySelector.tsx b/src/Components/CreateImageWizard/steps/Oscap/components/PolicySelector.tsx index 5e2cf9c8..6f407aec 100644 --- a/src/Components/CreateImageWizard/steps/Oscap/components/PolicySelector.tsx +++ b/src/Components/CreateImageWizard/steps/Oscap/components/PolicySelector.tsx @@ -10,15 +10,15 @@ import { import { useSelectorHandlers } from './useSelectorHandlers'; +import { + useGetComplianceCustomizationsQuery, + useLazyGetComplianceCustomizationsQuery, +} from '../../../../../store/backendApi'; import { PolicyRead, usePoliciesQuery, } from '../../../../../store/complianceApi'; import { useAppDispatch, useAppSelector } from '../../../../../store/hooks'; -import { - useGetOscapCustomizationsForPolicyQuery, - useLazyGetOscapCustomizationsForPolicyQuery, -} from '../../../../../store/imageBuilderApi'; import { changeCompliance, changeFileSystemConfigurationType, @@ -97,7 +97,7 @@ const PolicySelector = () => { filter: `os_major_version=${majorVersion}`, }); - const { data: currentProfileData } = useGetOscapCustomizationsForPolicyQuery( + const { data: currentProfileData } = useGetComplianceCustomizationsQuery( { distribution: release, policy: policyID!, @@ -105,7 +105,7 @@ const PolicySelector = () => { { skip: !policyID }, ); - const [trigger] = useLazyGetOscapCustomizationsForPolicyQuery(); + const [trigger] = useLazyGetComplianceCustomizationsQuery(); useEffect(() => { if (!policies || policies.data === undefined) { diff --git a/src/Components/CreateImageWizard/steps/Packages/Packages.tsx b/src/Components/CreateImageWizard/steps/Packages/Packages.tsx index f067d16c..8106fd4b 100644 --- a/src/Components/CreateImageWizard/steps/Packages/Packages.tsx +++ b/src/Components/CreateImageWizard/steps/Packages/Packages.tsx @@ -50,6 +50,7 @@ import { Thead, Tr, } from '@patternfly/react-table'; +import { orderBy } from 'lodash'; import { useDispatch } from 'react-redux'; import CustomHelperText from './components/CustomHelperText'; @@ -66,7 +67,6 @@ import { } from '../../../../constants'; import { useGetArchitecturesQuery } from '../../../../store/backendApi'; import { - ApiPackageSourcesResponse, ApiRepositoryResponseRead, ApiSearchRpmResponse, useCreateRepositoryMutation, @@ -700,7 +700,7 @@ const Packages = () => { ); } - const unpackedData: IBPackageWithRepositoryInfo[] = + let unpackedData: IBPackageWithRepositoryInfo[] = combinedPackageData.flatMap((item) => { // Spread modules into separate rows by application stream if (item.sources) { @@ -724,13 +724,16 @@ const Packages = () => { }); // group by name, but sort by application stream in descending order - unpackedData.sort((a, b) => { - if (a.name === b.name) { - return (b.stream ?? '').localeCompare(a.stream ?? ''); - } else { - return a.name.localeCompare(b.name); - } - }); + unpackedData = orderBy( + unpackedData, + [ + 'name', + (pkg) => pkg.stream || '', + (pkg) => pkg.repository || '', + (pkg) => pkg.module_name || '', + ], + ['asc', 'desc', 'asc', 'asc'], + ); if (toggleSelected === 'toggle-available') { if (activeTabKey === Repos.INCLUDED) { @@ -866,8 +869,6 @@ const Packages = () => { dispatch(addPackage(pkg)); if (pkg.type === 'module') { setActiveStream(pkg.stream || ''); - setActiveSortIndex(2); - setPage(1); dispatch( addModule({ name: pkg.module_name || '', @@ -993,7 +994,18 @@ const Packages = () => { } }; - const initialExpandedPkgs: IBPackageWithRepositoryInfo[] = []; + 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 [expandedPkgs, setExpandedPkgs] = useState(initialExpandedPkgs); const setPkgExpanded = ( @@ -1001,12 +1013,13 @@ const Packages = () => { isExpanding: boolean, ) => setExpandedPkgs((prevExpanded) => { - const otherExpandedPkgs = prevExpanded.filter((p) => p.name !== pkg.name); - return isExpanding ? [...otherExpandedPkgs, pkg] : otherExpandedPkgs; + const pkgKey = getPackageUniqueKey(pkg); + const otherExpandedPkgs = prevExpanded.filter((key) => key !== pkgKey); + return isExpanding ? [...otherExpandedPkgs, pkgKey] : otherExpandedPkgs; }); const isPkgExpanded = (pkg: IBPackageWithRepositoryInfo) => - expandedPkgs.includes(pkg); + expandedPkgs.includes(getPackageUniqueKey(pkg)); const initialExpandedGroups: GroupWithRepositoryInfo['name'][] = []; const [expandedGroups, setExpandedGroups] = useState(initialExpandedGroups); @@ -1030,51 +1043,37 @@ const Packages = () => { 'asc' | 'desc' >('asc'); - const getSortableRowValues = ( - pkg: IBPackageWithRepositoryInfo, - ): (string | number | ApiPackageSourcesResponse[] | undefined)[] => { - return [pkg.name, pkg.summary, pkg.stream, pkg.end_date, pkg.repository]; - }; + const sortedPackages = useMemo(() => { + if (!transformedPackages || !Array.isArray(transformedPackages)) { + return []; + } - 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); - } - }); + 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]); const getSortParams = (columnIndex: number) => ({ sortBy: { @@ -1100,14 +1099,14 @@ const Packages = () => { (module) => module.name === pkg.name, ); isSelected = - packages.some((p) => p.name === pkg.name) && !isModuleWithSameName; + packages.some((p) => p.name === pkg.name && p.stream === pkg.stream) && + !isModuleWithSameName; } if (pkg.type === 'module') { - // the package is selected if it's added to the packages state - // and its module stream matches one in enabled_modules + // the package is selected if its module stream matches one in enabled_modules isSelected = - packages.some((p) => p.name === pkg.name) && + packages.some((p) => p.name === pkg.name && p.stream === pkg.stream) && modules.some( (m) => m.name === pkg.module_name && m.stream === pkg.stream, ); @@ -1208,7 +1207,7 @@ const Packages = () => { .slice(computeStart(), computeEnd()) .map((grp, rowIndex) => ( @@ -1308,7 +1307,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 bd96d505..081fd54e 100644 --- a/src/Components/CreateImageWizard/steps/Registration/index.tsx +++ b/src/Components/CreateImageWizard/steps/Registration/index.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React from 'react'; import { ClipboardCopy, @@ -16,6 +16,7 @@ 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, @@ -24,18 +25,7 @@ import { const RegistrationStep = () => { const { auth } = useChrome(); - 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 { orgId } = useGetUser(auth); 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 f05d4668..ba26cfda 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, { useEffect, useState } from 'react'; +import React, { 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,19 +44,8 @@ export const CreateSaveAndBuildBtn = ({ setIsOpen, isDisabled, }: CreateDropdownProps) => { - const [userData, setUserData] = useState(undefined); - const { analytics, auth, isBeta } = useChrome(); - - 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 { userData } = useGetUser(auth); const packages = useAppSelector(selectPackages); @@ -113,17 +102,7 @@ export const CreateSaveButton = ({ isDisabled, }: CreateDropdownProps) => { const { analytics, auth, isBeta } = useChrome(); - 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 { userData } = useGetUser(auth); 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 cc025a9f..6cc19cc3 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, { useEffect, useState } from 'react'; +import React 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,19 +37,8 @@ export const EditSaveAndBuildBtn = ({ blueprintId, isDisabled, }: EditDropdownProps) => { - const [userData, setUserData] = useState(undefined); - const { analytics, auth, isBeta } = useChrome(); - - 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 { userData } = useGetUser(auth); const { trigger: buildBlueprint } = useComposeBlueprintMutation(); const packages = useAppSelector(selectPackages); @@ -105,19 +94,8 @@ export const EditSaveButton = ({ blueprintId, isDisabled, }: EditDropdownProps) => { - const [userData, setUserData] = useState(undefined); - const { analytics, auth, isBeta } = useChrome(); - - 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 { userData } = useGetUser(auth); 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 c12d590f..f6a35745 100644 --- a/src/Components/CreateImageWizard/steps/Review/Footer/Footer.tsx +++ b/src/Components/CreateImageWizard/steps/Review/Footer/Footer.tsx @@ -18,6 +18,7 @@ import { EditSaveAndBuildBtn, EditSaveButton } from './EditDropdown'; import { useCreateBPWithNotification as useCreateBlueprintMutation, + useGetUser, useUpdateBPWithNotification as useUpdateBlueprintMutation, } from '../../../../../Hooks'; import { resolveRelPath } from '../../../../../Utilities/path'; @@ -33,6 +34,7 @@ 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(); @@ -52,14 +54,12 @@ 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 should be fine on-prem, we should - // be able to ignore the `org-id` + // NOTE: This is fine for on prem because we save the org id + // to state through a form field in the registration step return mapRequestFromState(store, ''); }; diff --git a/src/Components/CreateImageWizard/steps/Review/ReviewStep.tsx b/src/Components/CreateImageWizard/steps/Review/ReviewStep.tsx index f1c3a5e6..630a7fa1 100644 --- a/src/Components/CreateImageWizard/steps/Review/ReviewStep.tsx +++ b/src/Components/CreateImageWizard/steps/Review/ReviewStep.tsx @@ -25,6 +25,7 @@ import { KernelList, LocaleList, OscapList, + RegisterAapList, RegisterLaterList, RegisterNowList, RegisterSatelliteList, @@ -42,6 +43,7 @@ import isRhel from '../../../../../src/Utilities/isRhel'; import { targetOptions } from '../../../../constants'; import { useAppSelector } from '../../../../store/hooks'; import { + selectAapRegistration, selectBlueprintDescription, selectBlueprintName, selectCompliancePolicyID, @@ -65,6 +67,7 @@ import { useHasSpecificTargetOnly } from '../../utilities/hasSpecificTargetOnly' const Review = () => { const { goToStepById } = useWizardContext(); + const aapRegistration = useAppSelector(selectAapRegistration); const blueprintName = useAppSelector(selectBlueprintName); const blueprintDescription = useAppSelector(selectBlueprintDescription); const distribution = useAppSelector(selectDistribution); @@ -83,6 +86,7 @@ const Review = () => { const users = useAppSelector(selectUsers); const kernel = useAppSelector(selectKernel); + const [isExpandedAap, setIsExpandedAap] = useState(true); const [isExpandedImageOutput, setIsExpandedImageOutput] = useState(true); const [isExpandedTargetEnvs, setIsExpandedTargetEnvs] = useState(true); const [isExpandedFSC, setIsExpandedFSC] = useState(true); @@ -101,6 +105,8 @@ const Review = () => { const [isExpandableFirstBoot, setIsExpandedFirstBoot] = useState(true); const [isExpandedUsers, setIsExpandedUsers] = useState(true); + const onToggleAap = (isExpandedAap: boolean) => + setIsExpandedAap(isExpandedAap); const onToggleImageOutput = (isExpandedImageOutput: boolean) => setIsExpandedImageOutput(isExpandedImageOutput); const onToggleTargetEnvs = (isExpandedTargetEnvs: boolean) => @@ -499,6 +505,21 @@ const Review = () => { )} + {aapRegistration.callbackUrl && ( + onToggleAap(isExpandableAap)} + isExpanded={isExpandedAap} + isIndented + data-testid='aap-expandable' + > + + + )} {!process.env.IS_ON_PREMISE && ( { ); }; +export const RegisterAapList = () => { + const callbackUrl = useAppSelector(selectAapCallbackUrl); + const hostConfigKey = useAppSelector(selectAapHostConfigKey); + const tlsCertificateAuthority = useAppSelector( + selectAapTlsCertificateAuthority, + ); + const skipTlsVerification = useAppSelector(selectAapTlsConfirmation); + + const getTlsStatus = () => { + if (skipTlsVerification) { + return 'Insecure (TLS verification skipped)'; + } + return tlsCertificateAuthority ? 'Configured' : 'None'; + }; + + return ( + + + + Ansible Callback URL + + + {callbackUrl || 'None'} + + + Host Config Key + + + {hostConfigKey || 'None'} + + + TLS Certificate + + {getTlsStatus()} + + + ); +}; + export const RegisterNowList = () => { const activationKey = useAppSelector(selectActivationKey); const registrationType = useAppSelector(selectRegistrationType); diff --git a/src/Components/CreateImageWizard/utilities/requestMapper.ts b/src/Components/CreateImageWizard/utilities/requestMapper.ts index ef7f1d87..ae2ee53a 100644 --- a/src/Components/CreateImageWizard/utilities/requestMapper.ts +++ b/src/Components/CreateImageWizard/utilities/requestMapper.ts @@ -23,6 +23,7 @@ import { CockpitUploadTypes, } from '../../../store/cockpit/types'; import { + AapRegistration, AwsUploadRequestOptions, AzureUploadRequestOptions, BlueprintExportResponse, @@ -49,6 +50,11 @@ import { ApiRepositoryImportResponseRead } from '../../../store/service/contentS import { ComplianceType, initialState, + RegistrationType, + selectAapCallbackUrl, + selectAapHostConfigKey, + selectAapTlsCertificateAuthority, + selectAapTlsConfirmation, selectActivationKey, selectArchitecture, selectAwsAccountId, @@ -205,8 +211,9 @@ function commonRequestToState( snapshot_date = ''; } + // we need to check for the region for on-prem const awsUploadOptions = aws?.upload_request - .options as AwsUploadRequestOptions; + .options as AwsUploadRequestOptions & { region?: string | undefined }; const gcpUploadOptions = gcp?.upload_request .options as GcpUploadRequestOptions; const azureUploadOptions = azure?.upload_request @@ -309,6 +316,7 @@ 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, @@ -387,14 +395,7 @@ export const mapRequestToState = (request: BlueprintResponse): wizardState => { baseUrl: request.customizations.subscription?.['base-url'] || '', }, registration: { - registrationType: - request.customizations?.subscription && isRhel(request.distribution) - ? request.customizations.subscription.rhc - ? 'register-now-rhc' - : 'register-now-insights' - : getSatelliteCommand(request.customizations.files) - ? 'register-satellite' - : 'register-later', + registrationType: getRegistrationType(request), activationKey: isRhel(request.distribution) ? request.customizations.subscription?.['activation-key'] : undefined, @@ -403,6 +404,15 @@ export const mapRequestToState = (request: BlueprintResponse): wizardState => { caCert: request.customizations.cacerts?.pem_certs[0], }, }, + aapRegistration: { + callbackUrl: + request.customizations?.aap_registration?.ansible_callback_url, + hostConfigKey: request.customizations?.aap_registration?.host_config_key, + tlsCertificateAuthority: + request.customizations?.aap_registration?.tls_certificate_authority, + skipTlsVerification: + request.customizations?.aap_registration?.skip_tls_verification, + }, ...commonRequestToState(request), }; }; @@ -452,6 +462,15 @@ export const mapExportRequestToState = ( }, env: initialState.env, registration: initialState.registration, + aapRegistration: { + callbackUrl: + request.customizations?.aap_registration?.ansible_callback_url, + hostConfigKey: request.customizations?.aap_registration?.host_config_key, + tlsCertificateAuthority: + request.customizations?.aap_registration?.tls_certificate_authority, + skipTlsVerification: + request.customizations?.aap_registration?.skip_tls_verification, + }, ...commonRequestToState(blueprintResponse), }; }; @@ -461,6 +480,24 @@ const getFirstBootScript = (files?: File[]): string => { return firstBootFile?.data ? atob(firstBootFile.data) : ''; }; +const getAapRegistration = (state: RootState): AapRegistration | undefined => { + const callbackUrl = selectAapCallbackUrl(state); + const hostConfigKey = selectAapHostConfigKey(state); + const tlsCertificateAuthority = selectAapTlsCertificateAuthority(state); + const skipTlsVerification = selectAapTlsConfirmation(state); + + if (!callbackUrl && !hostConfigKey && !tlsCertificateAuthority) { + return undefined; + } + + return { + ansible_callback_url: callbackUrl || '', + host_config_key: hostConfigKey || '', + tls_certificate_authority: tlsCertificateAuthority || undefined, + skip_tls_verification: skipTlsVerification || undefined, + }; +}; + const getImageRequests = ( state: RootState, ): ImageRequest[] | CockpitImageRequest[] => { @@ -482,6 +519,24 @@ const getImageRequests = ( })); }; +const getRegistrationType = (request: BlueprintResponse): RegistrationType => { + const subscription = request.customizations.subscription; + const distribution = request.distribution; + const files = request.customizations.files; + + if (subscription && isRhel(distribution)) { + if (subscription.rhc) { + return 'register-now-rhc'; + } else { + return 'register-now-insights'; + } + } else if (getSatelliteCommand(files)) { + return 'register-satellite'; + } else { + return 'register-later'; + } +}; + const getSatelliteCommand = (files?: File[]): string => { const satelliteCommandFile = files?.find( (file) => file.path === SATELLITE_PATH, @@ -642,6 +697,7 @@ const getCustomizations = (state: RootState, orgID: string): Customizations => { pem_certs: [satCert], } : undefined, + aap_registration: getAapRegistration(state), }; }; diff --git a/src/Components/CreateImageWizard/utilities/useValidation.tsx b/src/Components/CreateImageWizard/utilities/useValidation.tsx index 70023391..1d43e7d0 100644 --- a/src/Components/CreateImageWizard/utilities/useValidation.tsx +++ b/src/Components/CreateImageWizard/utilities/useValidation.tsx @@ -11,6 +11,10 @@ import { useAppSelector } from '../../../store/hooks'; import { BlueprintsResponse } from '../../../store/imageBuilderApi'; import { useShowActivationKeyQuery } from '../../../store/rhsmApi'; import { + selectAapCallbackUrl, + selectAapHostConfigKey, + selectAapTlsCertificateAuthority, + selectAapTlsConfirmation, selectActivationKey, selectBlueprintDescription, selectBlueprintId, @@ -54,6 +58,8 @@ import { isSshKeyValid, isUserGroupValid, isUserNameValid, + isValidUrl, + validateMultipleCertificates, } from '../validators'; export type StepValidation = { @@ -205,6 +211,62 @@ export function useRegistrationValidation(): StepValidation { return { errors: {}, disabledNext: false }; } +export function useAAPValidation(): StepValidation { + const errors: Record = {}; + const callbackUrl = useAppSelector(selectAapCallbackUrl); + const hostConfigKey = useAppSelector(selectAapHostConfigKey); + const tlsCertificateAuthority = useAppSelector( + selectAapTlsCertificateAuthority, + ); + const tlsConfirmation = useAppSelector(selectAapTlsConfirmation); + + if (!callbackUrl && !hostConfigKey && !tlsCertificateAuthority) { + return { errors: {}, disabledNext: false }; + } + if (!callbackUrl || callbackUrl.trim() === '') { + errors.callbackUrl = 'Ansible Callback URL is required'; + } else if (!isValidUrl(callbackUrl)) { + errors.callbackUrl = 'Callback URL must be a valid URL'; + } + + if (!hostConfigKey || hostConfigKey.trim() === '') { + errors.hostConfigKey = 'Host Config Key is required'; + } + + if (tlsCertificateAuthority && tlsCertificateAuthority.trim() !== '') { + const validation = validateMultipleCertificates(tlsCertificateAuthority); + if (validation.errors.length > 0) { + errors.certificate = validation.errors.join(' '); + } else if (validation.validCertificates.length === 0) { + errors.certificate = 'No valid certificates found in the input.'; + } + } + + if (callbackUrl && callbackUrl.trim() !== '') { + const isHttpsUrl = callbackUrl.toLowerCase().startsWith('https://'); + + // If URL is HTTP, require TLS certificate + if ( + !isHttpsUrl && + (!tlsCertificateAuthority || tlsCertificateAuthority.trim() === '') + ) { + errors.certificate = 'HTTP URL requires a custom TLS certificate'; + return { errors, disabledNext: true }; + } + + // For HTTPS URL, if the TLS confirmation is not checked, require certificate + if ( + !tlsConfirmation && + (!tlsCertificateAuthority || tlsCertificateAuthority.trim() === '') + ) { + errors.certificate = + 'HTTPS URL requires either a custom TLS certificate or confirmation that no custom certificate is needed'; + } + } + + return { errors, disabledNext: Object.keys(errors).length > 0 }; +} + export function useFilesystemValidation(): StepValidation { const mode = useAppSelector(selectFileSystemConfigurationType); const partitions = useAppSelector(selectPartitions); diff --git a/src/Components/CreateImageWizard/validators.ts b/src/Components/CreateImageWizard/validators.ts index b5e971b2..686f846b 100644 --- a/src/Components/CreateImageWizard/validators.ts +++ b/src/Components/CreateImageWizard/validators.ts @@ -138,3 +138,85 @@ export const isServiceValid = (service: string) => { /[a-zA-Z]+/.test(service) // contains at least one letter ); }; + +export const isValidUrl = (url: string): boolean => { + try { + const parsedUrl = new URL(url); + + const isHttpOrHttps = ['http:', 'https:'].includes(parsedUrl.protocol); + const hostname = parsedUrl.hostname; + const hasValidDomain = + /^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}(?:\.[a-zA-Z]{2,})*$/.test(hostname); + + return isHttpOrHttps && hasValidDomain; + } catch { + return false; + } +}; + +export const isValidCA = (ca: string) => { + if (!ca || typeof ca !== 'string') return false; + + const trimmed = ca.trim(); + + const pemPattern = + /^-----BEGIN CERTIFICATE-----[\r\n]+([\s\S]*?)[\r\n]+-----END CERTIFICATE-----$/; + + if (!pemPattern.test(trimmed)) { + return false; + } + + const match = trimmed.match(pemPattern); + if (!match || !match[1]) { + return false; + } + + const base64Content = match[1].replace(/[\r\n\s]/g, ''); + + const base64Pattern = /^[A-Za-z0-9+/]+(=*)$/; + return base64Pattern.test(base64Content) && base64Content.length > 0; +}; + +export const parseMultipleCertificates = (input: string): string[] => { + if (!input || typeof input !== 'string') return []; + + const blockPattern = + /-----BEGIN CERTIFICATE-----[\s\S]*?(?=-----BEGIN CERTIFICATE-----|$)/g; + + const matches = input.match(blockPattern); + return matches ? matches.map((m) => m.trim()) : []; +}; + +export const validateMultipleCertificates = ( + input: string, +): { + certificates: string[]; + validCertificates: string[]; + invalidCertificates: string[]; + errors: string[]; +} => { + const certificates = parseMultipleCertificates(input); + const validCertificates: string[] = []; + const invalidCertificates: string[] = []; + const errors: string[] = []; + + if (certificates.length === 0 && input.trim() !== '') { + errors.push( + 'No valid certificate format found. Certificates must be in PEM/DER/CER format.', + ); + return { certificates, validCertificates, invalidCertificates, errors }; + } + + certificates.forEach((cert, index) => { + if (isValidCA(cert)) { + validCertificates.push(cert); + } else { + invalidCertificates.push(cert); + errors.push( + `Certificate ${index + 1} is not valid. Must be in PEM/DER/CER format.`, + ); + } + }); + + return { certificates, validCertificates, invalidCertificates, errors }; +}; diff --git a/src/Components/ImagesTable/ImageDetails.tsx b/src/Components/ImagesTable/ImageDetails.tsx index d46b7263..21b8a0db 100644 --- a/src/Components/ImagesTable/ImageDetails.tsx +++ b/src/Components/ImagesTable/ImageDetails.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React 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 { @@ -119,7 +119,7 @@ const AwsSourceName = ({ id }: AwsSourceNamePropTypes) => { return ; }; -const parseGcpSharedWith = ( +export const parseGcpSharedWith = ( sharedWith: GcpUploadRequestOptions['share_with_accounts'], ) => { if (sharedWith) { @@ -134,19 +134,9 @@ 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(); - - 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 { userData } = useGetUser(auth); if (!isAwsUploadRequestOptions(options)) { throw TypeError( diff --git a/src/Components/ImagesTable/ImagesTable.tsx b/src/Components/ImagesTable/ImagesTable.tsx index 32782d3a..69af395c 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 { ChromeUser } from '@redhat-cloud-services/types'; +import { useFlag } from '@unleash/proxy-client-react'; import { useDispatch } from 'react-redux'; import { NavigateFunction, useNavigate } from 'react-router-dom'; @@ -58,6 +58,7 @@ import { SEARCH_INPUT, STATUS_POLLING_INTERVAL, } from '../../constants'; +import { useGetUser } from '../../Hooks'; import { useGetBlueprintComposesQuery, useGetBlueprintsQuery, @@ -87,11 +88,12 @@ 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 = @@ -104,16 +106,7 @@ const ImagesTable = () => { const blueprintsLimit = useAppSelector(selectLimit) || PAGINATION_LIMIT; const { analytics, auth } = useChrome(); - - 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 { userData } = useGetUser(auth); const searchParamsGetBlueprints: GetBlueprintsApiArg = { limit: blueprintsLimit, @@ -382,8 +375,14 @@ type AzureRowPropTypes = { }; const AzureRow = ({ compose, rowIndex }: AzureRowPropTypes) => { + const launchEofFlag = useFlag('image-builder.launcheof'); + const details = ; - const instance = ; + const instance = launchEofFlag ? ( + + ) : ( + + ); const status = ; return ( @@ -403,13 +402,18 @@ 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 = ; + const instance = launchEofFlag ? ( + + ) : ( + + ); const status = ( { const navigate = useNavigate(); - const [userData, setUserData] = useState(undefined); const { analytics, auth } = useChrome(); - - 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 { userData } = useGetUser(auth); const target = ; const status = ; @@ -553,18 +547,8 @@ const Row = ({ details, instance, }: RowPropTypes) => { - const [userData, setUserData] = useState(undefined); const { analytics, auth } = useChrome(); - - 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 { userData } = useGetUser(auth); const [isExpanded, setIsExpanded] = useState(false); const handleToggle = () => setIsExpanded(!isExpanded); diff --git a/src/Components/ImagesTable/Instance.tsx b/src/Components/ImagesTable/Instance.tsx index 5d11cfdf..254f0b05 100644 --- a/src/Components/ImagesTable/Instance.tsx +++ b/src/Components/ImagesTable/Instance.tsx @@ -1,4 +1,4 @@ -import React, { Suspense, useEffect, useState } from 'react'; +import React, { Suspense, useState } from 'react'; import path from 'path'; @@ -20,7 +20,6 @@ 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'; @@ -31,6 +30,7 @@ import { MODAL_ANCHOR, SEARCH_INPUT, } from '../../constants'; +import { useGetUser } from '../../Hooks'; import { useGetBlueprintsQuery, useGetComposeStatusQuery, @@ -54,7 +54,10 @@ import { isOciUploadStatus, } from '../../store/typeGuards'; import { resolveRelPath } from '../../Utilities/path'; +import { useFlag } from '../../Utilities/useGetEnvironment'; import useProvisioningPermissions from '../../Utilities/useProvisioningPermissions'; +import { AWSLaunchModal } from '../Launch/AWSLaunchModal'; +import { GcpLaunchModal } from '../Launch/GcpLaunchModal'; type CloudInstancePropTypes = { compose: ComposesResponseItem; @@ -97,21 +100,12 @@ const ProvisioningLink = ({ compose, composeStatus, }: ProvisioningLinkPropTypes) => { - const [userData, setUserData] = useState(undefined); + const launchEofFlag = useFlag('image-builder.launcheof'); 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 [wizardOpen, setWizardOpen] = useState(false); + const [isModalOpen, setIsModalOpen] = useState(false); const [exposedScalprumModule, error] = useLoadModule( { scope: 'provisioning', @@ -182,7 +176,7 @@ const ProvisioningLink = ({ account_id: userData?.identity.internal?.account_id || 'Not found', }); - setWizardOpen(true); + setIsModalOpen(true); }} > Launch @@ -202,6 +196,10 @@ const ProvisioningLink = ({ ); + const handleModalToggle = (_event: KeyboardEvent | React.MouseEvent) => { + setIsModalOpen(!isModalOpen); + }; + return ( <> @@ -209,7 +207,23 @@ const ProvisioningLink = ({ compose.blueprint_version !== selectedBlueprintVersion ? buttonWithTooltip : btn} - {wizardOpen && ( + {launchEofFlag && isModalOpen && provider === 'aws' && ( + + )} + {launchEofFlag && isModalOpen && provider === 'gcp' && ( + + )} + {!launchEofFlag && isModalOpen && ( setWizardOpen(false)} + onClose={() => setIsModalOpen(false)} image={{ name: compose.image_name || compose.id, id: compose.id, diff --git a/src/Components/ImagesTable/Status.tsx b/src/Components/ImagesTable/Status.tsx index b3c4ca8a..fbe08ceb 100644 --- a/src/Components/ImagesTable/Status.tsx +++ b/src/Components/ImagesTable/Status.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React 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,18 +122,8 @@ export const CloudStatus = ({ compose }: CloudStatusPropTypes) => { const { data, isSuccess } = useGetComposeStatusQuery({ composeId: compose.id, }); - const [userData, setUserData] = useState(undefined); const { analytics, auth } = useChrome(); - - 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 { userData } = useGetUser(auth); if (!isSuccess) { return ; diff --git a/src/Components/Launch/AWSLaunchModal.tsx b/src/Components/Launch/AWSLaunchModal.tsx new file mode 100644 index 00000000..c33fe0b0 --- /dev/null +++ b/src/Components/Launch/AWSLaunchModal.tsx @@ -0,0 +1,104 @@ +import React from 'react'; + +import { + Button, + List, + ListComponent, + ListItem, + Modal, + ModalBody, + ModalFooter, + ModalHeader, + ModalVariant, + OrderType, +} from '@patternfly/react-core'; +import { ExternalLinkAltIcon } from '@patternfly/react-icons'; + +import { + ComposesResponseItem, + ComposeStatus, +} from '../../store/imageBuilderApi'; +import { isAwsUploadRequestOptions } from '../../store/typeGuards'; + +type LaunchProps = { + isOpen: boolean; + handleModalToggle: (event: KeyboardEvent | React.MouseEvent) => void; + compose: ComposesResponseItem; + composeStatus: ComposeStatus | undefined; +}; + +export const AWSLaunchModal = ({ + isOpen, + handleModalToggle, + compose, + composeStatus, +}: LaunchProps) => { + const options = compose.request.image_requests[0].upload_request.options; + + if (!isAwsUploadRequestOptions(options)) { + throw TypeError( + `Error: options must be of type AwsUploadRequestOptions, not ${typeof options}.`, + ); + } + + const amiId = + composeStatus?.image_status.status === 'success' && + composeStatus.image_status.upload_status?.options && + 'ami' in composeStatus.image_status.upload_status.options + ? composeStatus.image_status.upload_status.options.ami + : ''; + + return ( + + + + + + Navigate to the{' '} + {' '} + located on your AWS console. + + + Copy the image to make it a permanent copy in your account. +
+ Shared with Account{' '} + + {options.share_with_accounts?.[0]} + +
+ AMI ID: {amiId} +
+ Launch image as an instance. + + Connect to it via SSH using the following username:{' '} + ec2-user + +
+
+ + + +
+ ); +}; diff --git a/src/Components/Launch/AzureLaunchModal.tsx b/src/Components/Launch/AzureLaunchModal.tsx new file mode 100644 index 00000000..8cfba072 --- /dev/null +++ b/src/Components/Launch/AzureLaunchModal.tsx @@ -0,0 +1,116 @@ +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/GcpLaunchModal.tsx b/src/Components/Launch/GcpLaunchModal.tsx new file mode 100644 index 00000000..3757f456 --- /dev/null +++ b/src/Components/Launch/GcpLaunchModal.tsx @@ -0,0 +1,169 @@ +import React, { useState } from 'react'; + +import { + Button, + ClipboardCopy, + ClipboardCopyVariant, + List, + ListComponent, + ListItem, + Modal, + ModalBody, + ModalFooter, + ModalHeader, + ModalVariant, + OrderType, + TextInput, +} from '@patternfly/react-core'; +import { ExternalLinkAltIcon } from '@patternfly/react-icons'; + +import { generateDefaultName } from './useGenerateDefaultName'; + +import { + ComposesResponseItem, + ComposeStatus, +} from '../../store/imageBuilderApi'; +import { + isGcpUploadRequestOptions, + isGcpUploadStatus, +} from '../../store/typeGuards'; +import { parseGcpSharedWith } from '../ImagesTable/ImageDetails'; + +type LaunchProps = { + isOpen: boolean; + handleModalToggle: (event: KeyboardEvent | React.MouseEvent) => void; + compose: ComposesResponseItem; + composeStatus: ComposeStatus | undefined; +}; + +export const GcpLaunchModal = ({ + isOpen, + handleModalToggle, + compose, + composeStatus, +}: LaunchProps) => { + const [customerProjectId, setCustomerProjectId] = useState(''); + + const statusOptions = composeStatus?.image_status.upload_status?.options; + const composeOptions = + compose.request.image_requests[0].upload_request.options; + + if ( + (statusOptions && !isGcpUploadStatus(statusOptions)) || + !isGcpUploadRequestOptions(composeOptions) + ) { + throw TypeError( + `Error: options must be of type GcpUploadRequestOptions, not ${typeof statusOptions}.`, + ); + } + + const imageName = statusOptions?.image_name; + const projectId = statusOptions?.project_id; + if (!imageName || !projectId) { + throw TypeError( + `Error: Image name not found, unable to generate a command to copy ${typeof statusOptions}.`, + ); + } + const uniqueImageName = generateDefaultName(imageName); + const authorizeString = + composeOptions.share_with_accounts && + composeOptions.share_with_accounts.length === 1 + ? `Authorize gcloud CLI to the following + account: ${parseGcpSharedWith(composeOptions.share_with_accounts)}.` + : composeOptions.share_with_accounts + ? `Authorize gcloud CLI to use one of the following + accounts: ${parseGcpSharedWith(composeOptions.share_with_accounts)}.` + : 'Authorize gcloud CLI to use the account that the image is shared with.'; + const installationCommand = `sudo dnf install google-cloud-cli`; + const createImage = `gcloud compute images create ${uniqueImageName} --source-image=${imageName} --source-image-project=${projectId} --project=${ + customerProjectId || '' + }`; + const createInstance = `gcloud compute instances create ${uniqueImageName} --image=${uniqueImageName} --project=${ + customerProjectId || '' + }`; + return ( + + + + + + Install the gcloud CLI. See the{' '} + + documentation. + + {installationCommand} + + + {authorizeString} + + Enter your GCP project ID, and run the command to create the image + in your project. + setCustomerProjectId(value)} + aria-label='Project ID input' + placeholder='Project ID' + /> + + {createImage} + + + + Create an instance of your image by either accessing the{' '} + {' '} + or by running the following command: + + {createInstance} + + + + + + + + + ); +}; diff --git a/src/Components/Launch/OciLaunchModal.tsx b/src/Components/Launch/OciLaunchModal.tsx new file mode 100644 index 00000000..fa129be3 --- /dev/null +++ b/src/Components/Launch/OciLaunchModal.tsx @@ -0,0 +1,139 @@ +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/Launch/useGenerateDefaultName.ts b/src/Components/Launch/useGenerateDefaultName.ts new file mode 100644 index 00000000..ac7a2cc6 --- /dev/null +++ b/src/Components/Launch/useGenerateDefaultName.ts @@ -0,0 +1,34 @@ +export const generateDefaultName = (imageName: string) => { + const date = new Date(); + const day = date.getDate().toString().padStart(2, '0'); + const month = (date.getMonth() + 1).toString().padStart(2, '0'); + const year = date.getFullYear().toString(); + const hours = date.getHours().toString().padStart(2, '0'); + const minutes = date.getMinutes().toString().padStart(2, '0'); + + const dateTimeString = `${month}${day}${year}-${hours}${minutes}`; + + // gcloud images are valid in the form of: (?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?) + let newBlueprintName = imageName + .toLowerCase() + .replace(/[^a-z0-9-]/g, '-') + .replace(/-{2,}/g, '-') + .replace(/^-+|-+$/g, ''); + + if (!/^[a-z]/.test(newBlueprintName)) { + newBlueprintName = 'i' + newBlueprintName; + } + + const maxLength = 63; + const uniquePartLength = dateTimeString.length + 1; + const baseNameMaxLength = maxLength - uniquePartLength; + if (newBlueprintName.length > baseNameMaxLength) { + newBlueprintName = newBlueprintName.substring(0, baseNameMaxLength); + } + + while (newBlueprintName.endsWith('-')) { + newBlueprintName = newBlueprintName.slice(0, -1); + } + + return `${newBlueprintName}-${dateTimeString}`; +}; diff --git a/src/Components/ShareImageModal/RegionsSelect.tsx b/src/Components/ShareImageModal/RegionsSelect.tsx index 13ca0dcc..abd20390 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 df6abc94..cb2f6aef 100644 --- a/src/Hooks/index.tsx +++ b/src/Hooks/index.tsx @@ -4,3 +4,4 @@ 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 new file mode 100644 index 00000000..445bdf22 --- /dev/null +++ b/src/Hooks/useGetUser.tsx @@ -0,0 +1,24 @@ +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 be4527c7..723b0714 100644 --- a/src/mockServiceWorker.js +++ b/src/mockServiceWorker.js @@ -7,7 +7,7 @@ * - Please do NOT modify this file. */ -const PACKAGE_VERSION = '2.10.4' +const PACKAGE_VERSION = '2.10.5' const INTEGRITY_CHECKSUM = 'f5825c521429caf22a4dd13b66e243af' const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') const activeClientIds = new Set() diff --git a/src/store/backendApi.ts b/src/store/backendApi.ts index 55e94173..6fa85557 100644 --- a/src/store/backendApi.ts +++ b/src/store/backendApi.ts @@ -43,6 +43,12 @@ export const useLazyGetOscapCustomizationsQuery = process.env.IS_ON_PREMISE ? cockpitQueries.useLazyGetOscapCustomizationsQuery : serviceQueries.useLazyGetOscapCustomizationsQuery; +export const useGetComplianceCustomizationsQuery = + serviceQueries.useGetOscapCustomizationsForPolicyQuery; + +export const useLazyGetComplianceCustomizationsQuery = + serviceQueries.useLazyGetOscapCustomizationsForPolicyQuery; + export const useComposeBlueprintMutation = process.env.IS_ON_PREMISE ? cockpitQueries.useComposeBlueprintMutation : serviceQueries.useComposeBlueprintMutation; diff --git a/src/store/wizardSlice.ts b/src/store/wizardSlice.ts index 2fde3b5a..ebb85bb2 100644 --- a/src/store/wizardSlice.ts +++ b/src/store/wizardSlice.ts @@ -44,7 +44,8 @@ export type RegistrationType = | 'register-now' | 'register-now-insights' | 'register-now-rhc' - | 'register-satellite'; + | 'register-satellite' + | 'register-aap'; export type ComplianceType = 'openscap' | 'compliance'; @@ -89,6 +90,12 @@ export type wizardState = { architecture: ImageRequest['architecture']; distribution: Distributions; imageTypes: ImageTypes[]; + aapRegistration: { + callbackUrl: string | undefined; + hostConfigKey: string | undefined; + tlsCertificateAuthority: string | undefined; + skipTlsVerification: boolean | undefined; + }; aws: { accountId: string; shareMethod: AwsShareMethod; @@ -189,6 +196,12 @@ export const initialState: wizardState = { architecture: X86_64, distribution: RHEL_10, imageTypes: [], + aapRegistration: { + callbackUrl: undefined, + hostConfigKey: undefined, + tlsCertificateAuthority: undefined, + skipTlsVerification: undefined, + }, aws: { accountId: '', shareMethod: 'sources', @@ -376,6 +389,26 @@ export const selectSatelliteCaCertificate = (state: RootState) => { return state.wizard.registration.satelliteRegistration.caCert; }; +export const selectAapRegistration = (state: RootState) => { + return state.wizard.aapRegistration; +}; + +export const selectAapCallbackUrl = (state: RootState) => { + return state.wizard.aapRegistration?.callbackUrl; +}; + +export const selectAapHostConfigKey = (state: RootState) => { + return state.wizard.aapRegistration?.hostConfigKey; +}; + +export const selectAapTlsCertificateAuthority = (state: RootState) => { + return state.wizard.aapRegistration?.tlsCertificateAuthority; +}; + +export const selectAapTlsConfirmation = (state: RootState) => { + return state.wizard.aapRegistration?.skipTlsVerification; +}; + export const selectComplianceProfileID = (state: RootState) => { return state.wizard.compliance.profileID; }; @@ -627,6 +660,22 @@ export const wizardSlice = createSlice({ changeSatelliteCaCertificate: (state, action: PayloadAction) => { state.registration.satelliteRegistration.caCert = action.payload; }, + changeAapCallbackUrl: (state, action: PayloadAction) => { + state.aapRegistration.callbackUrl = action.payload; + }, + + changeAapHostConfigKey: (state, action: PayloadAction) => { + state.aapRegistration.hostConfigKey = action.payload; + }, + changeAapTlsCertificateAuthority: ( + state, + action: PayloadAction, + ) => { + state.aapRegistration.tlsCertificateAuthority = action.payload; + }, + changeAapTlsConfirmation: (state, action: PayloadAction) => { + state.aapRegistration.skipTlsVerification = action.payload; + }, changeActivationKey: ( state, action: PayloadAction, @@ -1230,6 +1279,10 @@ export const { changeTimezone, changeSatelliteRegistrationCommand, changeSatelliteCaCertificate, + changeAapCallbackUrl, + changeAapHostConfigKey, + changeAapTlsCertificateAuthority, + changeAapTlsConfirmation, addNtpServer, removeNtpServer, changeHostname, diff --git a/src/test/Components/Blueprints/ImportBlueprintModal.test.tsx b/src/test/Components/Blueprints/ImportBlueprintModal.test.tsx index c2d58300..ef2dc56e 100644 --- a/src/test/Components/Blueprints/ImportBlueprintModal.test.tsx +++ b/src/test/Components/Blueprints/ImportBlueprintModal.test.tsx @@ -714,6 +714,9 @@ describe('Import modal', () => { ), ); + // AAP + await clickNext(); + // Firstboot await clickNext(); expect( diff --git a/src/test/Components/CreateImageWizard/steps/Oscap/Compliance.test.tsx b/src/test/Components/CreateImageWizard/steps/Oscap/Compliance.test.tsx index 26d78723..9b438ad2 100644 --- a/src/test/Components/CreateImageWizard/steps/Oscap/Compliance.test.tsx +++ b/src/test/Components/CreateImageWizard/steps/Oscap/Compliance.test.tsx @@ -24,6 +24,8 @@ vi.mock('@unleash/proxy-client-react', () => ({ switch (flag) { case 'image-builder.compliance.enabled': return true; + case 'image-builder.aap.enabled': + return true; default: return false; } diff --git a/src/test/Components/CreateImageWizard/steps/Oscap/Oscap.test.tsx b/src/test/Components/CreateImageWizard/steps/Oscap/Oscap.test.tsx index 6feb8098..90a3b183 100644 --- a/src/test/Components/CreateImageWizard/steps/Oscap/Oscap.test.tsx +++ b/src/test/Components/CreateImageWizard/steps/Oscap/Oscap.test.tsx @@ -332,4 +332,30 @@ describe('OpenSCAP edit mode', () => { user.click(selectedBtn); await screen.findByText('neovim'); }); + + test('customized policy shows only non-removed rules', async () => { + const { oscapCustomizations, oscapCustomizationsPolicy } = await import( + '../../../../fixtures/oscap' + ); + + const profileId = 'xccdf_org.ssgproject.content_profile_cis_workstation_l1'; + const normalProfile = oscapCustomizations(profileId); + expect(normalProfile.packages).toEqual(['aide', 'neovim']); + const customPolicy = oscapCustomizationsPolicy('custom-policy-123'); + expect(customPolicy.packages).toEqual(['neovim']); + await renderCreateMode(); + await selectRhel9(); + await selectGuestImageTarget(); + await goToOscapStep(); + await selectProfile(); + + await waitFor(() => { + expect(screen.getByText(/aide, neovim/i)).toBeInTheDocument(); + }); + + expect(customPolicy.packages).not.toContain('aide'); + expect(customPolicy.packages).toContain('neovim'); + expect(normalProfile.packages).toContain('aide'); + expect(normalProfile.packages).toContain('neovim'); + }); }); diff --git a/src/test/Components/CreateImageWizard/steps/Packages/Packages.test.tsx b/src/test/Components/CreateImageWizard/steps/Packages/Packages.test.tsx index 85a2a12e..42fecc29 100644 --- a/src/test/Components/CreateImageWizard/steps/Packages/Packages.test.tsx +++ b/src/test/Components/CreateImageWizard/steps/Packages/Packages.test.tsx @@ -513,6 +513,123 @@ 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/Components/CreateImageWizard/steps/Services/Services.test.tsx b/src/test/Components/CreateImageWizard/steps/Services/Services.test.tsx index 4a97876a..bdfac014 100644 --- a/src/test/Components/CreateImageWizard/steps/Services/Services.test.tsx +++ b/src/test/Components/CreateImageWizard/steps/Services/Services.test.tsx @@ -109,12 +109,12 @@ describe('Step Services', () => { router = undefined; }); - test('clicking Next loads First boot script', async () => { + test('clicking Next loads Ansible Automation Platform', async () => { await renderCreateMode(); await goToServicesStep(); await clickNext(); await screen.findByRole('heading', { - name: 'First boot configuration', + name: 'Ansible Automation Platform', }); }); diff --git a/src/test/fixtures/compliance.ts b/src/test/fixtures/compliance.ts index 5556352f..83afabd7 100644 --- a/src/test/fixtures/compliance.ts +++ b/src/test/fixtures/compliance.ts @@ -36,8 +36,20 @@ export const mockPolicies = { profile_title: 'DISA STIG with GUI for Red Hat Enterprise Linux 8', ref_id: 'xccdf_org.ssgproject.content_profile_stig_gui', }, + { + id: 'custom-policy-123', + title: 'Custom CIS Policy (Partial Rules)', + description: 'A customized policy where user removed some rules', + compliance_threshold: 100, + total_system_count: 5, + type: 'policy', + os_major_version: 8, + profile_title: + 'Custom CIS Red Hat Enterprise Linux 8 Benchmark for Level 1 - Workstation', + ref_id: 'xccdf_org.ssgproject.content_profile_cis_workstation_l1', + }, ], meta: { - total: 3, + total: 4, }, }; diff --git a/src/test/fixtures/oscap.ts b/src/test/fixtures/oscap.ts index 6f807460..de50ce9b 100644 --- a/src/test/fixtures/oscap.ts +++ b/src/test/fixtures/oscap.ts @@ -124,9 +124,17 @@ export const oscapCustomizationsPolicy = ( ): GetOscapCustomizationsApiResponse => { const policyData = mockPolicies.data.find((p) => p.id === policy); const customizations = oscapCustomizations(policyData!.ref_id); - // filter out a single package to simulate the customizations being tailored - customizations.packages = customizations.packages!.filter( - (p) => p !== 'aide', - ); + + // Simulate different levels of customization based on policy + if (policy === 'custom-policy-123') { + // This policy has user-customized rules - only neovim remains + customizations.packages = ['neovim']; // User removed aide package + } else { + // Other policies: filter out a single package to simulate basic customizations + customizations.packages = customizations.packages!.filter( + (p) => p !== 'aide', + ); + } + return customizations; }; diff --git a/src/test/fixtures/packages.ts b/src/test/fixtures/packages.ts index 0f494415..08aefb20 100644 --- a/src/test/fixtures/packages.ts +++ b/src/test/fixtures/packages.ts @@ -75,6 +75,64 @@ 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 [ { diff --git a/src/test/setup.ts b/src/test/setup.ts index 067ab8d1..e15d20b7 100644 --- a/src/test/setup.ts +++ b/src/test/setup.ts @@ -64,6 +64,8 @@ vi.mock('@unleash/proxy-client-react', () => ({ return true; case 'image-builder.templates.enabled': return true; + case 'image-builder.aap.enabled': + return true; default: return false; }