diff --git a/.github/workflows/commitlint.yml b/.github/workflows/commitlint.yml index 15d84dfbb..c3579d2a6 100644 --- a/.github/workflows/commitlint.yml +++ b/.github/workflows/commitlint.yml @@ -4,6 +4,7 @@ on: pull_request: types: - opened + - edited - reopened - synchronize - ready_for_review @@ -21,7 +22,7 @@ jobs: with: node-version: lts/* - - uses: actions/cache@v4 + - uses: actions/cache@v5 with: path: ~/.npm key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }} @@ -29,5 +30,7 @@ jobs: - name: Install npm packages run: npm ci - - name: Validate PR commits with commitlint - run: npx commitlint --from ${{ github.event.pull_request.base.sha }} --verbose + - name: Lint PR title + env: + PR_TITLE: ${{ github.event.pull_request.title }} + run: echo "$PR_TITLE" | npx commitlint --verbose \ No newline at end of file diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 308b8bcad..5f6f2e4c0 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -13,7 +13,7 @@ jobs: with: node-version: lts/* - name: Cache .npm - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ~/.npm key: ${{ runner.os }}-node-${{ hashFiles('lapis-e2e/**/package-lock.json') }} @@ -57,10 +57,15 @@ jobs: WEBSITE_TAG: ${{ steps.branchTag.outputs.branchTag }} BACKEND_TAG: ${{ steps.branchTag.outputs.branchTag }} - - name: Run Playwright tests + - name: Run Playwright tests (all browsers) + if: github.ref == 'refs/heads/main' run: npm run e2e - - uses: actions/upload-artifact@v4 + - name: Run Playwright tests (chromium only) + if: github.ref != 'refs/heads/main' + run: npm run e2e:chromium + + - uses: actions/upload-artifact@v6 if: ${{ !cancelled() }} with: name: playwright-report diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml index dd947f1aa..6ba0d4a9b 100644 --- a/.github/workflows/website.yml +++ b/.github/workflows/website.yml @@ -17,7 +17,7 @@ jobs: - uses: actions/setup-node@v6 with: node-version: lts/* - - uses: actions/cache@v4 + - uses: actions/cache@v5 with: path: ~/.npm key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }} @@ -37,8 +37,8 @@ jobs: - uses: actions/checkout@v6 - uses: actions/setup-node@v6 with: - node-version: 22 - - uses: actions/cache@v4 + node-version: lts/* + - uses: actions/cache@v5 with: path: ~/.npm key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c3bbffc9a..bda464925 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,9 @@ Contributions are very welcome! Just fork the repository, develop in a branch an ## Commit Messages -We follow [conventional commits](https://www.conventionalcommits.org) when writing commit messages. +Commits on the `main` branch should follow [conventional commits](https://www.conventionalcommits.org). +Since we squash-merge pull requests, the PR title should also follow conventional commits +(because it will become the commit message of the squashed commit). The messages themselves should help future developers understand **why** changes were made. ## Code Style diff --git a/Dockerfile_website b/Dockerfile_website index 02b5f0977..823df38e9 100644 --- a/Dockerfile_website +++ b/Dockerfile_website @@ -1,6 +1,6 @@ # Dockerfile for the website # This is here so that the build context can access the backend config files -FROM node:22-alpine AS base +FROM node:24-alpine AS base WORKDIR /app COPY website/package.json website/package-lock.json website/patches ./ diff --git a/package.json b/package.json index 4b7274c65..007e273f1 100644 --- a/package.json +++ b/package.json @@ -8,9 +8,5 @@ "@commitlint/cli": "^19.4.0", "@commitlint/config-conventional": "^19.2.2", "@commitlint/types": "^19.0.3" - }, - "scripts": { - "commitlint:last-commit": "commitlint --from=HEAD~1 --verbose", - "commitlint:merge-base": "commitlint --from=$(git merge-base HEAD main) --verbose" } } diff --git a/website/package-lock.json b/website/package-lock.json index c4becf25d..7d73e8fb0 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -9,64 +9,64 @@ "version": "0.0.1", "hasInstallScript": true, "dependencies": { - "@astrojs/node": "^9.4.4", + "@astrojs/node": "^9.5.1", "@auth/core": "^0.37.4", - "@genspectrum/dashboard-components": "^1.11.1", - "@tanstack/react-query": "^5.90.1", - "astro": "^5.13.10", + "@genspectrum/dashboard-components": "^1.12.0", + "@tanstack/react-query": "^5.90.16", + "astro": "^5.16.9", "auth-astro": "^4.2.0", - "axios": "^1.12.2", - "cookie": "^1.0.2", - "dayjs": "^1.11.18", - "katex": "^0.16.25", - "patch-package": "^8.0.0", + "axios": "^1.13.2", + "cookie": "^1.1.1", + "dayjs": "^1.11.19", + "katex": "^0.16.27", + "patch-package": "^8.0.1", "react": "^18.3.1", "react-dom": "^18.3.1", "react-katex": "^3.1.0", "react-toastify": "^11.0.5", "uuid": "^11.1.0", - "winston": "^3.17.0", + "winston": "^3.19.0", "winston-daily-rotate-file": "^5.0.0", - "yaml": "^2.8.1" + "yaml": "^2.8.2" }, "devDependencies": { - "@astrojs/check": "^0.9.4", - "@astrojs/react": "^4.3.1", - "@eslint/js": "^9.36.0", + "@astrojs/check": "^0.9.6", + "@astrojs/react": "^4.4.2", + "@eslint/js": "^9.39.2", "@iconify-json/mdi": "^1.2.3", "@iconify-json/mdi-light": "^1.2.2", - "@iconify/tailwind4": "^1.0.6", - "@playwright/test": "^1.55.0", - "@tailwindcss/vite": "^4.1.13", - "@tanstack/eslint-plugin-query": "^5.90.1", + "@iconify/tailwind4": "^1.2.0", + "@playwright/test": "^1.57.0", + "@tailwindcss/vite": "^4.1.18", + "@tanstack/eslint-plugin-query": "^5.91.2", "@testing-library/dom": "^10.4.1", - "@testing-library/jest-dom": "^6.8.0", - "@testing-library/react": "^16.3.0", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.1", "@testing-library/user-event": "^14.6.1", "@types/node": "^24.5.2", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", "@types/react-katex": "^3.0.4", "@types/topojson-specification": "^1.0.5", - "@typescript-eslint/eslint-plugin": "^8.44.1", - "@typescript-eslint/parser": "^8.44.1", + "@typescript-eslint/eslint-plugin": "^8.53.0", + "@typescript-eslint/parser": "^8.53.0", "@vitest/browser": "^3.2.4", "astro-eslint-parser": "^1.2.2", - "daisyui": "^5.1.14", + "daisyui": "^5.5.14", "dotenv": "^16.5.0", - "eslint": "^9.36.0", - "eslint-plugin-astro": "^1.3.1", + "eslint": "^9.39.2", + "eslint-plugin-astro": "^1.5.0", "eslint-plugin-import": "^2.32.0", "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^5.2.0", - "msw": "^2.11.3", + "msw": "^2.12.7", "playwright": "^1.55.0", - "prettier": "^3.6.2", + "prettier": "^3.7.4", "prettier-plugin-astro": "^0.14.1", - "prettier-plugin-tailwindcss": "^0.6.14", + "prettier-plugin-tailwindcss": "^0.7.2", "tailwindcss": "^4.0.9", - "typescript": "^5.9.2", - "typescript-eslint": "^8.44.1", + "typescript": "^5.9.3", + "typescript-eslint": "^8.53.0", "vitest": "^3.2.4", "vitest-browser-react": "^1.0.1", "zod": "^3.25.74" @@ -80,110 +80,85 @@ "license": "MIT" }, "node_modules/@antfu/install-pkg": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.0.0.tgz", - "integrity": "sha512-xvX6P/lo1B3ej0OsaErAjqgFYzYVcJpamjLAFLYh9vRJngBrMoUG7aVnrGTeqM7yxbyTD5p3F2+0/QUEh8Vzhw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz", + "integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==", "dev": true, "license": "MIT", "dependencies": { - "package-manager-detector": "^0.2.8", - "tinyexec": "^0.3.2" + "package-manager-detector": "^1.3.0", + "tinyexec": "^1.0.1" }, "funding": { "url": "https://github.com/sponsors/antfu" } }, - "node_modules/@antfu/utils": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-8.1.1.tgz", - "integrity": "sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/@asamuzakjp/css-color": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.1.1.tgz", - "integrity": "sha512-hpRD68SV2OMcZCsrbdkccTw5FXjNDLo5OuqSHyHZfwweGsDWZwDJ2+gONyNAbazZclobMirACLw0lk8WVxIqxA==", + "node_modules/@antfu/install-pkg/node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", "dev": true, "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@csstools/css-calc": "^2.1.2", - "@csstools/css-color-parser": "^3.0.8", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "lru-cache": "^10.4.3" + "engines": { + "node": ">=18" } }, - "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC", - "optional": true, - "peer": true - }, "node_modules/@astrojs/check": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/@astrojs/check/-/check-0.9.4.tgz", - "integrity": "sha512-IOheHwCtpUfvogHHsvu0AbeRZEnjJg3MopdLddkJE70mULItS/Vh37BHcI00mcOJcH1vhD3odbpvWokpxam7xA==", + "version": "0.9.6", + "resolved": "https://registry.npmjs.org/@astrojs/check/-/check-0.9.6.tgz", + "integrity": "sha512-jlaEu5SxvSgmfGIFfNgcn5/f+29H61NJzEMfAZ82Xopr4XBchXB1GVlcJsE+elUlsYSbXlptZLX+JMG3b/wZEA==", "dev": true, "license": "MIT", "dependencies": { - "@astrojs/language-server": "^2.15.0", + "@astrojs/language-server": "^2.16.1", "chokidar": "^4.0.1", "kleur": "^4.1.5", "yargs": "^17.7.2" }, "bin": { - "astro-check": "dist/bin.js" + "astro-check": "bin/astro-check.js" }, "peerDependencies": { "typescript": "^5.0.0" } }, "node_modules/@astrojs/compiler": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-2.12.2.tgz", - "integrity": "sha512-w2zfvhjNCkNMmMMOn5b0J8+OmUaBL1o40ipMvqcG6NRpdC+lKxmTi48DT8Xw0SzJ3AfmeFLB45zXZXtmbsjcgw==", + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-2.13.0.tgz", + "integrity": "sha512-mqVORhUJViA28fwHYaWmsXSzLO9osbdZ5ImUfxBarqsYdMlPbqAqGJCxsNzvppp1BEzc1mJNjOVvQqeDN8Vspw==", "license": "MIT" }, "node_modules/@astrojs/internal-helpers": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.7.3.tgz", - "integrity": "sha512-6Pl0bQEIChuW5wqN7jdKrzWfCscW2rG/Cz+fzt4PhSQX2ivBpnhXgFUCs0M3DCYvjYHnPVG2W36X5rmFjZ62sw==", + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.7.5.tgz", + "integrity": "sha512-vreGnYSSKhAjFJCWAwe/CNhONvoc5lokxtRoZims+0wa3KbHBdPHSSthJsKxPd8d/aic6lWKpRTYGY/hsgK6EA==", "license": "MIT" }, "node_modules/@astrojs/language-server": { - "version": "2.15.4", - "resolved": "https://registry.npmjs.org/@astrojs/language-server/-/language-server-2.15.4.tgz", - "integrity": "sha512-JivzASqTPR2bao9BWsSc/woPHH7OGSGc9aMxXL4U6egVTqBycB3ZHdBJPuOCVtcGLrzdWTosAqVPz1BVoxE0+A==", + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/@astrojs/language-server/-/language-server-2.16.2.tgz", + "integrity": "sha512-J3hVx/mFi3FwEzKf8ExYXQNERogD6RXswtbU+TyrxoXRBiQoBO5ooo7/lRWJ+rlUKUd7+rziMPI9jYB7TRlh0w==", "dev": true, "license": "MIT", "dependencies": { "@astrojs/compiler": "^2.10.3", "@astrojs/yaml2ts": "^0.2.2", "@jridgewell/sourcemap-codec": "^1.4.15", - "@volar/kit": "~2.4.7", - "@volar/language-core": "~2.4.7", - "@volar/language-server": "~2.4.7", - "@volar/language-service": "~2.4.7", + "@volar/kit": "~2.4.23", + "@volar/language-core": "~2.4.23", + "@volar/language-server": "~2.4.23", + "@volar/language-service": "~2.4.23", "fast-glob": "^3.2.12", "muggle-string": "^0.4.1", - "volar-service-css": "0.0.62", - "volar-service-emmet": "0.0.62", - "volar-service-html": "0.0.62", - "volar-service-prettier": "0.0.62", - "volar-service-typescript": "0.0.62", - "volar-service-typescript-twoslash-queries": "0.0.62", - "volar-service-yaml": "0.0.62", - "vscode-html-languageservice": "^5.2.0", - "vscode-uri": "^3.0.8" + "volar-service-css": "0.0.67", + "volar-service-emmet": "0.0.67", + "volar-service-html": "0.0.67", + "volar-service-prettier": "0.0.67", + "volar-service-typescript": "0.0.67", + "volar-service-typescript-twoslash-queries": "0.0.67", + "volar-service-yaml": "0.0.67", + "vscode-html-languageservice": "^5.5.2", + "vscode-uri": "^3.1.0" }, "bin": { "astro-ls": "bin/nodeServer.js" @@ -202,18 +177,18 @@ } }, "node_modules/@astrojs/markdown-remark": { - "version": "6.3.7", - "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-6.3.7.tgz", - "integrity": "sha512-KXGdq6/BC18doBCYXp08alHlWChH0hdD2B1qv9wIyOHbvwI5K6I7FhSta8dq1hBQNdun8YkKPR013D/Hm8xd0g==", + "version": "6.3.10", + "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-6.3.10.tgz", + "integrity": "sha512-kk4HeYR6AcnzC4QV8iSlOfh+N8TZ3MEStxPyenyCtemqn8IpEATBFMTJcfrNW32dgpt6MY3oCkMM/Tv3/I4G3A==", "license": "MIT", "dependencies": { - "@astrojs/internal-helpers": "0.7.3", + "@astrojs/internal-helpers": "0.7.5", "@astrojs/prism": "3.3.0", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", "hast-util-to-text": "^4.0.2", "import-meta-resolve": "^4.2.0", - "js-yaml": "^4.1.0", + "js-yaml": "^4.1.1", "mdast-util-definitions": "^6.0.0", "rehype-raw": "^7.0.0", "rehype-stringify": "^10.0.1", @@ -221,27 +196,27 @@ "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remark-smartypants": "^3.0.2", - "shiki": "^3.12.2", - "smol-toml": "^1.4.2", + "shiki": "^3.19.0", + "smol-toml": "^1.5.2", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.0.0", - "unist-util-visit-parents": "^6.0.1", + "unist-util-visit-parents": "^6.0.2", "vfile": "^6.0.3" } }, "node_modules/@astrojs/node": { - "version": "9.4.4", - "resolved": "https://registry.npmjs.org/@astrojs/node/-/node-9.4.4.tgz", - "integrity": "sha512-zQelZmeejnpw3Y5cj2gCyAZ6HT7tjgsWLZH8k40s3bTaT6lqJXlPtKJeIsuEcod21vZLODqBEQeu0CWrWm01EQ==", + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/@astrojs/node/-/node-9.5.1.tgz", + "integrity": "sha512-7k+SU877OUQylPr0mFcWrGvNuC78Lp9w+GInY8Rwc+LkHyDP9xls+nZAioK0WDWd+fyeQnlHbpDGURO3ZHuDVg==", "license": "MIT", "dependencies": { - "@astrojs/internal-helpers": "0.7.3", + "@astrojs/internal-helpers": "0.7.5", "send": "^1.2.0", "server-destroy": "^1.0.1" }, "peerDependencies": { - "astro": "^5.7.0" + "astro": "^5.14.3" } }, "node_modules/@astrojs/prism": { @@ -257,15 +232,15 @@ } }, "node_modules/@astrojs/react": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@astrojs/react/-/react-4.3.1.tgz", - "integrity": "sha512-Jhv35TsDHuQLvwof2z10P3g1s9wIR4UN9jE7O4NZBJNXOt/+qk+L0rY9th4SX7VzccKmRltUGxAhI1cXH52gXw==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@astrojs/react/-/react-4.4.2.tgz", + "integrity": "sha512-1tl95bpGfuaDMDn8O3x/5Dxii1HPvzjvpL2YTuqOOrQehs60I2DKiDgh1jrKc7G8lv+LQT5H15V6QONQ+9waeQ==", "dev": true, "license": "MIT", "dependencies": { "@vitejs/plugin-react": "^4.7.0", "ultrahtml": "^1.6.0", - "vite": "^6.3.6" + "vite": "^6.4.1" }, "engines": { "node": "18.20.8 || ^20.3.0 || >=22.0.0" @@ -506,9 +481,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -539,12 +514,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", - "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", "license": "MIT", "dependencies": { - "@babel/types": "^7.28.4" + "@babel/types": "^7.28.5" }, "bin": { "parser": "bin/babel-parser.js" @@ -632,57 +607,28 @@ } }, "node_modules/@babel/types": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", - "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@bundled-es-modules/cookie": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@bundled-es-modules/cookie/-/cookie-2.0.1.tgz", - "integrity": "sha512-8o+5fRPLNbjbdGRRmJj3h6Hh1AQJf2dk3qQ/5ZFb+PXkRNiSoMGGUKlsgLfrxneb72axVJyIYji64E2+nNfYyw==", - "dev": true, - "license": "ISC", - "dependencies": { - "cookie": "^0.7.2" - } - }, - "node_modules/@bundled-es-modules/cookie/node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@bundled-es-modules/statuses": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@bundled-es-modules/statuses/-/statuses-1.0.1.tgz", - "integrity": "sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==", - "dev": true, - "license": "ISC", - "dependencies": { - "statuses": "^2.0.1" - } - }, "node_modules/@capsizecss/unpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@capsizecss/unpack/-/unpack-2.4.0.tgz", - "integrity": "sha512-GrSU71meACqcmIUxPYOJvGKF0yryjN/L1aCuE9DViCTJI7bfkjgYDPD1zbNDcINJwSSP6UaBZY9GAbYDO7re0Q==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@capsizecss/unpack/-/unpack-4.0.0.tgz", + "integrity": "sha512-VERIM64vtTP1C4mxQ5thVT9fK0apjPFobqybMtA1UdUujWka24ERHbRHFGmpbbhp73MhV+KSsHQH9C6uOTdEQA==", "license": "MIT", "dependencies": { - "blob-to-buffer": "^1.2.8", - "cross-fetch": "^3.0.4", - "fontkit": "^2.0.2" + "fontkitten": "^1.0.0" + }, + "engines": { + "node": ">=18" } }, "node_modules/@colors/colors": { @@ -694,138 +640,23 @@ "node": ">=0.1.90" } }, - "node_modules/@csstools/color-helpers": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.2.tgz", - "integrity": "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "optional": true, - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@csstools/css-calc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.2.tgz", - "integrity": "sha512-TklMyb3uBB28b5uQdxjReG4L80NxAqgrECqLZFQbyLekwwlcDDS8r3f07DKqeo8C4926Br0gf/ZDe17Zv4wIuw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" - } - }, - "node_modules/@csstools/css-color-parser": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.8.tgz", - "integrity": "sha512-pdwotQjCCnRPuNi06jFuP68cykU1f3ZWExLe/8MQ1LOs8Xq+fTkYgd+2V8mWUWMrOn9iS2HftPVaMZDaXzGbhQ==", + "node_modules/@cyberalien/svg-utils": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@cyberalien/svg-utils/-/svg-utils-1.0.11.tgz", + "integrity": "sha512-qEE9mnyI+avfGT3emKuRs3ucYkITeaV0Xi7VlYN41f+uGnZBecQP3jwz/AF437H9J4Q7qPClHKm4NiTYpNE6hA==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "@csstools/color-helpers": "^5.0.2", - "@csstools/css-calc": "^2.1.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" - } - }, - "node_modules/@csstools/css-parser-algorithms": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz", - "integrity": "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-tokenizer": "^3.0.3" - } - }, - "node_modules/@csstools/css-tokenizer": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz", - "integrity": "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=18" + "@iconify/types": "^2.0.0" } }, "node_modules/@dabh/diagnostics": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", - "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz", + "integrity": "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==", "license": "MIT", "dependencies": { - "colorspace": "1.1.x", + "@so-ric/colorspace": "^1.1.6", "enabled": "2.0.x", "kuler": "^2.0.0" } @@ -851,9 +682,9 @@ } }, "node_modules/@emmetio/css-parser": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@emmetio/css-parser/-/css-parser-0.4.0.tgz", - "integrity": "sha512-z7wkxRSZgrQHXVzObGkXG+Vmj3uRlpM11oCZ9pbaz0nFejvCDmAiNDpY75+wgXOcffKpj4rzGtwGaZxfJKsJxw==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@emmetio/css-parser/-/css-parser-0.4.1.tgz", + "integrity": "sha512-2bC6m0MV/voF4CTZiAbG5MWKbq5EBmDPKu9Sb7s7nVcEzNQlrZP6mFFFlIaISM8X6514H9shWMme1fCm8cWAfQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1303,9 +1134,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", - "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1322,9 +1153,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, "license": "MIT", "engines": { @@ -1332,12 +1163,13 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", - "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.6", + "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" }, @@ -1350,6 +1182,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1360,6 +1193,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -1368,19 +1202,22 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", - "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", "dev": true, "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/core": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", - "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1452,9 +1289,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.36.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.36.0.tgz", - "integrity": "sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==", + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", + "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", "dev": true, "license": "MIT", "engines": { @@ -1465,22 +1302,23 @@ } }, "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", - "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.15.2", + "@eslint/core": "^0.17.0", "levn": "^0.4.1" }, "engines": { @@ -1513,9 +1351,9 @@ "license": "MIT" }, "node_modules/@genspectrum/dashboard-components": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@genspectrum/dashboard-components/-/dashboard-components-1.11.1.tgz", - "integrity": "sha512-nsyGuETthi5kAwb4shb21vPtobSq96bPfSvxLPTAFbNDH9wNsjC0X7otnKvQqKWxYNE3H4j561LZJinwqlRiiA==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@genspectrum/dashboard-components/-/dashboard-components-1.12.0.tgz", + "integrity": "sha512-RW7j1Q1TqtP7dKR//BaIrvCfaQmVZ/Z5ZTWDUDs5o+uhliSQKlnCk44qALmxa3MsgFVcBK8st5s+S3kKqS+ZaQ==", "license": "AGPL-3.0-only", "dependencies": { "@floating-ui/dom": "^1.6.5", @@ -1626,20 +1464,37 @@ } }, "node_modules/@iconify/tailwind4": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@iconify/tailwind4/-/tailwind4-1.0.6.tgz", - "integrity": "sha512-43ZXe+bC7CuE2LCgROdqbQeFYJi/J7L/k1UpSy8KDQlWVsWxPzLSWbWhlJx4uRYLOh1NRyw02YlDOgzBOFNd+A==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@iconify/tailwind4/-/tailwind4-1.2.0.tgz", + "integrity": "sha512-+t7XqfojOB0zzZdd8gV7IQZGq1AaIHTlsxMVzagxYR0hAlJCLUD63o3iSlNKRMH3ZR7gZ8y5c9dJ7J431avRbA==", "dev": true, "license": "MIT", "dependencies": { + "@iconify/tools": "^5.0.0", "@iconify/types": "^2.0.0", - "@iconify/utils": "^2.2.1" + "@iconify/utils": "^3.1.0" }, "funding": { "url": "https://github.com/sponsors/cyberalien" }, "peerDependencies": { - "tailwindcss": ">= 4" + "tailwindcss": ">= 4.0.0" + } + }, + "node_modules/@iconify/tools": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@iconify/tools/-/tools-5.0.0.tgz", + "integrity": "sha512-GY/FsuNdWA/FbkLqgQ8b1PHFkNvjMeSFWaVJdLldYGHBp0lZ64HJlcS0qzLfglacHTd8zYdfQjF74RxGqyGMgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cyberalien/svg-utils": "^1.0.11", + "@iconify/types": "^2.0.0", + "@iconify/utils": "^3.1.0", + "fflate": "^0.8.2", + "modern-tar": "^0.7.2", + "pathe": "^2.0.3", + "svgo": "^4.0.0" } }, "node_modules/@iconify/types": { @@ -1650,20 +1505,15 @@ "license": "MIT" }, "node_modules/@iconify/utils": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-2.3.0.tgz", - "integrity": "sha512-GmQ78prtwYW6EtzXRU1rY+KwOKfz32PD7iJh6Iyqw68GiKuoZ2A6pRtzWONz5VQJbp50mEjXh/7NkumtrAgRKA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.1.0.tgz", + "integrity": "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==", "dev": true, "license": "MIT", "dependencies": { - "@antfu/install-pkg": "^1.0.0", - "@antfu/utils": "^8.1.0", + "@antfu/install-pkg": "^1.1.0", "@iconify/types": "^2.0.0", - "debug": "^4.4.0", - "globals": "^15.14.0", - "kolorist": "^1.8.0", - "local-pkg": "^1.0.0", - "mlly": "^1.7.4" + "mlly": "^1.8.0" } }, "node_modules/@img/sharp-darwin-arm64": { @@ -2212,19 +2062,6 @@ } } }, - "node_modules/@isaacs/fs-minipass": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", - "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.4" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -2314,9 +2151,9 @@ } }, "node_modules/@mswjs/interceptors": { - "version": "0.39.2", - "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.39.2.tgz", - "integrity": "sha512-RuzCup9Ct91Y7V79xwCb146RaBRHZ7NBbrIUySumd1rpKqHL5OonaqrGIbug5hNwP/fRyxFMA6ISgw4FTtYFYg==", + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.40.0.tgz", + "integrity": "sha512-EFd6cVbHsgLa6wa4RljGj6Wk75qoHxUSyc5asLyyPSyuhIcdS2Q3Phw6ImS1q+CkALthJRShiYfKANcQMuMqsQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2423,13 +2260,13 @@ } }, "node_modules/@playwright/test": { - "version": "1.55.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.55.0.tgz", - "integrity": "sha512-04IXzPwHrW69XusN/SIdDdKZBzMfOT9UNT/YiJit/xpy2VuAoB8NHc8Aplb96zsWDddLnbkPL3TsmrS04ZU2xQ==", + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.57.0.tgz", + "integrity": "sha512-6TyEnHgd6SArQO8UO2OMTxshln3QMWBtPGrOCgs3wVEmQmwyuNtB10IZMfmYDE0riwNR1cu4q+pPcxMVtaG3TA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright": "1.55.0" + "playwright": "1.57.0" }, "bin": { "playwright": "cli.js" @@ -2735,60 +2572,60 @@ "license": "MIT" }, "node_modules/@shikijs/core": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.13.0.tgz", - "integrity": "sha512-3P8rGsg2Eh2qIHekwuQjzWhKI4jV97PhvYjYUzGqjvJfqdQPz+nMlfWahU24GZAyW1FxFI1sYjyhfh5CoLmIUA==", + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.20.0.tgz", + "integrity": "sha512-f2ED7HYV4JEk827mtMDwe/yQ25pRiXZmtHjWF8uzZKuKiEsJR7Ce1nuQ+HhV9FzDcbIo4ObBCD9GPTzNuy9S1g==", "license": "MIT", "dependencies": { - "@shikijs/types": "3.13.0", + "@shikijs/types": "3.20.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "node_modules/@shikijs/engine-javascript": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.13.0.tgz", - "integrity": "sha512-Ty7xv32XCp8u0eQt8rItpMs6rU9Ki6LJ1dQOW3V/56PKDcpvfHPnYFbsx5FFUP2Yim34m/UkazidamMNVR4vKg==", + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.20.0.tgz", + "integrity": "sha512-OFx8fHAZuk7I42Z9YAdZ95To6jDePQ9Rnfbw9uSRTSbBhYBp1kEOKv/3jOimcj3VRUKusDYM6DswLauwfhboLg==", "license": "MIT", "dependencies": { - "@shikijs/types": "3.13.0", + "@shikijs/types": "3.20.0", "@shikijs/vscode-textmate": "^10.0.2", - "oniguruma-to-es": "^4.3.3" + "oniguruma-to-es": "^4.3.4" } }, "node_modules/@shikijs/engine-oniguruma": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.13.0.tgz", - "integrity": "sha512-O42rBGr4UDSlhT2ZFMxqM7QzIU+IcpoTMzb3W7AlziI1ZF7R8eS2M0yt5Ry35nnnTX/LTLXFPUjRFCIW+Operg==", + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.20.0.tgz", + "integrity": "sha512-Yx3gy7xLzM0ZOjqoxciHjA7dAt5tyzJE3L4uQoM83agahy+PlW244XJSrmJRSBvGYELDhYXPacD4R/cauV5bzQ==", "license": "MIT", "dependencies": { - "@shikijs/types": "3.13.0", + "@shikijs/types": "3.20.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "node_modules/@shikijs/langs": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.13.0.tgz", - "integrity": "sha512-672c3WAETDYHwrRP0yLy3W1QYB89Hbpj+pO4KhxK6FzIrDI2FoEXNiNCut6BQmEApYLfuYfpgOZaqbY+E9b8wQ==", + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.20.0.tgz", + "integrity": "sha512-le+bssCxcSHrygCWuOrYJHvjus6zhQ2K7q/0mgjiffRbkhM4o1EWu2m+29l0yEsHDbWaWPNnDUTRVVBvBBeKaA==", "license": "MIT", "dependencies": { - "@shikijs/types": "3.13.0" + "@shikijs/types": "3.20.0" } }, "node_modules/@shikijs/themes": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.13.0.tgz", - "integrity": "sha512-Vxw1Nm1/Od8jyA7QuAenaV78BG2nSr3/gCGdBkLpfLscddCkzkL36Q5b67SrLLfvAJTOUzW39x4FHVCFriPVgg==", + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.20.0.tgz", + "integrity": "sha512-U1NSU7Sl26Q7ErRvJUouArxfM2euWqq1xaSrbqMu2iqa+tSp0D1Yah8216sDYbdDHw4C8b75UpE65eWorm2erQ==", "license": "MIT", "dependencies": { - "@shikijs/types": "3.13.0" + "@shikijs/types": "3.20.0" } }, "node_modules/@shikijs/types": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.13.0.tgz", - "integrity": "sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw==", + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.20.0.tgz", + "integrity": "sha512-lhYAATn10nkZcBQ0BlzSbJA3wcmL5MXUUF8d2Zzon6saZDlToKaiRX60n2+ZaHJCmXEcZRWNzn+k9vplr8Jhsw==", "license": "MIT", "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", @@ -2801,64 +2638,106 @@ "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", "license": "MIT" }, - "node_modules/@swc/helpers": { - "version": "0.5.17", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", - "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", - "license": "Apache-2.0", + "node_modules/@so-ric/colorspace": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz", + "integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==", + "license": "MIT", + "dependencies": { + "color": "^5.0.2", + "text-hex": "1.0.x" + } + }, + "node_modules/@so-ric/colorspace/node_modules/color": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/color/-/color-5.0.3.tgz", + "integrity": "sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==", + "license": "MIT", + "dependencies": { + "color-convert": "^3.1.3", + "color-string": "^2.1.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@so-ric/colorspace/node_modules/color-convert": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.3.tgz", + "integrity": "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==", + "license": "MIT", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=14.6" + } + }, + "node_modules/@so-ric/colorspace/node_modules/color-name": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz", + "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==", + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/@so-ric/colorspace/node_modules/color-string": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.4.tgz", + "integrity": "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==", + "license": "MIT", "dependencies": { - "tslib": "^2.8.0" + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=18" } }, "node_modules/@tailwindcss/node": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.13.tgz", - "integrity": "sha512-eq3ouolC1oEFOAvOMOBAmfCIqZBJuvWvvYWh5h5iOYfe1HFC6+GZ6EIL0JdM3/niGRJmnrOc+8gl9/HGUaaptw==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.18.tgz", + "integrity": "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==", "dev": true, "license": "MIT", "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", - "jiti": "^2.5.1", - "lightningcss": "1.30.1", - "magic-string": "^0.30.18", + "jiti": "^2.6.1", + "lightningcss": "1.30.2", + "magic-string": "^0.30.21", "source-map-js": "^1.2.1", - "tailwindcss": "4.1.13" + "tailwindcss": "4.1.18" } }, "node_modules/@tailwindcss/oxide": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.13.tgz", - "integrity": "sha512-CPgsM1IpGRa880sMbYmG1s4xhAy3xEt1QULgTJGQmZUeNgXFR7s1YxYygmJyBGtou4SyEosGAGEeYqY7R53bIA==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.18.tgz", + "integrity": "sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==", "dev": true, - "hasInstallScript": true, "license": "MIT", - "dependencies": { - "detect-libc": "^2.0.4", - "tar": "^7.4.3" - }, "engines": { "node": ">= 10" }, "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.1.13", - "@tailwindcss/oxide-darwin-arm64": "4.1.13", - "@tailwindcss/oxide-darwin-x64": "4.1.13", - "@tailwindcss/oxide-freebsd-x64": "4.1.13", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.13", - "@tailwindcss/oxide-linux-arm64-gnu": "4.1.13", - "@tailwindcss/oxide-linux-arm64-musl": "4.1.13", - "@tailwindcss/oxide-linux-x64-gnu": "4.1.13", - "@tailwindcss/oxide-linux-x64-musl": "4.1.13", - "@tailwindcss/oxide-wasm32-wasi": "4.1.13", - "@tailwindcss/oxide-win32-arm64-msvc": "4.1.13", - "@tailwindcss/oxide-win32-x64-msvc": "4.1.13" + "@tailwindcss/oxide-android-arm64": "4.1.18", + "@tailwindcss/oxide-darwin-arm64": "4.1.18", + "@tailwindcss/oxide-darwin-x64": "4.1.18", + "@tailwindcss/oxide-freebsd-x64": "4.1.18", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.18", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.18", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.18", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.18", + "@tailwindcss/oxide-linux-x64-musl": "4.1.18", + "@tailwindcss/oxide-wasm32-wasi": "4.1.18", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.18", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.18" } }, "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.13.tgz", - "integrity": "sha512-BrpTrVYyejbgGo57yc8ieE+D6VT9GOgnNdmh5Sac6+t0m+v+sKQevpFVpwX3pBrM2qKrQwJ0c5eDbtjouY/+ew==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.18.tgz", + "integrity": "sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==", "cpu": [ "arm64" ], @@ -2873,9 +2752,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.13.tgz", - "integrity": "sha512-YP+Jksc4U0KHcu76UhRDHq9bx4qtBftp9ShK/7UGfq0wpaP96YVnnjFnj3ZFrUAjc5iECzODl/Ts0AN7ZPOANQ==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.18.tgz", + "integrity": "sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==", "cpu": [ "arm64" ], @@ -2890,9 +2769,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.13.tgz", - "integrity": "sha512-aAJ3bbwrn/PQHDxCto9sxwQfT30PzyYJFG0u/BWZGeVXi5Hx6uuUOQEI2Fa43qvmUjTRQNZnGqe9t0Zntexeuw==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.18.tgz", + "integrity": "sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==", "cpu": [ "x64" ], @@ -2907,9 +2786,9 @@ } }, "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.13.tgz", - "integrity": "sha512-Wt8KvASHwSXhKE/dJLCCWcTSVmBj3xhVhp/aF3RpAhGeZ3sVo7+NTfgiN8Vey/Fi8prRClDs6/f0KXPDTZE6nQ==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.18.tgz", + "integrity": "sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==", "cpu": [ "x64" ], @@ -2924,9 +2803,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.13.tgz", - "integrity": "sha512-mbVbcAsW3Gkm2MGwA93eLtWrwajz91aXZCNSkGTx/R5eb6KpKD5q8Ueckkh9YNboU8RH7jiv+ol/I7ZyQ9H7Bw==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.18.tgz", + "integrity": "sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==", "cpu": [ "arm" ], @@ -2941,9 +2820,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.13.tgz", - "integrity": "sha512-wdtfkmpXiwej/yoAkrCP2DNzRXCALq9NVLgLELgLim1QpSfhQM5+ZxQQF8fkOiEpuNoKLp4nKZ6RC4kmeFH0HQ==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.18.tgz", + "integrity": "sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==", "cpu": [ "arm64" ], @@ -2958,9 +2837,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.13.tgz", - "integrity": "sha512-hZQrmtLdhyqzXHB7mkXfq0IYbxegaqTmfa1p9MBj72WPoDD3oNOh1Lnxf6xZLY9C3OV6qiCYkO1i/LrzEdW2mg==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.18.tgz", + "integrity": "sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==", "cpu": [ "arm64" ], @@ -2975,9 +2854,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.13.tgz", - "integrity": "sha512-uaZTYWxSXyMWDJZNY1Ul7XkJTCBRFZ5Fo6wtjrgBKzZLoJNrG+WderJwAjPzuNZOnmdrVg260DKwXCFtJ/hWRQ==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.18.tgz", + "integrity": "sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==", "cpu": [ "x64" ], @@ -2992,9 +2871,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.13.tgz", - "integrity": "sha512-oXiPj5mi4Hdn50v5RdnuuIms0PVPI/EG4fxAfFiIKQh5TgQgX7oSuDWntHW7WNIi/yVLAiS+CRGW4RkoGSSgVQ==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.18.tgz", + "integrity": "sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==", "cpu": [ "x64" ], @@ -3009,9 +2888,9 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.13.tgz", - "integrity": "sha512-+LC2nNtPovtrDwBc/nqnIKYh/W2+R69FA0hgoeOn64BdCX522u19ryLh3Vf3F8W49XBcMIxSe665kwy21FkhvA==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.18.tgz", + "integrity": "sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==", "bundleDependencies": [ "@napi-rs/wasm-runtime", "@emnapi/core", @@ -3027,30 +2906,30 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.4.5", - "@emnapi/runtime": "^1.4.5", - "@emnapi/wasi-threads": "^1.0.4", - "@napi-rs/wasm-runtime": "^0.2.12", - "@tybys/wasm-util": "^0.10.0", - "tslib": "^2.8.0" + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.1.0", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.4.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { - "version": "1.4.5", + "version": "1.7.1", "dev": true, "inBundle": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/wasi-threads": "1.0.4", + "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { - "version": "1.4.5", + "version": "1.7.1", "dev": true, "inBundle": true, "license": "MIT", @@ -3060,7 +2939,7 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { - "version": "1.0.4", + "version": "1.1.0", "dev": true, "inBundle": true, "license": "MIT", @@ -3070,19 +2949,19 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.12", + "version": "1.1.0", "dev": true, "inBundle": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.10.0" + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", + "@tybys/wasm-util": "^0.10.1" } }, "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { - "version": "0.10.0", + "version": "0.10.1", "dev": true, "inBundle": true, "license": "MIT", @@ -3092,16 +2971,16 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { - "version": "2.8.0", + "version": "2.8.1", "dev": true, "inBundle": true, "license": "0BSD", "optional": true }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.13.tgz", - "integrity": "sha512-dziTNeQXtoQ2KBXmrjCxsuPk3F3CQ/yb7ZNZNA+UkNTeiTGgfeh+gH5Pi7mRncVgcPD2xgHvkFCh/MhZWSgyQg==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.18.tgz", + "integrity": "sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==", "cpu": [ "arm64" ], @@ -3116,9 +2995,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.13.tgz", - "integrity": "sha512-3+LKesjXydTkHk5zXX01b5KMzLV1xl2mcktBJkje7rhFUpUlYJy7IMOLqjIRQncLTa1WZZiFY/foAeB5nmaiTw==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.18.tgz", + "integrity": "sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==", "cpu": [ "x64" ], @@ -3133,28 +3012,28 @@ } }, "node_modules/@tailwindcss/vite": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.13.tgz", - "integrity": "sha512-0PmqLQ010N58SbMTJ7BVJ4I2xopiQn/5i6nlb4JmxzQf8zcS5+m2Cv6tqh+sfDwtIdjoEnOvwsGQ1hkUi8QEHQ==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.18.tgz", + "integrity": "sha512-jVA+/UpKL1vRLg6Hkao5jldawNmRo7mQYrZtNHMIVpLfLhDml5nMRUo/8MwoX2vNXvnaXNNMedrMfMugAVX1nA==", "dev": true, "license": "MIT", "dependencies": { - "@tailwindcss/node": "4.1.13", - "@tailwindcss/oxide": "4.1.13", - "tailwindcss": "4.1.13" + "@tailwindcss/node": "4.1.18", + "@tailwindcss/oxide": "4.1.18", + "tailwindcss": "4.1.18" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "node_modules/@tanstack/eslint-plugin-query": { - "version": "5.90.1", - "resolved": "https://registry.npmjs.org/@tanstack/eslint-plugin-query/-/eslint-plugin-query-5.90.1.tgz", - "integrity": "sha512-Ki4hl+8ZtnMFZ3amZbQl6sSMUq6L8oSJ14vmi3j5t1/SqXclL5SI/1kcuH36iIk05B/bN5pEOS1PTO3Ut/FbVA==", + "version": "5.91.2", + "resolved": "https://registry.npmjs.org/@tanstack/eslint-plugin-query/-/eslint-plugin-query-5.91.2.tgz", + "integrity": "sha512-UPeWKl/Acu1IuuHJlsN+eITUHqAaa9/04geHHPedY8siVarSaWprY0SVMKrkpKfk5ehRT7+/MZ5QwWuEtkWrFw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/utils": "^8.44.0" + "@typescript-eslint/utils": "^8.44.1" }, "funding": { "type": "github", @@ -3165,9 +3044,9 @@ } }, "node_modules/@tanstack/query-core": { - "version": "5.90.1", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.1.tgz", - "integrity": "sha512-hmi8i+mWP3QnD8yq3+6LWri9IEZAlFbpbM/UVB+TJtp5RIxUfzuARqyW39b+HCfBKKnFKSHWMXNB5YN8lo/E/Q==", + "version": "5.90.16", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.16.tgz", + "integrity": "sha512-MvtWckSVufs/ja463/K4PyJeqT+HMlJWtw6PrCpywznd2NSgO3m4KwO9RqbFqGg6iDE8vVMFWMeQI4Io3eEYww==", "license": "MIT", "funding": { "type": "github", @@ -3175,12 +3054,12 @@ } }, "node_modules/@tanstack/react-query": { - "version": "5.90.1", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.1.tgz", - "integrity": "sha512-tN7Fx2HuV2SBhl+STgL8enbfSInRoNU1B1+5LIU62klcMElE4lFzol4aReuRSUeD6ntzPayK0KrM6w9+ZlHEkw==", + "version": "5.90.16", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.16.tgz", + "integrity": "sha512-bpMGOmV4OPmif7TNMteU/Ehf/hoC0Kf98PDc0F4BZkFrEapRMEqI/V6YS0lyzwSV6PQpY1y4xxArUIfBW5LVxQ==", "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.90.1" + "@tanstack/query-core": "5.90.16" }, "funding": { "type": "github", @@ -3224,9 +3103,9 @@ } }, "node_modules/@testing-library/jest-dom": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.8.0.tgz", - "integrity": "sha512-WgXcWzVM6idy5JaftTVC8Vs83NKRmGJz4Hqs4oyOuO2J4r/y79vvKZsb+CaGyCSEbUPI6OsewfPd0G1A0/TUZQ==", + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", + "integrity": "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==", "dev": true, "license": "MIT", "dependencies": { @@ -3251,9 +3130,9 @@ "license": "MIT" }, "node_modules/@testing-library/react": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.0.tgz", - "integrity": "sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw==", + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.1.tgz", + "integrity": "sha512-gr4KtAWqIOQoucWYD/f6ki+j5chXfcPc74Col/6poTyqTmn7zRmodWahWRCp8tYd+GMqBonw6hstNzqjbs6gjw==", "dev": true, "license": "MIT", "dependencies": { @@ -3354,13 +3233,6 @@ "@types/deep-eql": "*" } }, - "node_modules/@types/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/debug": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", @@ -3383,15 +3255,6 @@ "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", "license": "MIT" }, - "node_modules/@types/fontkit": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@types/fontkit/-/fontkit-2.0.8.tgz", - "integrity": "sha512-wN+8bYxIpJf+5oZdrdtaX04qUuWHcKxcDEgRS9Qm9ZClSHjzEn13SxUC+5eRM+4yXIeTYk8mTzLAWGF64847ew==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/geojson": { "version": "7946.0.16", "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", @@ -3450,6 +3313,7 @@ "version": "24.5.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.5.2.tgz", "integrity": "sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ==", + "devOptional": true, "license": "MIT", "dependencies": { "undici-types": "~7.12.0" @@ -3494,9 +3358,9 @@ } }, "node_modules/@types/statuses": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.5.tgz", - "integrity": "sha512-jmIUGWrAiwu3dZpxntxieC+1n/5c3mjrImkmOSQ2NC5uP6cYO4aAZDdSmRcI5C1oiTmqlZGHC+/NmJrKogbP5A==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.6.tgz", + "integrity": "sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==", "dev": true, "license": "MIT" }, @@ -3529,21 +3393,20 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.44.1.tgz", - "integrity": "sha512-molgphGqOBT7t4YKCSkbasmu1tb1MgrZ2szGzHbclF7PNmOkSTQVHy+2jXOSnxvR3+Xe1yySHFZoqMpz3TfQsw==", + "version": "8.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.53.0.tgz", + "integrity": "sha512-eEXsVvLPu8Z4PkFibtuFJLJOTAV/nPdgtSjkGoPpddpFk3/ym2oy97jynY6ic2m6+nc5M8SE1e9v/mHKsulcJg==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.44.1", - "@typescript-eslint/type-utils": "8.44.1", - "@typescript-eslint/utils": "8.44.1", - "@typescript-eslint/visitor-keys": "8.44.1", - "graphemer": "^1.4.0", - "ignore": "^7.0.0", + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.53.0", + "@typescript-eslint/type-utils": "8.53.0", + "@typescript-eslint/utils": "8.53.0", + "@typescript-eslint/visitor-keys": "8.53.0", + "ignore": "^7.0.5", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.1.0" + "ts-api-utils": "^2.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3553,15 +3416,15 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.44.1", + "@typescript-eslint/parser": "^8.53.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.4.tgz", - "integrity": "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "dev": true, "license": "MIT", "engines": { @@ -3569,17 +3432,17 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.44.1.tgz", - "integrity": "sha512-EHrrEsyhOhxYt8MTg4zTF+DJMuNBzWwgvvOYNj/zm1vnaD/IC5zCXFehZv94Piqa2cRFfXrTFxIvO95L7Qc/cw==", + "version": "8.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.53.0.tgz", + "integrity": "sha512-npiaib8XzbjtzS2N4HlqPvlpxpmZ14FjSJrteZpPxGUaYPlvhzlzUZ4mZyABo0EFrOWnvyd0Xxroq//hKhtAWg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.44.1", - "@typescript-eslint/types": "8.44.1", - "@typescript-eslint/typescript-estree": "8.44.1", - "@typescript-eslint/visitor-keys": "8.44.1", - "debug": "^4.3.4" + "@typescript-eslint/scope-manager": "8.53.0", + "@typescript-eslint/types": "8.53.0", + "@typescript-eslint/typescript-estree": "8.53.0", + "@typescript-eslint/visitor-keys": "8.53.0", + "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3594,15 +3457,15 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.44.1.tgz", - "integrity": "sha512-ycSa60eGg8GWAkVsKV4E6Nz33h+HjTXbsDT4FILyL8Obk5/mx4tbvCNsLf9zret3ipSumAOG89UcCs/KRaKYrA==", + "version": "8.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.53.0.tgz", + "integrity": "sha512-Bl6Gdr7NqkqIP5yP9z1JU///Nmes4Eose6L1HwpuVHwScgDPPuEWbUVhvlZmb8hy0vX9syLk5EGNL700WcBlbg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.44.1", - "@typescript-eslint/types": "^8.44.1", - "debug": "^4.3.4" + "@typescript-eslint/tsconfig-utils": "^8.53.0", + "@typescript-eslint/types": "^8.53.0", + "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3616,14 +3479,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.44.1.tgz", - "integrity": "sha512-NdhWHgmynpSvyhchGLXh+w12OMT308Gm25JoRIyTZqEbApiBiQHD/8xgb6LqCWCFcxFtWwaVdFsLPQI3jvhywg==", + "version": "8.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.53.0.tgz", + "integrity": "sha512-kWNj3l01eOGSdVBnfAF2K1BTh06WS0Yet6JUgb9Cmkqaz3Jlu0fdVUjj9UI8gPidBWSMqDIglmEXifSgDT/D0g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.44.1", - "@typescript-eslint/visitor-keys": "8.44.1" + "@typescript-eslint/types": "8.53.0", + "@typescript-eslint/visitor-keys": "8.53.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3634,9 +3497,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.44.1.tgz", - "integrity": "sha512-B5OyACouEjuIvof3o86lRMvyDsFwZm+4fBOqFHccIctYgBjqR3qT39FBYGN87khcgf0ExpdCBeGKpKRhSFTjKQ==", + "version": "8.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.53.0.tgz", + "integrity": "sha512-K6Sc0R5GIG6dNoPdOooQ+KtvT5KCKAvTcY8h2rIuul19vxH5OTQk7ArKkd4yTzkw66WnNY0kPPzzcmWA+XRmiA==", "dev": true, "license": "MIT", "engines": { @@ -3651,17 +3514,17 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.44.1.tgz", - "integrity": "sha512-KdEerZqHWXsRNKjF9NYswNISnFzXfXNDfPxoTh7tqohU/PRIbwTmsjGK6V9/RTYWau7NZvfo52lgVk+sJh0K3g==", + "version": "8.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.53.0.tgz", + "integrity": "sha512-BBAUhlx7g4SmcLhn8cnbxoxtmS7hcq39xKCgiutL3oNx1TaIp+cny51s8ewnKMpVUKQUGb41RAUWZ9kxYdovuw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.44.1", - "@typescript-eslint/typescript-estree": "8.44.1", - "@typescript-eslint/utils": "8.44.1", - "debug": "^4.3.4", - "ts-api-utils": "^2.1.0" + "@typescript-eslint/types": "8.53.0", + "@typescript-eslint/typescript-estree": "8.53.0", + "@typescript-eslint/utils": "8.53.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3676,9 +3539,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.44.1.tgz", - "integrity": "sha512-Lk7uj7y9uQUOEguiDIDLYLJOrYHQa7oBiURYVFqIpGxclAFQ78f6VUOM8lI2XEuNOKNB7XuvM2+2cMXAoq4ALQ==", + "version": "8.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.53.0.tgz", + "integrity": "sha512-Bmh9KX31Vlxa13+PqPvt4RzKRN1XORYSLlAE+sO1i28NkisGbTtSLFVB3l7PWdHtR3E0mVMuC7JilWJ99m2HxQ==", "dev": true, "license": "MIT", "engines": { @@ -3690,22 +3553,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.44.1.tgz", - "integrity": "sha512-qnQJ+mVa7szevdEyvfItbO5Vo+GfZ4/GZWWDRRLjrxYPkhM+6zYB2vRYwCsoJLzqFCdZT4mEqyJoyzkunsZ96A==", + "version": "8.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.53.0.tgz", + "integrity": "sha512-pw0c0Gdo7Z4xOG987u3nJ8akL9093yEEKv8QTJ+Bhkghj1xyj8cgPaavlr9rq8h7+s6plUJ4QJYw2gCZodqmGw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.44.1", - "@typescript-eslint/tsconfig-utils": "8.44.1", - "@typescript-eslint/types": "8.44.1", - "@typescript-eslint/visitor-keys": "8.44.1", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.1.0" + "@typescript-eslint/project-service": "8.53.0", + "@typescript-eslint/tsconfig-utils": "8.53.0", + "@typescript-eslint/types": "8.53.0", + "@typescript-eslint/visitor-keys": "8.53.0", + "debug": "^4.4.3", + "minimatch": "^9.0.5", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3719,16 +3581,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.44.1.tgz", - "integrity": "sha512-DpX5Fp6edTlocMCwA+mHY8Mra+pPjRZ0TfHkXI8QFelIKcbADQz1LUPNtzOFUriBB2UYqw4Pi9+xV4w9ZczHFg==", + "version": "8.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.53.0.tgz", + "integrity": "sha512-XDY4mXTez3Z1iRDI5mbRhH4DFSt46oaIFsLg+Zn97+sYrXACziXSQcSelMybnVZ5pa1P6xYkPr5cMJyunM1ZDA==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.44.1", - "@typescript-eslint/types": "8.44.1", - "@typescript-eslint/typescript-estree": "8.44.1" + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.53.0", + "@typescript-eslint/types": "8.53.0", + "@typescript-eslint/typescript-estree": "8.53.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3743,13 +3605,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.44.1.tgz", - "integrity": "sha512-576+u0QD+Jp3tZzvfRfxon0EA2lzcDt3lhUbsC6Lgzy9x2VR4E+JUiNyGHi5T8vk0TV+fpJ5GLG1JsJuWCaKhw==", + "version": "8.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.53.0.tgz", + "integrity": "sha512-LZ2NqIHFhvFwxG0qZeLL9DvdNAHPGCY5dIRwBhyYeU+LfLhcStE1ImjsuTG/WaVh3XysGaeLW8Rqq7cGkPCFvw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.44.1", + "@typescript-eslint/types": "8.53.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -3962,14 +3824,14 @@ } }, "node_modules/@volar/kit": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/@volar/kit/-/kit-2.4.11.tgz", - "integrity": "sha512-ups5RKbMzMCr6RKafcCqDRnJhJDNWqo2vfekwOAj6psZ15v5TlcQFQAyokQJ3wZxVkzxrQM+TqTRDENfQEXpmA==", + "version": "2.4.27", + "resolved": "https://registry.npmjs.org/@volar/kit/-/kit-2.4.27.tgz", + "integrity": "sha512-ilZoQDMLzqmSsImJRWx4YiZ4FcvvPrPnFVmL6hSsIWB6Bn3qc7k88J9yP32dagrs5Y8EXIlvvD/mAFaiuEOACQ==", "dev": true, "license": "MIT", "dependencies": { - "@volar/language-service": "2.4.11", - "@volar/typescript": "2.4.11", + "@volar/language-service": "2.4.27", + "@volar/typescript": "2.4.27", "typesafe-path": "^0.2.2", "vscode-languageserver-textdocument": "^1.0.11", "vscode-uri": "^3.0.8" @@ -3979,25 +3841,25 @@ } }, "node_modules/@volar/language-core": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.11.tgz", - "integrity": "sha512-lN2C1+ByfW9/JRPpqScuZt/4OrUUse57GLI6TbLgTIqBVemdl1wNcZ1qYGEo2+Gw8coYLgCy7SuKqn6IrQcQgg==", + "version": "2.4.27", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.27.tgz", + "integrity": "sha512-DjmjBWZ4tJKxfNC1F6HyYERNHPYS7L7OPFyCrestykNdUZMFYzI9WTyvwPcaNaHlrEUwESHYsfEw3isInncZxQ==", "dev": true, "license": "MIT", "dependencies": { - "@volar/source-map": "2.4.11" + "@volar/source-map": "2.4.27" } }, "node_modules/@volar/language-server": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/@volar/language-server/-/language-server-2.4.11.tgz", - "integrity": "sha512-W9P8glH1M8LGREJ7yHRCANI5vOvTrRO15EMLdmh5WNF9sZYSEbQxiHKckZhvGIkbeR1WAlTl3ORTrJXUghjk7g==", + "version": "2.4.27", + "resolved": "https://registry.npmjs.org/@volar/language-server/-/language-server-2.4.27.tgz", + "integrity": "sha512-SymGNkErcHg8GjiG65iQN8sLkhqu1pwKhFySmxeBuYq5xFYagKBW36eiNITXQTdvT0tutI1GXcXdq/FdE/IyjA==", "dev": true, "license": "MIT", "dependencies": { - "@volar/language-core": "2.4.11", - "@volar/language-service": "2.4.11", - "@volar/typescript": "2.4.11", + "@volar/language-core": "2.4.27", + "@volar/language-service": "2.4.27", + "@volar/typescript": "2.4.27", "path-browserify": "^1.0.1", "request-light": "^0.7.0", "vscode-languageserver": "^9.0.1", @@ -4007,33 +3869,33 @@ } }, "node_modules/@volar/language-service": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/@volar/language-service/-/language-service-2.4.11.tgz", - "integrity": "sha512-KIb6g8gjUkS2LzAJ9bJCLIjfsJjeRtmXlu7b2pDFGD3fNqdbC53cCAKzgWDs64xtQVKYBU13DLWbtSNFtGuMLQ==", + "version": "2.4.27", + "resolved": "https://registry.npmjs.org/@volar/language-service/-/language-service-2.4.27.tgz", + "integrity": "sha512-SxKZ8yLhpWa7Y5e/RDxtNfm7j7xsXp/uf2urijXEffRNpPSmVdfzQrFFy5d7l8PNpZy+bHg+yakmqBPjQN+MOw==", "dev": true, "license": "MIT", "dependencies": { - "@volar/language-core": "2.4.11", + "@volar/language-core": "2.4.27", "vscode-languageserver-protocol": "^3.17.5", "vscode-languageserver-textdocument": "^1.0.11", "vscode-uri": "^3.0.8" } }, "node_modules/@volar/source-map": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.11.tgz", - "integrity": "sha512-ZQpmafIGvaZMn/8iuvCFGrW3smeqkq/IIh9F1SdSx9aUl0J4Iurzd6/FhmjNO5g2ejF3rT45dKskgXWiofqlZQ==", + "version": "2.4.27", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.27.tgz", + "integrity": "sha512-ynlcBReMgOZj2i6po+qVswtDUeeBRCTgDurjMGShbm8WYZgJ0PA4RmtebBJ0BCYol1qPv3GQF6jK7C9qoVc7lg==", "dev": true, "license": "MIT" }, "node_modules/@volar/typescript": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.11.tgz", - "integrity": "sha512-2DT+Tdh88Spp5PyPbqhyoYavYCPDsqbHLFwcUI9K1NlY1YgUJvujGdrqUp0zWxnW7KWNTr3xSpMuv2WnaTKDAw==", + "version": "2.4.27", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.27.tgz", + "integrity": "sha512-eWaYCcl/uAPInSK2Lze6IqVWaBu/itVqR5InXcHXFyles4zO++Mglt3oxdgj75BDcv1Knr9Y93nowS8U3wqhxg==", "dev": true, "license": "MIT", "dependencies": { - "@volar/language-core": "2.4.11", + "@volar/language-core": "2.4.27", "path-browserify": "^1.0.1", "vscode-uri": "^3.0.8" } @@ -4087,18 +3949,6 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/agent-base": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", - "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">= 14" - } - }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -4432,30 +4282,30 @@ } }, "node_modules/astro": { - "version": "5.13.10", - "resolved": "https://registry.npmjs.org/astro/-/astro-5.13.10.tgz", - "integrity": "sha512-PgIrIYvrR7fCoSPPt1sGlpoYK/FNil1BwKazND1DyaZC7SbWLi9hdIHM3ApdrL2SWK7oiADRPw7cTn80UyDWqA==", + "version": "5.16.9", + "resolved": "https://registry.npmjs.org/astro/-/astro-5.16.9.tgz", + "integrity": "sha512-gJvoZv0v8xCcKBcsxz1ZfXqoJ7sJJcyoKP8bUTjkuD4vDShLe0N26em4LQxitVv/2HLOpldQg67bEHB/qGoxJA==", "license": "MIT", "dependencies": { - "@astrojs/compiler": "^2.12.2", - "@astrojs/internal-helpers": "0.7.3", - "@astrojs/markdown-remark": "6.3.7", + "@astrojs/compiler": "^2.13.0", + "@astrojs/internal-helpers": "0.7.5", + "@astrojs/markdown-remark": "6.3.10", "@astrojs/telemetry": "3.3.0", - "@capsizecss/unpack": "^2.4.0", + "@capsizecss/unpack": "^4.0.0", "@oslojs/encoding": "^1.1.0", - "@rollup/pluginutils": "^5.2.0", + "@rollup/pluginutils": "^5.3.0", "acorn": "^8.15.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "boxen": "8.0.1", - "ci-info": "^4.3.0", + "ci-info": "^4.3.1", "clsx": "^2.1.1", "common-ancestor-path": "^1.0.1", - "cookie": "^1.0.2", + "cookie": "^1.1.1", "cssesc": "^3.0.0", - "debug": "^4.4.1", + "debug": "^4.4.3", "deterministic-object-hash": "^2.0.2", - "devalue": "^5.3.2", + "devalue": "^5.6.1", "diff": "^5.2.0", "dlv": "^1.1.3", "dset": "^3.1.4", @@ -4463,41 +4313,42 @@ "esbuild": "^0.25.0", "estree-walker": "^3.0.3", "flattie": "^1.1.1", - "fontace": "~0.3.0", + "fontace": "~0.4.0", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.2.0", "import-meta-resolve": "^4.2.0", - "js-yaml": "^4.1.0", - "kleur": "^4.1.5", - "magic-string": "^0.30.18", - "magicast": "^0.3.5", + "js-yaml": "^4.1.1", + "magic-string": "^0.30.21", + "magicast": "^0.5.1", "mrmime": "^2.0.1", "neotraverse": "^0.6.18", "p-limit": "^6.2.0", - "p-queue": "^8.1.0", - "package-manager-detector": "^1.3.0", + "p-queue": "^8.1.1", + "package-manager-detector": "^1.6.0", + "piccolore": "^0.1.3", "picomatch": "^4.0.3", "prompts": "^2.4.2", "rehype": "^13.0.2", - "semver": "^7.7.2", - "shiki": "^3.12.0", - "smol-toml": "^1.4.2", - "tinyexec": "^0.3.2", - "tinyglobby": "^0.2.14", + "semver": "^7.7.3", + "shiki": "^3.20.0", + "smol-toml": "^1.6.0", + "svgo": "^4.0.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", "tsconfck": "^3.1.6", "ultrahtml": "^1.6.0", - "unifont": "~0.5.2", + "unifont": "~0.7.1", "unist-util-visit": "^5.0.0", - "unstorage": "^1.17.0", + "unstorage": "^1.17.3", "vfile": "^6.0.3", - "vite": "^6.3.6", + "vite": "^6.4.1", "vitefu": "^1.1.1", "xxhash-wasm": "^1.1.0", "yargs-parser": "^21.1.1", "yocto-spinner": "^0.2.3", "zod": "^3.25.76", - "zod-to-json-schema": "^3.24.6", + "zod-to-json-schema": "^3.25.1", "zod-to-ts": "^1.2.0" }, "bin": { @@ -4571,16 +4422,19 @@ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "license": "ISC" }, - "node_modules/astro/node_modules/package-manager-detector": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.3.0.tgz", - "integrity": "sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==", - "license": "MIT" - }, - "node_modules/astro/node_modules/unstorage": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.1.tgz", - "integrity": "sha512-KKGwRTT0iVBCErKemkJCLs7JdxNVfqTPc/85ae1XES0+bsHbc/sFBfVi5kJp156cc51BHinIH2l3k0EZ24vOBQ==", + "node_modules/astro/node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/astro/node_modules/unstorage": { + "version": "1.17.3", + "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.3.tgz", + "integrity": "sha512-i+JYyy0DoKmQ3FximTHbGadmIYb8JEpq7lxUjnjeB702bCPum0vzo6oy5Mfu0lpqISw7hCyMW2yj4nWC8bqJ3Q==", "license": "MIT", "dependencies": { "anymatch": "^3.1.3", @@ -4589,7 +4443,7 @@ "h3": "^1.15.4", "lru-cache": "^10.4.3", "node-fetch-native": "^1.6.7", - "ofetch": "^1.4.1", + "ofetch": "^1.5.1", "ufo": "^1.6.1" }, "peerDependencies": { @@ -4714,15 +4568,6 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "license": "MIT" }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "license": "ISC", - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/auth-astro": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/auth-astro/-/auth-astro-4.2.0.tgz", @@ -4756,9 +4601,9 @@ } }, "node_modules/axios": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", - "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", + "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -4789,6 +4634,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, "license": "MIT" }, "node_modules/base-64": { @@ -4797,45 +4643,11 @@ "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==", "license": "MIT" }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/blob-to-buffer": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/blob-to-buffer/-/blob-to-buffer-1.2.9.tgz", - "integrity": "sha512-BF033y5fN6OCofD3vgHmNtwZWRcq9NLyyxyILx9hfMy1sXYy4ojFl765hJ2lP0YaN2fuxPaLO2Vzzoxy0FLFFA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC" }, "node_modules/boxen": { "version": "8.0.1", @@ -4893,15 +4705,6 @@ "node": ">=8" } }, - "node_modules/brotli": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", - "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", - "license": "MIT", - "dependencies": { - "base64-js": "^1.1.2" - } - }, "node_modules/browserslist": { "version": "4.25.4", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.4.tgz", @@ -5166,20 +4969,10 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/chownr": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", - "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, "node_modules/ci-info": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.0.tgz", - "integrity": "sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", + "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", "funding": [ { "type": "github", @@ -5281,15 +5074,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -5336,46 +5120,12 @@ "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", "license": "MIT", + "optional": true, "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, - "node_modules/colorspace": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", - "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", - "license": "MIT", - "dependencies": { - "color": "^3.1.3", - "text-hex": "1.0.x" - } - }, - "node_modules/colorspace/node_modules/color": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", - "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.3", - "color-string": "^1.6.0" - } - }, - "node_modules/colorspace/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/colorspace/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "license": "MIT" - }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -5420,12 +5170,13 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, "license": "MIT" }, "node_modules/confbox": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.1.tgz", - "integrity": "sha512-hkT3yDPFbs95mNCy1+7qNKC6Pro+/ibzYxtM2iqEigpf0sVw+bg4Zh9/snjsBcf990vfIsg5+1U7VyiyBb3etg==", + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", "dev": true, "license": "MIT" }, @@ -5437,12 +5188,16 @@ "license": "MIT" }, "node_modules/cookie": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", - "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", "license": "MIT", "engines": { "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/cookie-es": { @@ -5451,15 +5206,6 @@ "integrity": "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==", "license": "MIT" }, - "node_modules/cross-fetch": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz", - "integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==", - "license": "MIT", - "dependencies": { - "node-fetch": "^2.7.0" - } - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -5483,6 +5229,22 @@ "uncrypto": "^0.1.3" } }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/css-tree": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz", @@ -5496,6 +5258,18 @@ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" } }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/css.escape": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", @@ -5515,22 +5289,39 @@ "node": ">=4" } }, - "node_modules/cssstyle": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.3.0.tgz", - "integrity": "sha512-6r0NiY0xizYqfBvWp1G7WXJ06/bZyrk7Dc6PHql82C/pKGUTKu4yAX4Y8JPamb1ob9nBKuxWzCGTRuGwU3yxJQ==", - "dev": true, + "node_modules/csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "@asamuzakjp/css-color": "^3.1.1", - "rrweb-cssom": "^0.8.0" + "css-tree": "~2.2.0" }, "engines": { - "node": ">=18" + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" } }, + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", + "license": "CC0-1.0" + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -5622,43 +5413,15 @@ } }, "node_modules/daisyui": { - "version": "5.1.14", - "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-5.1.14.tgz", - "integrity": "sha512-c9Op7tB8vLzzazFaLeyFsmmVMLrU8sYo1sHuStBGTbaAzLVxrulelGQBROo/lS2ZxWm/CnCb9NWdKbTL+MaKFA==", + "version": "5.5.14", + "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-5.5.14.tgz", + "integrity": "sha512-L47rvw7I7hK68TA97VB8Ee0woHew+/ohR6Lx6Ah/krfISOqcG4My7poNpX5Mo5/ytMxiR40fEaz6njzDi7cuSg==", "dev": true, "license": "MIT", "funding": { "url": "https://github.com/saadeghi/daisyui?sponsor=1" } }, - "node_modules/data-urls": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", - "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/data-urls/node_modules/whatwg-mimetype": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", - "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=18" - } - }, "node_modules/data-view-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", @@ -5714,15 +5477,15 @@ } }, "node_modules/dayjs": { - "version": "1.11.18", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.18.tgz", - "integrity": "sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==", + "version": "1.11.19", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", + "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", "license": "MIT" }, "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -5736,15 +5499,6 @@ } } }, - "node_modules/decimal.js": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", - "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/decode-named-character-reference": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz", @@ -5872,9 +5626,9 @@ } }, "node_modules/devalue": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.3.2.tgz", - "integrity": "sha512-UDsjUbpQn9kvm68slnrs+mfxwFkIflOhkanmyabZ8zOYk8SMEIbJ3TK+88g70hSIeytu4y18f0z/hYHMTrXIWw==", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.1.tgz", + "integrity": "sha512-jDwizj+IlEZBunHcOuuFVBnIMPAEHvTsJj0BcIp94xYguLRVBcXO853px/MyIJvbVzWdsGvrRweIUWJw8hBP7A==", "license": "MIT" }, "node_modules/devlop": { @@ -5890,12 +5644,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/dfa": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz", - "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==", - "license": "MIT" - }, "node_modules/diff": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", @@ -5931,6 +5679,59 @@ "dev": true, "license": "MIT" }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, "node_modules/dompurify": { "version": "3.2.5", "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.5.tgz", @@ -5940,6 +5741,20 @@ "@types/trusted-types": "^2.0.7" } }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, "node_modules/dotenv": { "version": "16.5.0", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", @@ -6044,9 +5859,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.18.3", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", - "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", + "version": "5.18.4", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz", + "integrity": "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==", "dev": true, "license": "MIT", "dependencies": { @@ -6319,25 +6134,24 @@ } }, "node_modules/eslint": { - "version": "9.36.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.36.0.tgz", - "integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==", + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", + "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.0", - "@eslint/config-helpers": "^0.3.1", - "@eslint/core": "^0.15.2", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.36.0", - "@eslint/plugin-kit": "^0.3.5", + "@eslint/js": "9.39.2", + "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", @@ -6446,9 +6260,9 @@ } }, "node_modules/eslint-plugin-astro": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-astro/-/eslint-plugin-astro-1.3.1.tgz", - "integrity": "sha512-2XaLCMQm8htW1UvJvy1Zcmg8l0ziskitiUfJTn/w1Mk7r4Mxj0fZeNpN6UTNrm64XBIXSa5h8UCGrg8mdu47+g==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-astro/-/eslint-plugin-astro-1.5.0.tgz", + "integrity": "sha512-IWy4kY3DKTJxd7g652zIWpBGFuxw7NIIt16kyqc8BlhnIKvI8yGJj+Maua0DiNYED3F/D8AmzoTTTA6A95WX9g==", "dev": true, "license": "MIT", "dependencies": { @@ -6457,7 +6271,7 @@ "@typescript-eslint/types": "^7.7.1 || ^8", "astro-eslint-parser": "^1.0.2", "eslint-compat-utils": "^0.6.0", - "globals": "^15.0.0", + "globals": "^16.0.0", "postcss": "^8.4.14", "postcss-selector-parser": "^7.0.0" }, @@ -6825,13 +6639,6 @@ "node": ">=12.0.0" } }, - "node_modules/exsolve": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.1.tgz", - "integrity": "sha512-Smf0iQtkQVJLaph8r/qS8C8SWfQkaq9Q/dFcD44MLbJj6DNhlWefVuaS21SjfqOsBbjVlKtbCj6L9ekXK6EZUg==", - "dev": true, - "license": "MIT" - }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -6842,6 +6649,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, "license": "MIT" }, "node_modules/fast-glob": { @@ -6889,9 +6697,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", - "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", "dev": true, "funding": [ { @@ -6916,10 +6724,13 @@ } }, "node_modules/fdir": { - "version": "6.4.4", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", - "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -6935,6 +6746,13 @@ "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", "license": "MIT" }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "dev": true, + "license": "MIT" + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -7058,30 +6876,24 @@ } }, "node_modules/fontace": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/fontace/-/fontace-0.3.0.tgz", - "integrity": "sha512-czoqATrcnxgWb/nAkfyIrRp6Q8biYj7nGnL6zfhTcX+JKKpWHFBnb8uNMw/kZr7u++3Y3wYSYoZgHkCcsuBpBg==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/fontace/-/fontace-0.4.0.tgz", + "integrity": "sha512-moThBCItUe2bjZip5PF/iZClpKHGLwMvR79Kp8XpGRBrvoRSnySN4VcILdv3/MJzbhvUA5WeiUXF5o538m5fvg==", "license": "MIT", "dependencies": { - "@types/fontkit": "^2.0.8", - "fontkit": "^2.0.4" + "fontkitten": "^1.0.0" } }, - "node_modules/fontkit": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/fontkit/-/fontkit-2.0.4.tgz", - "integrity": "sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==", + "node_modules/fontkitten": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fontkitten/-/fontkitten-1.0.0.tgz", + "integrity": "sha512-b0RdzQeztiiUFWEDzq6Ka26qkNVNLCehoRtifOIGNbQ4CfxyYRh73fyWaQX/JshPVcueITOEeoSWPy5XQv8FUg==", "license": "MIT", "dependencies": { - "@swc/helpers": "^0.5.12", - "brotli": "^1.3.2", - "clone": "^2.1.2", - "dfa": "^1.2.0", - "fast-deep-equal": "^3.1.3", - "restructure": "^3.0.0", - "tiny-inflate": "^1.0.3", - "unicode-properties": "^1.4.0", - "unicode-trie": "^2.0.0" + "tiny-inflate": "^1.0.3" + }, + "engines": { + "node": ">=20" } }, "node_modules/for-each": { @@ -7126,26 +6938,19 @@ } }, "node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "license": "MIT", "dependencies": { - "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=12" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "license": "ISC" - }, "node_modules/fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", @@ -7293,27 +7098,6 @@ "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==", "license": "ISC" }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -7327,32 +7111,10 @@ "node": ">=10.13.0" } }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/globals": { - "version": "15.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", - "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", + "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", "dev": true, "license": "MIT", "engines": { @@ -7397,17 +7159,10 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true, - "license": "MIT" - }, "node_modules/graphql": { - "version": "16.10.0", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.10.0.tgz", - "integrity": "sha512-AjqGKbDGUFRKIRCP9tCKiIGHyriz2oHEbPIbEtcSLSs4YjReZOIPQQWek4+6hjw62H9QShXHyaGivGiYVLeYFQ==", + "version": "16.12.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.12.0.tgz", + "integrity": "sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ==", "dev": true, "license": "MIT", "engines": { @@ -7440,22 +7195,6 @@ "uncrypto": "^0.1.3" } }, - "node_modules/happy-dom": { - "version": "17.4.4", - "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-17.4.4.tgz", - "integrity": "sha512-/Pb0ctk3HTZ5xEL3BZ0hK1AqDSAUuRQitOmROPHhfUYEWpmTImwfD8vFDGADmMAX0JYgbcgxWoLFKtsWhcpuVA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "webidl-conversions": "^7.0.0", - "whatwg-mimetype": "^3.0.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", @@ -7658,15 +7397,15 @@ } }, "node_modules/hast-util-to-parse5": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz", - "integrity": "sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.1.tgz", + "integrity": "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", - "property-information": "^6.0.0", + "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "web-namespaces": "^2.0.0", "zwitch": "^2.0.0" @@ -7676,16 +7415,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/hast-util-to-parse5/node_modules/property-information": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", - "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/hast-util-to-text": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", @@ -7739,21 +7468,6 @@ "dev": true, "license": "MIT" }, - "node_modules/html-encoding-sniffer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", - "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "whatwg-encoding": "^3.1.1" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/html-escaper": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", @@ -7762,81 +7476,34 @@ }, "node_modules/html-void-elements": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", - "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/http-cache-semantics": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", - "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", - "license": "BSD-2-Clause" - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "license": "BSD-2-Clause" + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.8" } }, "node_modules/ignore": { @@ -7896,17 +7563,6 @@ "node": ">=8" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -7959,7 +7615,8 @@ "version": "0.3.2", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "license": "MIT" + "license": "MIT", + "optional": true }, "node_modules/is-async-function": { "version": "2.1.1", @@ -8249,15 +7906,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/is-regex": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", @@ -8461,9 +8109,9 @@ } }, "node_modules/jiti": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", - "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", "devOptional": true, "license": "MIT", "bin": { @@ -8486,9 +8134,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -8497,75 +8145,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsdom": { - "version": "26.1.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz", - "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "cssstyle": "^4.2.1", - "data-urls": "^5.0.0", - "decimal.js": "^10.5.0", - "html-encoding-sniffer": "^4.0.0", - "http-proxy-agent": "^7.0.2", - "https-proxy-agent": "^7.0.6", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.16", - "parse5": "^7.2.1", - "rrweb-cssom": "^0.8.0", - "saxes": "^6.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^5.1.1", - "w3c-xmlserializer": "^5.0.0", - "webidl-conversions": "^7.0.0", - "whatwg-encoding": "^3.1.1", - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.1.1", - "ws": "^8.18.0", - "xml-name-validator": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "canvas": "^3.0.0" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, - "node_modules/jsdom/node_modules/tough-cookie": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", - "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", - "dev": true, - "license": "BSD-3-Clause", - "optional": true, - "peer": true, - "dependencies": { - "tldts": "^6.1.32" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/jsdom/node_modules/whatwg-mimetype": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", - "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=18" - } - }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -8640,9 +8219,9 @@ "license": "MIT" }, "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", "license": "MIT", "dependencies": { "universalify": "^2.0.0" @@ -8677,9 +8256,9 @@ } }, "node_modules/katex": { - "version": "0.16.25", - "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.25.tgz", - "integrity": "sha512-woHRUZ/iF23GBP1dkDQMh1QBad9dmr8/PAwNA54VrSOVYgI12MAcE14TqnDdQOdzyEonGzMepYnqBMYdsoAr8Q==", + "version": "0.16.27", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.27.tgz", + "integrity": "sha512-aeQoDkuRWSqQN6nSvVCEFvfXdqo1OQiCmmW1kc9xSdjutPv7BGO7pqY9sQRJpMOGrEdfDgF2TfRXe5eUAD2Waw==", "funding": [ "https://opencollective.com/katex", "https://github.com/sponsors/katex" @@ -8724,18 +8303,12 @@ "version": "4.1.5", "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" } }, - "node_modules/kolorist": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", - "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==", - "dev": true, - "license": "MIT" - }, "node_modules/kuler": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", @@ -8763,9 +8336,9 @@ } }, "node_modules/lightningcss": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", - "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", + "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==", "devOptional": true, "license": "MPL-2.0", "dependencies": { @@ -8779,22 +8352,43 @@ "url": "https://opencollective.com/parcel" }, "optionalDependencies": { - "lightningcss-darwin-arm64": "1.30.1", - "lightningcss-darwin-x64": "1.30.1", - "lightningcss-freebsd-x64": "1.30.1", - "lightningcss-linux-arm-gnueabihf": "1.30.1", - "lightningcss-linux-arm64-gnu": "1.30.1", - "lightningcss-linux-arm64-musl": "1.30.1", - "lightningcss-linux-x64-gnu": "1.30.1", - "lightningcss-linux-x64-musl": "1.30.1", - "lightningcss-win32-arm64-msvc": "1.30.1", - "lightningcss-win32-x64-msvc": "1.30.1" + "lightningcss-android-arm64": "1.30.2", + "lightningcss-darwin-arm64": "1.30.2", + "lightningcss-darwin-x64": "1.30.2", + "lightningcss-freebsd-x64": "1.30.2", + "lightningcss-linux-arm-gnueabihf": "1.30.2", + "lightningcss-linux-arm64-gnu": "1.30.2", + "lightningcss-linux-arm64-musl": "1.30.2", + "lightningcss-linux-x64-gnu": "1.30.2", + "lightningcss-linux-x64-musl": "1.30.2", + "lightningcss-win32-arm64-msvc": "1.30.2", + "lightningcss-win32-x64-msvc": "1.30.2" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz", + "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-darwin-arm64": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", - "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz", + "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==", "cpu": [ "arm64" ], @@ -8812,9 +8406,9 @@ } }, "node_modules/lightningcss-darwin-x64": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", - "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz", + "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==", "cpu": [ "x64" ], @@ -8832,9 +8426,9 @@ } }, "node_modules/lightningcss-freebsd-x64": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", - "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz", + "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==", "cpu": [ "x64" ], @@ -8852,9 +8446,9 @@ } }, "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", - "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz", + "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==", "cpu": [ "arm" ], @@ -8872,9 +8466,9 @@ } }, "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", - "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz", + "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==", "cpu": [ "arm64" ], @@ -8892,9 +8486,9 @@ } }, "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", - "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz", + "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==", "cpu": [ "arm64" ], @@ -8912,9 +8506,9 @@ } }, "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", - "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz", + "integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==", "cpu": [ "x64" ], @@ -8932,9 +8526,9 @@ } }, "node_modules/lightningcss-linux-x64-musl": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", - "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz", + "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==", "cpu": [ "x64" ], @@ -8952,9 +8546,9 @@ } }, "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", - "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz", + "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==", "cpu": [ "arm64" ], @@ -8972,9 +8566,9 @@ } }, "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", - "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz", + "integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==", "cpu": [ "x64" ], @@ -9022,24 +8616,6 @@ "@types/trusted-types": "^2.0.2" } }, - "node_modules/local-pkg": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.1.tgz", - "integrity": "sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "mlly": "^1.7.4", - "pkg-types": "^2.0.1", - "quansync": "^0.2.8" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -9137,23 +8713,23 @@ } }, "node_modules/magic-string": { - "version": "0.30.19", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", - "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==", + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "node_modules/magicast": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", - "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.1.tgz", + "integrity": "sha512-xrHS24IxaLrvuo613F719wvOIv9xPHFWQHuvGUBmPnCA/3MQxKI3b+r7n1jAoDHmsbC5bRhTZYR77invLAxVnw==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.25.4", - "@babel/types": "^7.25.4", - "source-map-js": "^1.2.0" + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "source-map-js": "^1.2.1" } }, "node_modules/markdown-table": { @@ -10072,75 +9648,27 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/minizlib": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", - "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", - "dev": true, - "license": "MIT", - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/mlly": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz", - "integrity": "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz", + "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==", "dev": true, "license": "MIT", "dependencies": { - "acorn": "^8.14.0", - "pathe": "^2.0.1", - "pkg-types": "^1.3.0", - "ufo": "^1.5.4" + "acorn": "^8.15.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.1" } }, - "node_modules/mlly/node_modules/confbox": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", - "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/mlly/node_modules/pkg-types": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", - "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "node_modules/modern-tar": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/modern-tar/-/modern-tar-0.7.2.tgz", + "integrity": "sha512-TGG1ZRk1TAQ3neuZwahAHke3rKsSlro+ooMYtjh9sl2gGPVMLMuWiHgwC7im9T5bSM566RSo2Dko56ETgEvZcA==", "dev": true, "license": "MIT", - "dependencies": { - "confbox": "^0.1.8", - "mlly": "^1.7.4", - "pathe": "^2.0.1" + "engines": { + "node": ">=18.0.0" } }, "node_modules/moment": { @@ -10168,30 +9696,29 @@ "license": "MIT" }, "node_modules/msw": { - "version": "2.11.3", - "resolved": "https://registry.npmjs.org/msw/-/msw-2.11.3.tgz", - "integrity": "sha512-878imp8jxIpfzuzxYfX0qqTq1IFQz/1/RBHs/PyirSjzi+xKM/RRfIpIqHSCWjH0GxidrjhgiiXC+DWXNDvT9w==", + "version": "2.12.7", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.12.7.tgz", + "integrity": "sha512-retd5i3xCZDVWMYjHEVuKTmhqY8lSsxujjVrZiGbbdoxxIBg5S7rCuYy/YQpfrTYIxpd/o0Kyb/3H+1udBMoYg==", "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { - "@bundled-es-modules/cookie": "^2.0.1", - "@bundled-es-modules/statuses": "^1.0.1", "@inquirer/confirm": "^5.0.0", - "@mswjs/interceptors": "^0.39.1", + "@mswjs/interceptors": "^0.40.0", "@open-draft/deferred-promise": "^2.2.0", - "@types/cookie": "^0.6.0", - "@types/statuses": "^2.0.4", - "graphql": "^16.8.1", + "@types/statuses": "^2.0.6", + "cookie": "^1.0.2", + "graphql": "^16.12.0", "headers-polyfill": "^4.0.2", "is-node-process": "^1.2.0", "outvariant": "^1.4.3", "path-to-regexp": "^6.3.0", "picocolors": "^1.1.1", "rettime": "^0.7.0", + "statuses": "^2.0.2", "strict-event-emitter": "^0.5.1", "tough-cookie": "^6.0.0", - "type-fest": "^4.26.1", + "type-fest": "^5.2.0", "until-async": "^3.0.2", "yargs": "^17.7.2" }, @@ -10213,6 +9740,32 @@ } } }, + "node_modules/msw/node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/msw/node_modules/type-fest": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.3.1.tgz", + "integrity": "sha512-VCn+LMHbd4t6sF3wfU/+HKT63C9OoyrSIf4b+vtWHpt2U7/4InZG467YDNMFMR70DdHjAdpPWmw2lzRdg0Xqqg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "dependencies": { + "tagged-tag": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/muggle-string": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", @@ -10277,58 +9830,16 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, "node_modules/node-fetch-native": { "version": "1.6.7", "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", "license": "MIT" }, - "node_modules/node-fetch/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, - "node_modules/node-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, - "node_modules/node-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/node-mock-http": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.3.tgz", - "integrity": "sha512-jN8dK25fsfnMrVsEhluUTPkBFY+6ybu7jSB1n+ri/vOGjJxU8J9CZhpSGkHXSkFjtUhbmoncG/YG9ta5Ludqog==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.4.tgz", + "integrity": "sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==", "license": "MIT" }, "node_modules/node-releases": { @@ -10347,14 +9858,17 @@ "node": ">=0.10.0" } }, - "node_modules/nwsapi": { - "version": "2.2.20", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.20.tgz", - "integrity": "sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } }, "node_modules/oauth4webapi": { "version": "3.3.0", @@ -10496,14 +10010,14 @@ } }, "node_modules/ofetch": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.4.1.tgz", - "integrity": "sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.5.1.tgz", + "integrity": "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==", "license": "MIT", "dependencies": { - "destr": "^2.0.3", - "node-fetch-native": "^1.6.4", - "ufo": "^1.5.4" + "destr": "^2.0.5", + "node-fetch-native": "^1.6.7", + "ufo": "^1.6.1" } }, "node_modules/ohash": { @@ -10524,15 +10038,6 @@ "node": ">= 0.8" } }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, "node_modules/one-time": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", @@ -10549,9 +10054,9 @@ "license": "MIT" }, "node_modules/oniguruma-to-es": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.3.tgz", - "integrity": "sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.4.tgz", + "integrity": "sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA==", "license": "MIT", "dependencies": { "oniguruma-parser": "^0.12.1", @@ -10620,15 +10125,6 @@ "node": ">= 0.8.0" } }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/outvariant": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz", @@ -10715,9 +10211,9 @@ } }, "node_modules/p-queue": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-8.1.0.tgz", - "integrity": "sha512-mxLDbbGIBEXTJL0zEx8JIylaj3xQ7Z/7eEVjcF9fJX4DBiH9oqe+oahYnlKKxm0Ci9TlWTyhSHgygxMxjIB2jw==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-8.1.1.tgz", + "integrity": "sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ==", "license": "MIT", "dependencies": { "eventemitter3": "^5.0.1", @@ -10743,19 +10239,9 @@ } }, "node_modules/package-manager-detector": { - "version": "0.2.11", - "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.11.tgz", - "integrity": "sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "quansync": "^0.2.7" - } - }, - "node_modules/pako": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", - "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.6.0.tgz", + "integrity": "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==", "license": "MIT" }, "node_modules/parent-module": { @@ -10814,9 +10300,9 @@ } }, "node_modules/patch-package": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.0.tgz", - "integrity": "sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.1.tgz", + "integrity": "sha512-VsKRIA8f5uqHQ7NGhwIna6Bx6D9s/1iXlA1hthBVBEbkq+t4kXD0HHt+rJhf/Z+Ci0F/HCB2hvn0qLdLG+Qxlw==", "license": "MIT", "dependencies": { "@yarnpkg/lockfile": "^1.1.0", @@ -10824,15 +10310,14 @@ "ci-info": "^3.7.0", "cross-spawn": "^7.0.3", "find-yarn-workspace-root": "^2.0.0", - "fs-extra": "^9.0.0", + "fs-extra": "^10.0.0", "json-stable-stringify": "^1.0.2", "klaw-sync": "^6.0.0", "minimist": "^1.2.6", "open": "^7.4.2", - "rimraf": "^2.6.3", "semver": "^7.5.3", "slash": "^2.0.0", - "tmp": "^0.0.33", + "tmp": "^0.2.4", "yaml": "^2.2.2" }, "bin": { @@ -10875,15 +10360,6 @@ "node": ">=8" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -10924,6 +10400,12 @@ "node": ">= 14.16" } }, + "node_modules/piccolore": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/piccolore/-/piccolore-0.1.3.tgz", + "integrity": "sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw==", + "license": "ISC" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -10943,25 +10425,25 @@ } }, "node_modules/pkg-types": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.1.0.tgz", - "integrity": "sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", "dev": true, "license": "MIT", "dependencies": { - "confbox": "^0.2.1", - "exsolve": "^1.0.1", - "pathe": "^2.0.3" + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" } }, "node_modules/playwright": { - "version": "1.55.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.55.0.tgz", - "integrity": "sha512-sdCWStblvV1YU909Xqx0DhOjPZE4/5lJsIS84IfN9dAZfcl/CIZ5O8l3o0j7hPMjDvqoTF8ZUcc+i/GL5erstA==", + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.57.0.tgz", + "integrity": "sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.55.0" + "playwright-core": "1.57.0" }, "bin": { "playwright": "cli.js" @@ -10974,9 +10456,9 @@ } }, "node_modules/playwright-core": { - "version": "1.55.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.55.0.tgz", - "integrity": "sha512-GvZs4vU3U5ro2nZpeiwyb0zuFaqb9sUiAJuyrWpcGouD8y9/HLgGbNRjIph7zU9D3hnPaisMl9zG9CgFi/biIg==", + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.57.0.tgz", + "integrity": "sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -11068,10 +10550,11 @@ } }, "node_modules/prettier": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", - "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz", + "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", "dev": true, + "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" }, @@ -11098,13 +10581,13 @@ } }, "node_modules/prettier-plugin-tailwindcss": { - "version": "0.6.14", - "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.6.14.tgz", - "integrity": "sha512-pi2e/+ZygeIqntN+vC573BcW5Cve8zUB0SSAGxqpB4f96boZF4M3phPVoOFCeypwkpRYdi7+jQ5YJJUwrkGUAg==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.7.2.tgz", + "integrity": "sha512-LkphyK3Fw+q2HdMOoiEHWf93fNtYJwfamoKPl7UwtjFQdei/iIBoX11G6j706FzN3ymX9mPVi97qIY8328vdnA==", "dev": true, "license": "MIT", "engines": { - "node": ">=14.21.3" + "node": ">=20.19" }, "peerDependencies": { "@ianvs/prettier-plugin-sort-imports": "*", @@ -11117,14 +10600,12 @@ "prettier": "^3.0", "prettier-plugin-astro": "*", "prettier-plugin-css-order": "*", - "prettier-plugin-import-sort": "*", "prettier-plugin-jsdoc": "*", "prettier-plugin-marko": "*", "prettier-plugin-multiline-arrays": "*", "prettier-plugin-organize-attributes": "*", "prettier-plugin-organize-imports": "*", "prettier-plugin-sort-imports": "*", - "prettier-plugin-style-order": "*", "prettier-plugin-svelte": "*" }, "peerDependenciesMeta": { @@ -11155,9 +10636,6 @@ "prettier-plugin-css-order": { "optional": true }, - "prettier-plugin-import-sort": { - "optional": true - }, "prettier-plugin-jsdoc": { "optional": true }, @@ -11176,9 +10654,6 @@ "prettier-plugin-sort-imports": { "optional": true }, - "prettier-plugin-style-order": { - "optional": true - }, "prettier-plugin-svelte": { "optional": true } @@ -11293,23 +10768,6 @@ "node": ">=6" } }, - "node_modules/quansync": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.8.tgz", - "integrity": "sha512-4+saucphJMazjt7iOM27mbFCk+D9dd/zmgMDCzRZ8MEoBfYp7lAvoN38et/phRQF6wOPMy/OROBGgoWeSKyluA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/antfu" - }, - { - "type": "individual", - "url": "https://github.com/sponsors/sxzz" - } - ], - "license": "MIT" - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -11484,9 +10942,9 @@ "license": "MIT" }, "node_modules/regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/regex/-/regex-6.0.1.tgz", - "integrity": "sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", + "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", "license": "MIT", "dependencies": { "regex-utilities": "^2.3.0" @@ -11728,12 +11186,6 @@ "node": ">=4" } }, - "node_modules/restructure": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.2.tgz", - "integrity": "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==", - "license": "MIT" - }, "node_modules/retext": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/retext/-/retext-9.0.0.tgz", @@ -11813,19 +11265,6 @@ "node": ">=0.10.0" } }, - "node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, "node_modules/rollup": { "version": "4.34.9", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.9.tgz", @@ -11864,15 +11303,6 @@ "fsevents": "~2.3.2" } }, - "node_modules/rrweb-cssom": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", - "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -11988,15 +11418,6 @@ "node": ">=10" } }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/sass-formatter": { "version": "0.7.9", "resolved": "https://registry.npmjs.org/sass-formatter/-/sass-formatter-0.7.9.tgz", @@ -12007,20 +11428,11 @@ "suf-log": "^2.5.3" } }, - "node_modules/saxes": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", - "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", - "dev": true, - "license": "ISC", - "optional": true, - "peer": true, - "dependencies": { - "xmlchars": "^2.2.0" - }, - "engines": { - "node": ">=v12.22.7" - } + "node_modules/sax": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.3.tgz", + "integrity": "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==", + "license": "BlueOak-1.0.0" }, "node_modules/scheduler": { "version": "0.23.2", @@ -12032,9 +11444,9 @@ } }, "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==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -12217,17 +11629,17 @@ } }, "node_modules/shiki": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.13.0.tgz", - "integrity": "sha512-aZW4l8Og16CokuCLf8CF8kq+KK2yOygapU5m3+hoGw0Mdosc6fPitjM+ujYarppj5ZIKGyPDPP1vqmQhr+5/0g==", + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.20.0.tgz", + "integrity": "sha512-kgCOlsnyWb+p0WU+01RjkCH+eBVsjL1jOwUYWv0YDWkM2/A46+LDKVs5yZCUXjJG6bj4ndFoAg5iLIIue6dulg==", "license": "MIT", "dependencies": { - "@shikijs/core": "3.13.0", - "@shikijs/engine-javascript": "3.13.0", - "@shikijs/engine-oniguruma": "3.13.0", - "@shikijs/langs": "3.13.0", - "@shikijs/themes": "3.13.0", - "@shikijs/types": "3.13.0", + "@shikijs/core": "3.20.0", + "@shikijs/engine-javascript": "3.20.0", + "@shikijs/engine-oniguruma": "3.20.0", + "@shikijs/langs": "3.20.0", + "@shikijs/themes": "3.20.0", + "@shikijs/types": "3.20.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } @@ -12333,6 +11745,7 @@ "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", "license": "MIT", + "optional": true, "dependencies": { "is-arrayish": "^0.3.1" } @@ -12368,9 +11781,9 @@ } }, "node_modules/smol-toml": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.4.2.tgz", - "integrity": "sha512-rInDH6lCNiEyn3+hH8KVGFdbjc099j47+OSgbMrfDYX1CmXLfdKd7qi6IfcWj2wFxvSVkuI46M+wPGYfEOEj6g==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.0.tgz", + "integrity": "sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw==", "license": "BSD-3-Clause", "engines": { "node": ">= 18" @@ -12707,14 +12120,39 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true, + "node_modules/svgo": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.0.tgz", + "integrity": "sha512-VvrHQ+9uniE+Mvx3+C9IEe/lWasXCU0nXMY2kZeLrHNICuRiC8uMPyM14UEaMOFA5mhyQqEkB02VoQ16n3DLaw==", "license": "MIT", - "optional": true, - "peer": true + "dependencies": { + "commander": "^11.1.0", + "css-select": "^5.1.0", + "css-tree": "^3.0.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.1.1", + "sax": "^1.4.1" + }, + "bin": { + "svgo": "bin/svgo.js" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "node_modules/svgo/node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } }, "node_modules/synckit": { "version": "0.9.2", @@ -12733,17 +12171,30 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/tagged-tag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz", + "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/tailwindcss": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.13.tgz", - "integrity": "sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", + "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", "dev": true, "license": "MIT" }, "node_modules/tapable": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.3.tgz", - "integrity": "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", "dev": true, "license": "MIT", "engines": { @@ -12754,34 +12205,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/tar": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", - "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", - "dev": true, - "license": "ISC", - "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.0.1", - "mkdirp": "^3.0.1", - "yallist": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/tar/node_modules/yallist": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", - "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, "node_modules/text-hex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", @@ -12805,16 +12228,17 @@ "version": "0.3.2", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, "license": "MIT" }, "node_modules/tinyglobby": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", - "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "license": "MIT", "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { "node": ">=12.0.0" @@ -12853,40 +12277,13 @@ "node": ">=14.0.0" } }, - "node_modules/tldts": { - "version": "6.1.86", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", - "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "tldts-core": "^6.1.86" - }, - "bin": { - "tldts": "bin/cli.js" - } - }, - "node_modules/tldts-core": { - "version": "6.1.86", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", - "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", "license": "MIT", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, "engines": { - "node": ">=0.6.0" + "node": ">=14.14" } }, "node_modules/to-regex-range": { @@ -12967,21 +12364,6 @@ "dev": true, "license": "MIT" }, - "node_modules/tr46": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.0.tgz", - "integrity": "sha512-IUWnUK7ADYR5Sl1fZlO1INDUhVhatWl7BtJWsIhwJ0UAK7ilzzIa8uIqOO/aYVWHZPJkKbEL+362wrzoeRF7bw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "punycode": "^2.3.1" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/trim-lines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", @@ -13012,9 +12394,9 @@ } }, "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==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", + "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", "dev": true, "license": "MIT", "engines": { @@ -13187,9 +12569,9 @@ "license": "MIT" }, "node_modules/typescript": { - "version": "5.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", - "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -13200,9 +12582,9 @@ } }, "node_modules/typescript-auto-import-cache": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/typescript-auto-import-cache/-/typescript-auto-import-cache-0.3.5.tgz", - "integrity": "sha512-fAIveQKsoYj55CozUiBoj4b/7WpN0i4o74wiGY5JVUEoD0XiqDk1tJqTEjgzL2/AizKQrXxyRosSebyDzBZKjw==", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/typescript-auto-import-cache/-/typescript-auto-import-cache-0.3.6.tgz", + "integrity": "sha512-RpuHXrknHdVdK7wv/8ug3Fr0WNsNi5l5aB8MYYuXhq2UH5lnEB1htJ1smhtD5VeCsGr2p8mUDtd83LCQDFVgjQ==", "dev": true, "license": "MIT", "dependencies": { @@ -13210,16 +12592,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.44.1.tgz", - "integrity": "sha512-0ws8uWGrUVTjEeN2OM4K1pLKHK/4NiNP/vz6ns+LjT/6sqpaYzIVFajZb1fj/IDwpsrrHb3Jy0Qm5u9CPcKaeg==", + "version": "8.53.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.53.0.tgz", + "integrity": "sha512-xHURCQNxZ1dsWn0sdOaOfCSQG0HKeqSj9OexIxrz6ypU6wHYOdX2I3D2b8s8wFSsSOYJb+6q283cLiLlkEsBYw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.44.1", - "@typescript-eslint/parser": "8.44.1", - "@typescript-eslint/typescript-estree": "8.44.1", - "@typescript-eslint/utils": "8.44.1" + "@typescript-eslint/eslint-plugin": "8.53.0", + "@typescript-eslint/parser": "8.53.0", + "@typescript-eslint/typescript-estree": "8.53.0", + "@typescript-eslint/utils": "8.53.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -13274,28 +12656,9 @@ "version": "7.12.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.12.0.tgz", "integrity": "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==", + "devOptional": true, "license": "MIT" }, - "node_modules/unicode-properties": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz", - "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==", - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.0", - "unicode-trie": "^2.0.0" - } - }, - "node_modules/unicode-trie": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", - "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", - "license": "MIT", - "dependencies": { - "pako": "^0.2.5", - "tiny-inflate": "^1.0.0" - } - }, "node_modules/unified": { "version": "11.0.5", "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", @@ -13316,14 +12679,14 @@ } }, "node_modules/unifont": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/unifont/-/unifont-0.5.2.tgz", - "integrity": "sha512-LzR4WUqzH9ILFvjLAUU7dK3Lnou/qd5kD+IakBtBK4S15/+x2y9VX+DcWQv6s551R6W+vzwgVS6tFg3XggGBgg==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/unifont/-/unifont-0.7.1.tgz", + "integrity": "sha512-0lg9M1cMYvXof8//wZBq6EDEfbwv4++t7+dYpXeS2ypaLuZJmUFYEwTm412/1ED/Wfo/wyzSu6kNZEr9hgRNfg==", "license": "MIT", "dependencies": { - "css-tree": "^3.0.0", - "ofetch": "^1.4.1", - "ohash": "^2.0.0" + "css-tree": "^3.1.0", + "ofetch": "^1.5.1", + "ohash": "^2.0.11" } }, "node_modules/unist-util-find-after": { @@ -13436,9 +12799,9 @@ } }, "node_modules/unist-util-visit-parents": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", - "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", @@ -13571,9 +12934,9 @@ } }, "node_modules/vite": { - "version": "6.3.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.6.tgz", - "integrity": "sha512-0msEVHJEScQbhkbVTb/4iHZdJ6SXp/AvxL2sjwYQFfBqleHtnCqv1J3sa9zbWz/6kW1m9Tfzn92vW+kZ1WV6QA==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", + "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", "license": "MIT", "dependencies": { "esbuild": "^0.25.0", @@ -13803,9 +13166,9 @@ } }, "node_modules/volar-service-css": { - "version": "0.0.62", - "resolved": "https://registry.npmjs.org/volar-service-css/-/volar-service-css-0.0.62.tgz", - "integrity": "sha512-JwNyKsH3F8PuzZYuqPf+2e+4CTU8YoyUHEHVnoXNlrLe7wy9U3biomZ56llN69Ris7TTy/+DEX41yVxQpM4qvg==", + "version": "0.0.67", + "resolved": "https://registry.npmjs.org/volar-service-css/-/volar-service-css-0.0.67.tgz", + "integrity": "sha512-zV7C6enn9T9tuvQ6iSUyYEs34iPXR69Pf9YYWpbFYPWzVs22w96BtE8p04XYXbmjU6unt5oFt+iLL77bMB5fhA==", "dev": true, "license": "MIT", "dependencies": { @@ -13823,13 +13186,13 @@ } }, "node_modules/volar-service-emmet": { - "version": "0.0.62", - "resolved": "https://registry.npmjs.org/volar-service-emmet/-/volar-service-emmet-0.0.62.tgz", - "integrity": "sha512-U4dxWDBWz7Pi4plpbXf4J4Z/ss6kBO3TYrACxWNsE29abu75QzVS0paxDDhI6bhqpbDFXlpsDhZ9aXVFpnfGRQ==", + "version": "0.0.67", + "resolved": "https://registry.npmjs.org/volar-service-emmet/-/volar-service-emmet-0.0.67.tgz", + "integrity": "sha512-UDBL5x7KptmuJZNCCXMlCndMhFult/tj+9jXq3FH1ZGS1E4M/1U5hC06pg1c6e4kn+vnR6bqmvX0vIhL4f98+A==", "dev": true, "license": "MIT", "dependencies": { - "@emmetio/css-parser": "^0.4.0", + "@emmetio/css-parser": "^0.4.1", "@emmetio/html-matcher": "^1.3.0", "@vscode/emmet-helper": "^2.9.3", "vscode-uri": "^3.0.8" @@ -13844,9 +13207,9 @@ } }, "node_modules/volar-service-html": { - "version": "0.0.62", - "resolved": "https://registry.npmjs.org/volar-service-html/-/volar-service-html-0.0.62.tgz", - "integrity": "sha512-Zw01aJsZRh4GTGUjveyfEzEqpULQUdQH79KNEiKVYHZyuGtdBRYCHlrus1sueSNMxwwkuF5WnOHfvBzafs8yyQ==", + "version": "0.0.67", + "resolved": "https://registry.npmjs.org/volar-service-html/-/volar-service-html-0.0.67.tgz", + "integrity": "sha512-ljREMF79JbcjNvObiv69HK2HCl5UT7WTD10zi6CRFUHMbPfiF2UZ42HGLsEGSzaHGZz6H4IFjSS/qfENRLUviQ==", "dev": true, "license": "MIT", "dependencies": { @@ -13864,9 +13227,9 @@ } }, "node_modules/volar-service-prettier": { - "version": "0.0.62", - "resolved": "https://registry.npmjs.org/volar-service-prettier/-/volar-service-prettier-0.0.62.tgz", - "integrity": "sha512-h2yk1RqRTE+vkYZaI9KYuwpDfOQRrTEMvoHol0yW4GFKc75wWQRrb5n/5abDrzMPrkQbSip8JH2AXbvrRtYh4w==", + "version": "0.0.67", + "resolved": "https://registry.npmjs.org/volar-service-prettier/-/volar-service-prettier-0.0.67.tgz", + "integrity": "sha512-B4KnPJPNWFTkEDa6Fn08i5PpO6T1CecmLLTFZoXz2eI4Fxwba/3nDaaVSsEP7e/vEe+U5YqV9fBzayJT71G5xg==", "dev": true, "license": "MIT", "dependencies": { @@ -13886,15 +13249,15 @@ } }, "node_modules/volar-service-typescript": { - "version": "0.0.62", - "resolved": "https://registry.npmjs.org/volar-service-typescript/-/volar-service-typescript-0.0.62.tgz", - "integrity": "sha512-p7MPi71q7KOsH0eAbZwPBiKPp9B2+qrdHAd6VY5oTo9BUXatsOAdakTm9Yf0DUj6uWBAaOT01BSeVOPwucMV1g==", + "version": "0.0.67", + "resolved": "https://registry.npmjs.org/volar-service-typescript/-/volar-service-typescript-0.0.67.tgz", + "integrity": "sha512-rfQBy36Rm1PU9vLWHk8BYJ4r2j/CI024vocJcH4Nb6K2RTc2Irmw6UOVY5DdGiPRV5r+e10wLMK5njj/EcL8sA==", "dev": true, "license": "MIT", "dependencies": { "path-browserify": "^1.0.1", "semver": "^7.6.2", - "typescript-auto-import-cache": "^0.3.3", + "typescript-auto-import-cache": "^0.3.5", "vscode-languageserver-textdocument": "^1.0.11", "vscode-nls": "^5.2.0", "vscode-uri": "^3.0.8" @@ -13909,9 +13272,9 @@ } }, "node_modules/volar-service-typescript-twoslash-queries": { - "version": "0.0.62", - "resolved": "https://registry.npmjs.org/volar-service-typescript-twoslash-queries/-/volar-service-typescript-twoslash-queries-0.0.62.tgz", - "integrity": "sha512-KxFt4zydyJYYI0kFAcWPTh4u0Ha36TASPZkAnNY784GtgajerUqM80nX/W1d0wVhmcOFfAxkVsf/Ed+tiYU7ng==", + "version": "0.0.67", + "resolved": "https://registry.npmjs.org/volar-service-typescript-twoslash-queries/-/volar-service-typescript-twoslash-queries-0.0.67.tgz", + "integrity": "sha512-LD2R7WivDYp1SPgZrxx/0222xVTitDjm36oKo5+bfYG5kEgnw+BOPVHdwmvpJKg/RfssfxDI1ouwD4XkEDEfbA==", "dev": true, "license": "MIT", "dependencies": { @@ -13927,14 +13290,14 @@ } }, "node_modules/volar-service-yaml": { - "version": "0.0.62", - "resolved": "https://registry.npmjs.org/volar-service-yaml/-/volar-service-yaml-0.0.62.tgz", - "integrity": "sha512-k7gvv7sk3wa+nGll3MaSKyjwQsJjIGCHFjVkl3wjaSP2nouKyn9aokGmqjrl39mi88Oy49giog2GkZH526wjig==", + "version": "0.0.67", + "resolved": "https://registry.npmjs.org/volar-service-yaml/-/volar-service-yaml-0.0.67.tgz", + "integrity": "sha512-jkdP/RF6wPIXEE3Ktnd81oJPn7aAvnVSiaqQHThC2Hrvo6xd9pEcqtbBUI+YfqVgvcMtXAkbtNO61K2GPhAiuA==", "dev": true, "license": "MIT", "dependencies": { "vscode-uri": "^3.0.8", - "yaml-language-server": "~1.15.0" + "yaml-language-server": "~1.19.2" }, "peerDependencies": { "@volar/language-service": "~2.4.0" @@ -13946,29 +13309,29 @@ } }, "node_modules/vscode-css-languageservice": { - "version": "6.3.2", - "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-6.3.2.tgz", - "integrity": "sha512-GEpPxrUTAeXWdZWHev1OJU9lz2Q2/PPBxQ2TIRmLGvQiH3WZbqaNoute0n0ewxlgtjzTW3AKZT+NHySk5Rf4Eg==", + "version": "6.3.9", + "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-6.3.9.tgz", + "integrity": "sha512-1tLWfp+TDM5ZuVWht3jmaY5y7O6aZmpeXLoHl5bv1QtRsRKt4xYGRMmdJa5Pqx/FTkgRbsna9R+Gn2xE+evVuA==", "dev": true, "license": "MIT", "dependencies": { "@vscode/l10n": "^0.0.18", "vscode-languageserver-textdocument": "^1.0.12", "vscode-languageserver-types": "3.17.5", - "vscode-uri": "^3.0.8" + "vscode-uri": "^3.1.0" } }, "node_modules/vscode-html-languageservice": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-5.3.1.tgz", - "integrity": "sha512-ysUh4hFeW/WOWz/TO9gm08xigiSsV/FOAZ+DolgJfeLftna54YdmZ4A+lIn46RbdO3/Qv5QHTn1ZGqmrXQhZyA==", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-5.6.1.tgz", + "integrity": "sha512-5Mrqy5CLfFZUgkyhNZLA1Ye5g12Cb/v6VM7SxUzZUaRKWMDz4md+y26PrfRTSU0/eQAl3XpO9m2og+GGtDMuaA==", "dev": true, "license": "MIT", "dependencies": { "@vscode/l10n": "^0.0.18", "vscode-languageserver-textdocument": "^1.0.12", "vscode-languageserver-types": "^3.17.5", - "vscode-uri": "^3.0.8" + "vscode-uri": "^3.1.0" } }, "node_modules/vscode-json-languageservice": { @@ -14057,21 +13420,6 @@ "dev": true, "license": "MIT" }, - "node_modules/w3c-xmlserializer": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", - "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "xml-name-validator": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/web-namespaces": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", @@ -14082,61 +13430,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/whatwg-encoding": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", - "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "iconv-lite": "0.6.3" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/whatwg-mimetype": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", - "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/whatwg-url": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", - "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "tr46": "^5.1.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -14283,13 +13576,13 @@ } }, "node_modules/winston": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz", - "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==", + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.19.0.tgz", + "integrity": "sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==", "license": "MIT", "dependencies": { "@colors/colors": "^1.6.0", - "@dabh/diagnostics": "^2.0.2", + "@dabh/diagnostics": "^2.0.8", "async": "^3.2.3", "is-stream": "^2.0.0", "logform": "^2.7.0", @@ -14375,12 +13668,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" - }, "node_modules/ws": { "version": "8.18.3", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", @@ -14403,27 +13690,6 @@ } } }, - "node_modules/xml-name-validator": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", - "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/xxhash-wasm": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.1.0.tgz", @@ -14448,40 +13714,42 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", - "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", "license": "ISC", "bin": { "yaml": "bin.mjs" }, "engines": { "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" } }, "node_modules/yaml-language-server": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/yaml-language-server/-/yaml-language-server-1.15.0.tgz", - "integrity": "sha512-N47AqBDCMQmh6mBLmI6oqxryHRzi33aPFPsJhYy3VTUGCdLHYjGh4FZzpUjRlphaADBBkDmnkM/++KNIOHi5Rw==", + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/yaml-language-server/-/yaml-language-server-1.19.2.tgz", + "integrity": "sha512-9F3myNmJzUN/679jycdMxqtydPSDRAarSj3wPiF7pchEPnO9Dg07Oc+gIYLqXR4L+g+FSEVXXv2+mr54StLFOg==", "dev": true, "license": "MIT", "dependencies": { - "ajv": "^8.11.0", + "@vscode/l10n": "^0.0.18", + "ajv": "^8.17.1", + "ajv-draft-04": "^1.0.0", "lodash": "4.17.21", + "prettier": "^3.5.0", "request-light": "^0.5.7", "vscode-json-languageservice": "4.1.8", - "vscode-languageserver": "^7.0.0", + "vscode-languageserver": "^9.0.0", "vscode-languageserver-textdocument": "^1.0.1", "vscode-languageserver-types": "^3.16.0", - "vscode-nls": "^5.0.0", "vscode-uri": "^3.0.2", - "yaml": "2.2.2" + "yaml": "2.7.1" }, "bin": { "yaml-language-server": "bin/yaml-language-server" - }, - "optionalDependencies": { - "prettier": "2.8.7" } }, "node_modules/yaml-language-server/node_modules/ajv": { @@ -14501,6 +13769,21 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/yaml-language-server/node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/yaml-language-server/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", @@ -14508,23 +13791,6 @@ "dev": true, "license": "MIT" }, - "node_modules/yaml-language-server/node_modules/prettier": { - "version": "2.8.7", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.7.tgz", - "integrity": "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==", - "dev": true, - "license": "MIT", - "optional": true, - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, "node_modules/yaml-language-server/node_modules/request-light": { "version": "0.5.8", "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.5.8.tgz", @@ -14532,53 +13798,15 @@ "dev": true, "license": "MIT" }, - "node_modules/yaml-language-server/node_modules/vscode-jsonrpc": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz", - "integrity": "sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.0.0 || >=10.0.0" - } - }, - "node_modules/yaml-language-server/node_modules/vscode-languageserver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-7.0.0.tgz", - "integrity": "sha512-60HTx5ID+fLRcgdHfmz0LDZAXYEV68fzwG0JWwEPBode9NuMYTIxuYXPg4ngO8i8+Ou0lM7y6GzaYWbiDL0drw==", - "dev": true, - "license": "MIT", - "dependencies": { - "vscode-languageserver-protocol": "3.16.0" - }, - "bin": { - "installServerIntoExtension": "bin/installServerIntoExtension" - } - }, - "node_modules/yaml-language-server/node_modules/vscode-languageserver-protocol": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0.tgz", - "integrity": "sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A==", - "dev": true, - "license": "MIT", - "dependencies": { - "vscode-jsonrpc": "6.0.0", - "vscode-languageserver-types": "3.16.0" - } - }, - "node_modules/yaml-language-server/node_modules/vscode-languageserver-types": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz", - "integrity": "sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==", - "dev": true, - "license": "MIT" - }, "node_modules/yaml-language-server/node_modules/yaml": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz", - "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", + "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", "dev": true, "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, "engines": { "node": ">= 14" } @@ -14708,12 +13936,12 @@ } }, "node_modules/zod-to-json-schema": { - "version": "3.24.6", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz", - "integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==", + "version": "3.25.1", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz", + "integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==", "license": "ISC", "peerDependencies": { - "zod": "^3.24.1" + "zod": "^3.25 || ^4" } }, "node_modules/zod-to-ts": { diff --git a/website/package.json b/website/package.json index e67541fa6..1421e1070 100644 --- a/website/package.json +++ b/website/package.json @@ -20,67 +20,68 @@ "test:headed": "vitest --browser.headless false", "test:browser": "vitest --project browser", "test:browser:headed": "vitest --project browser --browser.headless false", - "e2e": "playwright test" + "e2e": "playwright test", + "e2e:chromium": "playwright test --project=chromium" }, "dependencies": { - "@astrojs/node": "^9.4.4", + "@astrojs/node": "^9.5.1", "@auth/core": "^0.37.4", - "@genspectrum/dashboard-components": "^1.11.1", - "@tanstack/react-query": "^5.90.1", - "astro": "^5.13.10", + "@genspectrum/dashboard-components": "^1.12.0", + "@tanstack/react-query": "^5.90.16", + "astro": "^5.16.9", "auth-astro": "^4.2.0", - "axios": "^1.12.2", - "cookie": "^1.0.2", - "dayjs": "^1.11.18", - "katex": "^0.16.25", - "patch-package": "^8.0.0", + "axios": "^1.13.2", + "cookie": "^1.1.1", + "dayjs": "^1.11.19", + "katex": "^0.16.27", + "patch-package": "^8.0.1", "react": "^18.3.1", "react-dom": "^18.3.1", "react-katex": "^3.1.0", "react-toastify": "^11.0.5", "uuid": "^11.1.0", - "winston": "^3.17.0", + "winston": "^3.19.0", "winston-daily-rotate-file": "^5.0.0", - "yaml": "^2.8.1" + "yaml": "^2.8.2" }, "devDependencies": { - "@astrojs/check": "^0.9.4", - "@astrojs/react": "^4.3.1", - "@eslint/js": "^9.36.0", + "@astrojs/check": "^0.9.6", + "@astrojs/react": "^4.4.2", + "@eslint/js": "^9.39.2", "@iconify-json/mdi": "^1.2.3", "@iconify-json/mdi-light": "^1.2.2", - "@iconify/tailwind4": "^1.0.6", - "@playwright/test": "^1.55.0", - "@tailwindcss/vite": "^4.1.13", - "@tanstack/eslint-plugin-query": "^5.90.1", + "@iconify/tailwind4": "^1.2.0", + "@playwright/test": "^1.57.0", + "@tailwindcss/vite": "^4.1.18", + "@tanstack/eslint-plugin-query": "^5.91.2", "@testing-library/dom": "^10.4.1", - "@testing-library/jest-dom": "^6.8.0", - "@testing-library/react": "^16.3.0", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.1", "@testing-library/user-event": "^14.6.1", "@types/node": "^24.5.2", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", "@types/react-katex": "^3.0.4", "@types/topojson-specification": "^1.0.5", - "@typescript-eslint/eslint-plugin": "^8.44.1", - "@typescript-eslint/parser": "^8.44.1", + "@typescript-eslint/eslint-plugin": "^8.53.0", + "@typescript-eslint/parser": "^8.53.0", "@vitest/browser": "^3.2.4", "astro-eslint-parser": "^1.2.2", - "daisyui": "^5.1.14", + "daisyui": "^5.5.14", "dotenv": "^16.5.0", - "eslint": "^9.36.0", - "eslint-plugin-astro": "^1.3.1", + "eslint": "^9.39.2", + "eslint-plugin-astro": "^1.5.0", "eslint-plugin-import": "^2.32.0", "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^5.2.0", - "msw": "^2.11.3", + "msw": "^2.12.7", "playwright": "^1.55.0", - "prettier": "^3.6.2", + "prettier": "^3.7.4", "prettier-plugin-astro": "^0.14.1", - "prettier-plugin-tailwindcss": "^0.6.14", + "prettier-plugin-tailwindcss": "^0.7.2", "tailwindcss": "^4.0.9", - "typescript": "^5.9.2", - "typescript-eslint": "^8.44.1", + "typescript": "^5.9.3", + "typescript-eslint": "^8.53.0", "vitest": "^3.2.4", "vitest-browser-react": "^1.0.1", "zod": "^3.25.74" diff --git a/website/src/components/LapisUnreachableWrapperClient.browser.spec.tsx b/website/src/components/LapisUnreachableWrapperClient.browser.spec.tsx index 3108cf7b0..c33f73371 100644 --- a/website/src/components/LapisUnreachableWrapperClient.browser.spec.tsx +++ b/website/src/components/LapisUnreachableWrapperClient.browser.spec.tsx @@ -1,31 +1,28 @@ +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { describe, expect } from 'vitest'; import { render } from 'vitest-browser-react'; -import LapisUnreachableWrapperClient from './LapisUnreachableWrapperClient'; +import { LapisUnreachableWrapperClient } from './LapisUnreachableWrapperClient'; import { DUMMY_LAPIS_URL } from '../../routeMocker'; import { it } from '../../test-extend'; +const queryClient = new QueryClient(); + describe('LapisUnreachableWrapperClient', () => { it('displays children when LAPIS is reachable', async ({ routeMockers: { lapis } }) => { lapis.mockPostAggregated({}, { data: [{ count: 100 }] }); - const { getByText } = render( - -
Content is visible
-
, - ); + const content = 'Content is visible'; + + const { getByText } = renderWithContent(content); - await expect.element(getByText('Content is visible')).toBeVisible(); + await expect.element(getByText(content)).toBeVisible(); }); it('displays error message when LAPIS is unreachable', async ({ routeMockers: { lapis } }) => { lapis.mockLapisDown(); - const { getByText } = render( - -
Content is visible
-
, - ); + const { getByText } = renderWithContent('Content is visible'); await expect.element(getByText('Data Source Unreachable')).toBeVisible(); await expect.element(getByText('Unable to connect to the data source')).toBeVisible(); @@ -34,23 +31,17 @@ describe('LapisUnreachableWrapperClient', () => { it('does not display children when LAPIS is unreachable', async ({ routeMockers: { lapis } }) => { lapis.mockLapisDown(); - const { getByText } = render( - -
Content should not be visible
-
, - ); + const content = 'Content is visible'; - await expect.poll(() => getByText('Content should not be visible').query()).not.toBeInTheDocument(); + const { getByText } = renderWithContent(content); + + await expect.poll(() => getByText(content).query()).not.toBeInTheDocument(); }); it('displays error when LAPIS returns invalid response', async ({ routeMockers: { lapis } }) => { lapis.mockPostAggregated({}, { data: [] }, 200); - const { getByText } = render( - -
Content is visible
-
, - ); + const { getByText } = renderWithContent('Content is visible'); await expect.element(getByText('Data Source Unreachable')).toBeVisible(); }); @@ -58,12 +49,18 @@ describe('LapisUnreachableWrapperClient', () => { it('displays error when LAPIS returns 500', async ({ routeMockers: { lapis } }) => { lapis.mockPostAggregated({}, { data: [{ count: 100 }] }, 500); - const { getByText } = render( - -
Content is visible
-
, - ); + const { getByText } = renderWithContent('Content is visible'); await expect.element(getByText('Data Source Unreachable')).toBeVisible(); }); }); + +function renderWithContent(content: string) { + return render( + + +
{content}
+
+
, + ); +} diff --git a/website/src/components/LapisUnreachableWrapperClient.tsx b/website/src/components/LapisUnreachableWrapperClient.tsx index 18307c761..ddec59b3a 100644 --- a/website/src/components/LapisUnreachableWrapperClient.tsx +++ b/website/src/components/LapisUnreachableWrapperClient.tsx @@ -2,14 +2,13 @@ import { useQuery } from '@tanstack/react-query'; import { type FC, type ReactNode } from 'react'; import { checkLapisHealth } from '../lapis/checkLapisHealth'; -import { withQueryProvider } from './subscriptions/backendApi/withQueryProvider'; type LapisUnreachableWrapperClientProps = { lapisUrl: string; children: ReactNode; }; -const LapisUnreachableWrapperClientInner: FC = ({ lapisUrl, children }) => { +export const LapisUnreachableWrapperClient: FC = ({ lapisUrl, children }) => { const { data: isReachable, isLoading } = useQuery({ queryKey: ['lapis-reachable', lapisUrl], queryFn: () => checkLapisHealth(lapisUrl), @@ -37,7 +36,3 @@ const LapisUnreachableWrapperClientInner: FC return <>{children}; }; - -const LapisUnreachableWrapperClient = withQueryProvider(LapisUnreachableWrapperClientInner); - -export default LapisUnreachableWrapperClient; diff --git a/website/src/components/genspectrum/GsMutationComparison.astro b/website/src/components/genspectrum/GsMutationComparison.astro deleted file mode 100644 index edb4f07df..000000000 --- a/website/src/components/genspectrum/GsMutationComparison.astro +++ /dev/null @@ -1,25 +0,0 @@ ---- -import type { NamedLapisFilter, SequenceType } from '@genspectrum/dashboard-components/util'; - -import { defaultTablePageSize } from '../../views/View'; -import ComponentWrapper from '../ComponentWrapper.astro'; - -interface Props { - lapisFilters: NamedLapisFilter[]; - sequenceType: SequenceType; - height?: string; - pageSize?: number; -} - -const { lapisFilters, sequenceType, height, pageSize } = Astro.props; ---- - - - - diff --git a/website/src/components/genspectrum/GsMutationComparison.tsx b/website/src/components/genspectrum/GsMutationComparison.tsx new file mode 100644 index 000000000..eac5b1a70 --- /dev/null +++ b/website/src/components/genspectrum/GsMutationComparison.tsx @@ -0,0 +1,35 @@ +import type { NamedLapisFilter, SequenceType } from '@genspectrum/dashboard-components/util'; +import type { FC } from 'react'; + +import { defaultTablePageSize } from '../../views/View'; +import { ComponentWrapper } from '../ComponentWrapper.tsx'; + +export type GsMutationComparisonProps = { + lapisFilters: NamedLapisFilter[]; + sequenceType: SequenceType; + height?: string; + pageSize?: number; +}; + +export const GsMutationComparison: FC = ({ + lapisFilters, + sequenceType, + height, + pageSize, +}) => { + return ( + + + + ); +}; diff --git a/website/src/components/genspectrum/GsMutationFilter.tsx b/website/src/components/genspectrum/GsMutationFilter.tsx index a8d97e663..ea4a63f96 100644 --- a/website/src/components/genspectrum/GsMutationFilter.tsx +++ b/website/src/components/genspectrum/GsMutationFilter.tsx @@ -13,7 +13,7 @@ export function GsMutationFilter({ initialValue, width, enabledMutationTypes, - onMutationChange = () => {}, + onMutationChange, }: { width?: string; initialValue?: MutationFilter | string[] | undefined; diff --git a/website/src/components/genspectrum/GsSequencesByLocation.tsx b/website/src/components/genspectrum/GsSequencesByLocation.tsx index 54c51e378..9848e7bed 100644 --- a/website/src/components/genspectrum/GsSequencesByLocation.tsx +++ b/website/src/components/genspectrum/GsSequencesByLocation.tsx @@ -28,6 +28,7 @@ export const GsSequencesByLocation: FC = ({ {mapData === undefined ? ( = ({ /> ) : ( { const handler = new DummyPageStateHandler(); - it('should render enabled link for short URL', () => { + it('should render enabled link for short URL', async () => { const shortState: DummyPageState = { data: 'short' }; - const { getByRole, container } = render( - , + const { result: state } = renderHook(() => useState({ data: 'initial' })); + + const { getByRole } = render( + , ); - const link = getByRole('button'); - expect(link.element()).toBeInTheDocument(); - expect(link.element()).toHaveAttribute('href', '/test?data=short'); - expect(container.textContent).not.toMatch(urlTooLongMessage); + await clickApply(getByRole); + + expect(state.current[0].data).toEqual('short'); }); - it('should render disabled span and error message for long URL', () => { + it('should render disabled span and error message for long URL', async () => { const longData = 'x'.repeat(2000); const longState: DummyPageState = { data: longData }; + const { result: state } = renderHook(() => useState({ data: 'initial' })); + const { getByRole, container } = render( - , + , ); - const button = getByRole('button'); - expect(button.element()).not.toHaveAttribute('href'); expect(container.textContent).toMatch(urlTooLongMessage); + expect(getByRole('button', { name: 'Apply filters' }).element()).not.toHaveAttribute('onClick'); + + await clickApply(getByRole); + expect(state.current[0].data).toEqual('initial'); }); - it('should update when state changes from short to long', () => { + it('should update when state changes from short to long', async () => { const shortState: DummyPageState = { data: 'short' }; + const { result: state } = renderHook(() => useState({ data: 'initial' })); + const { getByRole, container, rerender } = render( - , + , ); - const button = getByRole('button'); - expect(button.element()).toHaveAttribute('href', '/test?data=short'); + await clickApply(getByRole); + expect(state.current[0].data).toEqual('short'); expect(container.textContent).not.toMatch(urlTooLongMessage); const longData = 'x'.repeat(2000); const longState: DummyPageState = { data: longData }; - rerender(); + rerender( + , + ); - const updatedButton = getByRole('button'); - expect(updatedButton.element()).not.toHaveAttribute('href'); expect(container.textContent).toMatch(urlTooLongMessage); + expect(getByRole('button', { name: 'Apply filters' }).element()).not.toHaveAttribute('onClick'); + + await clickApply(getByRole); + expect(state.current[0].data).toEqual('short'); }); - it('should update when state changes from long to short', () => { + it('should update when state changes from long to short', async () => { const longData = 'x'.repeat(2000); const longState: DummyPageState = { data: longData }; + const { result: state } = renderHook(() => useState({ data: 'initial' })); + const { getByRole, container, rerender } = render( - , + , ); - const button = getByRole('button'); - expect(button.element()).not.toHaveAttribute('href'); + expect(getByRole('button', { name: 'Apply filters' }).element()).not.toHaveAttribute('onClick'); expect(container.textContent).toMatch(urlTooLongMessage); + await clickApply(getByRole); + expect(state.current[0].data).toEqual('initial'); const shortState: DummyPageState = { data: 'short' }; - rerender(); + rerender( + , + ); - const updatedButton = getByRole('button'); - expect(updatedButton.element()).toHaveAttribute('href', '/test?data=short'); expect(container.textContent).not.toMatch(urlTooLongMessage); + await clickApply(getByRole); + expect(state.current[0].data).toEqual('short'); }); }); + +async function clickApply(getByRole: RenderResult['getByRole']) { + await act(async () => { + await getByRole('button', { name: 'Apply filters' }).click(); + }); +} diff --git a/website/src/components/pageStateSelectors/ApplyFilterButton.tsx b/website/src/components/pageStateSelectors/ApplyFilterButton.tsx index d01b61b8f..d3d7b5736 100644 --- a/website/src/components/pageStateSelectors/ApplyFilterButton.tsx +++ b/website/src/components/pageStateSelectors/ApplyFilterButton.tsx @@ -1,3 +1,5 @@ +import type { Dispatch, SetStateAction } from 'react'; + import type { WithClassName } from '../../types/WithClassName.ts'; import type { PageStateHandler } from '../../views/pageStateHandlers/PageStateHandler.ts'; @@ -8,15 +10,21 @@ const MAX_URL_LENGTH = 2000; export function ApplyFilterButton({ pageStateHandler, newPageState, + setPageState, className = '', }: WithClassName<{ pageStateHandler: PageStateHandler; newPageState: PageState; + setPageState: Dispatch>; }>) { const url = pageStateHandler.toUrl(newPageState); const fullUrl = `${window.location.origin}${url}`; const urlTooLong = fullUrl.length > MAX_URL_LENGTH; + const applyFilters = () => { + setPageState(newPageState); + }; + return urlTooLong ? ( <> @@ -30,8 +38,8 @@ export function ApplyFilterButton({ ) : ( - + ); } diff --git a/website/src/components/pageStateSelectors/CompareSideBySidePageStateSelector.tsx b/website/src/components/pageStateSelectors/CompareSideBySidePageStateSelector.tsx index a71cfc05e..014046f0e 100644 --- a/website/src/components/pageStateSelectors/CompareSideBySidePageStateSelector.tsx +++ b/website/src/components/pageStateSelectors/CompareSideBySidePageStateSelector.tsx @@ -1,37 +1,38 @@ -import { useMemo, useState } from 'react'; +import { type Dispatch, type SetStateAction, useEffect, useMemo, useState } from 'react'; import { ApplyFilterButton } from './ApplyFilterButton.tsx'; import { BaselineSelector } from './BaselineSelector.tsx'; import { SelectorHeadline } from './SelectorHeadline.tsx'; import { makeVariantFilterConfig, VariantSelector } from './VariantSelector.tsx'; -import type { OrganismsConfig } from '../../config.ts'; import { Inset } from '../../styles/Inset.tsx'; +import { BaseView } from '../../views/BaseView.ts'; +import type { OrganismConstants } from '../../views/OrganismConstants.ts'; import type { CompareSideBySideData } from '../../views/View.ts'; -import { type OrganismViewKey, Routing } from '../../views/routing.ts'; -import type { compareSideBySideViewKey } from '../../views/viewKeys.ts'; +import { CompareSideBySideStateHandler } from '../../views/pageStateHandlers/CompareSideBySidePageStateHandler.ts'; export function CompareSideBySidePageStateSelector({ + view, filterId, - initialPageState, - organismViewKey, - organismsConfig, + pageState, + setPageState, enableAdvancedQueryFilter, }: { + view: BaseView; filterId: number; - initialPageState: CompareSideBySideData; - organismViewKey: OrganismViewKey & `${string}.${typeof compareSideBySideViewKey}`; - organismsConfig: OrganismsConfig; + pageState: CompareSideBySideData; + setPageState: Dispatch>; enableAdvancedQueryFilter: boolean; }) { - const view = useMemo(() => new Routing(organismsConfig), [organismsConfig]).getOrganismView(organismViewKey); + const [draftPageState, setDraftPageState] = useState(pageState); + useEffect(() => setDraftPageState(pageState), [pageState]); + const variantFilterConfig = useMemo( () => makeVariantFilterConfig(view.organismConstants), [view.organismConstants], ); - const [pageState, setPageState] = useState(initialPageState); const { filterOfCurrentId, currentLapisFilter } = useMemo(() => { - const filterOfCurrentId = pageState.filters.get(filterId) ?? { + const filterOfCurrentId = draftPageState.filters.get(filterId) ?? { datasetFilter: { locationFilters: {}, dateFilters: {}, @@ -47,7 +48,7 @@ export function CompareSideBySidePageStateSelector({ filterOfCurrentId.variantFilter, ), }; - }, [pageState, filterId, view.pageStateHandler]); + }, [draftPageState, filterId, view.pageStateHandler]); return (
@@ -60,8 +61,8 @@ export function CompareSideBySidePageStateSelector({ lapisFilter={currentLapisFilter} datasetFilter={filterOfCurrentId.datasetFilter} setDatasetFilter={(newDatasetFilter) => { - setPageState((previousState) => { - const updatedFilters = new Map(initialPageState.filters); + setDraftPageState((previousState) => { + const updatedFilters = new Map(previousState.filters); updatedFilters.set(filterId, { ...filterOfCurrentId, datasetFilter: newDatasetFilter, @@ -78,8 +79,8 @@ export function CompareSideBySidePageStateSelector({ { - setPageState((previousState) => { - const updatedFilters = new Map(initialPageState.filters); + setDraftPageState((previousState) => { + const updatedFilters = new Map(previousState.filters); updatedFilters.set(filterId, { ...filterOfCurrentId, variantFilter: newVariantFilter, @@ -96,7 +97,11 @@ export function CompareSideBySidePageStateSelector({
- +
); diff --git a/website/src/components/pageStateSelectors/CompareVariantsPageStateSelector.tsx b/website/src/components/pageStateSelectors/CompareVariantsPageStateSelector.tsx index cbfd106aa..0d03db9c8 100644 --- a/website/src/components/pageStateSelectors/CompareVariantsPageStateSelector.tsx +++ b/website/src/components/pageStateSelectors/CompareVariantsPageStateSelector.tsx @@ -1,39 +1,38 @@ -import { useMemo, useState } from 'react'; +import { type Dispatch, type SetStateAction, useEffect, useMemo, useState } from 'react'; import { ApplyFilterButton } from './ApplyFilterButton.tsx'; import { BaselineSelector } from './BaselineSelector.tsx'; import { SelectorHeadline } from './SelectorHeadline.tsx'; import { makeVariantFilterConfig } from './VariantSelector.tsx'; import { VariantsSelector } from './VariantsSelector.tsx'; -import { type OrganismsConfig } from '../../config.ts'; import { Inset } from '../../styles/Inset.tsx'; +import { GenericCompareVariantsView } from '../../views/BaseView.ts'; +import type { OrganismConstants } from '../../views/OrganismConstants.ts'; import type { CompareVariantsData, Id, VariantFilter } from '../../views/View.ts'; -import { type OrganismViewKey, Routing } from '../../views/routing.ts'; -import type { compareVariantsViewKey } from '../../views/viewKeys.ts'; export function CompareVariantsPageStateSelector({ - organismViewKey, - organismsConfig, - initialPageState, + view, + pageState, + setPageState, enableAdvancedQueryFilter, }: { - organismViewKey: OrganismViewKey & `${string}.${typeof compareVariantsViewKey}`; - organismsConfig: OrganismsConfig; - initialPageState: CompareVariantsData; + view: GenericCompareVariantsView; + pageState: CompareVariantsData; + setPageState: Dispatch>; enableAdvancedQueryFilter: boolean; }) { - const view = useMemo(() => new Routing(organismsConfig), [organismsConfig]).getOrganismView(organismViewKey); - const [pageState, setPageState] = useState(initialPageState); + const [draftPageState, setDraftPageState] = useState(pageState); + useEffect(() => setDraftPageState(pageState), [pageState]); const variantFilterConfigs = useMemo(() => { return new Map( - pageState.variants.entries().map(([id]) => [id, makeVariantFilterConfig(view.organismConstants)]), + draftPageState.variants.entries().map(([id]) => [id, makeVariantFilterConfig(view.organismConstants)]), ); - }, [pageState.variants, view.organismConstants]); + }, [draftPageState.variants, view.organismConstants]); const currentLapisFilter = useMemo(() => { - return view.pageStateHandler.datasetFilterToLapisFilter(pageState.datasetFilter); - }, [pageState, view.pageStateHandler]); + return view.pageStateHandler.datasetFilterToLapisFilter(draftPageState.datasetFilter); + }, [draftPageState, view.pageStateHandler]); return (
@@ -43,9 +42,9 @@ export function CompareVariantsPageStateSelector({ { - setPageState((previousState) => ({ + setDraftPageState((previousState) => ({ ...previousState, datasetFilter: newDatasetFilter, })); @@ -58,10 +57,10 @@ export function CompareVariantsPageStateSelector({ Variant Filters ) => { - setPageState((previousState) => ({ + setDraftPageState((previousState) => ({ ...previousState, variants: newVariantFilters, })); @@ -75,7 +74,8 @@ export function CompareVariantsPageStateSelector({
diff --git a/website/src/components/pageStateSelectors/CompareVariantsToBaselineStateSelector.tsx b/website/src/components/pageStateSelectors/CompareVariantsToBaselineStateSelector.tsx index 5aa4cb498..d57cb6bc5 100644 --- a/website/src/components/pageStateSelectors/CompareVariantsToBaselineStateSelector.tsx +++ b/website/src/components/pageStateSelectors/CompareVariantsToBaselineStateSelector.tsx @@ -1,29 +1,28 @@ -import { useMemo, useState } from 'react'; +import { type Dispatch, type SetStateAction, useEffect, useMemo, useState } from 'react'; import { ApplyFilterButton } from './ApplyFilterButton.tsx'; import { BaselineSelector } from './BaselineSelector.tsx'; import { SelectorHeadline } from './SelectorHeadline.tsx'; import { makeVariantFilterConfig, VariantSelector } from './VariantSelector.tsx'; import { VariantsSelector } from './VariantsSelector.tsx'; -import { type OrganismsConfig } from '../../config.ts'; import { Inset } from '../../styles/Inset.tsx'; +import { GenericCompareToBaselineView } from '../../views/BaseView.ts'; +import type { OrganismConstants } from '../../views/OrganismConstants.ts'; import type { CompareToBaselineData, Id, VariantFilter } from '../../views/View.ts'; -import { type OrganismViewKey, Routing } from '../../views/routing.ts'; -import { type compareToBaselineViewKey } from '../../views/viewKeys.ts'; export function CompareVariantsToBaselineStateSelector({ - organismViewKey, - organismsConfig, - initialPageState, + view, + pageState, + setPageState, enableAdvancedQueryFilter, }: { - organismViewKey: OrganismViewKey & `${string}.${typeof compareToBaselineViewKey}`; - organismsConfig: OrganismsConfig; - initialPageState: CompareToBaselineData; + view: GenericCompareToBaselineView; + pageState: CompareToBaselineData; + setPageState: Dispatch>; enableAdvancedQueryFilter: boolean; }) { - const view = useMemo(() => new Routing(organismsConfig), [organismsConfig]).getOrganismView(organismViewKey); - const [pageState, setPageState] = useState(initialPageState); + const [draftPageState, setDraftPageState] = useState(pageState); + useEffect(() => setDraftPageState(pageState), [pageState]); const variantFilterConfig = useMemo( () => makeVariantFilterConfig(view.organismConstants), @@ -31,12 +30,12 @@ export function CompareVariantsToBaselineStateSelector({ ); const variantFilterConfigs = useMemo(() => { - return new Map(pageState.variants.entries().map(([id]) => [id, { ...variantFilterConfig }])); - }, [pageState.variants, variantFilterConfig]); + return new Map(draftPageState.variants.entries().map(([id]) => [id, { ...variantFilterConfig }])); + }, [draftPageState.variants, variantFilterConfig]); const currentLapisFilter = useMemo(() => { - return view.pageStateHandler.baselineFilterToLapisFilter(pageState); - }, [pageState, view.pageStateHandler]); + return view.pageStateHandler.baselineFilterToLapisFilter(draftPageState); + }, [draftPageState, view.pageStateHandler]); return (
@@ -47,9 +46,9 @@ export function CompareVariantsToBaselineStateSelector({ { - setPageState((previousState) => ({ + setDraftPageState((previousState) => ({ ...previousState, datasetFilter: newDatasetFilter, })); @@ -64,13 +63,13 @@ export function CompareVariantsToBaselineStateSelector({ { - setPageState((previousState) => ({ + setDraftPageState((previousState) => ({ ...previousState, baselineFilter: newVariantFilter, })); }} variantFilterConfig={variantFilterConfig} - variantFilter={pageState.baselineFilter} + variantFilter={draftPageState.baselineFilter} lapisFilter={currentLapisFilter} enableAdvancedQueryFilter={enableAdvancedQueryFilter} /> @@ -81,10 +80,10 @@ export function CompareVariantsToBaselineStateSelector({ Variant Filters ) => { - setPageState((previousState) => ({ + setDraftPageState((previousState) => ({ ...previousState, variants: newVariantFilters, })); @@ -98,7 +97,8 @@ export function CompareVariantsToBaselineStateSelector({
diff --git a/website/src/components/pageStateSelectors/DynamicDateFilter.tsx b/website/src/components/pageStateSelectors/DynamicDateFilter.tsx index 26a966a73..61c447345 100644 --- a/website/src/components/pageStateSelectors/DynamicDateFilter.tsx +++ b/website/src/components/pageStateSelectors/DynamicDateFilter.tsx @@ -46,7 +46,7 @@ export function DynamicDateFilter({ // When the value has a "Custom" label, try to match it back to one of the generated options // by comparing dateFrom and dateTo. If there's a match, use that option's label instead of "Custom". const normalizedValue = useMemo(() => { - if (value === undefined || value.label !== CustomDateRangeLabel) { + if (value?.label !== CustomDateRangeLabel) { return value; } diff --git a/website/src/components/pageStateSelectors/FallbackElement.tsx b/website/src/components/pageStateSelectors/FallbackElement.tsx deleted file mode 100644 index ed5d3090e..000000000 --- a/website/src/components/pageStateSelectors/FallbackElement.tsx +++ /dev/null @@ -1,38 +0,0 @@ -export function SequencingEffortsSelectorFallback() { - return
; -} - -export function AnalyzeSingleVariantSelectorFallback() { - return
; -} - -export function CompareSideBySideSelectorFallback({ numFilters }: { numFilters: number }) { - return ( -
- {numFilters > 2 &&
} - {numFilters <= 2 &&
} -
- ); -} - -export function CompareToBaselineSelectorFallback({ numFilters }: { numFilters: number }) { - return ( - <> -
- {Array.from({ length: numFilters }).map((_, index) => ( -
- ))} - - ); -} - -export function CompareVariantsSelectorFallback({ numFilters }: { numFilters: number }) { - return ( - <> -
- {Array.from({ length: numFilters }).map((_, index) => ( -
- ))} - - ); -} diff --git a/website/src/components/pageStateSelectors/SequencingEffortsPageStateSelector.tsx b/website/src/components/pageStateSelectors/SequencingEffortsPageStateSelector.tsx index ee58b8bca..e095837a5 100644 --- a/website/src/components/pageStateSelectors/SequencingEffortsPageStateSelector.tsx +++ b/website/src/components/pageStateSelectors/SequencingEffortsPageStateSelector.tsx @@ -1,33 +1,31 @@ -import { useMemo, useState } from 'react'; +import { type Dispatch, type SetStateAction, useEffect, useMemo, useState } from 'react'; import { ApplyFilterButton } from './ApplyFilterButton.tsx'; import { BaselineSelector } from './BaselineSelector.tsx'; import { LineageFilterInput } from './LineageFilterInput.tsx'; import { SelectorHeadline } from './SelectorHeadline.tsx'; -import { type OrganismsConfig } from '../../config.ts'; import { Inset } from '../../styles/Inset.tsx'; +import { GenericSequencingEffortsView } from '../../views/BaseView.ts'; +import type { OrganismConstants } from '../../views/OrganismConstants.ts'; import type { DatasetAndVariantData } from '../../views/View.ts'; -import { type OrganismViewKey, Routing } from '../../views/routing.ts'; -import type { sequencingEffortsViewKey } from '../../views/viewKeys.ts'; export function SequencingEffortsPageStateSelector({ - organismViewKey, - organismsConfig, - initialPageState, + view, + pageState, + setPageState, enableAdvancedQueryFilter, }: { - organismViewKey: OrganismViewKey & `${string}.${typeof sequencingEffortsViewKey}`; - organismsConfig: OrganismsConfig; - initialPageState: DatasetAndVariantData; + view: GenericSequencingEffortsView; + pageState: DatasetAndVariantData; + setPageState: Dispatch>; enableAdvancedQueryFilter: boolean; }) { - const view = useMemo(() => new Routing(organismsConfig), [organismsConfig]).getOrganismView(organismViewKey); - - const [pageState, setPageState] = useState(initialPageState); + const [draftPageState, setDraftPageState] = useState(pageState); + useEffect(() => setDraftPageState(pageState), [pageState]); const currentLapisFilter = useMemo(() => { - return view.pageStateHandler.toLapisFilter(pageState); - }, [pageState, view.pageStateHandler]); + return view.pageStateHandler.toLapisFilter(draftPageState); + }, [draftPageState, view.pageStateHandler]); return (
@@ -37,9 +35,9 @@ export function SequencingEffortsPageStateSelector({ { - setPageState((previousState) => ({ + setDraftPageState((previousState) => ({ ...previousState, datasetFilter: newDatasetFilter, })); @@ -50,7 +48,7 @@ export function SequencingEffortsPageStateSelector({ { - setPageState((previousState) => ({ + setDraftPageState((previousState) => ({ ...previousState, variantFilter: { lineages: { @@ -62,7 +60,7 @@ export function SequencingEffortsPageStateSelector({ }} key={lineageFilterConfig.lapisField} lapisFilter={currentLapisFilter} - value={pageState.variantFilter.lineages?.[lineageFilterConfig.lapisField]} + value={draftPageState.variantFilter.lineages?.[lineageFilterConfig.lapisField]} /> ))} @@ -71,7 +69,8 @@ export function SequencingEffortsPageStateSelector({
diff --git a/website/src/components/pageStateSelectors/SingleVariantPageStateSelector.browser.spec.tsx b/website/src/components/pageStateSelectors/SingleVariantPageStateSelector.browser.spec.tsx index b3e4d6b0f..ad9f50fa8 100644 --- a/website/src/components/pageStateSelectors/SingleVariantPageStateSelector.browser.spec.tsx +++ b/website/src/components/pageStateSelectors/SingleVariantPageStateSelector.browser.spec.tsx @@ -1,40 +1,51 @@ +import { act } from '@testing-library/react'; +import { useState } from 'react'; import { describe, expect } from 'vitest'; -import { render } from 'vitest-browser-react'; +import { render, renderHook } from 'vitest-browser-react'; import { SingleVariantPageStateSelector } from './SingleVariantPageStateSelector.tsx'; import { DUMMY_LAPIS_URL, testOrganismsConfig } from '../../../routeMocker.ts'; import { it } from '../../../test-extend'; +import type { CovidVariantData } from '../../views/covid.ts'; +import { Routing } from '../../views/routing.ts'; describe('SingleVariantPageStateSelector', () => { - it('should remember the covid collection id', ({ routeMockers }) => { + it('should remember the covid collection id', async ({ routeMockers }) => { routeMockers.lapis.mockPostAggregated({}, { data: [] }); routeMockers.lapis.mockReferenceGenome({ nucleotideSequences: [{ name: 'main', sequence: 'ATGC' }], genes: [], }); + routeMockers.lapis.mockLineageDefinition('nextcladePangoLineage', {}); - const initialPageState = { - datasetFilter: { - locationFilters: {}, - dateFilters: {}, - textFilters: {}, - numberFilters: {}, - }, - variantFilter: {}, - collectionId: 5, - }; + const { result } = renderHook(() => + useState({ + datasetFilter: { + locationFilters: {}, + dateFilters: {}, + textFilters: {}, + numberFilters: {}, + }, + variantFilter: {}, + collectionId: 5, + }), + ); const { getByRole } = render( , ); - expect(getByRole('button').element()).toHaveAttribute('href', expect.stringContaining('collectionId=5')); + await act(async () => { + await getByRole('button', { name: 'Apply filters' }).click(); + }); + + expect(result.current[0].collectionId).equals(5); }); }); diff --git a/website/src/components/pageStateSelectors/SingleVariantPageStateSelector.tsx b/website/src/components/pageStateSelectors/SingleVariantPageStateSelector.tsx index d9a75c966..cc40f5e0a 100644 --- a/website/src/components/pageStateSelectors/SingleVariantPageStateSelector.tsx +++ b/website/src/components/pageStateSelectors/SingleVariantPageStateSelector.tsx @@ -1,38 +1,38 @@ -import { useMemo, useState } from 'react'; +import { type Dispatch, type SetStateAction, useEffect, useMemo, useState } from 'react'; import { ApplyFilterButton } from './ApplyFilterButton.tsx'; import { BaselineSelector } from './BaselineSelector.tsx'; import { SelectorHeadline } from './SelectorHeadline.tsx'; import { makeVariantFilterConfig, VariantSelector } from './VariantSelector.tsx'; -import { type OrganismsConfig } from '../../config.ts'; import { Inset } from '../../styles/Inset.tsx'; +import type { GenericSingleVariantView } from '../../views/BaseView.ts'; +import type { OrganismConstants } from '../../views/OrganismConstants.ts'; import { type DatasetAndVariantData } from '../../views/View.ts'; -import { type OrganismViewKey, Routing } from '../../views/routing.ts'; -import type { singleVariantViewKey } from '../../views/viewKeys.ts'; export function SingleVariantPageStateSelector({ - organismViewKey, - organismsConfig, - initialPageState, + view, + pageState, + setPageState, enableAdvancedQueryFilter, }: { - organismViewKey: OrganismViewKey & `${string}.${typeof singleVariantViewKey}`; - organismsConfig: OrganismsConfig; - initialPageState: DatasetAndVariantData; + view: GenericSingleVariantView; + pageState: DatasetAndVariantData; + setPageState: Dispatch>; enableAdvancedQueryFilter: boolean; }) { - const view = useMemo(() => new Routing(organismsConfig), [organismsConfig]).getOrganismView(organismViewKey); const variantFilterConfig = useMemo( () => makeVariantFilterConfig(view.organismConstants), [view.organismConstants], ); - const [pageState, setPageState] = useState(initialPageState); + + const [draftPageState, setDraftPageState] = useState(pageState); + useEffect(() => setDraftPageState(pageState), [pageState]); const baselineFilterConfigs = view.organismConstants.baselineFilterConfigs; const currentLapisFilter = useMemo(() => { - return view.pageStateHandler.toLapisFilter(pageState); - }, [pageState, view.pageStateHandler]); + return view.pageStateHandler.toLapisFilter(draftPageState); + }, [draftPageState, view.pageStateHandler]); return (
@@ -42,9 +42,9 @@ export function SingleVariantPageStateSelector({ { - setPageState((previousState) => ({ + setDraftPageState((previousState) => ({ ...previousState, datasetFilter: newDatasetFilter, })); @@ -59,13 +59,13 @@ export function SingleVariantPageStateSelector({ { - setPageState((previousState) => ({ + setDraftPageState((previousState) => ({ ...previousState, variantFilter: newVariantFilter, })); }} variantFilterConfig={variantFilterConfig} - variantFilter={pageState.variantFilter} + variantFilter={draftPageState.variantFilter} lapisFilter={currentLapisFilter} enableAdvancedQueryFilter={enableAdvancedQueryFilter} /> @@ -76,7 +76,8 @@ export function SingleVariantPageStateSelector({
diff --git a/website/src/components/pageStateSelectors/wasap/InfoBlocks.tsx b/website/src/components/pageStateSelectors/wasap/InfoBlocks.tsx index e5bf4cdf4..e8505938d 100644 --- a/website/src/components/pageStateSelectors/wasap/InfoBlocks.tsx +++ b/website/src/components/pageStateSelectors/wasap/InfoBlocks.tsx @@ -97,6 +97,13 @@ export function DefineClinicalSignatureInfo() { indicate that either not all sequences of the variant have the mutation or not all sequences with the mutation belong to the variant (or both).

+ +

Time Frame

+

+ Time frame for sequence selection. Shorter windows reduce dominance of well-sequenced historical + variants, yielding higher Jaccard values for emerging variants with fewer clinical sequences. Use this + filter to exclude variants no longer in circulation when assessing significance of current mutations. +

); } diff --git a/website/src/components/pageStateSelectors/wasap/WasapPageStateSelector.tsx b/website/src/components/pageStateSelectors/wasap/WasapPageStateSelector.tsx index 811c21333..62d18f6de 100644 --- a/website/src/components/pageStateSelectors/wasap/WasapPageStateSelector.tsx +++ b/website/src/components/pageStateSelectors/wasap/WasapPageStateSelector.tsx @@ -1,5 +1,5 @@ import { useQuery } from '@tanstack/react-query'; -import { useState } from 'react'; +import { type Dispatch, type SetStateAction, useState } from 'react'; import { ApplyFilterButton } from '../ApplyFilterButton'; import { DynamicDateFilter } from '../DynamicDateFilter'; @@ -34,11 +34,13 @@ export function WasapPageStateSelector({ pageStateHandler, initialBaseFilterState, initialAnalysisFilterState, + setPageState, }: { config: WasapPageConfig; pageStateHandler: PageStateHandler; initialBaseFilterState: WasapBaseFilter; initialAnalysisFilterState: WasapAnalysisFilter; + setPageState: Dispatch>; }) { const [baseFilterState, setBaseFilterState] = useState(initialBaseFilterState); @@ -201,7 +203,11 @@ export function WasapPageStateSelector({ } })()} - + ); } diff --git a/website/src/components/pageStateSelectors/wasap/filters/VariantExplorerFilter.browser.spec.tsx b/website/src/components/pageStateSelectors/wasap/filters/VariantExplorerFilter.browser.spec.tsx index e2e23b58e..dca132918 100644 --- a/website/src/components/pageStateSelectors/wasap/filters/VariantExplorerFilter.browser.spec.tsx +++ b/website/src/components/pageStateSelectors/wasap/filters/VariantExplorerFilter.browser.spec.tsx @@ -17,6 +17,7 @@ describe('VariantExplorerFilter', () => { minProportion: 0.05, minCount: 10, minJaccard: 0.5, + timeFrame: 'all', }; it('calls setPageState when changing sequence type', async ({ routeMockers: { lapis } }) => { diff --git a/website/src/components/pageStateSelectors/wasap/filters/VariantExplorerFilter.tsx b/website/src/components/pageStateSelectors/wasap/filters/VariantExplorerFilter.tsx index 86624a8c3..635f01fb7 100644 --- a/website/src/components/pageStateSelectors/wasap/filters/VariantExplorerFilter.tsx +++ b/website/src/components/pageStateSelectors/wasap/filters/VariantExplorerFilter.tsx @@ -1,6 +1,11 @@ import { Inset } from '../../../../styles/Inset'; import { GsLineageFilter } from '../../../genspectrum/GsLineageFilter'; -import type { WasapVariantFilter } from '../../../views/wasap/wasapPageConfig'; +import { + VARIANT_TIME_FRAME, + variantTimeFrameLabel, + type VariantTimeFrame, + type WasapVariantFilter, +} from '../../../views/wasap/wasapPageConfig'; import { SelectorHeadline } from '../../SelectorHeadline'; import { DefineClinicalSignatureInfo } from '../InfoBlocks'; import { LabeledField } from '../utils/LabeledField'; @@ -70,6 +75,21 @@ export function VariantExplorerFilter({ step={0.01} onChange={(v) => setPageState({ ...pageState, minJaccard: v })} /> + + + ); diff --git a/website/src/components/views/analyzeSingleVariant/CollectionsList.tsx b/website/src/components/views/analyzeSingleVariant/CollectionsList.tsx index 0af04924c..d20b9b051 100644 --- a/website/src/components/views/analyzeSingleVariant/CollectionsList.tsx +++ b/website/src/components/views/analyzeSingleVariant/CollectionsList.tsx @@ -1,15 +1,12 @@ import { useQuery } from '@tanstack/react-query'; -import { useMemo, useState } from 'react'; +import { type Dispatch, type SetStateAction, useState } from 'react'; import { z } from 'zod'; import { getClientLogger } from '../../../clientLogger.ts'; -import type { OrganismsConfig } from '../../../config.ts'; import { type CovidVariantData } from '../../../views/covid.ts'; -import { Routing } from '../../../views/routing.ts'; import { useErrorToast } from '../../ErrorReportInstruction.tsx'; -import { withQueryProvider } from '../../subscriptions/backendApi/withQueryProvider.tsx'; -export const collectionVariantClassName = 'border bg-white px-4 py-2 hover:bg-cyan border-gray-200'; +export const collectionVariantClassName = 'border bg-white px-4 py-2 hover:bg-cyan border-gray-200 cursor-pointer'; type CollectionVariant = { name: string; @@ -25,12 +22,11 @@ type Collection = { type CollectionsListProps = { initialCollectionId?: number; - organismsConfig: OrganismsConfig; + pageState: CovidVariantData; + setPageState: Dispatch>; }; -export const CollectionsList = withQueryProvider(CollectionsListInner); - -function CollectionsListInner({ initialCollectionId, organismsConfig }: CollectionsListProps) { +export function CollectionsList({ initialCollectionId, pageState, setPageState }: CollectionsListProps) { const [selectedCollectionId, setSelectedCollectionId] = useState(initialCollectionId ?? 1); const query = useQuery({ @@ -56,7 +52,8 @@ function CollectionsListInner({ initialCollectionId, organismsConfig }: Collecti /> c.id === selectedCollectionId) ?? query.data[0]} - organismsConfig={organismsConfig} + pageState={pageState} + setPageState={setPageState} /> ); @@ -96,10 +93,11 @@ const querySchema = z.object({ type CollectionVariantListProps = { collection: Collection; - organismsConfig: OrganismsConfig; + pageState: CovidVariantData; + setPageState: Dispatch>; }; -function CollectionVariantList({ collection, organismsConfig }: CollectionVariantListProps) { +function CollectionVariantList({ collection, pageState, setPageState }: CollectionVariantListProps) { const variants = collection.variants; return ( @@ -108,8 +106,9 @@ function CollectionVariantList({ collection, organismsConfig }: CollectionVarian ))} @@ -118,32 +117,37 @@ function CollectionVariantList({ collection, organismsConfig }: CollectionVarian type VariantLinkProps = { variant: CollectionVariant; - organismsConfig: OrganismsConfig; collectionId: number; + pageState: CovidVariantData; + setPageState: Dispatch>; }; -function VariantLink({ variant, organismsConfig, collectionId }: VariantLinkProps) { - const variantLink = useVariantLink(organismsConfig, collectionId, variant); +function VariantLink({ variant, collectionId, pageState, setPageState }: VariantLinkProps) { + const newPageState = useVariantLinkPageState(pageState, collectionId, variant); + + const applyFilters = () => { + if (newPageState === undefined) { + return; + } + setPageState(newPageState); + }; return ( - + ); } const logger = getClientLogger('CollectionList'); -function useVariantLink(organismsConfig: OrganismsConfig, collectionId: number, variant: CollectionVariant) { - const routing = useMemo(() => new Routing(organismsConfig), [organismsConfig]); - +function useVariantLinkPageState( + currentPageState: CovidVariantData, + collectionId: number, + variant: CollectionVariant, +): CovidVariantData | undefined { const { showErrorToast } = useErrorToast(logger); - const currentPageState = routing - .getOrganismView('covid.singleVariantView') - .pageStateHandler.parsePageStateFromUrl(new URL(window.location.href)); - let newPageState: CovidVariantData; - const queryParseResult = querySchema.safeParse(JSON.parse(variant.query)); if (!queryParseResult.success) { @@ -158,7 +162,7 @@ function useVariantLink(organismsConfig: OrganismsConfig, collectionId: number, const query = queryParseResult.data; if (query.variantQuery !== undefined) { - newPageState = { + return { ...currentPageState, collectionId: collectionId, variantFilter: { @@ -168,7 +172,7 @@ function useVariantLink(organismsConfig: OrganismsConfig, collectionId: number, }, }; } else { - newPageState = { + return { ...currentPageState, collectionId: collectionId, variantFilter: { @@ -184,5 +188,4 @@ function useVariantLink(organismsConfig: OrganismsConfig, collectionId: number, }, }; } - return routing.getOrganismView('covid.singleVariantView').pageStateHandler.toUrl(newPageState); } diff --git a/website/src/components/views/analyzeSingleVariant/CovidSingleVariantDataDisplay.tsx b/website/src/components/views/analyzeSingleVariant/CovidSingleVariantDataDisplay.tsx index b07241b50..8ee878318 100644 --- a/website/src/components/views/analyzeSingleVariant/CovidSingleVariantDataDisplay.tsx +++ b/website/src/components/views/analyzeSingleVariant/CovidSingleVariantDataDisplay.tsx @@ -1,17 +1,13 @@ import { views } from '@genspectrum/dashboard-components/util'; -import { type FC, useMemo } from 'react'; +import { type Dispatch, type FC, type SetStateAction } from 'react'; import { CollectionsList } from './CollectionsList.tsx'; import { SelectVariant } from './SelectVariant.tsx'; -import type { OrganismsConfig } from '../../../config.ts'; -import type { Organisms } from '../../../types/Organism.ts'; import { chooseGranularityBasedOnDateRange } from '../../../util/chooseGranularityBasedOnDateRange.ts'; import { hasOnlyUndefinedValues } from '../../../util/hasOnlyUndefinedValues.ts'; -import type { CovidVariantData } from '../../../views/covid.ts'; +import { CovidAnalyzeSingleVariantView, type CovidVariantData } from '../../../views/covid.ts'; import { getLocationSubdivision } from '../../../views/locationHelpers.ts'; import { locationFieldsToFilterIdentifier } from '../../../views/pageStateHandlers/locationFilterFromToUrl.ts'; -import { Routing } from '../../../views/routing.ts'; -import type { singleVariantViewKey } from '../../../views/viewKeys.ts'; import { ComponentsGrid } from '../../ComponentsGrid.tsx'; import { GsAggregate } from '../../genspectrum/GsAggregate.tsx'; import { GsMutations } from '../../genspectrum/GsMutations.tsx'; @@ -21,18 +17,16 @@ import { GsRelativeGrowthAdvantage } from '../../genspectrum/GsRelativeGrowthAdv import { GsStatistics } from '../../genspectrum/GsStatistics.tsx'; export type CovidSingleVariantDataDisplayProps = { - organismViewKey: `${typeof Organisms.covid}.${typeof singleVariantViewKey}`; - organismsConfig: OrganismsConfig; + view: CovidAnalyzeSingleVariantView; pageState: CovidVariantData; + setPageState: Dispatch>; }; export const CovidSingleVariantDataDisplay: FC = ({ - organismViewKey, - organismsConfig, + view, pageState, + setPageState, }) => { - const view = useMemo(() => new Routing(organismsConfig), [organismsConfig]).getOrganismView(organismViewKey); - const variantFilter = view.pageStateHandler.toLapisFilter(pageState); const datasetLapisFilter = view.pageStateHandler.toLapisFilterWithoutVariant(pageState); const timeGranularity = chooseGranularityBasedOnDateRange({ @@ -55,7 +49,8 @@ export const CovidSingleVariantDataDisplay: FC diff --git a/website/src/components/views/analyzeSingleVariant/CovidSingleVariantReactPage.tsx b/website/src/components/views/analyzeSingleVariant/CovidSingleVariantReactPage.tsx new file mode 100644 index 000000000..438ea46b9 --- /dev/null +++ b/website/src/components/views/analyzeSingleVariant/CovidSingleVariantReactPage.tsx @@ -0,0 +1,76 @@ +import { type FC, useMemo } from 'react'; + +import { CollectionsList } from './CollectionsList.tsx'; +import { CovidSingleVariantDataDisplay } from './CovidSingleVariantDataDisplay.tsx'; +import type { OrganismsConfig } from '../../../config.ts'; +import { SingleVariantOrganismPageLayout } from '../../../layouts/OrganismPage/SingleVariantOrganismPageLayout.tsx'; +import { hasOnlyUndefinedValues } from '../../../util/hasOnlyUndefinedValues.ts'; +import { toDisplayName } from '../../../views/pageStateHandlers/PageStateHandler.ts'; +import { type OrganismViewKey, Routing } from '../../../views/routing.ts'; +import { SelectorHeadline } from '../../pageStateSelectors/SelectorHeadline.tsx'; +import { SingleVariantPageStateSelector } from '../../pageStateSelectors/SingleVariantPageStateSelector.tsx'; +import { sanitizeForFilename } from '../compareSideBySide/toDownloadLink.ts'; +import { usePageState } from '../usePageState.ts'; + +export type CovidSingleVariantReactPageProps = { + organismsConfig: OrganismsConfig; + isStaging: boolean; +}; + +export const CovidSingleVariantReactPage: FC = ({ organismsConfig, isStaging }) => { + const organismViewKey: OrganismViewKey = 'covid.singleVariantView'; + const view = useMemo(() => new Routing(organismsConfig).getOrganismView(organismViewKey), [organismsConfig]); + + const { pageState, setPageState } = usePageState(view.pageStateHandler); + + const variantFilter = view.pageStateHandler.toLapisFilter(pageState); + + const noVariantSelected = hasOnlyUndefinedValues(pageState.variantFilter); + + const displayName = toDisplayName(pageState.variantFilter); + const downloadLinks = noVariantSelected + ? [ + { + label: 'Download all accessions', + filter: variantFilter, + downloadFileBasename: `${view.organismConstants.organism}_accessions`, + }, + ] + : [ + { + label: `Download accessions of ${displayName}`, + filter: variantFilter, + downloadFileBasename: `${view.organismConstants.organism}_${sanitizeForFilename(displayName)}_accessions`, + }, + ]; + + return ( + + +
+
+ Collections + +
+ + } + dataDisplay={ + + } + /> + ); +}; diff --git a/website/src/components/views/analyzeSingleVariant/GenericAnalyseSingleVariantDataDisplay.tsx b/website/src/components/views/analyzeSingleVariant/GenericAnalyseSingleVariantDataDisplay.tsx index 8dbdf3865..0bd266e27 100644 --- a/website/src/components/views/analyzeSingleVariant/GenericAnalyseSingleVariantDataDisplay.tsx +++ b/website/src/components/views/analyzeSingleVariant/GenericAnalyseSingleVariantDataDisplay.tsx @@ -1,16 +1,15 @@ import { views } from '@genspectrum/dashboard-components/util'; -import { type FC, useMemo } from 'react'; +import { type Dispatch, type FC, type SetStateAction } from 'react'; import { QuickstartLinks } from './QuickstartLinks.tsx'; import { SelectVariant } from './SelectVariant.tsx'; -import type { OrganismsConfig } from '../../../config.ts'; import { Organisms } from '../../../types/Organism.ts'; import { chooseGranularityBasedOnDateRange } from '../../../util/chooseGranularityBasedOnDateRange.ts'; import { hasOnlyUndefinedValues } from '../../../util/hasOnlyUndefinedValues.ts'; import type { DatasetAndVariantData } from '../../../views/View.ts'; import { getLocationSubdivision } from '../../../views/locationHelpers.ts'; import { locationFieldsToFilterIdentifier } from '../../../views/pageStateHandlers/locationFilterFromToUrl.ts'; -import { type OrganismViewKey, Routing, type SingleVariantOrganism } from '../../../views/routing.ts'; +import { type SingleVariantOrganism, type ViewsMap } from '../../../views/routing.ts'; import type { singleVariantViewKey } from '../../../views/viewKeys.ts'; import { ComponentsGrid } from '../../ComponentsGrid.tsx'; import { GsAggregate } from '../../genspectrum/GsAggregate.tsx'; @@ -20,19 +19,16 @@ import { GsPrevalenceOverTime } from '../../genspectrum/GsPrevalenceOverTime.tsx import { GsStatistics } from '../../genspectrum/GsStatistics.tsx'; export type GenericAnalyseSingleVariantDataDisplayProps = { - organismViewKey: OrganismViewKey & - `${Exclude}.${typeof singleVariantViewKey}`; - organismsConfig: OrganismsConfig; + view: ViewsMap[Exclude][typeof singleVariantViewKey]; pageState: DatasetAndVariantData; + setPageState: Dispatch>; }; export const GenericAnalyseSingleVariantDataDisplay: FC = ({ - organismViewKey, - organismsConfig, + view, pageState, + setPageState, }) => { - const view = useMemo(() => new Routing(organismsConfig), [organismsConfig]).getOrganismView(organismViewKey); - const variantLapisFilter = view.pageStateHandler.toLapisFilter(pageState); const datasetLapisFilter = view.pageStateHandler.toLapisFilterWithoutVariant(pageState); const timeGranularity = chooseGranularityBasedOnDateRange({ @@ -52,7 +48,7 @@ export const GenericAnalyseSingleVariantDataDisplay: FC {noVariantSelected && ( - + )} diff --git a/website/src/components/views/analyzeSingleVariant/GenericAnalyseSingleVariantReactPage.tsx b/website/src/components/views/analyzeSingleVariant/GenericAnalyseSingleVariantReactPage.tsx new file mode 100644 index 000000000..477e9a5a8 --- /dev/null +++ b/website/src/components/views/analyzeSingleVariant/GenericAnalyseSingleVariantReactPage.tsx @@ -0,0 +1,73 @@ +import { type FC, useMemo } from 'react'; + +import { GenericAnalyseSingleVariantDataDisplay } from './GenericAnalyseSingleVariantDataDisplay'; +import { type OrganismsConfig } from '../../../config'; +import { SingleVariantOrganismPageLayout } from '../../../layouts/OrganismPage/SingleVariantOrganismPageLayout.tsx'; +import { Organisms } from '../../../types/Organism'; +import { hasOnlyUndefinedValues } from '../../../util/hasOnlyUndefinedValues'; +import { toDisplayName } from '../../../views/pageStateHandlers/PageStateHandler'; +import { type OrganismViewKey, type OrganismWithViewKey, Routing } from '../../../views/routing'; +import { singleVariantViewKey } from '../../../views/viewKeys'; +import { SingleVariantPageStateSelector } from '../../pageStateSelectors/SingleVariantPageStateSelector'; +import { sanitizeForFilename } from '../compareSideBySide/toDownloadLink'; +import { usePageState } from '../usePageState.ts'; + +export type GenericAnalyseSingleVariantReactPageProps = { + organism: Exclude, typeof Organisms.covid>; + organismsConfig: OrganismsConfig; + isStaging: boolean; +}; + +export const GenericAnalyseSingleVariantReactPage: FC = ({ + organism, + organismsConfig, + isStaging, +}) => { + const organismViewKey = `${organism}.${singleVariantViewKey}` satisfies OrganismViewKey; + const view = useMemo( + () => new Routing(organismsConfig).getOrganismView(organismViewKey), + [organismsConfig, organismViewKey], + ); + + const { pageState, setPageState } = usePageState(view.pageStateHandler); + + const variantLapisFilter = view.pageStateHandler.toLapisFilter(pageState); + + const noVariantSelected = hasOnlyUndefinedValues(pageState.variantFilter); + + const displayName = toDisplayName(pageState.variantFilter); + const downloadLinks = noVariantSelected + ? [ + { + label: 'Download all accessions', + filter: variantLapisFilter, + downloadFileBasename: `${view.organismConstants.organism}_accessions`, + }, + ] + : [ + { + label: `Download accessions of "${displayName}"`, + filter: variantLapisFilter, + downloadFileBasename: `${organism}_${sanitizeForFilename(displayName)}_accessions`, + }, + ]; + + return ( + + } + dataDisplay={ + + } + /> + ); +}; diff --git a/website/src/components/views/analyzeSingleVariant/GenericAnalyzeSingleVariantPage.astro b/website/src/components/views/analyzeSingleVariant/GenericAnalyzeSingleVariantPage.astro index 256b7079a..83fe543bf 100644 --- a/website/src/components/views/analyzeSingleVariant/GenericAnalyzeSingleVariantPage.astro +++ b/website/src/components/views/analyzeSingleVariant/GenericAnalyzeSingleVariantPage.astro @@ -1,70 +1,26 @@ --- -import { GenericAnalyseSingleVariantDataDisplay } from './GenericAnalyseSingleVariantDataDisplay'; +import { GenericAnalyseSingleVariantReactPage } from './GenericAnalyseSingleVariantReactPage'; import { isStaging, getDashboardsConfig } from '../../../config'; -import SingleVariantOrganismPageLayout from '../../../layouts/OrganismPage/SingleVariantOrganismPageLayout.astro'; +import BaseLayout from '../../../layouts/base/BaseLayout.astro'; import { Organisms } from '../../../types/Organism'; -import { hasOnlyUndefinedValues } from '../../../util/hasOnlyUndefinedValues'; -import { toDisplayName } from '../../../views/pageStateHandlers/PageStateHandler'; -import { type OrganismViewKey, type OrganismWithViewKey } from '../../../views/routing'; +import { type OrganismWithViewKey } from '../../../views/routing'; import { ServerSide } from '../../../views/serverSideRouting'; import { singleVariantViewKey } from '../../../views/viewKeys'; -import { AnalyzeSingleVariantSelectorFallback } from '../../pageStateSelectors/FallbackElement'; -import { SingleVariantPageStateSelector } from '../../pageStateSelectors/SingleVariantPageStateSelector'; -import { sanitizeForFilename } from '../compareSideBySide/toDownloadLink'; -type OrganismViewCompareVariant = OrganismWithViewKey; interface Props { - organism: Exclude; + organism: Exclude, typeof Organisms.covid>; } const { organism } = Astro.props; -const organismViewKey = `${organism}.${singleVariantViewKey}` satisfies OrganismViewKey; -const view = ServerSide.routing.getOrganismView(organismViewKey); -const pageState = view.pageStateHandler.parsePageStateFromUrl(Astro.url); - -const variantLapisFilter = view.pageStateHandler.toLapisFilter(pageState); - -const noVariantSelected = hasOnlyUndefinedValues(pageState.variantFilter); - -const organismConfig = getDashboardsConfig().dashboards.organisms; - -const displayName = toDisplayName(pageState.variantFilter); -const downloadLinks = noVariantSelected - ? [ - { - label: 'Download all accessions', - filter: variantLapisFilter, - downloadFileBasename: `${view.organismConstants.organism}_accessions`, - }, - ] - : [ - { - label: `Download accessions of "${displayName}"`, - filter: variantLapisFilter, - downloadFileBasename: `${organism}_${sanitizeForFilename(displayName)}_accessions`, - }, - ]; +const view = ServerSide.routing.getOrganismView(`${organism}.${singleVariantViewKey}`); --- - - + - - - -
- -
-
+ /> + diff --git a/website/src/components/views/analyzeSingleVariant/QuickstartLinks.tsx b/website/src/components/views/analyzeSingleVariant/QuickstartLinks.tsx index 9745d57d8..86eac39e9 100644 --- a/website/src/components/views/analyzeSingleVariant/QuickstartLinks.tsx +++ b/website/src/components/views/analyzeSingleVariant/QuickstartLinks.tsx @@ -1,8 +1,8 @@ -import type { FC } from 'react'; +import type { Dispatch, FC, SetStateAction } from 'react'; import { collectionVariantClassName } from './CollectionsList'; import { Organisms } from '../../../types/Organism'; -import type { DatasetFilter } from '../../../views/View.ts'; +import type { DatasetAndVariantData, DatasetFilter } from '../../../views/View.ts'; import { toDisplayName } from '../../../views/pageStateHandlers/PageStateHandler'; import type { SingleVariantOrganism, ViewsMap } from '../../../views/routing'; import { singleVariantViewKey } from '../../../views/viewKeys'; @@ -10,9 +10,10 @@ import { singleVariantViewKey } from '../../../views/viewKeys'; export type QuickstartLinksProps = { view: ViewsMap[Exclude][typeof singleVariantViewKey]; datasetFilter: DatasetFilter; + setPageState: Dispatch>; }; -export const QuickstartLinks: FC = ({ view, datasetFilter }) => { +export const QuickstartLinks: FC = ({ view, datasetFilter, setPageState }) => { const variants = view.organismConstants.predefinedVariants; return ( @@ -21,13 +22,17 @@ export const QuickstartLinks: FC = ({ view, datasetFilter
{variants.map((variant) => { - const href = view.pageStateHandler.toUrl({ - datasetFilter: datasetFilter, - variantFilter: variant, - }); + const applyFilters = () => { + setPageState({ + datasetFilter: datasetFilter, + variantFilter: variant, + }); + }; + + const displayName = toDisplayName(variant); return ( - - {toDisplayName(variant)} + + {displayName} ); })} diff --git a/website/src/components/views/compareSideBySide/GenericCompareSideBySideDataDisplay.tsx b/website/src/components/views/compareSideBySide/GenericCompareSideBySideDataDisplay.tsx new file mode 100644 index 000000000..76e5da97c --- /dev/null +++ b/website/src/components/views/compareSideBySide/GenericCompareSideBySideDataDisplay.tsx @@ -0,0 +1,87 @@ +import { type FC } from 'react'; + +import { Organisms } from '../../../types/Organism.ts'; +import { chooseGranularityBasedOnDateRange } from '../../../util/chooseGranularityBasedOnDateRange.ts'; +import { BaseView } from '../../../views/BaseView.ts'; +import { ComponentHeight, type OrganismConstants } from '../../../views/OrganismConstants.ts'; +import type { CompareSideBySideData, DatasetAndVariantData } from '../../../views/View.ts'; +import { CompareSideBySideStateHandler } from '../../../views/pageStateHandlers/CompareSideBySidePageStateHandler.ts'; +import { toLapisFilterWithoutVariant } from '../../../views/pageStateHandlers/toLapisFilterWithoutVariant.ts'; +import { GsAggregate } from '../../genspectrum/GsAggregate.tsx'; +import { GsMutations } from '../../genspectrum/GsMutations.tsx'; +import { GsPrevalenceOverTime } from '../../genspectrum/GsPrevalenceOverTime.tsx'; +import { GsRelativeGrowthAdvantage } from '../../genspectrum/GsRelativeGrowthAdvantage.tsx'; + +export type GenericCompareSideBySideDataDisplayProps = { + view: BaseView; + datasetAndVariantData: DatasetAndVariantData; + hideMutationComponents?: boolean; +}; + +export const GenericCompareSideBySideDataDisplay: FC = ({ + view, + datasetAndVariantData, + hideMutationComponents, +}) => { + const { datasetFilter, variantFilter } = datasetAndVariantData; + + const datasetLapisFilter = toLapisFilterWithoutVariant(datasetFilter, view.organismConstants.additionalFilters); + const timeGranularity = chooseGranularityBasedOnDateRange({ + earliestDate: new Date(view.organismConstants.earliestDate), + dateRange: datasetFilter.dateFilters[view.organismConstants.mainDateField], + }); + const numeratorFilter = view.pageStateHandler.variantFilterToLapisFilter(datasetFilter, variantFilter); + + return ( + <> + + {view.organismConstants.organism === Organisms.covid && ( + + )} + {hideMutationComponents !== true && ( + <> + + + + )} + + {view.organismConstants.aggregatedVisualizations.compareSideBySide.map(({ label, fields, views }) => ( + + ))} + + ); +}; diff --git a/website/src/components/views/compareSideBySide/GenericCompareSideBySidePage.astro b/website/src/components/views/compareSideBySide/GenericCompareSideBySidePage.astro index 81058b693..e6216e72e 100644 --- a/website/src/components/views/compareSideBySide/GenericCompareSideBySidePage.astro +++ b/website/src/components/views/compareSideBySide/GenericCompareSideBySidePage.astro @@ -1,132 +1,28 @@ --- -import { toDownloadLink } from './toDownloadLink'; -import { isStaging, getDashboardsConfig, getLapisUrl } from '../../../config'; -import OrganismViewPageLayout from '../../../layouts/OrganismPage/OrganismViewPageLayout.astro'; -import { chooseGranularityBasedOnDateRange } from '../../../util/chooseGranularityBasedOnDateRange'; -import { ComponentHeight } from '../../../views/OrganismConstants'; -import { toLapisFilterWithoutVariant } from '../../../views/pageStateHandlers/toLapisFilterWithoutVariant'; -import { type OrganismViewKey, type OrganismWithViewKey } from '../../../views/routing'; +import { GenericCompareSideBySideReactPage } from './GenericCompareSideBySideReactPage'; +import { isStaging, getDashboardsConfig } from '../../../config'; +import BaseLayout from '../../../layouts/base/BaseLayout.astro'; +import { type OrganismWithViewKey } from '../../../views/routing'; import { ServerSide } from '../../../views/serverSideRouting'; import { compareSideBySideViewKey } from '../../../views/viewKeys'; -import GsAggregate from '../../genspectrum/GsAggregate.astro'; -import GsMutations from '../../genspectrum/GsMutations.astro'; -import GsPrevalenceOverTime from '../../genspectrum/GsPrevalenceOverTime.astro'; -import { CompareSideBySidePageStateSelector } from '../../pageStateSelectors/CompareSideBySidePageStateSelector'; -import { CompareSideBySideSelectorFallback } from '../../pageStateSelectors/FallbackElement'; -type OrganismViewCompareVariant = OrganismWithViewKey; interface Props { - organism: OrganismViewCompareVariant; + organism: OrganismWithViewKey; hideMutationComponents?: boolean; } const { organism, hideMutationComponents } = Astro.props; -const organismViewKey = `${organism}.${compareSideBySideViewKey}` satisfies OrganismViewKey; -const view = ServerSide.routing.getOrganismView(organismViewKey); -const pageState = view.pageStateHandler.parsePageStateFromUrl(Astro.url); - -const downloadLinks = [...pageState.filters.entries()].map(toDownloadLink(view.pageStateHandler, organism)); +const view = ServerSide.routing.getOrganismView(`${organism}.${compareSideBySideViewKey}`); +const organismsConfig = getDashboardsConfig().dashboards.organisms; --- - - -
- { - Array.from(pageState.filters).map(([id, { variantFilter, datasetFilter }]) => { - const datasetLapisFilter = toLapisFilterWithoutVariant( - datasetFilter, - view.organismConstants.additionalFilters, - ); - const timeGranularity = chooseGranularityBasedOnDateRange({ - earliestDate: new Date(view.organismConstants.earliestDate), - dateRange: datasetFilter.dateFilters[view.organismConstants.mainDateField], - }); - const numeratorFilter = view.pageStateHandler.variantFilterToLapisFilter( - datasetFilter, - variantFilter, - ); - - return ( -
-
- {pageState.filters.size > 1 && ( - - Remove column - - )} - - - -
- - - {!hideMutationComponents && ( - <> - - - - )} - - {view.organismConstants.aggregatedVisualizations.compareSideBySide.map( - ({ label, fields, views }) => ( - - ), - )} -
- ); - }) - } - - Add column - -
-
-
+ + + diff --git a/website/src/components/views/compareSideBySide/GenericCompareSideBySideReactPage.tsx b/website/src/components/views/compareSideBySide/GenericCompareSideBySideReactPage.tsx new file mode 100644 index 000000000..e4f3b5a2c --- /dev/null +++ b/website/src/components/views/compareSideBySide/GenericCompareSideBySideReactPage.tsx @@ -0,0 +1,87 @@ +import { type FC, useMemo } from 'react'; + +import { GenericCompareSideBySideDataDisplay } from './GenericCompareSideBySideDataDisplay.tsx'; +import { toDownloadLink } from './toDownloadLink'; +import { type OrganismsConfig } from '../../../config'; +import { OrganismViewPageLayout } from '../../../layouts/OrganismPage/OrganismViewPageLayout.tsx'; +import { type OrganismViewKey, type OrganismWithViewKey, Routing } from '../../../views/routing'; +import { compareSideBySideViewKey } from '../../../views/viewKeys'; +import { CompareSideBySidePageStateSelector } from '../../pageStateSelectors/CompareSideBySidePageStateSelector'; +import { usePageState } from '../usePageState.ts'; + +export type GenericCompareSideBySideReactPageProps = { + organism: OrganismWithViewKey; + hideMutationComponents?: boolean; + organismsConfig: OrganismsConfig; + isStaging: boolean; +}; + +export const GenericCompareSideBySideReactPage: FC = ({ + organism, + hideMutationComponents, + organismsConfig, + isStaging, +}) => { + const organismViewKey = `${organism}.${compareSideBySideViewKey}` satisfies OrganismViewKey; + + const view = useMemo( + () => new Routing(organismsConfig).getOrganismView(organismViewKey), + [organismsConfig, organismViewKey], + ); + + const { pageState, setPageState } = usePageState(view.pageStateHandler); + + const downloadLinks = [...pageState.filters.entries()].map(toDownloadLink(view.pageStateHandler, organism)); + + return ( + +
+ {Array.from(pageState.filters).map(([id, datasetAndVariantData]) => { + return ( +
+
+ {pageState.filters.size > 1 && ( + + Remove column + + )} + +
+ + +
+ ); + })} + + Add column + +
+
+ ); +}; diff --git a/website/src/components/views/compareToBaseline/GenericCompareToBaselineDataDisplay.tsx b/website/src/components/views/compareToBaseline/GenericCompareToBaselineDataDisplay.tsx new file mode 100644 index 000000000..f2f7163ee --- /dev/null +++ b/website/src/components/views/compareToBaseline/GenericCompareToBaselineDataDisplay.tsx @@ -0,0 +1,41 @@ +import { type FC } from 'react'; + +import { SelectBaseline } from './SelectBaseline.tsx'; +import { chooseGranularityBasedOnDateRange } from '../../../util/chooseGranularityBasedOnDateRange.ts'; +import { GenericCompareToBaselineView } from '../../../views/BaseView.ts'; +import { ComponentHeight, type OrganismConstants } from '../../../views/OrganismConstants.ts'; +import type { CompareToBaselineData } from '../../../views/View.ts'; +import { ComponentsGrid } from '../../ComponentsGrid.tsx'; +import { GsPrevalenceOverTime } from '../../genspectrum/GsPrevalenceOverTime.tsx'; + +export type GenericCompareToBaselineDisplayProps = { + view: GenericCompareToBaselineView; + pageState: CompareToBaselineData; +}; + +export const GenericCompareToBaselineDataDisplay: FC = ({ view, pageState }) => { + const baselineLapisFilter = view.pageStateHandler.baselineFilterToLapisFilter(pageState); + const timeGranularity = chooseGranularityBasedOnDateRange({ + earliestDate: new Date(view.organismConstants.earliestDate), + dateRange: pageState.datasetFilter.dateFilters[view.organismConstants.mainDateField], + }); + + const numeratorLapisFilters = view.pageStateHandler.variantFiltersToNamedLapisFilters(pageState); + const noVariantSelected = pageState.variants.size < 1; + + return noVariantSelected ? ( + + ) : ( + + + + ); +}; diff --git a/website/src/components/views/compareToBaseline/GenericCompareToBaselinePage.astro b/website/src/components/views/compareToBaseline/GenericCompareToBaselinePage.astro index 9229c10c3..650f35d58 100644 --- a/website/src/components/views/compareToBaseline/GenericCompareToBaselinePage.astro +++ b/website/src/components/views/compareToBaseline/GenericCompareToBaselinePage.astro @@ -1,73 +1,25 @@ --- -import SelectBaseline from './SelectBaseline.astro'; +import { GenericCompareToBaselineReactPage } from './GenericCompareToBaselineReactPage'; import { isStaging, getDashboardsConfig } from '../../../config'; -import SingleVariantOrganismPageLayout from '../../../layouts/OrganismPage/SingleVariantOrganismPageLayout.astro'; -import { chooseGranularityBasedOnDateRange } from '../../../util/chooseGranularityBasedOnDateRange'; -import { ComponentHeight } from '../../../views/OrganismConstants'; -import { type OrganismViewKey, type OrganismWithViewKey } from '../../../views/routing'; +import BaseLayout from '../../../layouts/base/BaseLayout.astro'; +import { type OrganismWithViewKey } from '../../../views/routing'; import { ServerSide } from '../../../views/serverSideRouting'; import { compareToBaselineViewKey } from '../../../views/viewKeys'; -import ComponentsGrid from '../../ComponentsGrid.astro'; -import GsPrevalenceOverTime from '../../genspectrum/GsPrevalenceOverTime.astro'; -import { CompareVariantsToBaselineStateSelector } from '../../pageStateSelectors/CompareVariantsToBaselineStateSelector'; -import { CompareToBaselineSelectorFallback } from '../../pageStateSelectors/FallbackElement'; -import { sanitizeForFilename } from '../compareSideBySide/toDownloadLink'; -type OrganismViewCompareVariant = OrganismWithViewKey; interface Props { - organism: OrganismViewCompareVariant; + organism: OrganismWithViewKey; } const { organism } = Astro.props; -const organismViewKey = `${organism}.${compareToBaselineViewKey}` satisfies OrganismViewKey; -const view = ServerSide.routing.getOrganismView(organismViewKey); -const pageState = view.pageStateHandler.parsePageStateFromUrl(Astro.url); - -const baselineLapisFilter = view.pageStateHandler.baselineFilterToLapisFilter(pageState); -const timeGranularity = chooseGranularityBasedOnDateRange({ - earliestDate: new Date(view.organismConstants.earliestDate), - dateRange: pageState.datasetFilter.dateFilters[view.organismConstants.mainDateField], -}); - -const numeratorLapisFilters = view.pageStateHandler.variantFiltersToNamedLapisFilters(pageState); -const noVariantSelected = pageState.variants.size < 1; - -const downloadLinks = noVariantSelected - ? [] - : numeratorLapisFilters.map(({ lapisFilter, displayName }) => ({ - label: `Download accessions of "${displayName}"`, - filter: lapisFilter, - downloadFileBasename: `${organism}_${sanitizeForFilename(displayName)}_accessions`, - })); +const view = ServerSide.routing.getOrganismView(`${organism}.${compareToBaselineViewKey}`); --- - - + - - - { - noVariantSelected ? ( - - ) : ( - - - - ) - } - + /> + diff --git a/website/src/components/views/compareToBaseline/GenericCompareToBaselineReactPage.tsx b/website/src/components/views/compareToBaseline/GenericCompareToBaselineReactPage.tsx new file mode 100644 index 000000000..b6b71b463 --- /dev/null +++ b/website/src/components/views/compareToBaseline/GenericCompareToBaselineReactPage.tsx @@ -0,0 +1,58 @@ +import { type FC, useMemo } from 'react'; + +import { GenericCompareToBaselineDataDisplay } from './GenericCompareToBaselineDataDisplay'; +import { type OrganismsConfig } from '../../../config'; +import { SingleVariantOrganismPageLayout } from '../../../layouts/OrganismPage/SingleVariantOrganismPageLayout.tsx'; +import { type OrganismViewKey, type OrganismWithViewKey, Routing } from '../../../views/routing'; +import { compareToBaselineViewKey } from '../../../views/viewKeys'; +import { CompareVariantsToBaselineStateSelector } from '../../pageStateSelectors/CompareVariantsToBaselineStateSelector'; +import { sanitizeForFilename } from '../compareSideBySide/toDownloadLink'; +import { usePageState } from '../usePageState.ts'; + +export type GenericCompareToBaselineReactPageProps = { + organism: OrganismWithViewKey; + organismsConfig: OrganismsConfig; + isStaging: boolean; +}; + +export const GenericCompareToBaselineReactPage: FC = ({ + organism, + organismsConfig, + isStaging, +}) => { + const organismViewKey = `${organism}.${compareToBaselineViewKey}` satisfies OrganismViewKey; + const view = useMemo( + () => new Routing(organismsConfig).getOrganismView(organismViewKey), + [organismsConfig, organismViewKey], + ); + + const { pageState, setPageState } = usePageState(view.pageStateHandler); + + const numeratorLapisFilters = view.pageStateHandler.variantFiltersToNamedLapisFilters(pageState); + const noVariantSelected = pageState.variants.size < 1; + + const downloadLinks = noVariantSelected + ? [] + : numeratorLapisFilters.map(({ lapisFilter, displayName }) => ({ + label: `Download accessions of "${displayName}"`, + filter: lapisFilter, + downloadFileBasename: `${organism}_${sanitizeForFilename(displayName)}_accessions`, + })); + + return ( + + } + dataDisplay={} + /> + ); +}; diff --git a/website/src/components/views/compareToBaseline/SelectBaseline.astro b/website/src/components/views/compareToBaseline/SelectBaseline.astro deleted file mode 100644 index c01f303b1..000000000 --- a/website/src/components/views/compareToBaseline/SelectBaseline.astro +++ /dev/null @@ -1,15 +0,0 @@ ---- -import { PageHeadline } from '../../../styles/containers/PageHeadline'; ---- - -
- Analyze a variant compared to a baseline -
-

To proceed, please select a variant filter.

-

- If no baseline is selected all sequences will be used as a baseline for comparing against sequences with a - specified variant. Note that the dataset filter is applied to the entire dataset including both the baseline - and variant subsets. Adjust the dataset filter as needed to refine your selection. -

-
-
diff --git a/website/src/components/views/compareToBaseline/SelectBaseline.tsx b/website/src/components/views/compareToBaseline/SelectBaseline.tsx new file mode 100644 index 000000000..c2132159c --- /dev/null +++ b/website/src/components/views/compareToBaseline/SelectBaseline.tsx @@ -0,0 +1,19 @@ +import type { FC } from 'react'; + +import { PageHeadline } from '../../../styles/containers/PageHeadline.tsx'; + +export const SelectBaseline: FC = () => { + return ( +
+ Analyze a variant compared to a baseline +
+

To proceed, please select a variant filter.

+

+ If no baseline is selected all sequences will be used as a baseline for comparing against sequences + with a specified variant. Note that the dataset filter is applied to the entire dataset including + both the baseline and variant subsets. Adjust the dataset filter as needed to refine your selection. +

+
+
+ ); +}; diff --git a/website/src/components/views/compareVariants/GenericCompareVariantsDataDisplay.tsx b/website/src/components/views/compareVariants/GenericCompareVariantsDataDisplay.tsx new file mode 100644 index 000000000..6610477b3 --- /dev/null +++ b/website/src/components/views/compareVariants/GenericCompareVariantsDataDisplay.tsx @@ -0,0 +1,61 @@ +import { type FC } from 'react'; + +import { SelectVariants } from './SelectVariants.tsx'; +import { chooseGranularityBasedOnDateRange } from '../../../util/chooseGranularityBasedOnDateRange.ts'; +import { GenericCompareVariantsView } from '../../../views/BaseView.ts'; +import type { OrganismConstants } from '../../../views/OrganismConstants.ts'; +import type { CompareVariantsData } from '../../../views/View.ts'; +import { ComponentsGrid } from '../../ComponentsGrid.tsx'; +import { GsMutationComparison } from '../../genspectrum/GsMutationComparison.tsx'; +import { GsPrevalenceOverTime } from '../../genspectrum/GsPrevalenceOverTime.tsx'; + +export type GenericCompareVariantsDataDisplayProps = { + view: GenericCompareVariantsView; + pageState: CompareVariantsData; +}; + +const componentHeight = '540px'; // prevalence over time table with 10 rows + +export const GenericCompareVariantsDataDisplay: FC = ({ view, pageState }) => { + const datasetLapisFilter = view.pageStateHandler.datasetFilterToLapisFilter(pageState.datasetFilter); + const timeGranularity = chooseGranularityBasedOnDateRange({ + earliestDate: new Date(view.organismConstants.earliestDate), + dateRange: pageState.datasetFilter.dateFilters[view.organismConstants.mainDateField], + }); + + const numeratorLapisFilters = view.pageStateHandler.variantFiltersToNamedLapisFilters(pageState); + + const notEnoughVariantsSelected = pageState.variants.size < 2; + + return ( +
+ {notEnoughVariantsSelected ? ( + + ) : ( + + + + + + )} +
+ ); +}; diff --git a/website/src/components/views/compareVariants/GenericCompareVariantsPage.astro b/website/src/components/views/compareVariants/GenericCompareVariantsPage.astro index 8d077ac5d..4b5a062e1 100644 --- a/website/src/components/views/compareVariants/GenericCompareVariantsPage.astro +++ b/website/src/components/views/compareVariants/GenericCompareVariantsPage.astro @@ -1,88 +1,25 @@ --- -import SelectVariants from './SelectVariants.astro'; +import { GenericCompareVariantsReactPage } from './GenericCompareVariantsReactPage'; import { isStaging, getDashboardsConfig } from '../../../config'; -import SingleVariantOrganismPageLayout from '../../../layouts/OrganismPage/SingleVariantOrganismPageLayout.astro'; -import { chooseGranularityBasedOnDateRange } from '../../../util/chooseGranularityBasedOnDateRange'; -import { type OrganismViewKey, type OrganismWithViewKey } from '../../../views/routing'; +import BaseLayout from '../../../layouts/base/BaseLayout.astro'; +import { type OrganismWithViewKey } from '../../../views/routing'; import { ServerSide } from '../../../views/serverSideRouting'; import { compareVariantsViewKey } from '../../../views/viewKeys'; -import ComponentsGrid from '../../ComponentsGrid.astro'; -import GsMutationComparison from '../../genspectrum/GsMutationComparison.astro'; -import GsPrevalenceOverTime from '../../genspectrum/GsPrevalenceOverTime.astro'; -import { CompareVariantsPageStateSelector } from '../../pageStateSelectors/CompareVariantsPageStateSelector'; -import { CompareVariantsSelectorFallback } from '../../pageStateSelectors/FallbackElement'; -import { sanitizeForFilename } from '../compareSideBySide/toDownloadLink'; -type OrganismViewCompareVariant = OrganismWithViewKey; interface Props { - organism: OrganismViewCompareVariant; + organism: OrganismWithViewKey; } const { organism } = Astro.props; -const organismViewKey = `${organism}.${compareVariantsViewKey}` satisfies OrganismViewKey; -const view = ServerSide.routing.getOrganismView(organismViewKey); -const pageState = view.pageStateHandler.parsePageStateFromUrl(Astro.url); - -const datasetLapisFilter = view.pageStateHandler.datasetFilterToLapisFilter(pageState.datasetFilter); -const timeGranularity = chooseGranularityBasedOnDateRange({ - earliestDate: new Date(view.organismConstants.earliestDate), - dateRange: pageState.datasetFilter.dateFilters[view.organismConstants.mainDateField], -}); - -const numeratorLapisFilters = view.pageStateHandler.variantFiltersToNamedLapisFilters(pageState); - -const notEnoughVariantsSelected = pageState.variants.size < 2; - -const downloadLinks = notEnoughVariantsSelected - ? [] - : numeratorLapisFilters.map(({ lapisFilter, displayName }) => ({ - label: `Download accessions of "${displayName}"`, - filter: lapisFilter, - downloadFileBasename: `${organism}_${sanitizeForFilename(displayName)}_accessions`, - })); - -const componentHeight = '540px'; // prevalence over time table with 10 rows +const view = ServerSide.routing.getOrganismView(`${organism}.${compareVariantsViewKey}`); --- - - + - - - { - notEnoughVariantsSelected ? ( - - ) : ( - - - - - - ) - } - + /> + diff --git a/website/src/components/views/compareVariants/GenericCompareVariantsReactPage.tsx b/website/src/components/views/compareVariants/GenericCompareVariantsReactPage.tsx new file mode 100644 index 000000000..cffaa97fe --- /dev/null +++ b/website/src/components/views/compareVariants/GenericCompareVariantsReactPage.tsx @@ -0,0 +1,58 @@ +import { type FC, useMemo } from 'react'; + +import { GenericCompareVariantsDataDisplay } from './GenericCompareVariantsDataDisplay'; +import { type OrganismsConfig } from '../../../config'; +import { SingleVariantOrganismPageLayout } from '../../../layouts/OrganismPage/SingleVariantOrganismPageLayout.tsx'; +import { type OrganismViewKey, type OrganismWithViewKey, Routing } from '../../../views/routing'; +import { compareVariantsViewKey } from '../../../views/viewKeys'; +import { CompareVariantsPageStateSelector } from '../../pageStateSelectors/CompareVariantsPageStateSelector'; +import { sanitizeForFilename } from '../compareSideBySide/toDownloadLink'; +import { usePageState } from '../usePageState.ts'; + +export type GenericCompareVariantsReactPageProps = { + organism: OrganismWithViewKey; + organismsConfig: OrganismsConfig; + isStaging: boolean; +}; + +export const GenericCompareVariantsReactPage: FC = ({ + organism, + organismsConfig, + isStaging, +}) => { + const organismViewKey = `${organism}.${compareVariantsViewKey}` satisfies OrganismViewKey; + const view = useMemo( + () => new Routing(organismsConfig).getOrganismView(organismViewKey), + [organismsConfig, organismViewKey], + ); + + const { pageState, setPageState } = usePageState(view.pageStateHandler); + + const numeratorLapisFilters = view.pageStateHandler.variantFiltersToNamedLapisFilters(pageState); + const notEnoughVariantsSelected = pageState.variants.size < 2; + + const downloadLinks = notEnoughVariantsSelected + ? [] + : numeratorLapisFilters.map(({ lapisFilter, displayName }) => ({ + label: `Download accessions of "${displayName}"`, + filter: lapisFilter, + downloadFileBasename: `${organism}_${sanitizeForFilename(displayName)}_accessions`, + })); + + return ( + + } + dataDisplay={} + /> + ); +}; diff --git a/website/src/components/views/compareVariants/SelectVariants.astro b/website/src/components/views/compareVariants/SelectVariants.astro deleted file mode 100644 index cc249d57a..000000000 --- a/website/src/components/views/compareVariants/SelectVariants.astro +++ /dev/null @@ -1,14 +0,0 @@ ---- -import { PageHeadline } from '../../../styles/containers/PageHeadline'; ---- - -
- Compare Variants -
-

To proceed, please select two or more variants in the variant filter.

-

- Note that the dataset filter is applied to all variants. Adjust the dataset filter as needed to refine your - selection. -

-
-
diff --git a/website/src/components/views/compareVariants/SelectVariants.tsx b/website/src/components/views/compareVariants/SelectVariants.tsx new file mode 100644 index 000000000..b0aabb861 --- /dev/null +++ b/website/src/components/views/compareVariants/SelectVariants.tsx @@ -0,0 +1,18 @@ +import type { FC } from 'react'; + +import { PageHeadline } from '../../../styles/containers/PageHeadline.tsx'; + +export const SelectVariants: FC = () => { + return ( +
+ Compare Variants +
+

To proceed, please select two or more variants in the variant filter.

+

+ Note that the dataset filter is applied to all variants. Adjust the dataset filter as needed to + refine your selection. +

+
+
+ ); +}; diff --git a/website/src/components/views/sequencingEfforts/GenericSequencingEffortsPage.astro b/website/src/components/views/sequencingEfforts/GenericSequencingEffortsPage.astro index 45418e0a1..b23520e33 100644 --- a/website/src/components/views/sequencingEfforts/GenericSequencingEffortsPage.astro +++ b/website/src/components/views/sequencingEfforts/GenericSequencingEffortsPage.astro @@ -1,56 +1,25 @@ --- -import { GenericSequencingEffortsDataDisplay } from './GenericSequencingEffortsDataDisplay'; +import { GenericSequencingEffortsReactPage } from './GenericSequencingEffortsReactPage'; import { isStaging, getDashboardsConfig } from '../../../config'; -import SingleVariantOrganismPageLayout from '../../../layouts/OrganismPage/SingleVariantOrganismPageLayout.astro'; -import { type OrganismViewKey, type OrganismWithViewKey } from '../../../views/routing'; +import BaseLayout from '../../../layouts/base/BaseLayout.astro'; +import { type OrganismWithViewKey } from '../../../views/routing'; import { ServerSide } from '../../../views/serverSideRouting'; import { sequencingEffortsViewKey } from '../../../views/viewKeys'; -import { SequencingEffortsSelectorFallback } from '../../pageStateSelectors/FallbackElement'; -import { SequencingEffortsPageStateSelector } from '../../pageStateSelectors/SequencingEffortsPageStateSelector'; -type OrganismViewCompareVariant = OrganismWithViewKey; interface Props { - organism: OrganismViewCompareVariant; + organism: OrganismWithViewKey; } const { organism } = Astro.props; -const organismViewKey: OrganismViewKey = `${organism}.${sequencingEffortsViewKey}` satisfies OrganismViewKey; -const view = ServerSide.routing.getOrganismView(organismViewKey); -const pageState = view.pageStateHandler.parsePageStateFromUrl(Astro.url); - -const lapisFilter = view.pageStateHandler.toLapisFilter(pageState); - -const organismConfig = getDashboardsConfig().dashboards.organisms; +const view = ServerSide.routing.getOrganismView(`${organism}.${sequencingEffortsViewKey}`); --- - - + - - - -
- -
-
+ /> + diff --git a/website/src/components/views/sequencingEfforts/GenericSequencingEffortsReactPage.tsx b/website/src/components/views/sequencingEfforts/GenericSequencingEffortsReactPage.tsx new file mode 100644 index 000000000..865a06470 --- /dev/null +++ b/website/src/components/views/sequencingEfforts/GenericSequencingEffortsReactPage.tsx @@ -0,0 +1,62 @@ +import { type FC, useMemo } from 'react'; + +import { GenericSequencingEffortsDataDisplay } from './GenericSequencingEffortsDataDisplay'; +import { type OrganismsConfig } from '../../../config'; +import { SingleVariantOrganismPageLayout } from '../../../layouts/OrganismPage/SingleVariantOrganismPageLayout.tsx'; +import { type OrganismViewKey, type OrganismWithViewKey, Routing } from '../../../views/routing'; +import { sequencingEffortsViewKey } from '../../../views/viewKeys'; +import { SequencingEffortsPageStateSelector } from '../../pageStateSelectors/SequencingEffortsPageStateSelector'; +import { usePageState } from '../usePageState.ts'; + +export type GenericSequencingEffortsReactPageProps = { + organism: OrganismWithViewKey; + organismsConfig: OrganismsConfig; + isStaging: boolean; +}; + +export const GenericSequencingEffortsReactPage: FC = ({ + organism, + organismsConfig, + isStaging, +}) => { + const organismViewKey = `${organism}.${sequencingEffortsViewKey}` satisfies OrganismViewKey; + const view = useMemo( + () => new Routing(organismsConfig).getOrganismView(organismViewKey), + [organismsConfig, organismViewKey], + ); + + const { pageState, setPageState } = usePageState(view.pageStateHandler); + + const lapisFilter = view.pageStateHandler.toLapisFilter(pageState); + + const downloadLinks = [ + { + label: 'Download accessions', + filter: lapisFilter, + downloadFileBasename: `${organism}_sequencing_efforts_accessions`, + }, + ]; + + return ( + + } + dataDisplay={ + + } + /> + ); +}; diff --git a/website/src/components/views/usePageState.ts b/website/src/components/views/usePageState.ts new file mode 100644 index 000000000..0dafacecc --- /dev/null +++ b/website/src/components/views/usePageState.ts @@ -0,0 +1,34 @@ +import { type Dispatch, type SetStateAction, useCallback, useMemo, useState } from 'react'; + +import type { PageStateHandler } from '../../views/pageStateHandlers/PageStateHandler.ts'; + +/** + * Given a `PageStateHandler`, this hook initially parses the page state from the current URL. + * It returns a `pageState` object and a `setPageState` function. + * When the function is called, + * the new page state is turned into a URL and set as the current URL (added to the page history). + * This way, the URL and current page state are kept in sync. + */ +export function usePageState>(pageStateHandler: StateHandler) { + type PageState = StateHandler extends PageStateHandler ? PS : never; + + const [pageState, setPageStateRaw] = useState( + () => pageStateHandler.parsePageStateFromUrl(new URL(window.location.href)) as PageState, + ); + + const setPageState: Dispatch> = useCallback( + (newPageStateOrUpdater) => { + setPageStateRaw((prevState) => { + const newPageState = + typeof newPageStateOrUpdater === 'function' + ? newPageStateOrUpdater(prevState) + : newPageStateOrUpdater; + window.history.pushState(undefined, '', pageStateHandler.toUrl(newPageState)); + return newPageState; + }); + }, + [pageStateHandler], + ); + + return useMemo(() => ({ pageState, setPageState }), [pageState, setPageState]); +} diff --git a/website/src/components/views/wasap/Wasap.astro b/website/src/components/views/wasap/Wasap.astro index 2a9c7196f..4535ed13f 100644 --- a/website/src/components/views/wasap/Wasap.astro +++ b/website/src/components/views/wasap/Wasap.astro @@ -1,36 +1,16 @@ --- import { WasapPage } from './WasapPage'; -import { defaultBreadcrumbs } from '../../../layouts/Breadcrumbs'; -import DataPageLayout from '../../../layouts/OrganismPage/DataPageLayout.astro'; -import { dataOrigins } from '../../../types/dataOrigins'; -import { - wastewaterBreadcrumb, - wastewaterOrganismConfigs, - type WastewaterOrganismName, -} from '../../../types/wastewaterConfig'; +import BaseLayout from '../../../layouts/base/BaseLayout.astro'; +import { wastewaterOrganismConfigs, type WastewaterOrganismName } from '../../../types/wastewaterConfig'; type Props = { wastewaterOrganism: WastewaterOrganismName; }; const { wastewaterOrganism } = Astro.props; -const { name, path: href, lapisBaseUrl } = wastewaterOrganismConfigs[wastewaterOrganism]; - -const url = Astro.url; +const { name } = wastewaterOrganismConfigs[wastewaterOrganism]; --- - - - + + + diff --git a/website/src/components/views/wasap/WasapPage.tsx b/website/src/components/views/wasap/WasapPage.tsx index 0f87beea0..296b85aed 100644 --- a/website/src/components/views/wasap/WasapPage.tsx +++ b/website/src/components/views/wasap/WasapPage.tsx @@ -4,30 +4,43 @@ import { useMemo } from 'react'; import { type FC } from 'react'; import { toMutationAnnotations } from './resistanceMutations'; -import { useWasapPageData } from './useWasapPageData'; -import type { WasapAnalysisFilter, WasapPageConfig } from './wasapPageConfig'; +import { getLapisFilterForTimeFrame, useWasapPageData } from './useWasapPageData'; +import { + variantTimeFrameLabel, + type WasapAnalysisFilter, + type WasapPageConfig, + type WasapVariantFilter, +} from './wasapPageConfig'; import { getDateRange } from '../../../lapis/getDateRange'; import { getTotalCount } from '../../../lapis/getTotalCount'; -import { wastewaterOrganismConfigs, type WastewaterOrganismName } from '../../../types/wastewaterConfig'; +import { defaultBreadcrumbs } from '../../../layouts/Breadcrumbs.tsx'; +import { DataPageLayout } from '../../../layouts/OrganismPage/DataPageLayout.tsx'; +import { dataOrigins } from '../../../types/dataOrigins.ts'; +import { + wastewaterBreadcrumb, + wastewaterOrganismConfigs, + type WastewaterOrganismName, +} from '../../../types/wastewaterConfig'; import { Loading } from '../../../util/Loading'; import { WasapPageStateHandler } from '../../../views/pageStateHandlers/WasapPageStateHandler'; import { GsMutationsOverTime } from '../../genspectrum/GsMutationsOverTime'; import { WasapPageStateSelector } from '../../pageStateSelectors/wasap/WasapPageStateSelector'; import { withQueryProvider } from '../../subscriptions/backendApi/withQueryProvider'; +import { usePageState } from '../usePageState.ts'; export type WasapPageProps = { wastewaterOrganism: WastewaterOrganismName; - currentUrl: URL; }; -export const WasapPageInner: FC = ({ wastewaterOrganism, currentUrl }) => { +export const WasapPageInner: FC = ({ wastewaterOrganism }) => { const config = wastewaterOrganismConfigs[wastewaterOrganism]; // initialize page state from the URL const pageStateHandler = useMemo(() => new WasapPageStateHandler(config), [config]); - const { base, analysis } = useMemo( - () => pageStateHandler.parsePageStateFromUrl(currentUrl), - [pageStateHandler, currentUrl], - ); + + const { + pageState: { base, analysis }, + setPageState, + } = usePageState(pageStateHandler); // fetch which mutations should be analyzed const { data, isPending, isError } = useWasapPageData(config, analysis); @@ -59,47 +72,70 @@ export const WasapPageInner: FC = ({ wastewaterOrganism, current const memoizedLinkTemplate = useMemo(() => JSON.stringify(config.linkTemplate), [config.linkTemplate]); return ( - -
-
- -
- {isError ? ( - There was an error fetching the mutations to display. - ) : isPending ? ( - - ) : ( -
- {displayMutations !== undefined && displayMutations.length === 0 ? ( - - ) : ( - - )} - + +
+
+
- )} -
-
+ {isError ? ( + There was an error fetching the mutations to display. + ) : isPending ? ( + + ) : ( +
+ {displayMutations?.length === 0 ? ( + + ) : ( + + )} + {analysis.mode === 'variant' && config.variantAnalysisModeEnabled && ( + + )} + +
+ )} +
+ + ); }; @@ -130,6 +166,71 @@ const NoDataHelperText = ({ analysisFilter }: { analysisFilter: WasapAnalysisFil ); }; +/** + * Info stat about the amount of sequences used during the computation of the clinical variant signature. + * Will also show a warning of the count is small. + */ +const VariantFetchInfo = ({ + analysis, + clinicalLapisBaseUrl, + clinicalLapisLineageField, + clinicalLapisDateField, + warningThreshold, +}: { + analysis: WasapVariantFilter; + clinicalLapisBaseUrl: string; + clinicalLapisLineageField: string; + clinicalLapisDateField: string; + warningThreshold: number; +}) => { + const lapisFilter = { + ...getLapisFilterForTimeFrame(analysis.timeFrame, clinicalLapisDateField), + [clinicalLapisLineageField]: analysis.variant, + }; + + const { data, isPending, isError, error } = useQuery({ + queryKey: ['variantFetchInfo'], + queryFn: () => getTotalCount(clinicalLapisBaseUrl, lapisFilter), + }); + + const isHighlighted = data !== undefined && data < warningThreshold; + + let message = `The number of clinical sequences for ${analysis.variant}`; + if (analysis.timeFrame !== 'all') { + message += ` during the past ${variantTimeFrameLabel(analysis.timeFrame)}`; + } + if (isHighlighted) { + message += '. Clinical signature calculation with this few sequences is not recommended.'; + } + + return ( +
+
+
Clinical sequences for {analysis.variant}
+
+ {isPending ? ( + '…' + ) : isError ? ( + 'Error' + ) : isHighlighted ? ( + {data.toLocaleString('en-us')} + ) : ( + data.toLocaleString('en-us') + )} +
+
{isPending ? 'Loading …' : isError ? error.message : message}
+
+
+ ); +}; + +const WasapStats = ({ config }: { config: WasapPageConfig }) => ( +
+ + +
+); + const TotalCount = ({ config }: { config: WasapPageConfig }) => { const { data, isPending, isError, error } = useQuery({ queryKey: ['aggregatedCount'], @@ -139,7 +240,9 @@ const TotalCount = ({ config }: { config: WasapPageConfig }) => { return (
Amplicon sequences
-
{isPending ? '…' : isError ? 'Error' : data.toLocaleString()}
+
+ {isPending ? '…' : isError ? 'Error' : data.toLocaleString('en-us')} +
{isPending ? 'Loading total amplicon sequences count…' @@ -173,10 +276,3 @@ const DateRange = ({ config }: { config: WasapPageConfig }) => {
); }; - -const WasapStats = ({ config }: { config: WasapPageConfig }) => ( -
- - -
-); diff --git a/website/src/components/views/wasap/useWasapPageData.ts b/website/src/components/views/wasap/useWasapPageData.ts index ef7f9297a..e9a2531db 100644 --- a/website/src/components/views/wasap/useWasapPageData.ts +++ b/website/src/components/views/wasap/useWasapPageData.ts @@ -1,10 +1,23 @@ -import type { CustomColumn } from '@genspectrum/dashboard-components/util'; +import type { CustomColumn, LapisFilter } from '@genspectrum/dashboard-components/util'; import { useQuery } from '@tanstack/react-query'; +import dayjs from 'dayjs'; -import type { WasapAnalysisFilter, WasapPageConfig } from './wasapPageConfig'; +import type { VariantTimeFrame, WasapAnalysisFilter, WasapPageConfig } from './wasapPageConfig'; import { getCladeLineages } from '../../../lapis/getCladeLineages'; import { getMutations, getMutationsForVariant } from '../../../lapis/getMutations'; +/** + * Hook that fetches and returns `WasapPageData` for the W-ASAP page, + * depending on the analysis mode and analysis mode settings. + */ +export function useWasapPageData(config: WasapPageConfig, analysis: WasapAnalysisFilter) { + return useQuery({ + queryKey: ['wasap', analysis], + queryFn: () => + fetchMutationSelection(config, analysis).then((data) => wasapPageDataFromMutationSelection(data)), + }); +} + type AllMutations = { type: 'all'; }; @@ -47,6 +60,7 @@ async function fetchMutationSelection( analysis.minProportion, analysis.minCount, analysis.minJaccard, + getLapisFilterForTimeFrame(analysis.timeFrame, config.clinicalLapis.dateField), ).then((r) => ({ type: 'jaccard', mutationsWithScore: r })); case 'resistance': if (!config.resistanceAnalysisModeEnabled) { @@ -80,7 +94,7 @@ async function fetchMutationSelection( config.clinicalLapis.lapisBaseUrl, analysis.sequenceType, { - [config.clinicalLapis.lapisBaseUrl]: variant, + [config.clinicalLapis.lineageField]: variant, }, 0.8, 9, @@ -97,6 +111,26 @@ async function fetchMutationSelection( } } +export function getLapisFilterForTimeFrame(timeFrame: VariantTimeFrame, dateFieldName: string): LapisFilter { + let fromDate = undefined; + switch (timeFrame) { + case 'all': + break; + case '6months': + fromDate = dayjs().subtract(6, 'month').format('YYYY-MM-DD'); + break; + case '3months': + fromDate = dayjs().subtract(3, 'month').format('YYYY-MM-DD'); + break; + } + if (fromDate === undefined) { + return {}; + } + return { + [`${dateFieldName}From`]: fromDate, + }; +} + /** * The W-ASAP page data consists of the mutations to display in the mutations-over-time component, * and the additional custom columns that might optionally be displayed. @@ -139,15 +173,3 @@ function wasapPageDataFromMutationSelection(mutationSelection: MutationSelection }; } } - -/** - * Hook that fetches and returns `WasapPageData` for the W-ASAP page, - * depending on the analysis mode and analysis mode settings. - */ -export function useWasapPageData(config: WasapPageConfig, analysis: WasapAnalysisFilter) { - return useQuery({ - queryKey: ['wasap', analysis], - queryFn: () => - fetchMutationSelection(config, analysis).then((data) => wasapPageDataFromMutationSelection(data)), - }); -} diff --git a/website/src/components/views/wasap/wasapPageConfig.ts b/website/src/components/views/wasap/wasapPageConfig.ts index bf5c0af01..1ff438119 100644 --- a/website/src/components/views/wasap/wasapPageConfig.ts +++ b/website/src/components/views/wasap/wasapPageConfig.ts @@ -1,4 +1,4 @@ -import type { DateRangeOption, SequenceType } from '@genspectrum/dashboard-components/util'; +import type { DateRangeOption, SequenceType, TemporalGranularity } from '@genspectrum/dashboard-components/util'; import type { ResistanceMutationSet } from './resistanceMutations'; @@ -71,11 +71,13 @@ type VariantAnalysisModeConfig = variantAnalysisModeEnabled: true; clinicalLapis: { lapisBaseUrl: string; + dateField: string; lineageField: string; }; filterDefaults: { variant: WasapVariantFilter; }; + clinicalSequenceCountWarningThreshold: number; }; type ResistanceAnalysisModeConfig = @@ -143,7 +145,7 @@ export type WasapAnalysisMode = 'manual' | 'variant' | 'resistance' | 'untracked export type WasapBaseFilter = { locationName?: string; samplingDate?: DateRangeOption; - granularity: string; + granularity: TemporalGranularity; excludeEmpty: boolean; }; @@ -157,6 +159,25 @@ export type WasapManualFilter = { mutations?: string[]; }; +export const VARIANT_TIME_FRAME = { + all: 'all', + sixMonths: '6months', + threeMonths: '3months', +} as const; + +export type VariantTimeFrame = (typeof VARIANT_TIME_FRAME)[keyof typeof VARIANT_TIME_FRAME]; + +export function variantTimeFrameLabel(timeFrame: VariantTimeFrame): string { + switch (timeFrame) { + case VARIANT_TIME_FRAME.all: + return 'All'; + case VARIANT_TIME_FRAME.sixMonths: + return '6 months'; + case VARIANT_TIME_FRAME.threeMonths: + return '3 months'; + } +} + export type WasapVariantFilter = { mode: 'variant'; sequenceType: SequenceType; @@ -164,6 +185,7 @@ export type WasapVariantFilter = { minProportion: number; minCount: number; minJaccard: number; + timeFrame: VariantTimeFrame; }; export type WasapResistanceFilter = { diff --git a/website/src/lapis/getLastUpdatedDate.spec.ts b/website/src/lapis/getLastUpdatedDate.spec.ts index 410ee68a8..4dea079e4 100644 --- a/website/src/lapis/getLastUpdatedDate.spec.ts +++ b/website/src/lapis/getLastUpdatedDate.spec.ts @@ -28,7 +28,7 @@ describe('getLastUpdatedDate', () => { const result = await getLastUpdatedDate(DUMMY_LAPIS_URL); - expect(result).toBeUndefined(); + expect(result).toBeNull(); }); test('should handle non-numeric data versions gracefully', async () => { @@ -36,6 +36,6 @@ describe('getLastUpdatedDate', () => { const result = await getLastUpdatedDate(DUMMY_LAPIS_URL); - expect(result).toBeUndefined(); + expect(result).toBeNull(); }); }); diff --git a/website/src/lapis/getLastUpdatedDate.ts b/website/src/lapis/getLastUpdatedDate.ts index 4fcdac581..013724881 100644 --- a/website/src/lapis/getLastUpdatedDate.ts +++ b/website/src/lapis/getLastUpdatedDate.ts @@ -2,14 +2,14 @@ import axios from 'axios'; import dayjs from 'dayjs'; import { z } from 'zod'; -import { getInstanceLogger } from '../logger.ts'; +import { getClientLogger } from '../clientLogger.ts'; const lapisInfoSchema = z.object({ dataVersion: z.string(), }); export type LapisInfo = z.infer; -const logger = getInstanceLogger('getLastUpdatedDate'); +const logger = getClientLogger('getLastUpdatedDate'); export async function getLastUpdatedDate(lapisUrl: string) { try { @@ -20,7 +20,7 @@ export async function getLastUpdatedDate(lapisUrl: string) { const timestamp = Number(parsedLapisInfo.data.dataVersion); if (isNaN(timestamp)) { logger.error(`Got invalid timestamp from lapis info: '${parsedLapisInfo.data.dataVersion}'`); - return undefined; + return null; } return dayjs.unix(timestamp).locale('de').calendar(null, { sameElse: '[on] YYYY-MM-DD' }); } @@ -28,9 +28,9 @@ export async function getLastUpdatedDate(lapisUrl: string) { logger.error( `Failed to parse lapis info: ${JSON.stringify(parsedLapisInfo)} (was ${JSON.stringify(response.data)})`, ); - return undefined; + return null; } catch (error) { logger.error(`Failed to fetch lapis info: ${JSON.stringify(error)}`); - return undefined; + return null; } } diff --git a/website/src/lapis/getMutations.spec.ts b/website/src/lapis/getMutations.spec.ts index eeed16228..394b30edd 100644 --- a/website/src/lapis/getMutations.spec.ts +++ b/website/src/lapis/getMutations.spec.ts @@ -41,6 +41,28 @@ describe('getMutations', () => { expect(result).toEqual(['A123C', 'G234T']); }); + test('should return filtered mutations with multiple filters', async () => { + lapisRouteMocker.mockPostNucleotideMutations( + { minProportion: 0.1, pangoLineage: 'B.1.1.7', dateFrom: '2023-05-01' }, + { + data: [ + { mutation: 'A123C', count: 5 }, + { mutation: 'G234T', count: 2 }, + ], + }, + ); + + const result = await getMutations( + DUMMY_LAPIS_URL, + 'nucleotide', + { pangoLineage: 'B.1.1.7', dateFrom: '2023-05-01' }, + 0.1, + 3, + ); + + expect(result).toEqual(['A123C']); + }); + test('should throw on failed request', async () => { lapisRouteMocker.mockPostNucleotideMutations( { minProportion: 0.1 }, @@ -105,6 +127,50 @@ describe('getMutationsForVariant', () => { 0.1, 5, 0.9, + {}, + ); + + expect(result).toEqual([{ mutation: 'A2C', jaccardIndex: 1 }]); + }); + + test('should set date filter correctly', async () => { + // Setup: + // - Variant FOO has 20 sequences + // - A1C: in 20 FOO sequences, 40 total -> Jaccard = 20/(20+40-20) = 20/40 = 0.5 (excluded, < 0.9) + // - A2C: in 20 FOO sequences, 20 total -> Jaccard = 20/(20+20-20) = 20/20 = 1.0 (included, >= 0.9) + // - A3C: in 2 FOO sequences (excluded by minCount < 5) + lapisRouteMocker.mockPostAggregated({ pangoLineage: 'FOO', dateFrom: '2025-01-01' }, { data: [{ count: 20 }] }); + lapisRouteMocker.mockPostNucleotideMutationsMulti([ + { + body: { minProportion: 0.1, pangoLineage: 'FOO', dateFrom: '2025-01-01' }, + response: { + data: [ + { mutation: 'A1C', count: 20 }, + { mutation: 'A2C', count: 20 }, + { mutation: 'A3C', count: 2 }, + ], + }, + }, + { + body: { minProportion: 0, dateFrom: '2025-01-01' }, + response: { + data: [ + { mutation: 'A1C', count: 40 }, + { mutation: 'A2C', count: 20 }, + { mutation: 'A3C', count: 2 }, + ], + }, + }, + ]); + + const result = await getMutationsForVariant( + DUMMY_LAPIS_URL, + 'nucleotide', + { pangoLineage: 'FOO' }, + 0.1, + 5, + 0.9, + { dateFrom: '2025-01-01' }, ); expect(result).toEqual([{ mutation: 'A2C', jaccardIndex: 1 }]); @@ -137,6 +203,7 @@ describe('getMutationsForVariant', () => { 0.05, 5, 0.6, + {}, ); expect(result).toEqual([{ mutation: 'T100C', jaccardIndex: 10 / 15 }]); @@ -169,6 +236,7 @@ describe('getMutationsForVariant', () => { 0.01, 5, 0.01, + {}, ); expect(result).toEqual([]); @@ -211,6 +279,7 @@ describe('getMutationsForVariant', () => { 0.05, 5, 0.75, + {}, ); expect(result).toEqual([ @@ -247,6 +316,7 @@ describe('getMutationsForVariant', () => { 0.05, 5, 0.9, + {}, ); expect(result).toEqual([{ mutation: 'S:L452R', jaccardIndex: 1 }]); @@ -279,6 +349,7 @@ describe('getMutationsForVariant', () => { 0.05, 5, 0.9, + {}, ); expect(result).toEqual([{ mutation: 'S:L452R', jaccardIndex: 1 }]); diff --git a/website/src/lapis/getMutations.ts b/website/src/lapis/getMutations.ts index b82b22cb4..27b7ffe2a 100644 --- a/website/src/lapis/getMutations.ts +++ b/website/src/lapis/getMutations.ts @@ -54,18 +54,19 @@ export async function getMutationsForVariant( minProportion: number, minCount: number, minJaccardIndex: number, + dateFilter: LapisFilter | undefined, ) { return Promise.all([ // sequence counts WITH mutation and WITH lineage - getMutationsInternal(lapisUrl, mutationType, lineageFilter, minProportion).then((r) => + getMutationsInternal(lapisUrl, mutationType, { ...lineageFilter, ...dateFilter }, minProportion).then((r) => r.filter((item) => item.count >= minCount), ), // sequence counts WITH mutation (only) - getMutationsInternal(lapisUrl, mutationType, undefined, 0).then((r) => + getMutationsInternal(lapisUrl, mutationType, dateFilter, 0).then((r) => Object.fromEntries(r.map((item) => [item.mutation, item.count])), ), // sequence count WITH lineage (only) - getTotalCount(lapisUrl, lineageFilter), + getTotalCount(lapisUrl, { ...lineageFilter, ...dateFilter }), ]).then(([intersectionCounts, totalCounts, variantCount]) => intersectionCounts .map(({ mutation, count }) => ({ diff --git a/website/src/layouts/OrganismPage/AccessionsDownloadButton.astro b/website/src/layouts/OrganismPage/AccessionsDownloadButton.astro deleted file mode 100644 index 3457d3132..000000000 --- a/website/src/layouts/OrganismPage/AccessionsDownloadButton.astro +++ /dev/null @@ -1,53 +0,0 @@ ---- -import type { LapisFilter } from '@genspectrum/dashboard-components/util'; - -import { assembleDownloadUrl } from './assembleDownloadUrl'; -import { Modal } from '../../styles/containers/Modal'; -import { ModalContent } from '../../styles/containers/ModalContent'; -import { ModalHeader } from '../../styles/containers/ModalHeader'; - -type DownloadLink = { - label: string; - filter: LapisFilter; - downloadFileBasename: string; -}; - -interface Props { - accessionDownloadFields: string[]; - downloadLinks: DownloadLink[]; - lapisUrl: string; -} - -const { accessionDownloadFields, downloadLinks, lapisUrl } = Astro.props; - -const modalId = 'accessionDownloadModal'; ---- - - - - - -

You can download a list of accessions that are used for the visualizations on this page:

-
- { - downloadLinks - .map(({ label, filter, downloadFileBasename }) => ({ - label, - url: assembleDownloadUrl(accessionDownloadFields, filter, downloadFileBasename, lapisUrl), - })) - .map(({ label, url }) => ( - - - {label} - - )) - } -
-
- -
-
-
diff --git a/website/src/layouts/OrganismPage/AccessionsDownloadButton.tsx b/website/src/layouts/OrganismPage/AccessionsDownloadButton.tsx new file mode 100644 index 000000000..4bb1fd7a3 --- /dev/null +++ b/website/src/layouts/OrganismPage/AccessionsDownloadButton.tsx @@ -0,0 +1,63 @@ +import type { LapisFilter } from '@genspectrum/dashboard-components/util'; +import type { FC } from 'react'; + +import { assembleDownloadUrl } from './assembleDownloadUrl'; +import { Modal, useModalRef } from '../../styles/containers/Modal'; +import { ModalContent } from '../../styles/containers/ModalContent'; +import { ModalHeader } from '../../styles/containers/ModalHeader'; + +export type DownloadLink = { + label: string; + filter: LapisFilter; + downloadFileBasename: string; +}; + +export type AccessionsDownloadButtonProps = { + accessionDownloadFields: string[]; + downloadLinks: DownloadLink[]; + lapisUrl: string; +}; + +export const AccessionsDownloadButton: FC = ({ + accessionDownloadFields, + downloadLinks, + lapisUrl, +}) => { + const modalRef = useModalRef(); + + return ( + <> + + + + +

You can download a list of accessions that are used for the visualizations on this page:

+
+ {downloadLinks + .map(({ label, filter, downloadFileBasename }) => ({ + label, + url: assembleDownloadUrl( + accessionDownloadFields, + filter, + downloadFileBasename, + lapisUrl, + ), + })) + .map(({ label, url }) => ( + + + {label} + + ))} +
+
+ +
+
+
+ + ); +}; diff --git a/website/src/layouts/OrganismPage/DataPageLayout.astro b/website/src/layouts/OrganismPage/DataPageLayout.astro deleted file mode 100644 index 4cca64d02..000000000 --- a/website/src/layouts/OrganismPage/DataPageLayout.astro +++ /dev/null @@ -1,44 +0,0 @@ ---- -import type { ComponentProps } from 'astro/types'; - -import AccessionsDownloadButton from './AccessionsDownloadButton.astro'; -import LastUpdatedInfo from './LastUpdatedInfo.astro'; -import LapisUnreachableWrapperClient from '../../components/LapisUnreachableWrapperClient'; -import type { DataOrigin } from '../../types/dataOrigins'; -import { type BreadcrumbElement, Breadcrumbs } from '../Breadcrumbs'; -import BaseLayout from '../base/BaseLayout.astro'; -import DataInfo from '../base/footer/DataInfo.astro'; - -interface Props { - title: string; - breadcrumbs: BreadcrumbElement[]; - dataOrigins: DataOrigin[]; - lapisUrl: string; - downloadLinks?: ComponentProps['downloadLinks']; - accessionDownloadFields?: string[]; -} - -const { title, breadcrumbs, dataOrigins, lapisUrl, downloadLinks = [], accessionDownloadFields = [] } = Astro.props; ---- - - -
- -
- - - -
- { - downloadLinks.length > 0 && ( - - ) - } - - -
-
diff --git a/website/src/layouts/OrganismPage/DataPageLayout.tsx b/website/src/layouts/OrganismPage/DataPageLayout.tsx new file mode 100644 index 000000000..969d87802 --- /dev/null +++ b/website/src/layouts/OrganismPage/DataPageLayout.tsx @@ -0,0 +1,50 @@ +import type { FC, PropsWithChildren } from 'react'; + +import { LapisUnreachableWrapperClient } from '../../components/LapisUnreachableWrapperClient.tsx'; +import type { DataOrigin } from '../../types/dataOrigins.ts'; +import { type BreadcrumbElement, Breadcrumbs } from '../Breadcrumbs.tsx'; +import { AccessionsDownloadButton, type DownloadLink } from './AccessionsDownloadButton.tsx'; +import { LastUpdatedInfo } from './LastUpdatedInfo.tsx'; +import { withQueryProvider } from '../../components/subscriptions/backendApi/withQueryProvider.tsx'; +import { DataInfo } from '../base/footer/DataInfo.tsx'; + +export type DataPageLayoutProps = PropsWithChildren<{ + breadcrumbs: BreadcrumbElement[]; + dataOrigins: DataOrigin[]; + lapisUrl: string; + downloadLinks?: DownloadLink[]; + accessionDownloadFields?: string[]; +}>; + +export const DataPageLayoutInner: FC = ({ + breadcrumbs, + dataOrigins, + lapisUrl, + downloadLinks = [], + accessionDownloadFields = [], + children, +}) => { + return ( +
+
+ +
+
+ {children} +
+
+ {downloadLinks.length > 0 && ( + + )} + + +
+
+ ); +}; + +export const DataPageLayout = withQueryProvider(DataPageLayoutInner); diff --git a/website/src/layouts/OrganismPage/LastUpdatedInfo.astro b/website/src/layouts/OrganismPage/LastUpdatedInfo.astro deleted file mode 100644 index 1d29acab1..000000000 --- a/website/src/layouts/OrganismPage/LastUpdatedInfo.astro +++ /dev/null @@ -1,13 +0,0 @@ ---- -import { getLastUpdatedDate } from '../../lapis/getLastUpdatedDate'; - -interface Props { - lapisUrl: string; -} - -const { lapisUrl } = Astro.props; - -const lastUpdatedDate = await getLastUpdatedDate(lapisUrl); ---- - -Data last updated: {lastUpdatedDate ?? 'unknown'} diff --git a/website/src/layouts/OrganismPage/LastUpdatedInfo.tsx b/website/src/layouts/OrganismPage/LastUpdatedInfo.tsx new file mode 100644 index 000000000..67eeeae5e --- /dev/null +++ b/website/src/layouts/OrganismPage/LastUpdatedInfo.tsx @@ -0,0 +1,18 @@ +import { useQuery } from '@tanstack/react-query'; +import type { FC } from 'react'; + +import { getLastUpdatedDate } from '../../lapis/getLastUpdatedDate'; + +interface LastUpdatedInfoProps { + lapisUrl: string; +} + +export const LastUpdatedInfo: FC = ({ lapisUrl }) => { + const { data: lastUpdatedDate } = useQuery({ + queryKey: ['lastUpdatedDate', lapisUrl], + queryFn: () => getLastUpdatedDate(lapisUrl), + throwOnError: true, + }); + + return Data last updated: {lastUpdatedDate ?? 'unknown'}; +}; diff --git a/website/src/layouts/OrganismPage/OrganismViewPageLayout.astro b/website/src/layouts/OrganismPage/OrganismViewPageLayout.astro deleted file mode 100644 index 38d752847..000000000 --- a/website/src/layouts/OrganismPage/OrganismViewPageLayout.astro +++ /dev/null @@ -1,28 +0,0 @@ ---- -import type { ComponentProps } from 'astro/types'; - -import AccessionsDownloadButton from './AccessionsDownloadButton.astro'; -import DataPageLayout from './DataPageLayout.astro'; -import { getLapisUrl } from '../../config'; -import type { OrganismConstants } from '../../views/OrganismConstants'; -import { type View } from '../../views/View'; -import type { PageStateHandler } from '../../views/pageStateHandlers/PageStateHandler'; - -interface Props { - view: View>; - downloadLinks: ComponentProps['downloadLinks']; -} - -const { view, downloadLinks } = Astro.props; ---- - - - - diff --git a/website/src/layouts/OrganismPage/OrganismViewPageLayout.tsx b/website/src/layouts/OrganismPage/OrganismViewPageLayout.tsx new file mode 100644 index 000000000..d14a6f850 --- /dev/null +++ b/website/src/layouts/OrganismPage/OrganismViewPageLayout.tsx @@ -0,0 +1,34 @@ +import type { FC, PropsWithChildren } from 'react'; + +import { type DownloadLink } from './AccessionsDownloadButton.tsx'; +import { DataPageLayout } from './DataPageLayout.tsx'; +import type { OrganismConstants } from '../../views/OrganismConstants'; +import { type View } from '../../views/View'; +import type { PageStateHandler } from '../../views/pageStateHandlers/PageStateHandler'; + +export type OrganismViewPageLayoutProps = PropsWithChildren<{ + view: View>; + downloadLinks: DownloadLink[]; + lapisUrl: string; +}>; + +export const OrganismViewPageLayout: FC = ({ + view, + downloadLinks, + lapisUrl, + children, +}) => { + return ( + + + {children} + + + ); +}; diff --git a/website/src/layouts/OrganismPage/SingleVariantOrganismPageLayout.astro b/website/src/layouts/OrganismPage/SingleVariantOrganismPageLayout.astro deleted file mode 100644 index 2841b7624..000000000 --- a/website/src/layouts/OrganismPage/SingleVariantOrganismPageLayout.astro +++ /dev/null @@ -1,31 +0,0 @@ ---- -import type { ComponentProps } from 'astro/types'; - -import AccessionsDownloadButton from './AccessionsDownloadButton.astro'; -import OrganismViewPageLayout from './OrganismViewPageLayout.astro'; -import { getLapisUrl } from '../../config'; -import type { OrganismConstants } from '../../views/OrganismConstants'; -import type { View } from '../../views/View'; -import { type PageStateHandler } from '../../views/pageStateHandlers/PageStateHandler'; - -interface Props { - view: View>; - downloadLinks: ComponentProps['downloadLinks']; -} - -const { view, downloadLinks } = Astro.props; ---- - - - -
-
- -
- -
-
-
diff --git a/website/src/layouts/OrganismPage/SingleVariantOrganismPageLayout.tsx b/website/src/layouts/OrganismPage/SingleVariantOrganismPageLayout.tsx new file mode 100644 index 000000000..ceebf3a44 --- /dev/null +++ b/website/src/layouts/OrganismPage/SingleVariantOrganismPageLayout.tsx @@ -0,0 +1,37 @@ +import type { FC, ReactNode } from 'react'; + +import { type DownloadLink } from './AccessionsDownloadButton.tsx'; +import { OrganismViewPageLayout } from './OrganismViewPageLayout.tsx'; +import type { OrganismsConfig } from '../../config.ts'; +import type { OrganismConstants } from '../../views/OrganismConstants'; +import type { View } from '../../views/View'; +import { type PageStateHandler } from '../../views/pageStateHandlers/PageStateHandler'; + +export type SingleVariantOrganismPageLayoutProps = { + view: View>; + downloadLinks: DownloadLink[]; + organismsConfig: OrganismsConfig; + filters: ReactNode; + dataDisplay: ReactNode; +}; + +export const SingleVariantOrganismPageLayout: FC = ({ + view, + downloadLinks, + organismsConfig, + filters, + dataDisplay, +}) => { + return ( + +
+
{filters}
+ {dataDisplay} +
+
+ ); +}; diff --git a/website/src/layouts/base/BaseLayout.astro b/website/src/layouts/base/BaseLayout.astro index 8f8bccc9c..bcf85d40f 100644 --- a/website/src/layouts/base/BaseLayout.astro +++ b/website/src/layouts/base/BaseLayout.astro @@ -34,14 +34,13 @@ const { title, forceLoggedOutState = false } = Astro.props; - + +
-
-
diff --git a/website/src/layouts/base/footer/DataInfo.astro b/website/src/layouts/base/footer/DataInfo.astro deleted file mode 100644 index 93cc13254..000000000 --- a/website/src/layouts/base/footer/DataInfo.astro +++ /dev/null @@ -1,26 +0,0 @@ ---- -import DataOriginLink from './DataOriginLink.astro'; -import { type DataOrigin, dataOriginConfig } from '../../../types/dataOrigins'; - -interface Props { - dataOrigins: DataOrigin[]; -} - -const { dataOrigins } = Astro.props; ---- - -
-
- Enabled by data from: - { - dataOrigins.map((dataOrigin, index) => ( - - {index > 0 && (index === dataOrigins.length - 1 ? ' and ' : ', ')} - - {dataOriginConfig[dataOrigin].name} - - - )) - } -
-
diff --git a/website/src/layouts/base/footer/DataInfo.tsx b/website/src/layouts/base/footer/DataInfo.tsx new file mode 100644 index 000000000..ae7b3bd6b --- /dev/null +++ b/website/src/layouts/base/footer/DataInfo.tsx @@ -0,0 +1,38 @@ +import { type FC, type PropsWithChildren } from 'react'; + +import { externalLinkIconCss } from '../../../components/iconCss.ts'; +import { type DataOrigin, dataOriginConfig } from '../../../types/dataOrigins'; + +export type DataInfoProps = { + dataOrigins: DataOrigin[]; +}; + +export const DataInfo: FC = ({ dataOrigins }) => { + return ( +
+
+ Enabled by data from: + {dataOrigins.map((dataOrigin, index) => ( + + {index > 0 && (index === dataOrigins.length - 1 ? ' and ' : ', ')} + + {dataOriginConfig[dataOrigin].name} + + + ))} +
+
+ ); +}; + +type DataOriginLinkProps = PropsWithChildren<{ + href: string; +}>; + +const DataOriginLink: FC = ({ href, children }) => { + return ( + + {children} + + ); +}; diff --git a/website/src/layouts/base/footer/DataOriginLink.astro b/website/src/layouts/base/footer/DataOriginLink.astro deleted file mode 100644 index 3e0700e55..000000000 --- a/website/src/layouts/base/footer/DataOriginLink.astro +++ /dev/null @@ -1,12 +0,0 @@ ---- -import { externalLinkIconCss } from '../../../components/iconCss'; -interface Props { - href: string; -} - -const { href } = Astro.props; ---- - - - - diff --git a/website/src/layouts/base/footer/Footer.astro b/website/src/layouts/base/footer/Footer.astro index 0b90db516..918a8612d 100644 --- a/website/src/layouts/base/footer/Footer.astro +++ b/website/src/layouts/base/footer/Footer.astro @@ -1,12 +1,8 @@ --- import PrimaryFooter from './PrimaryFooter.astro'; -import SecondaryFooter from './SecondaryFooter.astro'; ---
- - - diff --git a/website/src/layouts/base/footer/SecondaryFooter.astro b/website/src/layouts/base/footer/SecondaryFooter.astro deleted file mode 100644 index 1725a6973..000000000 --- a/website/src/layouts/base/footer/SecondaryFooter.astro +++ /dev/null @@ -1,7 +0,0 @@ ---- - ---- - -
- -
diff --git a/website/src/pages/covid/compare-side-by-side.astro b/website/src/pages/covid/compare-side-by-side.astro index 85799976f..159e32004 100644 --- a/website/src/pages/covid/compare-side-by-side.astro +++ b/website/src/pages/covid/compare-side-by-side.astro @@ -1,130 +1,6 @@ --- -import GsAggregate from '../../components/genspectrum/GsAggregate.astro'; -import GsMutations from '../../components/genspectrum/GsMutations.astro'; -import GsPrevalenceOverTime from '../../components/genspectrum/GsPrevalenceOverTime.astro'; -import GsRelativeGrowthAdvantage from '../../components/genspectrum/GsRelativeGrowthAdvantage.astro'; -import { CompareSideBySidePageStateSelector } from '../../components/pageStateSelectors/CompareSideBySidePageStateSelector'; -import { CompareSideBySideSelectorFallback } from '../../components/pageStateSelectors/FallbackElement'; -import { toDownloadLink } from '../../components/views/compareSideBySide/toDownloadLink'; -import { isStaging, getDashboardsConfig, getLapisUrl } from '../../config'; -import OrganismViewPageLayout from '../../layouts/OrganismPage/OrganismViewPageLayout.astro'; -import { chooseGranularityBasedOnDateRange } from '../../util/chooseGranularityBasedOnDateRange'; -import { ComponentHeight } from '../../views/OrganismConstants'; -import { toLapisFilterWithoutVariant } from '../../views/pageStateHandlers/toLapisFilterWithoutVariant'; -import type { OrganismViewKey } from '../../views/routing'; -import { ServerSide } from '../../views/serverSideRouting'; - -const organismViewKey: OrganismViewKey = 'covid.compareSideBySideView'; -const view = ServerSide.routing.getOrganismView(organismViewKey); - -const pageState = view.pageStateHandler.parsePageStateFromUrl(Astro.url); - -const downloadLinks = [...pageState.filters.entries()].map( - toDownloadLink(view.pageStateHandler, view.organismConstants.organism), -); +import GenericCompareSideBySidePage from '../../components/views/compareSideBySide/GenericCompareSideBySidePage.astro'; +import { Organisms } from '../../types/Organism'; --- - - -
- { - Array.from(pageState.filters).map(([id, { variantFilter, datasetFilter }]) => { - const baselineLapisFilter = toLapisFilterWithoutVariant( - datasetFilter, - view.organismConstants.additionalFilters, - ); - const timeGranularity = chooseGranularityBasedOnDateRange({ - earliestDate: new Date(view.organismConstants.earliestDate), - dateRange: datasetFilter.dateFilters[view.organismConstants.mainDateField], - }); - const numeratorFilter = { - ...variantFilter.lineages, - ...variantFilter.mutations, - variantQuery: variantFilter.variantQuery, - ...baselineLapisFilter, - }; - - return ( -
-
- {pageState.filters.size > 1 && ( - - Remove column - - )} - - - -
- - - - - - {view.organismConstants.aggregatedVisualizations.compareSideBySide.map( - ({ label, fields, views }) => ( - - ), - )} -
- ); - }) - } - - Add column - -
-
-
+ diff --git a/website/src/pages/covid/single-variant.astro b/website/src/pages/covid/single-variant.astro index 0a56d0eaa..fa95aaefb 100644 --- a/website/src/pages/covid/single-variant.astro +++ b/website/src/pages/covid/single-variant.astro @@ -1,72 +1,16 @@ --- -import { AnalyzeSingleVariantSelectorFallback } from '../../components/pageStateSelectors/FallbackElement'; -import { SelectorHeadline } from '../../components/pageStateSelectors/SelectorHeadline'; -import { SingleVariantPageStateSelector } from '../../components/pageStateSelectors/SingleVariantPageStateSelector'; -import { CollectionsList } from '../../components/views/analyzeSingleVariant/CollectionsList'; -import { CovidSingleVariantDataDisplay } from '../../components/views/analyzeSingleVariant/CovidSingleVariantDataDisplay'; -import { sanitizeForFilename } from '../../components/views/compareSideBySide/toDownloadLink'; +import { CovidSingleVariantReactPage } from '../../components/views/analyzeSingleVariant/CovidSingleVariantReactPage'; import { isStaging, getDashboardsConfig } from '../../config'; -import SingleVariantOrganismPageLayout from '../../layouts/OrganismPage/SingleVariantOrganismPageLayout.astro'; -import { hasOnlyUndefinedValues } from '../../util/hasOnlyUndefinedValues'; -import { toDisplayName } from '../../views/pageStateHandlers/PageStateHandler'; -import { type OrganismViewKey } from '../../views/routing'; +import BaseLayout from '../../layouts/base/BaseLayout.astro'; import { ServerSide } from '../../views/serverSideRouting'; -const organismViewKey: OrganismViewKey = 'covid.singleVariantView'; -const view = ServerSide.routing.getOrganismView(organismViewKey); -const pageState = view.pageStateHandler.parsePageStateFromUrl(Astro.url); - -const variantFilter = view.pageStateHandler.toLapisFilter(pageState); - -const noVariantSelected = hasOnlyUndefinedValues(pageState.variantFilter); - -const displayName = toDisplayName(pageState.variantFilter); -const downloadLinks = noVariantSelected - ? [ - { - label: 'Download all accessions', - filter: variantFilter, - downloadFileBasename: `${view.organismConstants.organism}_accessions`, - }, - ] - : [ - { - label: `Download accessions of ${displayName}`, - filter: variantFilter, - downloadFileBasename: `${view.organismConstants.organism}_${sanitizeForFilename(displayName)}_accessions`, - }, - ]; -const organismConfig = getDashboardsConfig().dashboards.organisms; +const view = ServerSide.routing.getOrganismView('covid.singleVariantView'); --- - -
- - - -
-
- Collections - -
-
- -
- -
-
+ + + diff --git a/website/src/pages/index.astro b/website/src/pages/index.astro index 6887477bc..1f93fad8f 100644 --- a/website/src/pages/index.astro +++ b/website/src/pages/index.astro @@ -2,7 +2,7 @@ import Link from '../components/Link.astro'; import MainMenu from '../components/MainMenu.astro'; import BaseLayout from '../layouts/base/BaseLayout.astro'; -import DataInfo from '../layouts/base/footer/DataInfo.astro'; +import { DataInfo } from '../layouts/base/footer/DataInfo.tsx'; import { BrandName } from '../layouts/base/header/Brand'; import { getPathogenMegaMenuSections } from '../layouts/base/header/getPathogenMegaMenuSections'; import { dataOrigins } from '../types/dataOrigins'; @@ -110,12 +110,14 @@ import { Page } from '../types/pages'; Feedback, suggestions, bug reports and active contributions are highly welcome, please create an issue in our Github repository - . For general questions, please contact Chaoran Chen (chaoran.chen@bsse.ethz.ch). For media requests, please - reach out to ETH Zurich Media Relations (mediarelations@hk.ethz.ch). A list of all team members can be found + . For general questions, please contact Chaoran Chen (chaoran.chen@bsse.ethz.ch). For media requests, please reach + out to ETH Zurich Media Relations (mediarelations@hk.ethz.ch). A list of all team members can be found here .
- +
+ +
diff --git a/website/src/pages/swiss-wastewater/flu.astro b/website/src/pages/swiss-wastewater/flu.astro index 9395dd487..119324242 100644 --- a/website/src/pages/swiss-wastewater/flu.astro +++ b/website/src/pages/swiss-wastewater/flu.astro @@ -4,7 +4,8 @@ import type { SequenceType } from '@genspectrum/dashboard-components/util'; import InfluenzaWastewaterInfo from '../../components/InfluenzaWastewaterInfo.astro'; import GsWastewaterMutationsOverTime from '../../components/genspectrum/GsWastewaterMutationsOverTime.astro'; import { defaultBreadcrumbs } from '../../layouts/Breadcrumbs'; -import DataPageLayout from '../../layouts/OrganismPage/DataPageLayout.astro'; +import { DataPageLayout } from '../../layouts/OrganismPage/DataPageLayout.tsx'; +import BaseLayout from '../../layouts/base/BaseLayout.astro'; import { dataOrigins } from '../../types/dataOrigins'; import { getMutationAnnotation, @@ -16,48 +17,49 @@ import { const lapisUrl = `${wastewaterConfig.lapisBaseUrl}/influenza`; --- - + { - name: 'Influenza', - href: wastewaterConfig.pages.influenza.path, - }, - ]} - dataOrigins={[dataOrigins.wise]} - lapisUrl={lapisUrl} -> - { - InfluenzaTypes.map((reference) => { - const mutationAnnotations = getMutationAnnotation(reference); - return ( - -
-

{reference}

- {(['nucleotide', 'amino acid'] satisfies SequenceType[]).map((sequenceType) => ( - - { + const mutationAnnotations = getMutationAnnotation(reference); + return ( + +
+

{reference}

+ {(['nucleotide', 'amino acid'] satisfies SequenceType[]).map((sequenceType) => ( + - - ))} -
-
- ); - }) - } - + collapsible={sequenceType === 'nucleotide'} + linkSuffix={reference} + > + +
+ ))} +
+
+ ); + }) + } +
+ diff --git a/website/src/pages/swiss-wastewater/rsv.astro b/website/src/pages/swiss-wastewater/rsv.astro index b8ca93065..0a37abb92 100644 --- a/website/src/pages/swiss-wastewater/rsv.astro +++ b/website/src/pages/swiss-wastewater/rsv.astro @@ -4,56 +4,58 @@ import type { SequenceType } from '@genspectrum/dashboard-components/util'; import RsvWastewaterInfo from '../../components/RsvWastewaterInfo.astro'; import GsWastewaterMutationsOverTime from '../../components/genspectrum/GsWastewaterMutationsOverTime.astro'; import { defaultBreadcrumbs } from '../../layouts/Breadcrumbs'; -import DataPageLayout from '../../layouts/OrganismPage/DataPageLayout.astro'; +import { DataPageLayout } from '../../layouts/OrganismPage/DataPageLayout.tsx'; +import BaseLayout from '../../layouts/base/BaseLayout.astro'; import { dataOrigins } from '../../types/dataOrigins'; import { wastewaterConfig, RSVTypes, wastewaterBreadcrumb } from '../../types/wastewaterConfig'; const lapisUrl = `${wastewaterConfig.lapisBaseUrl}/rsv`; --- - - -
+ + ( - <> -
-

{reference}

- {(['nucleotide', 'amino acid'] satisfies SequenceType[]).map((sequenceType) => ( - - + +
+ { + RSVTypes.map((reference) => ( + <> +
+

{reference}

+ {(['nucleotide', 'amino acid'] satisfies SequenceType[]).map((sequenceType) => ( + - - ))} -
- - )) - } -
-
- + collapsible={sequenceType === 'nucleotide'} + linkSuffix={reference} + > + +
+ ))} +
+ + )) + } +
+
+
+ diff --git a/website/src/types/wastewaterConfig.spec.ts b/website/src/types/wastewaterConfig.spec.ts new file mode 100644 index 000000000..e1b58e06b --- /dev/null +++ b/website/src/types/wastewaterConfig.spec.ts @@ -0,0 +1,13 @@ +import { describe, expect, test } from 'vitest'; + +import { wastewaterOrganismConfigs } from './wastewaterConfig'; + +describe.each(Object.entries(wastewaterOrganismConfigs))('wastewaterConfig %s', (_configName, config) => { + test('default resistance set name is valid', () => { + if (config.resistanceAnalysisModeEnabled) { + const resistanceSetNames = config.resistanceMutationSets.map((s) => s.name); + const defaultSetName = config.filterDefaults.resistance.resistanceSet; + expect(resistanceSetNames).include(defaultSetName); + } + }); +}); diff --git a/website/src/types/wastewaterConfig.ts b/website/src/types/wastewaterConfig.ts index 5fb496f42..1cbc8f9b4 100644 --- a/website/src/types/wastewaterConfig.ts +++ b/website/src/types/wastewaterConfig.ts @@ -1,7 +1,7 @@ import type { MutationAnnotation } from '@genspectrum/dashboard-components/util'; import { covidResistanceMutations } from '../components/views/wasap/resistanceMutations'; -import type { WasapPageConfig } from '../components/views/wasap/wasapPageConfig'; +import { VARIANT_TIME_FRAME, type WasapPageConfig } from '../components/views/wasap/wasapPageConfig'; export const wastewaterOrganisms = { covid: 'covid', @@ -28,17 +28,19 @@ export const wastewaterOrganismConfigs: Record, -> implements View -{ +> implements View { public readonly pathname; public readonly viewTitle; public readonly viewBreadcrumbEntries; diff --git a/website/src/views/OrganismConstants.ts b/website/src/views/OrganismConstants.ts index e3c32330c..b6c349517 100644 --- a/website/src/views/OrganismConstants.ts +++ b/website/src/views/OrganismConstants.ts @@ -24,6 +24,7 @@ type AggregatedVisualizations = { export interface OrganismConstants { readonly organism: Organism; + readonly earliestDate: string; readonly dataOrigins: DataOrigin[]; readonly accessionDownloadFields: string[]; readonly mainDateField: string; diff --git a/website/src/views/pageStateHandlers/CompareSideBySidePageStateHandler.spec.ts b/website/src/views/pageStateHandlers/CompareSideBySidePageStateHandler.spec.ts index a369acc58..99e178fb6 100644 --- a/website/src/views/pageStateHandlers/CompareSideBySidePageStateHandler.spec.ts +++ b/website/src/views/pageStateHandlers/CompareSideBySidePageStateHandler.spec.ts @@ -9,6 +9,7 @@ const mockDateRangeOption = { label: 'Last 7 Days', dateFrom: '2024-11-22', date const mockConstants: OrganismConstants = { organism: Organisms.covid, + earliestDate: 'earliestDate', dataOrigins: [], locationFields: ['country', 'region'], mainDateField: 'date', diff --git a/website/src/views/pageStateHandlers/CompareToBaselinePageStateHandler.spec.ts b/website/src/views/pageStateHandlers/CompareToBaselinePageStateHandler.spec.ts index 174923786..29f131f63 100644 --- a/website/src/views/pageStateHandlers/CompareToBaselinePageStateHandler.spec.ts +++ b/website/src/views/pageStateHandlers/CompareToBaselinePageStateHandler.spec.ts @@ -9,6 +9,7 @@ const mockDateRangeOption = { label: 'Last 7 Days', dateFrom: '2024-11-22', date const mockConstants: OrganismConstants = { organism: Organisms.covid, + earliestDate: 'earliestDate', dataOrigins: [], locationFields: ['country', 'region'], mainDateField: 'date', diff --git a/website/src/views/pageStateHandlers/CompareVariantsPageStateHandler.spec.ts b/website/src/views/pageStateHandlers/CompareVariantsPageStateHandler.spec.ts index 5d05f6de0..fbdce1b4f 100644 --- a/website/src/views/pageStateHandlers/CompareVariantsPageStateHandler.spec.ts +++ b/website/src/views/pageStateHandlers/CompareVariantsPageStateHandler.spec.ts @@ -9,6 +9,7 @@ const mockDateRangeOption = { label: 'Last 7 Days', dateFrom: '2024-11-22', date const mockConstants: OrganismConstants = { organism: Organisms.covid, + earliestDate: 'earliestDate', dataOrigins: [], locationFields: ['country', 'region'], mainDateField: 'date', diff --git a/website/src/views/pageStateHandlers/SequencingEffortsPageStateHandler.ts b/website/src/views/pageStateHandlers/SequencingEffortsPageStateHandler.ts index 9016e37fc..6cb6acae8 100644 --- a/website/src/views/pageStateHandlers/SequencingEffortsPageStateHandler.ts +++ b/website/src/views/pageStateHandlers/SequencingEffortsPageStateHandler.ts @@ -16,9 +16,9 @@ import { parseTextFiltersFromUrl, setSearchFromTextFilters } from './textFilterF import { advancedQueryUrlParam } from '../../components/genspectrum/AdvancedQueryFilter.tsx'; import { formatUrl } from '../../util/formatUrl.ts'; -export class SequencingEffortsStateHandler - implements PageStateHandler -{ +export class SequencingEffortsStateHandler< + PageState extends DatasetAndVariantData = DatasetAndVariantData, +> implements PageStateHandler { protected readonly pathname; constructor( diff --git a/website/src/views/pageStateHandlers/SingleVariantPageStateHandler.spec.ts b/website/src/views/pageStateHandlers/SingleVariantPageStateHandler.spec.ts index cc9f7ce34..785986390 100644 --- a/website/src/views/pageStateHandlers/SingleVariantPageStateHandler.spec.ts +++ b/website/src/views/pageStateHandlers/SingleVariantPageStateHandler.spec.ts @@ -9,6 +9,7 @@ const mockDateRangeOption = { label: 'Last 7 Days', dateFrom: '2024-11-22', date const mockConstants: OrganismConstants = { organism: Organisms.covid, + earliestDate: 'earliestDate', dataOrigins: [], locationFields: ['country', 'region'], mainDateField: 'date', diff --git a/website/src/views/pageStateHandlers/SingleVariantPageStateHandler.ts b/website/src/views/pageStateHandlers/SingleVariantPageStateHandler.ts index 7577f0f0c..79518b758 100644 --- a/website/src/views/pageStateHandlers/SingleVariantPageStateHandler.ts +++ b/website/src/views/pageStateHandlers/SingleVariantPageStateHandler.ts @@ -19,9 +19,9 @@ import { toLapisFilterWithoutVariant } from './toLapisFilterWithoutVariant.ts'; import { advancedQueryUrlParam } from '../../components/genspectrum/AdvancedQueryFilter.tsx'; import { formatUrl } from '../../util/formatUrl.ts'; -export class SingleVariantPageStateHandler - implements PageStateHandler -{ +export class SingleVariantPageStateHandler< + PageState extends DatasetAndVariantData = DatasetAndVariantData, +> implements PageStateHandler { protected readonly pathname; constructor( diff --git a/website/src/views/pageStateHandlers/WasapPageStateHandler.spec.ts b/website/src/views/pageStateHandlers/WasapPageStateHandler.spec.ts new file mode 100644 index 000000000..4a8edb987 --- /dev/null +++ b/website/src/views/pageStateHandlers/WasapPageStateHandler.spec.ts @@ -0,0 +1,323 @@ +import { describe, expect, it } from 'vitest'; + +import { WasapPageStateHandler } from './WasapPageStateHandler'; +import { covidResistanceMutations } from '../../components/views/wasap/resistanceMutations'; +import { + VARIANT_TIME_FRAME, + type WasapManualFilter, + type WasapPageConfig, + type WasapResistanceFilter, + type WasapUntrackedFilter, + type WasapVariantFilter, +} from '../../components/views/wasap/wasapPageConfig'; + +const config: WasapPageConfig = { + name: 'SARS-CoV-2', + path: `/wastewater/covid`, + description: 'Analyze SARS-CoV-2 data that was collected by the WISE project.', + linkTemplate: { + nucleotideMutation: + 'https://open.cov-spectrum.org/explore/World/AllSamples/AllTimes/variants?nucMutations={{mutation}}', + aminoAcidMutation: + 'https://open.cov-spectrum.org/explore/World/AllSamples/AllTimes/variants?aaMutations={{mutation}}', + }, + manualAnalysisModeEnabled: true, + variantAnalysisModeEnabled: true, + resistanceAnalysisModeEnabled: true, + untrackedAnalysisModeEnabled: true, + resistanceMutationSets: covidResistanceMutations, + lapisBaseUrl: 'https://lapis.wasap.genspectrum.org', + samplingDateField: 'samplingDate', + locationNameField: 'locationName', + clinicalLapis: { + lapisBaseUrl: 'https://lapis.cov-spectrum.org/open/v2', + cladeField: 'nextstrainClade', + lineageField: 'nextcladePangoLineage', + dateField: 'date', + }, + browseDataUrl: 'https://db.wasap.genspectrum.org/covid/search', + browseDataDescription: 'Browse the data in the W-ASAP Loculus instance.', + defaultLocationName: 'Zürich (ZH)', + clinicalSequenceCountWarningThreshold: 50, + filterDefaults: { + manual: { + mode: 'manual', + sequenceType: 'nucleotide', + mutations: undefined, + }, + variant: { + mode: 'variant', + sequenceType: 'nucleotide', + variant: 'XFG*', + minProportion: 0.8, + minCount: 15, + minJaccard: 0.75, + timeFrame: VARIANT_TIME_FRAME.all, + }, + resistance: { + mode: 'resistance', + sequenceType: 'amino acid', + resistanceSet: '3CLpro', + }, + untracked: { + mode: 'untracked', + sequenceType: 'nucleotide', + excludeSet: 'predefined', + }, + }, +}; + +describe('WasapPageStateHandler', () => { + const handler = new WasapPageStateHandler(config); + + describe('default URL', () => { + it('should return the default page URL', () => { + const url = handler.getDefaultPageUrl(); + expect(url).toBe('/wastewater/covid'); + }); + }); + + describe('base filter', () => { + it('parses base filter with all fields', () => { + const url = + '/wastewater/covid?' + + 'locationName=Berlin&' + + 'samplingDate=2024-01-01--2024-12-31&' + + 'granularity=week&' + + 'excludeEmpty=false&' + + 'analysisMode=manual&' + + 'sequenceType=nucleotide&'; + const filter = handler.parsePageStateFromUrl(new URL(`http://example.com${url}`)); + + expect(filter.base.locationName).toBe('Berlin'); + expect(filter.base.samplingDate).toEqual({ + label: 'Custom', + dateFrom: '2024-01-01', + dateTo: '2024-12-31', + }); + expect(filter.base.granularity).toBe('week'); + expect(filter.base.excludeEmpty).toBe(false); + }); + + it('defaults base filter fields when missing from URL', () => { + const url = '/wastewater/covid?analysisMode=manual&sequenceType=nucleotide&'; + const filter = handler.parsePageStateFromUrl(new URL(`http://example.com${url}`)); + + expect(filter.base.locationName).toBe('Zürich (ZH)'); + expect(filter.base.samplingDate).toBeUndefined(); + expect(filter.base.granularity).toBe('day'); + expect(filter.base.excludeEmpty).toBe(true); + }); + + it('encodes excludeEmpty=false in URL but omits when true', () => { + const filterTrue = handler.parsePageStateFromUrl( + new URL('http://example.com/wastewater/covid?analysisMode=manual&sequenceType=nucleotide&'), + ); + const filterFalse = handler.parsePageStateFromUrl( + new URL( + 'http://example.com/wastewater/covid?analysisMode=manual&sequenceType=nucleotide&excludeEmpty=false&', + ), + ); + + const urlTrue = handler.toUrl(filterTrue); + const urlFalse = handler.toUrl(filterFalse); + + expect(urlTrue).not.toContain('excludeEmpty'); + expect(urlFalse).toContain('excludeEmpty=false'); + }); + + it('parses excludeEmpty string "false" as boolean false', () => { + const url = '/wastewater/covid?analysisMode=manual&sequenceType=nucleotide&excludeEmpty=false&'; + const filter = handler.parsePageStateFromUrl(new URL(`http://example.com${url}`)); + + expect(typeof filter.base.excludeEmpty).toBe('boolean'); + expect(filter.base.excludeEmpty).toBe(false); + }); + }); + + describe('manual mode', () => { + it('parses and encodes manual filter', () => { + const url = + '/wastewater/covid?' + + 'locationName=Z%C3%BCrich+%28ZH%29&' + + 'granularity=day&' + + 'analysisMode=manual&' + + 'sequenceType=nucleotide&'; + const filter = handler.parsePageStateFromUrl(new URL(`http://example.com${url}`)); + expect(filter.base.locationName).toBe('Zürich (ZH)'); + expect(filter.base.granularity).toBe('day'); + expect(filter.analysis.mode).toBe('manual'); + expect(filter.analysis.sequenceType).toBe('nucleotide'); + + const newUrl = handler.toUrl(filter); + expect(newUrl).toBe(url); + }); + + it('parses manual mode with multiple mutations', () => { + const url = + '/wastewater/covid?' + + 'analysisMode=manual&' + + 'sequenceType=nucleotide&' + + 'mutations=A23T%7CS:E44H%7CORFla:T123A&'; + const filter = handler.parsePageStateFromUrl(new URL(`http://example.com${url}`)); + + expect(filter.analysis.mode).toBe('manual'); + const analysis = filter.analysis as WasapManualFilter; + expect(analysis.mutations).toEqual(['A23T', 'S:E44H', 'ORFla:T123A']); + }); + + it('manual mode with no mutations specified', () => { + const url = '/wastewater/covid?analysisMode=manual&sequenceType=amino+acid&'; + const filter = handler.parsePageStateFromUrl(new URL(`http://example.com${url}`)); + + expect(filter.analysis.mode).toBe('manual'); + const analysis = filter.analysis as WasapManualFilter; + expect(analysis.sequenceType).toBe('amino acid'); + expect(analysis.mutations).toBeUndefined(); + }); + }); + + describe('variant mode', () => { + it('parses and encodes variant filter with all parameters', () => { + const url = + '/wastewater/covid?' + + 'locationName=Berlin&' + + 'granularity=week&' + + 'analysisMode=variant&' + + 'sequenceType=nucleotide&' + + 'variant=BA.2*&' + + 'minProportion=0.5&' + + 'minCount=10&' + + 'minJaccard=0.6&' + + 'timeFrame=3months&'; + const filter = handler.parsePageStateFromUrl(new URL(`http://example.com${url}`)); + + expect(filter.analysis.mode).toBe('variant'); + const analysis = filter.analysis as WasapVariantFilter; + expect(analysis.variant).toBe('BA.2*'); + expect(typeof analysis.minProportion).toBe('number'); + expect(analysis.minProportion).toBe(0.5); + expect(typeof analysis.minCount).toBe('number'); + expect(analysis.minCount).toBe(10); + expect(typeof analysis.minJaccard).toBe('number'); + expect(analysis.minJaccard).toBe(0.6); + expect(analysis.timeFrame).toBe(VARIANT_TIME_FRAME.threeMonths); + + const newUrl = handler.toUrl(filter); + expect(newUrl).toBe(url); + }); + + it('converts numeric string parameters to numbers', () => { + const url = + '/wastewater/covid?' + + 'analysisMode=variant&' + + 'minProportion=0.5&' + + 'minCount=10&' + + 'minJaccard=0.6&'; + const filter = handler.parsePageStateFromUrl(new URL(`http://example.com${url}`)); + + const analysis = filter.analysis as WasapVariantFilter; + expect(typeof analysis.minProportion).toBe('number'); + expect(typeof analysis.minCount).toBe('number'); + expect(typeof analysis.minJaccard).toBe('number'); + }); + + it('variant mode round-trip preserves numeric precision', () => { + const url = + '/wastewater/covid?' + 'analysisMode=variant&' + 'minProportion=0.123456&' + 'minJaccard=0.789012&'; + const filter1 = handler.parsePageStateFromUrl(new URL(`http://example.com${url}`)); + const url2 = handler.toUrl(filter1); + const filter2 = handler.parsePageStateFromUrl(new URL(`http://example.com${url2}`)); + + const analysis1 = filter1.analysis as WasapVariantFilter; + const analysis2 = filter2.analysis as WasapVariantFilter; + expect(analysis2.minProportion).toBe(analysis1.minProportion); + expect(analysis2.minJaccard).toBe(analysis1.minJaccard); + }); + }); + + describe('resistance mode', () => { + it('parses and encodes resistance filter', () => { + const url = + '/wastewater/covid?' + + 'locationName=Z%C3%BCrich+%28ZH%29&' + + 'granularity=day&' + + 'analysisMode=resistance&' + + 'resistanceSet=3CLpro&'; + const filter = handler.parsePageStateFromUrl(new URL(`http://example.com${url}`)); + + expect(filter.analysis.mode).toBe('resistance'); + const analysis = filter.analysis as WasapResistanceFilter; + expect(analysis.resistanceSet).toBe('3CLpro'); + + const newUrl = handler.toUrl(filter); + expect(newUrl).toBe(url); + }); + + it('resistance mode always uses amino acid sequence type', () => { + const url = '/wastewater/covid?analysisMode=resistance&resistanceSet=3CLpro&sequenceType=nucleotide&'; + const filter = handler.parsePageStateFromUrl(new URL(`http://example.com${url}`)); + + expect(filter.analysis.mode).toBe('resistance'); + const analysis = filter.analysis as WasapResistanceFilter; + expect(analysis.sequenceType).toBe('amino acid'); + }); + }); + + describe('untracked mode', () => { + it('parses and encodes untracked filter with predefined excludeSet', () => { + const url = + '/wastewater/covid?' + + 'locationName=Z%C3%BCrich+%28ZH%29&' + + 'granularity=day&' + + 'analysisMode=untracked&' + + 'sequenceType=nucleotide&' + + 'excludeSet=predefined&'; + const filter = handler.parsePageStateFromUrl(new URL(`http://example.com${url}`)); + + expect(filter.analysis.mode).toBe('untracked'); + const analysis = filter.analysis as WasapUntrackedFilter; + expect(analysis.excludeSet).toBe('predefined'); + expect(analysis.excludeVariants).toBeUndefined(); + + const newUrl = handler.toUrl(filter); + expect(newUrl).toBe(url); + }); + + it('parses and encodes untracked filter with custom excludeSet and variants', () => { + const url = + '/wastewater/covid?' + + 'locationName=Z%C3%BCrich+%28ZH%29&' + + 'granularity=day&' + + 'analysisMode=untracked&' + + 'sequenceType=nucleotide&' + + 'excludeSet=custom&' + + 'excludeVariants=XBB.1.5*%7CBA.2*%7CJN.1&'; + const filter = handler.parsePageStateFromUrl(new URL(`http://example.com${url}`)); + + expect(filter.analysis.mode).toBe('untracked'); + const analysis = filter.analysis as WasapUntrackedFilter; + expect(analysis.excludeSet).toBe('custom'); + expect(analysis.excludeVariants).toEqual(['XBB.1.5*', 'BA.2*', 'JN.1']); + + const newUrl = handler.toUrl(filter); + expect(newUrl).toBe(url); + }); + + it('untracked mode round-trip with pipe-separated variants', () => { + const url = + '/wastewater/covid?' + + 'analysisMode=untracked&' + + 'sequenceType=nucleotide&' + + 'excludeSet=custom&' + + 'excludeVariants=XBB*%7CBA.2*%7CJN.1%7CXFG*&'; + const filter1 = handler.parsePageStateFromUrl(new URL(`http://example.com${url}`)); + const url2 = handler.toUrl(filter1); + const filter2 = handler.parsePageStateFromUrl(new URL(`http://example.com${url2}`)); + + const analysis1 = filter1.analysis as WasapUntrackedFilter; + const analysis2 = filter2.analysis as WasapUntrackedFilter; + expect(analysis2.excludeVariants).toEqual(analysis1.excludeVariants); + }); + }); +}); diff --git a/website/src/views/pageStateHandlers/WasapPageStateHandler.ts b/website/src/views/pageStateHandlers/WasapPageStateHandler.ts index e4c17247e..d16b8e31f 100644 --- a/website/src/views/pageStateHandlers/WasapPageStateHandler.ts +++ b/website/src/views/pageStateHandlers/WasapPageStateHandler.ts @@ -1,12 +1,13 @@ -import { type SequenceType } from '@genspectrum/dashboard-components/util'; +import { type SequenceType, type TemporalGranularity } from '@genspectrum/dashboard-components/util'; import { type PageStateHandler } from './PageStateHandler'; import { parseDateRangesFromUrl, setSearchFromDateRange } from './dateFilterFromToUrl'; import { parseTextFiltersFromUrl } from './textFilterFromToUrl'; -import { type BaselineFilterConfig } from '../../components/pageStateSelectors/BaselineSelector'; +import type { BaselineFilterConfig } from '../../components/pageStateSelectors/BaselineSelector'; import { enabledAnalysisModes, type ExcludeSetName, + type VariantTimeFrame, type WasapAnalysisFilter, type WasapAnalysisMode, type WasapBaseFilter, @@ -62,6 +63,9 @@ export class WasapPageStateHandler implements PageStateHandler { minProportion: Number(texts.minProportion ?? this.config.filterDefaults.variant.minProportion), minCount: Number(texts.minCount ?? this.config.filterDefaults.variant.minCount), minJaccard: Number(texts.minJaccard ?? this.config.filterDefaults.variant.minJaccard), + timeFrame: + (texts.timeFrame as VariantTimeFrame | undefined) ?? + this.config.filterDefaults.variant.timeFrame, }; break; case 'resistance': @@ -92,7 +96,7 @@ export class WasapPageStateHandler implements PageStateHandler { const base: WasapBaseFilter = { locationName: texts.locationName ?? this.config.defaultLocationName, samplingDate: dateRanges.samplingDate, - granularity: texts.granularity ?? 'day', + granularity: (texts.granularity as TemporalGranularity | undefined) ?? 'day', excludeEmpty: texts.excludeEmpty !== 'false', }; @@ -126,6 +130,7 @@ export class WasapPageStateHandler implements PageStateHandler { setSearchFromString(search, 'minProportion', String(analysis.minProportion)); setSearchFromString(search, 'minCount', String(analysis.minCount)); setSearchFromString(search, 'minJaccard', String(analysis.minJaccard)); + setSearchFromString(search, 'timeFrame', analysis.timeFrame); break; case 'resistance': setSearchFromString(search, 'resistanceSet', analysis.resistanceSet); @@ -195,6 +200,10 @@ function generateWasapFilterConfig(pageConfig: WasapPageConfig): BaselineFilterC type: 'text', lapisField: 'minJaccard', }, + { + type: 'text', + lapisField: 'timeFrame', + }, { type: 'text', lapisField: 'resistanceSet', diff --git a/website/tests/CompareSideBySidePage.ts b/website/tests/CompareSideBySidePage.ts new file mode 100644 index 000000000..3b93102a0 --- /dev/null +++ b/website/tests/CompareSideBySidePage.ts @@ -0,0 +1,50 @@ +import { expect } from '@playwright/test'; + +import { ViewPage } from './ViewPage.ts'; +import { paths } from '../src/types/Organism.ts'; +import { type OrganismWithViewKey } from '../src/views/routing'; +import { compareSideBySideViewKey } from '../src/views/viewKeys'; + +export class CompareSideBySidePage extends ViewPage { + public readonly removeColumnButton = this.page.getByRole('link', { name: 'Remove column' }); + public readonly addColumnButton = this.page.getByRole('link', { name: 'Add column' }); + public readonly mutationField = this.page.getByRole('combobox', { name: 'Enter a mutation', exact: false }); + + public async goto(organism: OrganismWithViewKey) { + await this.page.goto(`${paths[organism].basePath}/compare-side-by-side`); + } + + public async addColumn(options: { + dateRangeOption: string; + expectedColumnCount: number; + lineage?: string; + lineageFieldPlaceholder?: string; + mutation?: string; + }) { + await expect(this.addColumnButton).toBeVisible(); + + await this.addColumnButton.click(); + await expect(this.mutationField).toHaveCount(options.expectedColumnCount); + + await this.page + .locator('gs-date-range-filter') + .getByRole('combobox') + .last() + .selectOption(options.dateRangeOption); + + if (options.lineage && options.lineageFieldPlaceholder) { + const { lineage, lineageFieldPlaceholder } = options; + + const lineageFieldLocator = this.page.getByPlaceholder(lineageFieldPlaceholder); + await expect(lineageFieldLocator).toHaveCount(options.expectedColumnCount); + + await this.fillLineageField(lineageFieldLocator.last(), lineage); + } + + if (options.mutation) { + await this.fillMutationField(this.mutationField.last(), options.mutation); + } + + await this.page.getByRole('button', { name: 'Apply filters' }).last().click(); + } +} diff --git a/website/tests/CompareToBaselinePage.ts b/website/tests/CompareToBaselinePage.ts new file mode 100644 index 000000000..a5a091b3b --- /dev/null +++ b/website/tests/CompareToBaselinePage.ts @@ -0,0 +1,34 @@ +import { type Page } from '@playwright/test'; + +import { CompareVariantsPage } from './CompareVariantsPage.ts'; +import { paths } from '../src/types/Organism.ts'; +import { type OrganismWithViewKey } from '../src/views/routing'; +import { compareToBaselineViewKey } from '../src/views/viewKeys'; + +export class CompareToBaselinePage extends CompareVariantsPage { + public readonly selectVariantsMessage; + public readonly mutationField; + + constructor(page: Page) { + super(page); + this.selectVariantsMessage = this.page.getByText('Analyze a variant compared to a baseline', { exact: false }); + this.mutationField = this.page.getByRole('combobox', { name: 'Enter a mutation', exact: false }); + } + + public async goto(organism: OrganismWithViewKey) { + await this.page.goto(`${paths[organism].basePath}/compare-to-baseline`); + } + + public async selectBaseline(options: { lineage?: string; lineageFieldPlaceholder?: string; mutation?: string }) { + if (options.lineage && options.lineageFieldPlaceholder) { + const { lineage, lineageFieldPlaceholder } = options; + + const lineageFieldLocator = this.page.getByPlaceholder(lineageFieldPlaceholder); + await this.fillLineageField(lineageFieldLocator.first(), lineage); + } + + if (options.mutation) { + await this.fillMutationField(this.mutationField.first(), options.mutation); + } + } +} diff --git a/website/tests/CompareVariantsPage.ts b/website/tests/CompareVariantsPage.ts index 16b7585aa..d566f074c 100644 --- a/website/tests/CompareVariantsPage.ts +++ b/website/tests/CompareVariantsPage.ts @@ -33,15 +33,11 @@ export class CompareVariantsPage extends ViewPage { const lineageFieldLocator = this.page.getByPlaceholder(lineageFieldPlaceholder); await expect(lineageFieldLocator).toHaveCount(numberMutationFields + 1); - await lineageFieldLocator.last().fill(lineage); - - const selectedLineage = this.page.getByRole('option', { name: lineage, exact: false }); - await selectedLineage.first().click(); + await this.fillLineageField(lineageFieldLocator.last(), lineage); } if (options.mutation) { - await this.mutationField.last().fill(options.mutation); - await this.mutationField.last().press('Enter'); + await this.fillMutationField(this.mutationField.last(), options.mutation); } } } diff --git a/website/tests/ViewPage.ts b/website/tests/ViewPage.ts index be146eb78..43ee2549e 100644 --- a/website/tests/ViewPage.ts +++ b/website/tests/ViewPage.ts @@ -1,4 +1,4 @@ -import { expect, type Page } from '@playwright/test'; +import { expect, type Locator, type Page } from '@playwright/test'; import { type Organism } from '../src/types/Organism.ts'; @@ -23,4 +23,15 @@ export abstract class ViewPage { public async selectDateRange(dateRangeOption: string) { await this.page.locator('gs-date-range-filter').getByRole('combobox').first().selectOption(dateRangeOption); } + + public async fillLineageField(locator: Locator, lineage: string) { + await locator.fill(lineage); + const selectedLineage = this.page.getByRole('option', { name: lineage, exact: false }); + await selectedLineage.first().click(); + } + + public async fillMutationField(locator: Locator, mutation: string) { + await locator.first().fill(mutation); + await locator.first().press('Enter'); + } } diff --git a/website/tests/compareSideBySide.spec.ts b/website/tests/compareSideBySide.spec.ts new file mode 100644 index 000000000..102df74d4 --- /dev/null +++ b/website/tests/compareSideBySide.spec.ts @@ -0,0 +1,40 @@ +import { expect } from '@playwright/test'; + +import { test } from './e2e.fixture.ts'; +import { organismOptions, organismsWithView } from './helpers.ts'; +import { compareSideBySideViewKey } from '../src/views/viewKeys'; + +test.describe('The Compare Side-By-Side page', () => { + for (const organism of organismsWithView(compareSideBySideViewKey)) { + test(`should show diagrams after selecting a variant in both columns - ${organism}`, async ({ + compareSideBySidePage, + }) => { + test.setTimeout(60_000); // there is a lot of data to load, so we need the test to run longer + + const options = organismOptions[organism]; + + await compareSideBySidePage.goto(organism); + await expect(compareSideBySidePage.removeColumnButton).not.toBeVisible(); + await expect(compareSideBySidePage.addColumnButton).toBeVisible(); + + await expect(compareSideBySidePage.diagramTitle('Prevalence Over Time')).not.toBeVisible(); + + await compareSideBySidePage.addColumn({ + dateRangeOption: 'All times', + expectedColumnCount: 1, + ...options, + }); + await expect(compareSideBySidePage.removeColumnButton).not.toBeVisible(); + + await compareSideBySidePage.addColumn({ + dateRangeOption: 'All times', + expectedColumnCount: 2, + ...options, + }); + await expect(compareSideBySidePage.removeColumnButton).toHaveCount(2); + + await expect(compareSideBySidePage.diagramTitle('Prevalence Over Time')).toHaveCount(2); + await compareSideBySidePage.expectToSeeNoComponentErrors(); + }); + } +}); diff --git a/website/tests/compareToBaseline.spec.ts b/website/tests/compareToBaseline.spec.ts new file mode 100644 index 000000000..b78929117 --- /dev/null +++ b/website/tests/compareToBaseline.spec.ts @@ -0,0 +1,27 @@ +import { expect } from '@playwright/test'; + +import { test } from './e2e.fixture.ts'; +import { organismOptions, organismsWithView } from './helpers.ts'; +import { compareToBaselineViewKey } from '../src/views/viewKeys'; + +test.describe('The Compare To Baseline page', () => { + for (const organism of organismsWithView(compareToBaselineViewKey)) { + test(`should show diagrams after selecting two variants and a baseline - ${organism}`, async ({ + compareToBaselinePage, + }) => { + const options = organismOptions[organism]; + + await compareToBaselinePage.goto(organism); + await expect(compareToBaselinePage.selectVariantsMessage).toBeVisible(); + await expect(compareToBaselinePage.diagramTitle('Prevalence Over Time')).not.toBeVisible(); + + await compareToBaselinePage.selectDateRange('All times'); + await compareToBaselinePage.selectBaseline(options); + await compareToBaselinePage.addVariant(options); + await compareToBaselinePage.submitFilters(); + + await expect(compareToBaselinePage.diagramTitle('Prevalence Over Time')).toBeVisible(); + await compareToBaselinePage.expectToSeeNoComponentErrors(); + }); + } +}); diff --git a/website/tests/compareVariants.spec.ts b/website/tests/compareVariants.spec.ts index eef8b89c7..8ffbdd53f 100644 --- a/website/tests/compareVariants.spec.ts +++ b/website/tests/compareVariants.spec.ts @@ -1,30 +1,9 @@ import { expect } from '@playwright/test'; import { test } from './e2e.fixture.ts'; -import { organismsWithView } from './helpers.ts'; -import { Organisms } from '../src/types/Organism.ts'; +import { organismOptions, organismsWithView } from './helpers.ts'; import { compareVariantsViewKey } from '../src/views/viewKeys'; -const organismOptions = { - [Organisms.covid]: { lineage: 'JN.1*', lineageFieldPlaceholder: 'Nextclade pango lineage' }, - [Organisms.h5n1]: { lineage: '2.3.4.4b', lineageFieldPlaceholder: 'Clade' }, - [Organisms.h1n1pdm]: { lineage: '6B.1A.5a.2a.1', lineageFieldPlaceholder: 'Clade HA' }, - [Organisms.h3n2]: { lineage: '3C.2a1b', lineageFieldPlaceholder: 'Clade HA' }, - [Organisms.victoria]: { lineage: 'V1A.3a.2', lineageFieldPlaceholder: 'Clade HA' }, - [Organisms.westNile]: { lineage: '1A', lineageFieldPlaceholder: 'Lineage' }, - [Organisms.rsvA]: { lineage: 'A.D.5.2', lineageFieldPlaceholder: 'Lineage' }, - [Organisms.rsvB]: { lineage: 'B.D.E.1', lineageFieldPlaceholder: 'Lineage' }, - [Organisms.mpox]: { lineage: 'F.1', lineageFieldPlaceholder: 'Lineage' }, - [Organisms.ebolaSudan]: { mutation: 'G5902T' }, - [Organisms.ebolaZaire]: { mutation: 'T18365C' }, - [Organisms.cchf]: { mutation: 'M:G3565A' }, - [Organisms.denv1]: { lineage: '1I_K.1.1', lineageFieldPlaceholder: 'Clade' }, - [Organisms.denv2]: { lineage: '2II_F.1.1', lineageFieldPlaceholder: 'Clade' }, - [Organisms.denv3]: { lineage: '3III_B.3.2', lineageFieldPlaceholder: 'Clade' }, - [Organisms.denv4]: { lineage: '4II_B.1.2', lineageFieldPlaceholder: 'Clade' }, - [Organisms.measles]: { lineage: 'B3', lineageFieldPlaceholder: 'Genotype' }, -}; - test.describe('The Compare Variants page', () => { for (const organism of organismsWithView(compareVariantsViewKey)) { test(`should show diagrams after selecting two variants ${organism}`, async ({ compareVariantsPage }) => { diff --git a/website/tests/e2e.fixture.ts b/website/tests/e2e.fixture.ts index 561452517..3e4dbf6dc 100644 --- a/website/tests/e2e.fixture.ts +++ b/website/tests/e2e.fixture.ts @@ -1,11 +1,15 @@ import { test as base } from '@playwright/test'; +import { CompareSideBySidePage } from './CompareSideBySidePage.ts'; +import { CompareToBaselinePage } from './CompareToBaselinePage.ts'; import { CompareVariantsPage } from './CompareVariantsPage.ts'; import { SequencingEffortsPage } from './SequencingEffortsPage.ts'; type E2EFixture = { compareVariantsPage: CompareVariantsPage; sequencingEffortsPage: SequencingEffortsPage; + compareToBaselinePage: CompareToBaselinePage; + compareSideBySidePage: CompareSideBySidePage; }; export const test = base.extend({ @@ -15,4 +19,10 @@ export const test = base.extend({ sequencingEffortsPage: async ({ page }, use) => { await use(new SequencingEffortsPage(page)); }, + compareToBaselinePage: async ({ page }, use) => { + await use(new CompareToBaselinePage(page)); + }, + compareSideBySidePage: async ({ page }, use) => { + await use(new CompareSideBySidePage(page)); + }, }); diff --git a/website/tests/helpers.ts b/website/tests/helpers.ts index 55120cbc5..5dfda525f 100644 --- a/website/tests/helpers.ts +++ b/website/tests/helpers.ts @@ -1,7 +1,29 @@ -import { allOrganisms } from '../src/types/Organism.ts'; +import { allOrganisms, Organisms } from '../src/types/Organism.ts'; import type { OrganismWithViewKey } from '../src/views/routing.ts'; import { ServerSide } from '../src/views/serverSideRouting.ts'; export function organismsWithView(viewKey: ViewKey): OrganismWithViewKey[] { return allOrganisms.filter((organism) => ServerSide.routing.isOrganismWithViewKey(organism, viewKey)); } + +export const organismOptions = { + [Organisms.covid]: { lineage: 'JN.1*', lineageFieldPlaceholder: 'Nextclade pango lineage' }, + [Organisms.influenzaA]: { lineage: 'H1', lineageFieldPlaceholder: 'HA subtype' }, + [Organisms.h5n1]: { lineage: '2.3.4.4b', lineageFieldPlaceholder: 'Clade' }, + [Organisms.h1n1pdm]: { lineage: '6B.1A.5a.2a.1', lineageFieldPlaceholder: 'Clade HA' }, + [Organisms.h3n2]: { lineage: '3C.2a1b', lineageFieldPlaceholder: 'Clade HA' }, + [Organisms.influenzaB]: { lineage: 'vic', lineageFieldPlaceholder: 'Lineage' }, + [Organisms.victoria]: { lineage: 'V1A.3a.2', lineageFieldPlaceholder: 'Clade HA' }, + [Organisms.westNile]: { lineage: '1A', lineageFieldPlaceholder: 'Lineage' }, + [Organisms.rsvA]: { lineage: 'A.D.5.2', lineageFieldPlaceholder: 'Lineage' }, + [Organisms.rsvB]: { lineage: 'B.D.E.1', lineageFieldPlaceholder: 'Lineage' }, + [Organisms.mpox]: { lineage: 'F.1', lineageFieldPlaceholder: 'Lineage' }, + [Organisms.ebolaSudan]: { mutation: 'G5902T' }, + [Organisms.ebolaZaire]: { mutation: 'T18365C' }, + [Organisms.cchf]: { mutation: 'M:G3565A' }, + [Organisms.denv1]: { lineage: '1I_K.1.1', lineageFieldPlaceholder: 'Clade' }, + [Organisms.denv2]: { lineage: '2II_F.1.1', lineageFieldPlaceholder: 'Clade' }, + [Organisms.denv3]: { lineage: '3III_B.3.2', lineageFieldPlaceholder: 'Clade' }, + [Organisms.denv4]: { lineage: '4II_B.1.2', lineageFieldPlaceholder: 'Clade' }, + [Organisms.measles]: { lineage: 'B3', lineageFieldPlaceholder: 'Genotype' }, +};