diff --git a/.github/workflows/check-links.yml b/.github/workflows/check-links.yml new file mode 100644 index 000000000..4e7a57eb6 --- /dev/null +++ b/.github/workflows/check-links.yml @@ -0,0 +1,38 @@ +name: Check Links + +on: + pull_request: + branches: [main] + push: + branches: [main] + +jobs: + check-links: + name: Check Internal Links + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v6 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 10 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: '22' + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build static site + run: pnpm build + env: + NODE_OPTIONS: --max-old-space-size=8192 + + - name: Check for broken links + run: node --import tsx scripts/check-broken-links.ts diff --git a/app/posts/[slug]/page.tsx b/app/posts/[slug]/page.tsx index a542b2363..4e9ded007 100644 --- a/app/posts/[slug]/page.tsx +++ b/app/posts/[slug]/page.tsx @@ -64,7 +64,6 @@ export async function generateMetadata({ ], publishedTime: post.publishedAt || post.date, modifiedTime: post.updatedAt || post.date, - authors: post.author ? [`https://devops-daily.com/authors/${post.author.slug}`] : undefined, section: post.category?.name, tags: post.tags, }, diff --git a/components/post-header.tsx b/components/post-header.tsx index 6d70d030a..88419d187 100644 --- a/components/post-header.tsx +++ b/components/post-header.tsx @@ -1,6 +1,6 @@ import Link from 'next/link'; import { Badge } from '@/components/ui/badge'; -import { Clock, Calendar, User, Info } from 'lucide-react'; +import { Clock, Calendar, Info } from 'lucide-react'; interface PostHeaderProps { post: { @@ -34,12 +34,6 @@ export function PostHeader({ post, hasAffiliateLinks = false }: PostHeaderProps) {post.readingTime} - {post.author && ( -
- - {post.author.name} -
- )}

{post.title}

{hasAffiliateLinks && ( diff --git a/content/exercises/terraform-digitalocean-droplet.json b/content/exercises/terraform-digitalocean-droplet.json index cdd3b1caf..9dc79ba14 100644 --- a/content/exercises/terraform-digitalocean-droplet.json +++ b/content/exercises/terraform-digitalocean-droplet.json @@ -247,11 +247,6 @@ "url": "https://www.terraform.io/docs/cloud/guides/recommended-practices/index.html", "type": "guide", "external": true - }, - { - "title": "Infrastructure as Code Fundamentals", - "url": "/posts/infrastructure-as-code-fundamentals", - "type": "tutorial" } ], "troubleshooting": [ diff --git a/content/news/2026/week-4.md b/content/news/2026/week-4.md index 870c250cc..b3651103b 100644 --- a/content/news/2026/week-4.md +++ b/content/news/2026/week-4.md @@ -184,7 +184,7 @@ On 2025-12-26, at 07:35 UTC, the SSL certificates for many *.bazel.build domains **šŸ“… Jan 16, 2026** • **šŸ“° Bazel Blog** -[**šŸ”— Read more**](/2026/01/16/ssl-cert-expiry.html) +[**šŸ”— Read more**](https://blog.bazel.build/2026/01/16/ssl-cert-expiry.html) ### šŸ“„ Building an agentic memory system for GitHub Copilot diff --git a/content/news/2026/week-5.md b/content/news/2026/week-5.md index 73603d26d..6e61f0449 100644 --- a/content/news/2026/week-5.md +++ b/content/news/2026/week-5.md @@ -232,7 +232,7 @@ We're pleased to announce the release of Bazel 9.0! This LTS release is the culm **šŸ“… Jan 20, 2026** • **šŸ“° Bazel Blog** -[**šŸ”— Read more**](/2026/01/20/bazel-9.html) +[**šŸ”— Read more**](https://blog.bazel.build/2026/01/20/bazel-9.html) --- diff --git a/content/news/2026/week-9.md b/content/news/2026/week-9.md index 59020be49..d71d2d925 100644 --- a/content/news/2026/week-9.md +++ b/content/news/2026/week-9.md @@ -152,7 +152,7 @@ We're thrilled to announce Bazel support in Dependabot Version Updates! šŸŽ‰ Thr **šŸ“… Feb 20, 2026** • **šŸ“° Bazel Blog** -[**šŸ”— Read more**](/2026/02/20/dependabot.html) +[**šŸ”— Read more**](https://blog.bazel.build/2026/02/20/dependabot.html) ### šŸ“„ How AI is reshaping developer choice (and Octoverse data proves it) diff --git a/content/posts/deployment-strategies-guide.md b/content/posts/deployment-strategies-guide.md index 80d6de92f..0ca7e8fbe 100644 --- a/content/posts/deployment-strategies-guide.md +++ b/content/posts/deployment-strategies-guide.md @@ -15,7 +15,7 @@ tags: - DevOps - Deployment - Kubernetes - - CI/CD + - CICD - Blue-Green Deployment - Canary Deployment - Rolling Deployment diff --git a/content/posts/designing-automation-with-failure-in-mind.md b/content/posts/designing-automation-with-failure-in-mind.md index 98629b781..c1f491c98 100644 --- a/content/posts/designing-automation-with-failure-in-mind.md +++ b/content/posts/designing-automation-with-failure-in-mind.md @@ -13,7 +13,7 @@ author: slug: 'devops-daily-team' tags: - DevOps - - CI/CD + - CICD - Kubernetes - Terraform - SRE diff --git a/content/posts/detect-network-connection-type-android.md b/content/posts/detect-network-connection-type-android.md index ccdfe7b81..e1d31e5d1 100644 --- a/content/posts/detect-network-connection-type-android.md +++ b/content/posts/detect-network-connection-type-android.md @@ -2,8 +2,8 @@ title: 'How to Detect Network Connection Type on Android' excerpt: "Learn how to detect WiFi, cellular, and other network types in Android apps using ConnectivityManager, NetworkCapabilities, and handle network changes with callbacks." category: - name: 'Android' - slug: 'android' + name: 'Networking' + slug: 'networking' date: '2024-11-05' publishedAt: '2024-11-05T12:00:00Z' updatedAt: '2024-11-05T12:00:00Z' diff --git a/content/posts/find-ssh-client-ip-address.md b/content/posts/find-ssh-client-ip-address.md index 38b462fc0..28c725d98 100644 --- a/content/posts/find-ssh-client-ip-address.md +++ b/content/posts/find-ssh-client-ip-address.md @@ -2,8 +2,8 @@ title: 'How to Find the IP Address of an SSH Client' excerpt: "Learn multiple ways to identify the IP address of clients connected to your SSH server, from environment variables to logs and active connection monitoring." category: - name: 'SSH' - slug: 'ssh' + name: 'Networking' + slug: 'networking' date: '2024-12-09' publishedAt: '2024-12-09T14:00:00Z' updatedAt: '2024-12-09T14:00:00Z' diff --git a/content/posts/gitops-deploy-docker-containers-github-actions-argocd.md b/content/posts/gitops-deploy-docker-containers-github-actions-argocd.md index 348fe2045..f61deb789 100644 --- a/content/posts/gitops-deploy-docker-containers-github-actions-argocd.md +++ b/content/posts/gitops-deploy-docker-containers-github-actions-argocd.md @@ -14,7 +14,7 @@ author: tags: - Docker - GitHub Actions - - CI/CD + - CICD - GitOps - ArgoCD - Kubernetes diff --git a/content/posts/how-to-close-tcp-and-udp-ports-via-windows-command-line.md b/content/posts/how-to-close-tcp-and-udp-ports-via-windows-command-line.md index 650437ad1..f32394dad 100644 --- a/content/posts/how-to-close-tcp-and-udp-ports-via-windows-command-line.md +++ b/content/posts/how-to-close-tcp-and-udp-ports-via-windows-command-line.md @@ -2,8 +2,8 @@ title: 'How to Close TCP and UDP Ports via Windows Command Line' excerpt: 'Learn how to close open ports on Windows using command-line tools. Find and terminate processes listening on ports, manage Windows Firewall rules, and stop services to free up ports.' category: - name: 'Windows' - slug: 'windows' + name: 'Networking' + slug: 'networking' date: '2025-03-28' publishedAt: '2025-03-28T13:00:00Z' updatedAt: '2025-03-28T13:00:00Z' diff --git a/content/posts/introduction-to-argocd.md b/content/posts/introduction-to-argocd.md index bfffab693..d89e28500 100644 --- a/content/posts/introduction-to-argocd.md +++ b/content/posts/introduction-to-argocd.md @@ -16,7 +16,7 @@ tags: - GitOps - ArgoCD - Kubernetes - - CI/CD + - CICD - Automation - Deployment --- diff --git a/content/posts/ssh-could-not-resolve-hostname-error.md b/content/posts/ssh-could-not-resolve-hostname-error.md index 8b29a8a8b..a031e4587 100644 --- a/content/posts/ssh-could-not-resolve-hostname-error.md +++ b/content/posts/ssh-could-not-resolve-hostname-error.md @@ -2,8 +2,8 @@ title: 'How to Fix SSH "Could Not Resolve Hostname" Error' excerpt: "Troubleshoot and fix the SSH 'nodename nor servname provided, or not known' error with DNS checks, host file configuration, and SSH config debugging." category: - name: 'SSH' - slug: 'ssh' + name: 'Networking' + slug: 'networking' date: '2025-01-18' publishedAt: '2025-01-18T09:30:00Z' updatedAt: '2025-01-18T09:30:00Z' diff --git a/content/posts/terraform-save-plan-apply-output-to-file.md b/content/posts/terraform-save-plan-apply-output-to-file.md index c1b3175ed..ed6b78d6f 100644 --- a/content/posts/terraform-save-plan-apply-output-to-file.md +++ b/content/posts/terraform-save-plan-apply-output-to-file.md @@ -14,7 +14,7 @@ author: tags: - Terraform - Infrastructure as Code - - CI/CD + - CICD - Best Practices - DevOps --- diff --git a/content/posts/using-ls-list-directories-sizes.md b/content/posts/using-ls-list-directories-sizes.md index 74e7dc163..da3d1a4b6 100644 --- a/content/posts/using-ls-list-directories-sizes.md +++ b/content/posts/using-ls-list-directories-sizes.md @@ -2,8 +2,8 @@ title: 'Using ls to list directories and their total sizes' excerpt: 'How to show directory sizes from the shell - practical commands for macOS and Linux, and why `ls` alone is not enough.' category: - name: 'Shell' - slug: 'shell' + name: 'Bash' + slug: 'bash' date: '2025-04-22' publishedAt: '2025-04-22T09:00:00Z' updatedAt: '2025-04-22T09:00:00Z' diff --git a/content/posts/which-postgresql-version-am-i-running.md b/content/posts/which-postgresql-version-am-i-running.md index becf75da6..6d6aa3017 100644 --- a/content/posts/which-postgresql-version-am-i-running.md +++ b/content/posts/which-postgresql-version-am-i-running.md @@ -2,8 +2,8 @@ title: 'How to Check Which Version of PostgreSQL You Are Running' excerpt: "Learn multiple ways to check your PostgreSQL version, including psql commands, SQL queries, and system commands. Find version numbers from the server, client, and package manager." category: - name: 'Database' - slug: 'database' + name: 'Linux' + slug: 'linux' date: '2024-12-15' publishedAt: '2024-12-15T09:00:00Z' updatedAt: '2024-12-15T09:00:00Z' diff --git a/content/posts/why-c-preprocessor-interprets-linux-as-one.md b/content/posts/why-c-preprocessor-interprets-linux-as-one.md index 4b4ff08f9..58bc64e72 100644 --- a/content/posts/why-c-preprocessor-interprets-linux-as-one.md +++ b/content/posts/why-c-preprocessor-interprets-linux-as-one.md @@ -2,8 +2,8 @@ title: 'Why Does the C Preprocessor Interpret "linux" as "1"?' excerpt: "Discover why the word 'linux' is predefined as the constant 1 in the C preprocessor on Linux systems, and how this historical quirk can cause unexpected compilation errors." category: - name: 'C' - slug: 'c' + name: 'Linux' + slug: 'linux' date: '2025-05-22' publishedAt: '2025-05-22T09:00:00Z' updatedAt: '2025-05-22T09:00:00Z' diff --git a/content/posts/why-your-ci-pipeline-is-slower.md b/content/posts/why-your-ci-pipeline-is-slower.md index 633b7a7a9..80d30701f 100644 --- a/content/posts/why-your-ci-pipeline-is-slower.md +++ b/content/posts/why-your-ci-pipeline-is-slower.md @@ -1,12 +1,16 @@ --- title: 'Why Your CI/CD Pipeline Is Slower Than It Should Be (and How to Fix It)' excerpt: 'Small pipeline changes give big wins. Parallelize jobs, cache dependencies, pin images, reuse build artifacts, and run only the tests you need.' -category: 'ci' +category: + name: 'DevOps' + slug: 'devops' date: '2025-06-10T09:00:00.000Z' publishedAt: '2025-06-10T09:00:00.000Z' updatedAt: '2025-06-10T09:00:00.000Z' readingTime: 4 -author: 'DevOps Daily' +author: + name: 'DevOps Daily Team' + slug: 'devops-daily-team' tags: - ci - cicd diff --git a/package.json b/package.json index 2337f4b84..635d7e64a 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,8 @@ "devops-daily:generate-news:no-ai": "tsx scripts/devops-daily/index.ts --skip-ai", "og:validate": "tsx scripts/git-hooks/validate-og-data.ts", "og:validate:staged": "tsx scripts/git-hooks/validate-og-data.ts --staged-only", - "hooks:install": "sh scripts/git-hooks/install.sh" + "hooks:install": "sh scripts/git-hooks/install.sh", + "check-links": "node --import tsx scripts/check-broken-links.ts" }, "dependencies": { "@dnd-kit/core": "^6.3.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 39cdf2ab9..77e918e36 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -293,10 +293,13 @@ importers: version: 5.9.3 vitest: specifier: ^4.0.18 - version: 4.0.18(@types/node@25.2.3)(@vitest/ui@4.0.18)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(tsx@4.21.0) + version: 4.0.18(@types/node@25.2.3)(@vitest/ui@4.0.18)(jiti@2.6.1)(jsdom@28.1.0(canvas@3.2.1))(lightningcss@1.30.2)(terser@5.46.0)(tsx@4.21.0) packages: + '@acemir/cssom@0.9.31': + resolution: {integrity: sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==} + '@alloc/quick-lru@5.2.0': resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} @@ -307,6 +310,16 @@ packages: peerDependencies: ajv: '>=8' + '@asamuzakjp/css-color@5.0.1': + resolution: {integrity: sha512-2SZFvqMyvboVV1d15lMf7XiI3m7SDqXUuKaTymJYLN6dSGadqp+fVojqJlVoMlbZnlTmu3S0TLwLTJpvBMO1Aw==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + '@asamuzakjp/dom-selector@6.8.1': + resolution: {integrity: sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==} + + '@asamuzakjp/nwsapi@2.3.9': + resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} + '@babel/code-frame@7.29.0': resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} engines: {node: '>=6.9.0'} @@ -802,6 +815,41 @@ packages: resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} + '@bramus/specificity@2.4.2': + resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==} + hasBin: true + + '@csstools/color-helpers@6.0.2': + resolution: {integrity: sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==} + engines: {node: '>=20.19.0'} + + '@csstools/css-calc@3.1.1': + resolution: {integrity: sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-parser-algorithms': ^4.0.0 + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/css-color-parser@4.0.2': + resolution: {integrity: sha512-0GEfbBLmTFf0dJlpsNU7zwxRIH0/BGEMuXLTCvFYxuL1tNhqzTbtnFICyJLTNK4a+RechKP75e7w42ClXSnJQw==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-parser-algorithms': ^4.0.0 + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/css-parser-algorithms@4.0.0': + resolution: {integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/css-syntax-patches-for-csstree@1.0.28': + resolution: {integrity: sha512-1NRf1CUBjnr3K7hu8BLxjQrKCxEe8FP/xmPTenAxCRZWVLbmGotkFvG9mfNpjA6k7Bw1bw4BilZq9cu19RA5pg==} + + '@csstools/css-tokenizer@4.0.0': + resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} + engines: {node: '>=20.19.0'} + '@date-fns/tz@1.4.1': resolution: {integrity: sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA==} @@ -1036,6 +1084,15 @@ packages: resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@exodus/bytes@1.14.1': + resolution: {integrity: sha512-OhkBFWI6GcRMUroChZiopRiSp2iAMvEBK47NhJooDqz1RERO4QuZIZnjP63TXX8GAiLABkYmX+fuQsdJ1dd2QQ==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + peerDependencies: + '@noble/hashes': ^1.8.0 || ^2.0.0 + peerDependenciesMeta: + '@noble/hashes': + optional: true + '@floating-ui/core@1.7.4': resolution: {integrity: sha512-C3HlIdsBxszvm5McXlB8PeOEWfBhcGBTZGkGlWc2U0KFY5IwG5OQEuQ8rq52DZmcHDlPLd+YFBK+cZcytwIFWg==} @@ -1108,89 +1165,105 @@ packages: resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} cpu: [arm64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-arm@1.2.4': resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} cpu: [arm] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-ppc64@1.2.4': resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} cpu: [ppc64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-riscv64@1.2.4': resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} cpu: [riscv64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-s390x@1.2.4': resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} cpu: [s390x] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-x64@1.2.4': resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} cpu: [x64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linuxmusl-arm64@1.2.4': resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} cpu: [arm64] os: [linux] + libc: [musl] '@img/sharp-libvips-linuxmusl-x64@1.2.4': resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} cpu: [x64] os: [linux] + libc: [musl] '@img/sharp-linux-arm64@0.34.5': resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + libc: [glibc] '@img/sharp-linux-arm@0.34.5': resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] + libc: [glibc] '@img/sharp-linux-ppc64@0.34.5': resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ppc64] os: [linux] + libc: [glibc] '@img/sharp-linux-riscv64@0.34.5': resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [riscv64] os: [linux] + libc: [glibc] '@img/sharp-linux-s390x@0.34.5': resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] + libc: [glibc] '@img/sharp-linux-x64@0.34.5': resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + libc: [glibc] '@img/sharp-linuxmusl-arm64@0.34.5': resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + libc: [musl] '@img/sharp-linuxmusl-x64@0.34.5': resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + libc: [musl] '@img/sharp-wasm32@0.34.5': resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} @@ -1272,24 +1345,28 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] '@next/swc-linux-arm64-musl@16.1.6': resolution: {integrity: sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [musl] '@next/swc-linux-x64-gnu@16.1.6': resolution: {integrity: sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] '@next/swc-linux-x64-musl@16.1.6': resolution: {integrity: sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [musl] '@next/swc-win32-arm64-msvc@16.1.6': resolution: {integrity: sha512-gQmm8izDTPgs+DCWH22kcDmuUp7NyiJgEl18bcr8irXA5N2m2O+JQIr6f3ct42GOs9c0h8QF3L5SzIxcYAAXXw==} @@ -2047,24 +2124,28 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] '@resvg/resvg-js-linux-arm64-musl@2.6.2': resolution: {integrity: sha512-3h3dLPWNgSsD4lQBJPb4f+kvdOSJHa5PjTYVsWHxLUzH4IFTJUAnmuWpw4KqyQ3NA5QCyhw4TWgxk3jRkQxEKg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [musl] '@resvg/resvg-js-linux-x64-gnu@2.6.2': resolution: {integrity: sha512-IVUe+ckIerA7xMZ50duAZzwf1U7khQe2E0QpUxu5MBJNao5RqC0zwV/Zm965vw6D3gGFUl7j4m+oJjubBVoftw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] '@resvg/resvg-js-linux-x64-musl@2.6.2': resolution: {integrity: sha512-UOf83vqTzoYQO9SZ0fPl2ZIFtNIz/Rr/y+7X8XRX1ZnBYsQ/tTb+cj9TE+KHOdmlTFBxhYzVkP2lRByCzqi4jQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [musl] '@resvg/resvg-js-win32-arm64-msvc@2.6.2': resolution: {integrity: sha512-7C/RSgCa+7vqZ7qAbItfiaAWhyRSoD4l4BQAbVDqRRsRgY+S+hgS3in0Rxr7IorKUpGE69X48q6/nOAuTJQxeQ==} @@ -2171,66 +2252,79 @@ packages: resolution: {integrity: sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.57.1': resolution: {integrity: sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.57.1': resolution: {integrity: sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.57.1': resolution: {integrity: sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.57.1': resolution: {integrity: sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-loong64-musl@4.57.1': resolution: {integrity: sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==} cpu: [loong64] os: [linux] + libc: [musl] '@rollup/rollup-linux-ppc64-gnu@4.57.1': resolution: {integrity: sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-ppc64-musl@4.57.1': resolution: {integrity: sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==} cpu: [ppc64] os: [linux] + libc: [musl] '@rollup/rollup-linux-riscv64-gnu@4.57.1': resolution: {integrity: sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.57.1': resolution: {integrity: sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.57.1': resolution: {integrity: sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.57.1': resolution: {integrity: sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.57.1': resolution: {integrity: sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-openbsd-x64@4.57.1': resolution: {integrity: sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==} @@ -2315,24 +2409,28 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-arm64-musl@4.1.18': resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [musl] '@tailwindcss/oxide-linux-x64-gnu@4.1.18': resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-x64-musl@4.1.18': resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [musl] '@tailwindcss/oxide-wasm32-wasi@4.1.18': resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==} @@ -2588,41 +2686,49 @@ packages: resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==} cpu: [arm64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-arm64-musl@1.11.1': resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==} cpu: [arm64] os: [linux] + libc: [musl] '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==} cpu: [ppc64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==} cpu: [riscv64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==} cpu: [riscv64] os: [linux] + libc: [musl] '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==} cpu: [s390x] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-x64-gnu@1.11.1': resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==} cpu: [x64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-x64-musl@1.11.1': resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==} cpu: [x64] os: [linux] + libc: [musl] '@unrs/resolver-binding-wasm32-wasi@1.11.1': resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==} @@ -2747,6 +2853,10 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + ajv-formats@2.1.1: resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} peerDependencies: @@ -2891,6 +3001,9 @@ packages: resolution: {integrity: sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==} hasBin: true + bidi-js@1.0.3: + resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} + bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} @@ -3034,6 +3147,10 @@ packages: resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==} engines: {node: '>=8.0.0'} + css-tree@3.1.0: + resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + css-what@6.2.2: resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} engines: {node: '>= 6'} @@ -3043,6 +3160,10 @@ packages: engines: {node: '>=4'} hasBin: true + cssstyle@6.1.0: + resolution: {integrity: sha512-Ml4fP2UT2K3CUBQnVlbdV/8aFDdlY69E+YnwJM+3VUWl08S3J8c8aRuJqCkD9Py8DHZ7zNNvsfKl8psocHZEFg==} + engines: {node: '>=20'} + csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} @@ -3093,6 +3214,10 @@ packages: damerau-levenshtein@1.0.8: resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} + data-urls@7.0.0: + resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + data-view-buffer@1.0.2: resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} engines: {node: '>= 0.4'} @@ -3131,6 +3256,9 @@ packages: decimal.js-light@2.5.1: resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + decompress-response@6.0.0: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} @@ -3717,9 +3845,21 @@ packages: resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==} engines: {node: '>=12.0.0'} + html-encoding-sniffer@6.0.0: + resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + htmlparser2@10.1.0: resolution: {integrity: sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==} + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + hyphenate-style-name@1.1.0: resolution: {integrity: sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==} @@ -3860,6 +4000,9 @@ packages: resolution: {integrity: sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==} engines: {node: '>=0.10.0'} + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + is-regex@1.2.1: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} @@ -3941,6 +4084,15 @@ packages: resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true + jsdom@28.1.0: + resolution: {integrity: sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + peerDependencies: + canvas: ^3.0.0 + peerDependenciesMeta: + canvas: + optional: true + jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} @@ -4041,24 +4193,28 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] lightningcss-linux-arm64-musl@1.30.2: resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [musl] lightningcss-linux-x64-gnu@1.30.2: resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [glibc] lightningcss-linux-x64-musl@1.30.2: resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [musl] lightningcss-win32-arm64-msvc@1.30.2: resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==} @@ -4109,6 +4265,10 @@ packages: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true + lru-cache@11.2.6: + resolution: {integrity: sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==} + engines: {node: 20 || >=22} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -4145,6 +4305,9 @@ packages: mdn-data@2.0.14: resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} + mdn-data@2.12.2: + resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} + merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -4349,6 +4512,9 @@ packages: parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + parse5@8.0.0: + resolution: {integrity: sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -4653,6 +4819,10 @@ packages: resolution: {integrity: sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw==} engines: {node: '>=11.0.0'} + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} @@ -4888,6 +5058,9 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + synckit@0.11.12: resolution: {integrity: sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==} engines: {node: ^14.18.0 || >=16.0.0} @@ -4968,6 +5141,13 @@ packages: resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} engines: {node: '>=14.0.0'} + tldts-core@7.0.23: + resolution: {integrity: sha512-0g9vrtDQLrNIiCj22HSe9d4mLVG3g5ph5DZ8zCKBr4OtrspmNB6ss7hVyzArAeE88ceZocIEGkyW1Ime7fxPtQ==} + + tldts@7.0.23: + resolution: {integrity: sha512-ASdhgQIBSay0R/eXggAkQ53G4nTJqTXqC2kbaBbdDwM7SkjyZyO0OaaN1/FH7U/yCeqOHDwFO5j8+Os/IS1dXw==} + hasBin: true + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -4979,9 +5159,17 @@ packages: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} engines: {node: '>=6'} + tough-cookie@6.0.0: + resolution: {integrity: sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==} + engines: {node: '>=16'} + tr46@1.0.1: resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} + tr46@6.0.0: + resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} + engines: {node: '>=20'} + ts-api-utils@2.4.0: resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} engines: {node: '>=18.12'} @@ -5209,6 +5397,10 @@ packages: jsdom: optional: true + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + watchpack@2.5.1: resolution: {integrity: sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==} engines: {node: '>=10.13.0'} @@ -5216,6 +5408,10 @@ packages: webidl-conversions@4.0.2: resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} + webidl-conversions@8.0.1: + resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} + engines: {node: '>=20'} + webpack-sources@1.4.3: resolution: {integrity: sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==} @@ -5242,6 +5438,14 @@ packages: resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} engines: {node: '>=18'} + whatwg-mimetype@5.0.0: + resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==} + engines: {node: '>=20'} + + whatwg-url@16.0.1: + resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + whatwg-url@7.1.0: resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} @@ -5337,6 +5541,10 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + xml2js@0.5.0: resolution: {integrity: sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==} engines: {node: '>=4.0.0'} @@ -5345,6 +5553,9 @@ packages: resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} engines: {node: '>=4.0'} + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} @@ -5363,6 +5574,9 @@ packages: snapshots: + '@acemir/cssom@0.9.31': + optional: true + '@alloc/quick-lru@5.2.0': {} '@apideck/better-ajv-errors@0.3.6(ajv@8.17.1)': @@ -5372,6 +5586,27 @@ snapshots: jsonpointer: 5.0.1 leven: 3.1.0 + '@asamuzakjp/css-color@5.0.1': + dependencies: + '@csstools/css-calc': 3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-color-parser': 4.0.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + lru-cache: 11.2.6 + optional: true + + '@asamuzakjp/dom-selector@6.8.1': + dependencies: + '@asamuzakjp/nwsapi': 2.3.9 + bidi-js: 1.0.3 + css-tree: 3.1.0 + is-potential-custom-element-name: 1.0.1 + lru-cache: 11.2.6 + optional: true + + '@asamuzakjp/nwsapi@2.3.9': + optional: true + '@babel/code-frame@7.29.0': dependencies: '@babel/helper-validator-identifier': 7.28.5 @@ -6026,6 +6261,39 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@bramus/specificity@2.4.2': + dependencies: + css-tree: 3.1.0 + optional: true + + '@csstools/color-helpers@6.0.2': + optional: true + + '@csstools/css-calc@3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + optional: true + + '@csstools/css-color-parser@4.0.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/color-helpers': 6.0.2 + '@csstools/css-calc': 3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + optional: true + + '@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/css-tokenizer': 4.0.0 + optional: true + + '@csstools/css-syntax-patches-for-csstree@1.0.28': + optional: true + + '@csstools/css-tokenizer@4.0.0': + optional: true + '@date-fns/tz@1.4.1': {} '@dnd-kit/accessibility@3.1.1(react@19.2.4)': @@ -6208,6 +6476,9 @@ snapshots: '@eslint/core': 0.17.0 levn: 0.4.1 + '@exodus/bytes@1.14.1': + optional: true + '@floating-ui/core@1.7.4': dependencies: '@floating-ui/utils': 0.2.10 @@ -7764,7 +8035,7 @@ snapshots: sirv: 3.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vitest: 4.0.18(@types/node@25.2.3)(@vitest/ui@4.0.18)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(tsx@4.21.0) + vitest: 4.0.18(@types/node@25.2.3)(@vitest/ui@4.0.18)(jiti@2.6.1)(jsdom@28.1.0(canvas@3.2.1))(lightningcss@1.30.2)(terser@5.46.0)(tsx@4.21.0) '@vitest/utils@4.0.18': dependencies: @@ -7861,6 +8132,9 @@ snapshots: acorn@8.16.0: {} + agent-base@7.1.4: + optional: true + ajv-formats@2.1.1(ajv@8.18.0): optionalDependencies: ajv: 8.18.0 @@ -8043,6 +8317,11 @@ snapshots: baseline-browser-mapping@2.9.19: {} + bidi-js@1.0.3: + dependencies: + require-from-string: 2.0.2 + optional: true + bl@4.1.0: dependencies: buffer: 5.7.1 @@ -8216,10 +8495,24 @@ snapshots: mdn-data: 2.0.14 source-map: 0.6.1 + css-tree@3.1.0: + dependencies: + mdn-data: 2.12.2 + source-map-js: 1.2.1 + optional: true + css-what@6.2.2: {} cssesc@3.0.0: {} + cssstyle@6.1.0: + dependencies: + '@asamuzakjp/css-color': 5.0.1 + '@csstools/css-syntax-patches-for-csstree': 1.0.28 + css-tree: 3.1.0 + lru-cache: 11.2.6 + optional: true + csstype@3.2.3: {} d3-array@3.2.4: @@ -8262,6 +8555,14 @@ snapshots: damerau-levenshtein@1.0.8: {} + data-urls@7.0.0: + dependencies: + whatwg-mimetype: 5.0.0 + whatwg-url: 16.0.1 + transitivePeerDependencies: + - '@noble/hashes' + optional: true + data-view-buffer@1.0.2: dependencies: call-bound: 1.0.4 @@ -8294,6 +8595,9 @@ snapshots: decimal.js-light@2.5.1: {} + decimal.js@10.6.0: + optional: true + decompress-response@6.0.0: dependencies: mimic-response: 3.1.0 @@ -9022,6 +9326,13 @@ snapshots: highlight.js@11.11.1: {} + html-encoding-sniffer@6.0.0: + dependencies: + '@exodus/bytes': 1.14.1 + transitivePeerDependencies: + - '@noble/hashes' + optional: true + htmlparser2@10.1.0: dependencies: domelementtype: 2.3.0 @@ -9029,6 +9340,22 @@ snapshots: domutils: 3.2.2 entities: 7.0.1 + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + optional: true + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + optional: true + hyphenate-style-name@1.1.0: {} iconv-lite@0.6.3: @@ -9159,6 +9486,9 @@ snapshots: is-obj@1.0.1: {} + is-potential-custom-element-name@1.0.1: + optional: true + is-regex@1.2.1: dependencies: call-bound: 1.0.4 @@ -9242,6 +9572,36 @@ snapshots: dependencies: argparse: 2.0.1 + jsdom@28.1.0(canvas@3.2.1): + dependencies: + '@acemir/cssom': 0.9.31 + '@asamuzakjp/dom-selector': 6.8.1 + '@bramus/specificity': 2.4.2 + '@exodus/bytes': 1.14.1 + cssstyle: 6.1.0 + data-urls: 7.0.0 + decimal.js: 10.6.0 + html-encoding-sniffer: 6.0.0 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + is-potential-custom-element-name: 1.0.1 + parse5: 8.0.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 6.0.0 + undici: 7.21.0 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 8.0.1 + whatwg-mimetype: 5.0.0 + whatwg-url: 16.0.1 + xml-name-validator: 5.0.0 + optionalDependencies: + canvas: 3.2.1 + transitivePeerDependencies: + - '@noble/hashes' + - supports-color + optional: true + jsesc@3.1.0: {} json-buffer@3.0.1: {} @@ -9379,6 +9739,9 @@ snapshots: dependencies: js-tokens: 4.0.0 + lru-cache@11.2.6: + optional: true + lru-cache@5.1.1: dependencies: yallist: 3.1.1 @@ -9410,6 +9773,9 @@ snapshots: mdn-data@2.0.14: {} + mdn-data@2.12.2: + optional: true + merge-stream@2.0.0: {} merge2@1.4.1: {} @@ -9617,6 +9983,11 @@ snapshots: dependencies: entities: 6.0.1 + parse5@8.0.0: + dependencies: + entities: 6.0.1 + optional: true + path-exists@4.0.0: {} path-is-absolute@1.0.1: {} @@ -9969,6 +10340,11 @@ snapshots: sax@1.4.4: {} + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + optional: true + scheduler@0.27.0: {} schema-utils@4.3.3: @@ -10253,6 +10629,9 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + symbol-tree@3.2.4: + optional: true + synckit@0.11.12: dependencies: '@pkgr/core': 0.2.9 @@ -10324,6 +10703,14 @@ snapshots: tinyrainbow@3.0.3: {} + tldts-core@7.0.23: + optional: true + + tldts@7.0.23: + dependencies: + tldts-core: 7.0.23 + optional: true + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -10332,10 +10719,20 @@ snapshots: totalist@3.0.1: {} + tough-cookie@6.0.0: + dependencies: + tldts: 7.0.23 + optional: true + tr46@1.0.1: dependencies: punycode: 2.3.1 + tr46@6.0.0: + dependencies: + punycode: 2.3.1 + optional: true + ts-api-utils@2.4.0(typescript@5.9.3): dependencies: typescript: 5.9.3 @@ -10545,7 +10942,7 @@ snapshots: terser: 5.46.0 tsx: 4.21.0 - vitest@4.0.18(@types/node@25.2.3)(@vitest/ui@4.0.18)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(tsx@4.21.0): + vitest@4.0.18(@types/node@25.2.3)(@vitest/ui@4.0.18)(jiti@2.6.1)(jsdom@28.1.0(canvas@3.2.1))(lightningcss@1.30.2)(terser@5.46.0)(tsx@4.21.0): dependencies: '@vitest/expect': 4.0.18 '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(tsx@4.21.0)) @@ -10570,6 +10967,7 @@ snapshots: optionalDependencies: '@types/node': 25.2.3 '@vitest/ui': 4.0.18(vitest@4.0.18) + jsdom: 28.1.0(canvas@3.2.1) transitivePeerDependencies: - jiti - less @@ -10583,6 +10981,11 @@ snapshots: - tsx - yaml + w3c-xmlserializer@5.0.0: + dependencies: + xml-name-validator: 5.0.0 + optional: true + watchpack@2.5.1: dependencies: glob-to-regexp: 0.4.1 @@ -10590,6 +10993,9 @@ snapshots: webidl-conversions@4.0.2: {} + webidl-conversions@8.0.1: + optional: true + webpack-sources@1.4.3: dependencies: source-list-map: 2.0.1 @@ -10634,6 +11040,18 @@ snapshots: whatwg-mimetype@4.0.0: {} + whatwg-mimetype@5.0.0: + optional: true + + whatwg-url@16.0.1: + dependencies: + '@exodus/bytes': 1.14.1 + tr46: 6.0.0 + webidl-conversions: 8.0.1 + transitivePeerDependencies: + - '@noble/hashes' + optional: true + whatwg-url@7.1.0: dependencies: lodash.sortby: 4.7.0 @@ -10862,6 +11280,9 @@ snapshots: wrappy@1.0.2: {} + xml-name-validator@5.0.0: + optional: true + xml2js@0.5.0: dependencies: sax: 1.4.4 @@ -10869,6 +11290,9 @@ snapshots: xmlbuilder@11.0.1: {} + xmlchars@2.2.0: + optional: true + yallist@3.1.1: {} yocto-queue@0.1.0: {} diff --git a/scripts/check-broken-links.ts b/scripts/check-broken-links.ts new file mode 100644 index 000000000..9d6e44b1b --- /dev/null +++ b/scripts/check-broken-links.ts @@ -0,0 +1,128 @@ +#!/usr/bin/env node + +import { readFileSync, readdirSync, statSync } from 'fs' +import { join } from 'path' +import * as cheerio from 'cheerio' + +/** + * Recursively get all HTML files from a directory + */ +function getHtmlFiles(dir: string): string[] { + const files: string[] = [] + const items = readdirSync(dir) + + for (const item of items) { + const fullPath = join(dir, item) + const stat = statSync(fullPath) + + if (stat.isDirectory()) { + files.push(...getHtmlFiles(fullPath)) + } else if (item.endsWith('.html')) { + files.push(fullPath) + } + } + + return files +} + +/** + * Extract all internal links from an HTML file + */ +function extractInternalLinks(htmlPath: string): string[] { + const html = readFileSync(htmlPath, 'utf-8') + const $ = cheerio.load(html) + const links: string[] = [] + + $('a[href]').each((_, element) => { + const href = $(element).attr('href') + if (href && href.startsWith('/') && !href.startsWith('//')) { + // Remove hash fragments and query params + const url = href.split('#')[0].split('?')[0] + if (url.length > 0) { + links.push(url) + } + } + }) + + return links +} + +/** + * Check if a URL path exists in the static build output + */ +function urlExists(url: string, outDir: string): boolean { + // Remove leading slash + const path = url.startsWith('/') ? url.slice(1) : url + + // Try multiple possible file locations + const possiblePaths = [ + join(outDir, path), // Direct path + join(outDir, path, 'index.html'), // Directory with index.html + join(outDir, `${path}.html`), // .html extension + ] + + for (const filePath of possiblePaths) { + try { + const stat = statSync(filePath) + if (stat.isFile() || stat.isDirectory()) { + return true + } + } catch { + // File doesn't exist, continue + } + } + + return false +} + +/** + * Main function to check for broken links + */ +function main() { + const outDir = join(process.cwd(), 'out') + console.log('\nšŸ” Checking for broken internal links...\n') + + const htmlFiles = getHtmlFiles(outDir) + console.log(`šŸ“„ Found ${htmlFiles.length} HTML files to scan\n`) + + const brokenLinks = new Map() + + for (const htmlFile of htmlFiles) { + const links = extractInternalLinks(htmlFile) + const uniqueLinks = [...new Set(links)] + + for (const link of uniqueLinks) { + if (!urlExists(link, outDir)) { + const relativePath = htmlFile.replace(outDir + '/', '') + if (!brokenLinks.has(link)) { + brokenLinks.set(link, []) + } + brokenLinks.get(link)!.push(relativePath) + } + } + } + + if (brokenLinks.size === 0) { + console.log('āœ… No broken internal links found!\n') + process.exit(0) + } + + console.error(`āŒ Found ${brokenLinks.size} broken internal link(s):\n`) + + for (const [link, sources] of brokenLinks.entries()) { + console.error(` ${link}`) + console.error(` Found in ${sources.length} file(s):${ + sources.length <= 3 + ? '' + : ` (showing first 3 of ${sources.length})` + }`) + for (const source of sources.slice(0, 3)) { + console.error(` - ${source}`) + } + console.error('') + } + + process.exit(1) +} + +main()