diff --git a/.agents/PAPERCUTS.md b/.agents/PAPERCUTS.md index fb261b2..ec16739 100644 --- a/.agents/PAPERCUTS.md +++ b/.agents/PAPERCUTS.md @@ -6,6 +6,7 @@ This is not a completed-work log or a bug tracker. Never include secrets, creden ## Open +- [ ] `2026-07-10T21:09:27Z` — `codex` — While building the TanStack/Cloudflare badseo app, Wrangler reported an EPERM writing its debug log under the user preferences directory even though the build succeeded. Set `WRANGLER_LOG_PATH` to a writable temporary path in sandboxed build commands or make the logging failure non-fatal and quiet. - [ ] `2026-07-10T17:53:20Z` — `codex` — While validating `.greptile/`, both `pnpm exec prettier --check` and the existing `pnpm format:check` attempted to reconcile `node_modules` and aborted because no TTY was available. Calling `node_modules/.bin/prettier` performed the non-installing check successfully; the agent/CI path needs a stable way to run package scripts without an interactive modules purge. - [ ] `2026-07-10T18:12:35Z` — `codex` — While validating referenced files in zsh, using `path` as a loop variable overwrote zsh's special `path` array and made commands such as `git`, `jq`, and `sed` appear missing later in the same shell. Use a neutral name such as `file_path` in shell loops. diff --git a/badseo/README.md b/badseo/README.md index 6733908..a405988 100644 --- a/badseo/README.md +++ b/badseo/README.md @@ -36,37 +36,39 @@ Browse them all at `/catalog`. ## How it's built -A single, dependency-free Cloudflare Worker (`src/index.ts`) that serves -hand-authored HTML with byte-level control over every SEO signal — status codes, -redirects, response headers (`X-Robots-Tag`, `Link: …; rel=canonical`), response -timing, and the raw ``. No framework: SSR machinery tends to _fix_ the very -things we're trying to break (it insists on injecting a ``, etc.). +badseo.dev is a TanStack Start app deployed to a Cloudflare Worker, following +the same Vite and Cloudflare setup as the repository's `web/` app. -- `src/index.ts` — router: fixtures → URLs, plus `robots.txt` and `sitemap.xml`. -- `src/lib.ts` — page rendering. The shared chrome (nav, footer, the OpenSEO - badge, the "what this page tests" panel) is deliberately **SEO-neutral**: it - emits no `<h1>`–`<h6>` and no `<img>`, so each fixture fully controls its own - headings and images and the audit measures exactly the defect we injected. +TanStack React routes render the healthy homepage and catalog. +A TanStack catch-all server route keeps the deliberate fixtures as raw +responses with byte-level control over status codes, redirects, headers +(`X-Robots-Tag`, `Link: …; rel=canonical`), timing, and the malformed `<head>` +states the audit needs to observe. + +- `src/routes/` — TanStack pages plus raw server routes for fixtures, + `robots.txt`, and `sitemap.xml`. +- `src/server/badseo.ts` — fixture dispatch and crawler-discovery responses. +- `src/lib.ts` — raw fixture HTML rendering. Its shared chrome is deliberately + **SEO-neutral**: it emits no `<h1>`–`<h6>` and no `<img>`. - `src/fixtures/*.ts` — the fixtures, one file per category. -- `src/pages.ts` — the homepage and catalog (both must audit clean). ## Run it locally ```bash -# from the badseo/ directory (uses the repo's wrangler) -npx wrangler dev # serves on http://localhost:8787 +# from the badseo/ directory +npm run dev # serves on http://localhost:8787 ``` ## Run the end-to-end audit The harness drives the **real** OpenSEO crawl + issue-detection functions (imported straight from `../src`) against a running badseo.dev, then asserts every -fixture triggers exactly the issues it declares — and that the homepage, catalog, -and support pages come back clean. +fixture triggers exactly the issues it declares — and that the homepage, +catalog, and support pages come back clean. ```bash -# with `wrangler dev` running in another terminal: -npx tsx scripts/run-audit.ts http://localhost:8787 +# with `npm run dev` running in another terminal: +npm run audit -- http://localhost:8787 ``` It prints a per-page pass/fail matrix and an issue-type coverage line, and exits @@ -113,20 +115,13 @@ Guidelines: ## Deploy -There's no bundling build — wrangler/esbuild bundles `src/index.ts` on deploy. -The `build` script is a typecheck (`tsc --noEmit`) that runs before the deploy: +Vite builds the TanStack Start client and Worker bundles, then TypeScript checks +the project before Wrangler deploys it: ```bash -npm run build # typecheck the Worker source -npm run deploy # build, then wrangler deploy → badseo.dev +npm run build # Vite build + typecheck +npm run deploy # build + wrangler deploy → badseo.dev ``` -`npm run deploy` runs `npm run build && wrangler deploy --env production`. To -deploy without the typecheck gate, run `npx wrangler deploy --env production` -directly. - -First-time setup: the `production` env in `wrangler.jsonc` binds the custom -domains `badseo.dev` and `www.badseo.dev`, so the zone must be on the Cloudflare -account before the first deploy. (The routes live under `production` so that -plain `wrangler dev` still serves on localhost.) To preview on a `*.workers.dev` -URL without the custom domain, deploy the top-level env: `npx wrangler deploy`. +The custom-domain routes for `badseo.dev` and `www.badseo.dev` live in +`wrangler.jsonc`, alongside the TanStack server entry and built asset directory. diff --git a/badseo/package.json b/badseo/package.json index 7592537..1f813ed 100644 --- a/badseo/package.json +++ b/badseo/package.json @@ -4,18 +4,28 @@ "type": "module", "description": "badseo.dev — a deliberately broken website full of SEO mistakes, used as e2e test fixtures for the OpenSEO site audit.", "scripts": { - "dev": "wrangler dev", - "start": "wrangler dev", + "dev": "vite dev", + "start": "vite dev", "typecheck": "tsc --noEmit", - "build": "tsc --noEmit", - "deploy": "npm run build && wrangler deploy --env production", - "audit": "tsx scripts/run-audit.ts", - "test:e2e": "tsx scripts/run-audit.ts" + "build": "vite build && tsc --noEmit", + "deploy": "npm run build && wrangler deploy", + "audit": "cd .. && tsx badseo/scripts/run-audit.ts", + "test:e2e": "cd .. && tsx badseo/scripts/run-audit.ts" + }, + "dependencies": { + "@tanstack/react-router": "^1.170.16", + "@tanstack/react-start": "^1.168.26", + "react": "^19.0.0", + "react-dom": "^19.0.0" }, "devDependencies": { - "@cloudflare/workers-types": "^4.20250109.0", + "@cloudflare/vite-plugin": "^1.42.3", + "@types/react": "^19.0.8", + "@types/react-dom": "^19.0.3", + "@vitejs/plugin-react": "^4.6.0", "tsx": "^4.21.0", "typescript": "^5.9.3", + "vite": "^7.3.6", "wrangler": "^4.67.0" } } diff --git a/badseo/pnpm-lock.yaml b/badseo/pnpm-lock.yaml new file mode 100644 index 0000000..70b3a18 --- /dev/null +++ b/badseo/pnpm-lock.yaml @@ -0,0 +1,2494 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@tanstack/react-router': + specifier: ^1.170.16 + version: 1.170.17(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@tanstack/react-start': + specifier: ^1.168.26 + version: 1.168.27(esbuild@0.28.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(rollup@4.62.2)(vite@7.3.6(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.5)) + react: + specifier: ^19.0.0 + version: 19.2.7 + react-dom: + specifier: ^19.0.0 + version: 19.2.7(react@19.2.7) + devDependencies: + '@cloudflare/vite-plugin': + specifier: ^1.42.3 + version: 1.43.0(vite@7.3.6(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.5))(workerd@1.20260701.1)(wrangler@4.107.0) + '@types/react': + specifier: ^19.0.8 + version: 19.2.17 + '@types/react-dom': + specifier: ^19.0.3 + version: 19.2.3(@types/react@19.2.17) + '@vitejs/plugin-react': + specifier: ^4.6.0 + version: 4.7.0(vite@7.3.6(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.5)) + tsx: + specifier: ^4.21.0 + version: 4.22.5 + typescript: + specifier: ^5.9.3 + version: 5.9.3 + vite: + specifier: ^7.3.6 + version: 7.3.6(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.5) + wrangler: + specifier: ^4.67.0 + version: 4.107.0 + +packages: + + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.7': + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.7': + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.7': + resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.29.7': + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.29.7': + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.29.7': + resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.29.7': + resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.7': + resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.7': + resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-transform-react-jsx-self@7.29.7': + resolution: {integrity: sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.29.7': + resolution: {integrity: sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.7': + resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.7': + resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} + engines: {node: '>=6.9.0'} + + '@cloudflare/kv-asset-handler@0.5.0': + resolution: {integrity: sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg==} + engines: {node: '>=22.0.0'} + + '@cloudflare/unenv-preset@2.16.1': + resolution: {integrity: sha512-ECxObrMfyTl5bhQf/lZCXwo5G6xX9IAUo+nDMKK4SZ8m4Jvvxp52vilxyySSWh2YTZz8+HQ07qGH/2rEom1vDw==} + peerDependencies: + unenv: 2.0.0-rc.24 + workerd: '>1.20260305.0 <2.0.0-0' + peerDependenciesMeta: + workerd: + optional: true + + '@cloudflare/vite-plugin@1.43.0': + resolution: {integrity: sha512-5ThLXS2ZXPoCa9xpRqDi9BebpkkbYbAhUU1lMomD+UXEp2SeB4/pVxeFF0j/9nbBlUhDOm5aariI31h8ZcXrBg==} + hasBin: true + peerDependencies: + vite: ^6.1.0 || ^7.0.0 || ^8.0.0 + wrangler: ^4.107.0 + + '@cloudflare/workerd-darwin-64@1.20260701.1': + resolution: {integrity: sha512-Zd9Y1bah6DwwBN2RW8vJohffQrIUazb8UXnqSNecOxM+jJLhUuvv5IOG8dbHcV83TyZAubea6gsQXo2yH1lDdw==} + engines: {node: '>=16'} + cpu: [x64] + os: [darwin] + + '@cloudflare/workerd-darwin-arm64@1.20260701.1': + resolution: {integrity: sha512-yBLsjS1qCWqFyCY37qRUrYfzHHvMGvjh8zRKJ6MvUivYDhkZTzqduppK38FoqYvayLJ5KbcxH7zo5rkxGqbsaA==} + engines: {node: '>=16'} + cpu: [arm64] + os: [darwin] + + '@cloudflare/workerd-linux-64@1.20260701.1': + resolution: {integrity: sha512-vMfqSIMfoo4xmZXEuUVqLpSFS921YKjiR9q7kDXPi6Vld1PK74UHg9LZuBavT2KSyemHUCTpj9y/4JSYOEyQbQ==} + engines: {node: '>=16'} + cpu: [x64] + os: [linux] + + '@cloudflare/workerd-linux-arm64@1.20260701.1': + resolution: {integrity: sha512-HRfwbKU2pK44V2NhoM0+iH0JJSj7nQ9Wv13ifIiGYCmTtDL8/zKtEhX7kQ3D4Vy/Cpjhttl0FkfqXj1aqLDPPg==} + engines: {node: '>=16'} + cpu: [arm64] + os: [linux] + + '@cloudflare/workerd-windows-64@1.20260701.1': + resolution: {integrity: sha512-ngxCiIN9s/fM2o1IBMD0o1/mcXrv2NJVdyznh51UH8sQuvrTrXvV2nM0Uj/qU2wMwF6prgNBcdcd7AZeZGiBQA==} + engines: {node: '>=16'} + cpu: [x64] + os: [win32] + + '@cspotcode/source-map-support@0.8.1': + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + + '@emnapi/runtime@1.11.1': + resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==} + + '@esbuild/aix-ppc64@0.28.1': + resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.28.1': + resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.28.1': + resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.28.1': + resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.28.1': + resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.28.1': + resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.28.1': + resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.28.1': + resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.28.1': + resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.28.1': + resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.28.1': + resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.28.1': + resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.28.1': + resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.28.1': + resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.28.1': + resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.28.1': + resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.28.1': + resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.28.1': + resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.28.1': + resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.28.1': + resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.28.1': + resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.28.1': + resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.28.1': + resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.28.1': + resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.28.1': + resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.28.1': + resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@img/colour@1.1.0': + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.34.5': + resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.34.5': + resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.2.4': + resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.2.4': + resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.2.4': + 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==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.34.5': + resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.34.5': + resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.34.5': + resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@jridgewell/trace-mapping@0.3.9': + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + + '@oozcitak/dom@2.0.2': + resolution: {integrity: sha512-GjpKhkSYC3Mj4+lfwEyI1dqnsKTgwGy48ytZEhm4A/xnH/8z9M3ZVXKr/YGQi3uCLs1AEBS+x5T2JPiueEDW8w==} + engines: {node: '>=20.0'} + + '@oozcitak/infra@2.0.2': + resolution: {integrity: sha512-2g+E7hoE2dgCz/APPOEK5s3rMhJvNxSMBrP+U+j1OWsIbtSpWxxlUjq1lU8RIsFJNYv7NMlnVsCuHcUzJW+8vA==} + engines: {node: '>=20.0'} + + '@oozcitak/url@3.0.0': + resolution: {integrity: sha512-ZKfET8Ak1wsLAiLWNfFkZc/BraDccuTJKR6svTYc7sVjbR+Iu0vtXdiDMY4o6jaFl5TW2TlS7jbLl4VovtAJWQ==} + engines: {node: '>=20.0'} + + '@oozcitak/util@10.0.0': + resolution: {integrity: sha512-hAX0pT/73190NLqBPPWSdBVGtbY6VOhWYK3qqHqtXQ1gK7kS2yz4+ivsN07hpJ6I3aeMtKP6J6npsEKOAzuTLA==} + engines: {node: '>=20.0'} + + '@poppinss/colors@4.1.6': + resolution: {integrity: sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==} + + '@poppinss/dumper@0.6.5': + resolution: {integrity: sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw==} + + '@poppinss/exception@1.2.3': + resolution: {integrity: sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==} + + '@rolldown/pluginutils@1.0.0-beta.27': + resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} + + '@rollup/rollup-android-arm-eabi@4.62.2': + resolution: {integrity: sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.62.2': + resolution: {integrity: sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.62.2': + resolution: {integrity: sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.62.2': + resolution: {integrity: sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.62.2': + resolution: {integrity: sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.62.2': + resolution: {integrity: sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.62.2': + resolution: {integrity: sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.62.2': + resolution: {integrity: sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.62.2': + resolution: {integrity: sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.62.2': + resolution: {integrity: sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-loong64-gnu@4.62.2': + resolution: {integrity: sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-loong64-musl@4.62.2': + resolution: {integrity: sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==} + cpu: [loong64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-ppc64-gnu@4.62.2': + resolution: {integrity: sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-ppc64-musl@4.62.2': + resolution: {integrity: sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==} + cpu: [ppc64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-riscv64-gnu@4.62.2': + resolution: {integrity: sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-musl@4.62.2': + resolution: {integrity: sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.62.2': + resolution: {integrity: sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.62.2': + resolution: {integrity: sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.62.2': + resolution: {integrity: sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-openbsd-x64@4.62.2': + resolution: {integrity: sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.62.2': + resolution: {integrity: sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.62.2': + resolution: {integrity: sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.62.2': + resolution: {integrity: sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.62.2': + resolution: {integrity: sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.62.2': + resolution: {integrity: sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==} + cpu: [x64] + os: [win32] + + '@sindresorhus/is@7.2.0': + resolution: {integrity: sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==} + engines: {node: '>=18'} + + '@speed-highlight/core@1.2.17': + resolution: {integrity: sha512-Z92FwKpCtfaW1V0jTU/fh3QzYEZN8wDwrzRIBoADCJfn4mJCNcJN/XegifX7BDrQ8/h9Xh/JnbyMchL0FqXrkg==} + + '@tanstack/history@1.162.0': + resolution: {integrity: sha512-79pf/RkhteYZTRgcR4F9kbk84P2N8rugQJswxfIqovlbRiT3yI7eBE+5QorIrZaOKktsgzRlXh1l/du/xpl4iA==} + engines: {node: '>=20.19'} + + '@tanstack/react-router@1.170.17': + resolution: {integrity: sha512-ppLkjCfSMaeug9rmFRYzOd4TIqWV+yTE7tzIny7alJsSnM7w4lzEZm6eqCehG0SPetpZ0R3K+UnanSmBgOAVcQ==} + engines: {node: '>=20.19'} + peerDependencies: + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + + '@tanstack/react-start-client@1.168.15': + resolution: {integrity: sha512-pW50PHvadgi50iNCw6deUOvqc9rzs30SstyFZY2tcS9z1XlqTlELSvGowjxdu2m0ymtqm1emj1jau1iP7+3+PQ==} + engines: {node: '>=22.12.0'} + peerDependencies: + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + + '@tanstack/react-start-rsc@0.1.26': + resolution: {integrity: sha512-+FMm3qtT1gWsl0i5sG/Q70mh1k7tzZUsPBaqbg4v34zVJZ+XGn1mJb34x2w7z1M0/e7co6GkZfV8BRpczrs8UA==} + engines: {node: '>=22.12.0'} + peerDependencies: + '@rspack/core': '>=2.0.0-0' + '@vitejs/plugin-rsc': '>=0.5.20' + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + react-server-dom-rspack: '>=0.0.2' + peerDependenciesMeta: + '@rspack/core': + optional: true + '@vitejs/plugin-rsc': + optional: true + react-server-dom-rspack: + optional: true + + '@tanstack/react-start-server@1.167.21': + resolution: {integrity: sha512-puJ7eFxaLuDzeM/tiLDJaXCA4uK+PZnEOoIC73zipsFqx865MGzrRS/GSZxeVxjavC5iHU+ZwC+rgI0qYSol1A==} + engines: {node: '>=22.12.0'} + peerDependencies: + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + + '@tanstack/react-start@1.168.27': + resolution: {integrity: sha512-rdGFDqfCW71gyofyAxaYxhelNKmeVjpmbpm0uFYbNHORCa///4aBxi7B7ecShibKv9O4GfJ66MPX5F0ozbm+ig==} + engines: {node: '>=22.12.0'} + peerDependencies: + '@rsbuild/core': ^2.0.0 + '@vitejs/plugin-rsc': '*' + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + vite: '>=7.0.0' + peerDependenciesMeta: + '@rsbuild/core': + optional: true + '@vitejs/plugin-rsc': + optional: true + vite: + optional: true + + '@tanstack/react-store@0.9.3': + resolution: {integrity: sha512-y2iHd/N9OkoQbFJLUX1T9vbc2O9tjH0pQRgTcx1/Nz4IlwLvkgpuglXUx+mXt0g5ZDFrEeDnONPqkbfxXJKwRg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@tanstack/router-core@1.171.14': + resolution: {integrity: sha512-Mo3hwx0qB0cJsVYGDjG0+Ouf7VV74h/vsoDMGztdlyzDanp4gBA2s7IVvm6hFrmQM6GpD9F0Z7SqD7OldfLE7g==} + engines: {node: '>=20.19'} + + '@tanstack/router-generator@1.167.18': + resolution: {integrity: sha512-kFvM4caRds9Q3EXg64bZubJ6rbDxyV0YDSBSGvOGzmKspQPdz5Xrh0uj5T1Ov8avUUg+c761u04VQAaEzSBXRw==} + engines: {node: '>=20.19'} + + '@tanstack/router-plugin@1.168.19': + resolution: {integrity: sha512-aFglwLc+bbPTgZlkXn3PvOwpjJAfgUyPGSuql4MP3XrqTTh6WkBiy2RYb6oaG5h0s7EKwivEuq85K3Y4V0Mt1g==} + engines: {node: '>=20.19'} + peerDependencies: + '@rsbuild/core': '>=1.0.2 || ^2.0.0' + '@tanstack/react-router': ^1.170.17 + vite: '>=5.0.0 || >=6.0.0 || >=7.0.0 || >=8.0.0' + vite-plugin-solid: ^2.11.10 || ^3.0.0-0 + webpack: '>=5.92.0' + peerDependenciesMeta: + '@rsbuild/core': + optional: true + '@tanstack/react-router': + optional: true + vite: + optional: true + vite-plugin-solid: + optional: true + webpack: + optional: true + + '@tanstack/router-utils@1.162.2': + resolution: {integrity: sha512-hTWqJtqIFFdvuCl8WXNyrodp2L9zo2G37xKRrcVmVRWpAB2h+U1LuRAfS4tsFTiWOIoE/B+WDVFB8JpoEdw6jQ==} + engines: {node: '>=20.19'} + + '@tanstack/start-client-core@1.170.13': + resolution: {integrity: sha512-o37M3msIK5ec87kPrIYJWXb1XPnjIe5/jrkGLXiXpFuVL99z7mhoBCzftKtVPtzqI8EElnRE/VGFYT9BHNnWcw==} + engines: {node: '>=22.12.0'} + + '@tanstack/start-fn-stubs@1.162.0': + resolution: {integrity: sha512-QWfUZ3Yo923tdQn38LyKMU8rcTw69zc+T4dAvgTWV4O56SqFRsGfS0lSWIMhJRwXIx/bvdi7nTUBDdZtTHtpTQ==} + engines: {node: '>=22.12.0'} + + '@tanstack/start-plugin-core@1.171.19': + resolution: {integrity: sha512-+fpW3Z/2vPT8HDV1c5p2WC6/g2k/AV/ujdJVDcn/VFd+gXRtzSX1D/LfozlaDbhDoEsqOnAk/mGwjg60JkUA2Q==} + engines: {node: '>=22.12.0'} + peerDependencies: + '@rsbuild/core': ^2.0.0 + vite: '>=7.0.0' + peerDependenciesMeta: + '@rsbuild/core': + optional: true + vite: + optional: true + + '@tanstack/start-server-core@1.169.16': + resolution: {integrity: sha512-lvAjQpH3nHJtd4xy0iHIaWbsTbyN9EBxuYCxbtXH0EpeBQPg+TCPhu9GQC9WbbA1rE//s82CpE55oYDQMqkU5A==} + engines: {node: '>=22.12.0'} + + '@tanstack/start-storage-context@1.167.16': + resolution: {integrity: sha512-zTegxlij4BC1DbCrC6rsVlMOQVMzOuG5IllacZEkrUdhiFwMIMYpk0VWGH+d0ucx5RBkmv8e8GNX3AOVBWclfg==} + engines: {node: '>=22.12.0'} + + '@tanstack/store@0.9.3': + resolution: {integrity: sha512-8reSzl/qGWGGVKhBoxXPMWzATSbZLZFWhwBAFO9NAyp0TxzfBP0mIrGb8CP8KrQTmvzXlR/vFPPUrHTLBGyFyw==} + + '@tanstack/virtual-file-routes@1.162.0': + resolution: {integrity: sha512-uhOeFyxLcU41HzvrxsGpiWdcMbScY1EDgbZ5K7DVRMYInbLYWAC0EA/kx9wXAoSM8q82bUG2hRl8+EAjE6XAbA==} + engines: {node: '>=20.19'} + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.17': + resolution: {integrity: sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==} + + '@vitejs/plugin-react@4.7.0': + resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + + ansis@4.3.1: + resolution: {integrity: sha512-BJ8/l4R5LRE7hW9WdSuGYrLSHi2ynxeFpDFbH0K/CgNeY/tyhk+vO6TYxXC5r5CpUhNVX310xzPsN/H9lCdfOA==} + engines: {node: '>=14'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + babel-dead-code-elimination@1.0.12: + resolution: {integrity: sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig==} + + baseline-browser-mapping@2.10.41: + resolution: {integrity: sha512-WwS7MHhqGHHlaVsqRZnhvCEMS0owDX+SxRlve7JkuH7My1Ara3ZriTmCQupPfYjxMZ8I/tgxtJYr2t7taHaH4A==} + engines: {node: '>=6.0.0'} + hasBin: true + + blake3-wasm@2.1.5: + resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==} + + browserslist@4.28.4: + resolution: {integrity: sha512-MTc8i/x9jBQd1iMw2CFGS+rwMa07eYjLR0CCTLDACl9xhxy+nIs3KeML/biicXtk9JrZ6dnnTatmc7ErPXIxqw==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + caniuse-lite@1.0.30001800: + resolution: {integrity: sha512-MMHtuAz9Ys840zAY5F4k6fV5GaivZ9sPk+nz0mY+GYVzRBnYkN0mpqkSR92oWRQ19yQWo4HvBV/FnC16AJX8MA==} + + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie-es@3.1.1: + resolution: {integrity: sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==} + + cookie@1.1.1: + resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} + engines: {node: '>=18'} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + diff@8.0.4: + resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==} + engines: {node: '>=0.3.1'} + + electron-to-chromium@1.5.384: + resolution: {integrity: sha512-g6KAKY1vkYsADvSPWvdJsuYT0ixdcu6lUtD9P/wJKGBEDlZVXh2AX42j1mPqqaQPDluWjara9ziQ7xqAeXCt5A==} + + error-stack-parser-es@1.0.5: + resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==} + + esbuild@0.28.1: + resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + exsolve@1.1.0: + resolution: {integrity: sha512-D+42+T12DdIlJM3uepa55qGiL3sYdLBOxIl2ifQCzCHz4c7eiolaHsi3BIqEr7JxBzxv2pYZQX9kw16ziMcEmw==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fetchdts@0.1.7: + resolution: {integrity: sha512-YoZjBdafyLIop9lSxXVI33oLD5kN31q4Td+CasofLLYeLXRFeOsuOw0Uo+XNRi9PZlbfdlN2GmRtm4tCEQ9/KA==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + h3@2.0.1-rc.20: + resolution: {integrity: sha512-28ljodXuUp0fZovdiSRq4G9OgrxCztrJe5VdYzXAB7ueRvI7pIUqLU14Xi3XqdYJ/khXjfpUOOD2EQa6CmBgsg==} + engines: {node: '>=20.11.1'} + hasBin: true + peerDependencies: + crossws: ^0.4.1 + peerDependenciesMeta: + crossws: + optional: true + + isbot@5.1.44: + resolution: {integrity: sha512-PGEHtwMnKbZpeSEXW2Utx+/JWed7dp6DiH0WWg33vGSDA7RUvpUeJSVlLrVkQ1RCpvDOUc/eH9ql7VsdbBZ8pA==} + engines: {node: '>=18'} + + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.3.0: + resolution: {integrity: sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + miniflare@4.20260701.0: + resolution: {integrity: sha512-L6eAAi6IKtyb/7J6L+YsH2vb1yBrJWKRXI293JYDiMl70+6nncdAgigex58w6WBd+CwvdMsqOyNyGs95Op5gWQ==} + engines: {node: '>=22.0.0'} + hasBin: true + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.15: + resolution: {integrity: sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + node-releases@2.0.50: + resolution: {integrity: sha512-J6l92tKHX6w8Jy5nO1Vuc01NoIiRGi/d6qBKVxh+IQ8Cr3b6HbVNfKiF8ZpFKufTwpwxMmce2W3iQZ861ZRyTg==} + engines: {node: '>=18'} + + path-to-regexp@6.3.0: + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.5: + resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} + engines: {node: '>=12'} + + postcss@8.5.16: + resolution: {integrity: sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==} + engines: {node: ^10 || ^12 || >=14} + + prettier@3.9.4: + resolution: {integrity: sha512-yWG/o/4oJfo036EKAfK6ACAoDOfHeRHx4tuxkfBZiauURiaSmYwlpOr5LQqKtIkRD2z1PLteme2WoxEnj4tHTg==} + engines: {node: '>=14'} + hasBin: true + + react-dom@19.2.7: + resolution: {integrity: sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==} + peerDependencies: + react: ^19.2.7 + + react-refresh@0.17.0: + resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} + engines: {node: '>=0.10.0'} + + react@19.2.7: + resolution: {integrity: sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==} + engines: {node: '>=0.10.0'} + + readdirp@5.0.0: + resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} + engines: {node: '>= 20.19.0'} + + rollup@4.62.2: + resolution: {integrity: sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + rou3@0.8.1: + resolution: {integrity: sha512-ePa+XGk00/3HuCqrEnK3LxJW7I0SdNg6EFzKUJG73hMAdDcOUC/i/aSz7LSDwLrGr33kal/rqOGydzwl6U7zBA==} + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + + seroval-plugins@1.5.4: + resolution: {integrity: sha512-S0xQPhUTefAhNvNWFg0c1J8qJArHt5KdtJ/cFAofo06KD1MVSeFWyl4iiu+ApDIuw0WhjpOfCdgConOfAnLgkw==} + engines: {node: '>=10'} + peerDependencies: + seroval: ^1.0 + + seroval@1.5.4: + resolution: {integrity: sha512-46uFvgrXTVxZcUorgSSRZ4y+ieqLLQRMlG4bnCZKW3qI6BZm7Rg4ntMW4p1mILEEBZWrFlcpp0AyIIlM6jD9iw==} + engines: {node: '>=10'} + + sharp@0.34.5: + resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} + + srvx@0.11.20: + resolution: {integrity: sha512-gdPvbwpJOJpMyBYcp39q78C4pmv/Aw5WwZKB3+/pfeUVxo3EvNXlOi1fxzoy2ECGvUeBhouXy9rBxstjQQOPog==} + engines: {node: '>=20.16.0'} + hasBin: true + + supports-color@10.2.2: + resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} + engines: {node: '>=18'} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tsx@4.22.5: + resolution: {integrity: sha512-F7JnSfPl5ASt6LqwWyUQ3T8BwN3q0eQEbFMYa2iRWaVQmmudo0d7fRmwM4O002gsvW1bs0yBYioutsAjqLJMvQ==} + engines: {node: '>=18.0.0'} + hasBin: true + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + ufo@1.6.4: + resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} + + undici@7.28.0: + resolution: {integrity: sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==} + engines: {node: '>=20.18.1'} + + unenv@2.0.0-rc.24: + resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==} + + unplugin@3.3.0: + resolution: {integrity: sha512-qa66K+crbfyE6JK10GjvbJeRrOsuC/JpbnHctfyp/i4oBTxWOzJfRZyDiOk1PtErMFRu8JhsU/wPvOdBNWe5Rg==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + '@farmfe/core': '*' + '@rspack/core': '*' + bun-types-no-globals: '*' + esbuild: '*' + rolldown: '*' + rollup: '*' + unloader: '*' + vite: '*' + webpack: '*' + peerDependenciesMeta: + '@farmfe/core': + optional: true + '@rspack/core': + optional: true + bun-types-no-globals: + optional: true + esbuild: + optional: true + rolldown: + optional: true + rollup: + optional: true + unloader: + optional: true + vite: + optional: true + webpack: + optional: true + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + vite@7.3.6: + resolution: {integrity: sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitefu@1.1.3: + resolution: {integrity: sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + vite: + optional: true + + webpack-virtual-modules@0.6.2: + resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + + workerd@1.20260701.1: + resolution: {integrity: sha512-uF813NG09JwNRRUfJ0zBomyTslSPM810dMj9LVvkQ7RAkLrQLzAlPU8Xh/3dIqZDo2bfd7tChbf2PtqLRARRJQ==} + engines: {node: '>=16'} + hasBin: true + + wrangler@4.107.0: + resolution: {integrity: sha512-fw69ThymNitZ0oIEBU2yNeq3kK59UKz/jyA3udwRrQIAIsxX57q5qLOpPTN7qc5t8n9pnUeofe0uxtMuhQZW8w==} + engines: {node: '>=22.0.0'} + hasBin: true + peerDependencies: + '@cloudflare/workers-types': ^4.20260701.1 + peerDependenciesMeta: + '@cloudflare/workers-types': + optional: true + + ws@8.21.0: + resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xmlbuilder2@4.0.3: + resolution: {integrity: sha512-bx8Q1STctnNaaDymWnkfQLKofs0mGNN7rLLapJlGuV3VlvegD7Ls4ggMjE3aUSWItCCzU0PEv45lI87iSigiCA==} + engines: {node: '>=20.0'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + youch-core@0.3.3: + resolution: {integrity: sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==} + + youch@4.1.0-beta.10: + resolution: {integrity: sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==} + + zod@4.4.3: + resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + +snapshots: + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/code-frame@7.29.7': + dependencies: + '@babel/helper-validator-identifier': 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.7': {} + + '@babel/core@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helpers': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.7': + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.29.7': + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + browserslist: 4.28.4 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.29.7': {} + + '@babel/helper-module-imports@7.29.7': + dependencies: + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.29.7': {} + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/helper-validator-option@7.29.7': {} + + '@babel/helpers@7.29.7': + dependencies: + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 + + '@babel/parser@7.29.7': + dependencies: + '@babel/types': 7.29.7 + + '@babel/plugin-transform-react-jsx-self@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-react-jsx-source@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/template@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + + '@babel/traverse@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.7': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@cloudflare/kv-asset-handler@0.5.0': {} + + '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260701.1)': + dependencies: + unenv: 2.0.0-rc.24 + optionalDependencies: + workerd: 1.20260701.1 + + '@cloudflare/vite-plugin@1.43.0(vite@7.3.6(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.5))(workerd@1.20260701.1)(wrangler@4.107.0)': + dependencies: + '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260701.1) + miniflare: 4.20260701.0 + unenv: 2.0.0-rc.24 + vite: 7.3.6(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.5) + wrangler: 4.107.0 + ws: 8.21.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - workerd + + '@cloudflare/workerd-darwin-64@1.20260701.1': + optional: true + + '@cloudflare/workerd-darwin-arm64@1.20260701.1': + optional: true + + '@cloudflare/workerd-linux-64@1.20260701.1': + optional: true + + '@cloudflare/workerd-linux-arm64@1.20260701.1': + optional: true + + '@cloudflare/workerd-windows-64@1.20260701.1': + optional: true + + '@cspotcode/source-map-support@0.8.1': + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + + '@emnapi/runtime@1.11.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@esbuild/aix-ppc64@0.28.1': + optional: true + + '@esbuild/android-arm64@0.28.1': + optional: true + + '@esbuild/android-arm@0.28.1': + optional: true + + '@esbuild/android-x64@0.28.1': + optional: true + + '@esbuild/darwin-arm64@0.28.1': + optional: true + + '@esbuild/darwin-x64@0.28.1': + optional: true + + '@esbuild/freebsd-arm64@0.28.1': + optional: true + + '@esbuild/freebsd-x64@0.28.1': + optional: true + + '@esbuild/linux-arm64@0.28.1': + optional: true + + '@esbuild/linux-arm@0.28.1': + optional: true + + '@esbuild/linux-ia32@0.28.1': + optional: true + + '@esbuild/linux-loong64@0.28.1': + optional: true + + '@esbuild/linux-mips64el@0.28.1': + optional: true + + '@esbuild/linux-ppc64@0.28.1': + optional: true + + '@esbuild/linux-riscv64@0.28.1': + optional: true + + '@esbuild/linux-s390x@0.28.1': + optional: true + + '@esbuild/linux-x64@0.28.1': + optional: true + + '@esbuild/netbsd-arm64@0.28.1': + optional: true + + '@esbuild/netbsd-x64@0.28.1': + optional: true + + '@esbuild/openbsd-arm64@0.28.1': + optional: true + + '@esbuild/openbsd-x64@0.28.1': + optional: true + + '@esbuild/openharmony-arm64@0.28.1': + optional: true + + '@esbuild/sunos-x64@0.28.1': + optional: true + + '@esbuild/win32-arm64@0.28.1': + optional: true + + '@esbuild/win32-ia32@0.28.1': + optional: true + + '@esbuild/win32-x64@0.28.1': + optional: true + + '@img/colour@1.1.0': {} + + '@img/sharp-darwin-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.2.4 + optional: true + + '@img/sharp-darwin-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.2.4 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-darwin-x64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-arm@1.2.4': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-riscv64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-s390x@1.2.4': + optional: true + + '@img/sharp-libvips-linux-x64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + optional: true + + '@img/sharp-linux-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.2.4 + optional: true + + '@img/sharp-linux-arm@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.2.4 + optional: true + + '@img/sharp-linux-ppc64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.2.4 + optional: true + + '@img/sharp-linux-riscv64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-riscv64': 1.2.4 + optional: true + + '@img/sharp-linux-s390x@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.2.4 + optional: true + + '@img/sharp-linux-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + optional: true + + '@img/sharp-wasm32@0.34.5': + dependencies: + '@emnapi/runtime': 1.11.1 + optional: true + + '@img/sharp-win32-arm64@0.34.5': + optional: true + + '@img/sharp-win32-ia32@0.34.5': + optional: true + + '@img/sharp-win32-x64@0.34.5': + optional: true + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@jridgewell/trace-mapping@0.3.9': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@oozcitak/dom@2.0.2': + dependencies: + '@oozcitak/infra': 2.0.2 + '@oozcitak/url': 3.0.0 + '@oozcitak/util': 10.0.0 + + '@oozcitak/infra@2.0.2': + dependencies: + '@oozcitak/util': 10.0.0 + + '@oozcitak/url@3.0.0': + dependencies: + '@oozcitak/infra': 2.0.2 + '@oozcitak/util': 10.0.0 + + '@oozcitak/util@10.0.0': {} + + '@poppinss/colors@4.1.6': + dependencies: + kleur: 4.1.5 + + '@poppinss/dumper@0.6.5': + dependencies: + '@poppinss/colors': 4.1.6 + '@sindresorhus/is': 7.2.0 + supports-color: 10.2.2 + + '@poppinss/exception@1.2.3': {} + + '@rolldown/pluginutils@1.0.0-beta.27': {} + + '@rollup/rollup-android-arm-eabi@4.62.2': + optional: true + + '@rollup/rollup-android-arm64@4.62.2': + optional: true + + '@rollup/rollup-darwin-arm64@4.62.2': + optional: true + + '@rollup/rollup-darwin-x64@4.62.2': + optional: true + + '@rollup/rollup-freebsd-arm64@4.62.2': + optional: true + + '@rollup/rollup-freebsd-x64@4.62.2': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.62.2': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.62.2': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.62.2': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.62.2': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.62.2': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.62.2': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-x64-musl@4.62.2': + optional: true + + '@rollup/rollup-openbsd-x64@4.62.2': + optional: true + + '@rollup/rollup-openharmony-arm64@4.62.2': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.62.2': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.62.2': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.62.2': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.62.2': + optional: true + + '@sindresorhus/is@7.2.0': {} + + '@speed-highlight/core@1.2.17': {} + + '@tanstack/history@1.162.0': {} + + '@tanstack/react-router@1.170.17(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@tanstack/history': 1.162.0 + '@tanstack/react-store': 0.9.3(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@tanstack/router-core': 1.171.14 + isbot: 5.1.44 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + + '@tanstack/react-start-client@1.168.15(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@tanstack/react-router': 1.170.17(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@tanstack/router-core': 1.171.14 + '@tanstack/start-client-core': 1.170.13 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + + '@tanstack/react-start-rsc@0.1.26(esbuild@0.28.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(rollup@4.62.2)(vite@7.3.6(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.5))': + dependencies: + '@tanstack/react-router': 1.170.17(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@tanstack/router-core': 1.171.14 + '@tanstack/router-utils': 1.162.2 + '@tanstack/start-client-core': 1.170.13 + '@tanstack/start-fn-stubs': 1.162.0 + '@tanstack/start-plugin-core': 1.171.19(@tanstack/react-router@1.170.17(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(esbuild@0.28.1)(rollup@4.62.2)(vite@7.3.6(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.5)) + '@tanstack/start-server-core': 1.169.16 + '@tanstack/start-storage-context': 1.167.16 + pathe: 2.0.3 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + transitivePeerDependencies: + - '@farmfe/core' + - '@rsbuild/core' + - bun-types-no-globals + - crossws + - esbuild + - rolldown + - rollup + - supports-color + - unloader + - vite + - vite-plugin-solid + - webpack + + '@tanstack/react-start-server@1.167.21(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@tanstack/react-router': 1.170.17(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@tanstack/router-core': 1.171.14 + '@tanstack/start-server-core': 1.169.16 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + transitivePeerDependencies: + - crossws + + '@tanstack/react-start@1.168.27(esbuild@0.28.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(rollup@4.62.2)(vite@7.3.6(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.5))': + dependencies: + '@tanstack/react-router': 1.170.17(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@tanstack/react-start-client': 1.168.15(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@tanstack/react-start-rsc': 0.1.26(esbuild@0.28.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(rollup@4.62.2)(vite@7.3.6(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.5)) + '@tanstack/react-start-server': 1.167.21(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@tanstack/router-utils': 1.162.2 + '@tanstack/start-client-core': 1.170.13 + '@tanstack/start-plugin-core': 1.171.19(@tanstack/react-router@1.170.17(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(esbuild@0.28.1)(rollup@4.62.2)(vite@7.3.6(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.5)) + '@tanstack/start-server-core': 1.169.16 + pathe: 2.0.3 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + vite: 7.3.6(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.5) + transitivePeerDependencies: + - '@farmfe/core' + - '@rspack/core' + - bun-types-no-globals + - crossws + - esbuild + - react-server-dom-rspack + - rolldown + - rollup + - supports-color + - unloader + - vite-plugin-solid + - webpack + + '@tanstack/react-store@0.9.3(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@tanstack/store': 0.9.3 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + use-sync-external-store: 1.6.0(react@19.2.7) + + '@tanstack/router-core@1.171.14': + dependencies: + '@tanstack/history': 1.162.0 + cookie-es: 3.1.1 + seroval: 1.5.4 + seroval-plugins: 1.5.4(seroval@1.5.4) + + '@tanstack/router-generator@1.167.18': + dependencies: + '@babel/types': 7.29.7 + '@tanstack/router-core': 1.171.14 + '@tanstack/router-utils': 1.162.2 + '@tanstack/virtual-file-routes': 1.162.0 + jiti: 2.7.0 + magic-string: 0.30.21 + prettier: 3.9.4 + zod: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@tanstack/router-plugin@1.168.19(@tanstack/react-router@1.170.17(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(esbuild@0.28.1)(rollup@4.62.2)(vite@7.3.6(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.5))': + dependencies: + '@babel/core': 7.29.7 + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 + '@tanstack/router-core': 1.171.14 + '@tanstack/router-generator': 1.167.18 + '@tanstack/router-utils': 1.162.2 + chokidar: 5.0.0 + unplugin: 3.3.0(esbuild@0.28.1)(rollup@4.62.2)(vite@7.3.6(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.5)) + zod: 4.4.3 + optionalDependencies: + '@tanstack/react-router': 1.170.17(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + vite: 7.3.6(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.5) + transitivePeerDependencies: + - '@farmfe/core' + - '@rspack/core' + - bun-types-no-globals + - esbuild + - rolldown + - rollup + - supports-color + - unloader + + '@tanstack/router-utils@1.162.2': + dependencies: + '@babel/generator': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + ansis: 4.3.1 + babel-dead-code-elimination: 1.0.12 + diff: 8.0.4 + pathe: 2.0.3 + tinyglobby: 0.2.17 + transitivePeerDependencies: + - supports-color + + '@tanstack/start-client-core@1.170.13': + dependencies: + '@tanstack/router-core': 1.171.14 + '@tanstack/start-fn-stubs': 1.162.0 + '@tanstack/start-storage-context': 1.167.16 + seroval: 1.5.4 + + '@tanstack/start-fn-stubs@1.162.0': {} + + '@tanstack/start-plugin-core@1.171.19(@tanstack/react-router@1.170.17(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(esbuild@0.28.1)(rollup@4.62.2)(vite@7.3.6(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.5))': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/core': 7.29.7 + '@babel/types': 7.29.7 + '@tanstack/router-core': 1.171.14 + '@tanstack/router-generator': 1.167.18 + '@tanstack/router-plugin': 1.168.19(@tanstack/react-router@1.170.17(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(esbuild@0.28.1)(rollup@4.62.2)(vite@7.3.6(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.5)) + '@tanstack/router-utils': 1.162.2 + '@tanstack/start-server-core': 1.169.16 + exsolve: 1.1.0 + lightningcss: 1.32.0 + pathe: 2.0.3 + picomatch: 4.0.5 + seroval: 1.5.4 + source-map: 0.7.6 + srvx: 0.11.20 + tinyglobby: 0.2.17 + ufo: 1.6.4 + vitefu: 1.1.3(vite@7.3.6(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.5)) + xmlbuilder2: 4.0.3 + zod: 4.4.3 + optionalDependencies: + vite: 7.3.6(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.5) + transitivePeerDependencies: + - '@farmfe/core' + - '@rspack/core' + - '@tanstack/react-router' + - bun-types-no-globals + - crossws + - esbuild + - rolldown + - rollup + - supports-color + - unloader + - vite-plugin-solid + - webpack + + '@tanstack/start-server-core@1.169.16': + dependencies: + '@tanstack/history': 1.162.0 + '@tanstack/router-core': 1.171.14 + '@tanstack/start-client-core': 1.170.13 + '@tanstack/start-storage-context': 1.167.16 + fetchdts: 0.1.7 + h3-v2: h3@2.0.1-rc.20 + seroval: 1.5.4 + transitivePeerDependencies: + - crossws + + '@tanstack/start-storage-context@1.167.16': + dependencies: + '@tanstack/router-core': 1.171.14 + + '@tanstack/store@0.9.3': {} + + '@tanstack/virtual-file-routes@1.162.0': {} + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.29.7 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.29.7 + + '@types/estree@1.0.9': {} + + '@types/react-dom@19.2.3(@types/react@19.2.17)': + dependencies: + '@types/react': 19.2.17 + + '@types/react@19.2.17': + dependencies: + csstype: 3.2.3 + + '@vitejs/plugin-react@4.7.0(vite@7.3.6(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.5))': + dependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-react-jsx-self': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-react-jsx-source': 7.29.7(@babel/core@7.29.7) + '@rolldown/pluginutils': 1.0.0-beta.27 + '@types/babel__core': 7.20.5 + react-refresh: 0.17.0 + vite: 7.3.6(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.5) + transitivePeerDependencies: + - supports-color + + ansis@4.3.1: {} + + argparse@2.0.1: {} + + babel-dead-code-elimination@1.0.12: + dependencies: + '@babel/core': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + transitivePeerDependencies: + - supports-color + + baseline-browser-mapping@2.10.41: {} + + blake3-wasm@2.1.5: {} + + browserslist@4.28.4: + dependencies: + baseline-browser-mapping: 2.10.41 + caniuse-lite: 1.0.30001800 + electron-to-chromium: 1.5.384 + node-releases: 2.0.50 + update-browserslist-db: 1.2.3(browserslist@4.28.4) + + caniuse-lite@1.0.30001800: {} + + chokidar@5.0.0: + dependencies: + readdirp: 5.0.0 + + convert-source-map@2.0.0: {} + + cookie-es@3.1.1: {} + + cookie@1.1.1: {} + + csstype@3.2.3: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + detect-libc@2.1.2: {} + + diff@8.0.4: {} + + electron-to-chromium@1.5.384: {} + + error-stack-parser-es@1.0.5: {} + + esbuild@0.28.1: + optionalDependencies: + '@esbuild/aix-ppc64': 0.28.1 + '@esbuild/android-arm': 0.28.1 + '@esbuild/android-arm64': 0.28.1 + '@esbuild/android-x64': 0.28.1 + '@esbuild/darwin-arm64': 0.28.1 + '@esbuild/darwin-x64': 0.28.1 + '@esbuild/freebsd-arm64': 0.28.1 + '@esbuild/freebsd-x64': 0.28.1 + '@esbuild/linux-arm': 0.28.1 + '@esbuild/linux-arm64': 0.28.1 + '@esbuild/linux-ia32': 0.28.1 + '@esbuild/linux-loong64': 0.28.1 + '@esbuild/linux-mips64el': 0.28.1 + '@esbuild/linux-ppc64': 0.28.1 + '@esbuild/linux-riscv64': 0.28.1 + '@esbuild/linux-s390x': 0.28.1 + '@esbuild/linux-x64': 0.28.1 + '@esbuild/netbsd-arm64': 0.28.1 + '@esbuild/netbsd-x64': 0.28.1 + '@esbuild/openbsd-arm64': 0.28.1 + '@esbuild/openbsd-x64': 0.28.1 + '@esbuild/openharmony-arm64': 0.28.1 + '@esbuild/sunos-x64': 0.28.1 + '@esbuild/win32-arm64': 0.28.1 + '@esbuild/win32-ia32': 0.28.1 + '@esbuild/win32-x64': 0.28.1 + + escalade@3.2.0: {} + + exsolve@1.1.0: {} + + fdir@6.5.0(picomatch@4.0.5): + optionalDependencies: + picomatch: 4.0.5 + + fetchdts@0.1.7: {} + + fsevents@2.3.3: + optional: true + + gensync@1.0.0-beta.2: {} + + h3@2.0.1-rc.20: + dependencies: + rou3: 0.8.1 + srvx: 0.11.20 + + isbot@5.1.44: {} + + jiti@2.7.0: {} + + js-tokens@4.0.0: {} + + js-yaml@4.3.0: + dependencies: + argparse: 2.0.1 + + jsesc@3.1.0: {} + + json5@2.2.3: {} + + kleur@4.1.5: {} + + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + miniflare@4.20260701.0: + dependencies: + '@cspotcode/source-map-support': 0.8.1 + sharp: 0.34.5 + undici: 7.28.0 + workerd: 1.20260701.1 + ws: 8.21.0 + youch: 4.1.0-beta.10 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + ms@2.1.3: {} + + nanoid@3.3.15: {} + + node-releases@2.0.50: {} + + path-to-regexp@6.3.0: {} + + pathe@2.0.3: {} + + picocolors@1.1.1: {} + + picomatch@4.0.5: {} + + postcss@8.5.16: + dependencies: + nanoid: 3.3.15 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prettier@3.9.4: {} + + react-dom@19.2.7(react@19.2.7): + dependencies: + react: 19.2.7 + scheduler: 0.27.0 + + react-refresh@0.17.0: {} + + react@19.2.7: {} + + readdirp@5.0.0: {} + + rollup@4.62.2: + dependencies: + '@types/estree': 1.0.9 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.62.2 + '@rollup/rollup-android-arm64': 4.62.2 + '@rollup/rollup-darwin-arm64': 4.62.2 + '@rollup/rollup-darwin-x64': 4.62.2 + '@rollup/rollup-freebsd-arm64': 4.62.2 + '@rollup/rollup-freebsd-x64': 4.62.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.62.2 + '@rollup/rollup-linux-arm-musleabihf': 4.62.2 + '@rollup/rollup-linux-arm64-gnu': 4.62.2 + '@rollup/rollup-linux-arm64-musl': 4.62.2 + '@rollup/rollup-linux-loong64-gnu': 4.62.2 + '@rollup/rollup-linux-loong64-musl': 4.62.2 + '@rollup/rollup-linux-ppc64-gnu': 4.62.2 + '@rollup/rollup-linux-ppc64-musl': 4.62.2 + '@rollup/rollup-linux-riscv64-gnu': 4.62.2 + '@rollup/rollup-linux-riscv64-musl': 4.62.2 + '@rollup/rollup-linux-s390x-gnu': 4.62.2 + '@rollup/rollup-linux-x64-gnu': 4.62.2 + '@rollup/rollup-linux-x64-musl': 4.62.2 + '@rollup/rollup-openbsd-x64': 4.62.2 + '@rollup/rollup-openharmony-arm64': 4.62.2 + '@rollup/rollup-win32-arm64-msvc': 4.62.2 + '@rollup/rollup-win32-ia32-msvc': 4.62.2 + '@rollup/rollup-win32-x64-gnu': 4.62.2 + '@rollup/rollup-win32-x64-msvc': 4.62.2 + fsevents: 2.3.3 + + rou3@0.8.1: {} + + scheduler@0.27.0: {} + + semver@6.3.1: {} + + semver@7.8.5: {} + + seroval-plugins@1.5.4(seroval@1.5.4): + dependencies: + seroval: 1.5.4 + + seroval@1.5.4: {} + + sharp@0.34.5: + dependencies: + '@img/colour': 1.1.0 + detect-libc: 2.1.2 + semver: 7.8.5 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.34.5 + '@img/sharp-darwin-x64': 0.34.5 + '@img/sharp-libvips-darwin-arm64': 1.2.4 + '@img/sharp-libvips-darwin-x64': 1.2.4 + '@img/sharp-libvips-linux-arm': 1.2.4 + '@img/sharp-libvips-linux-arm64': 1.2.4 + '@img/sharp-libvips-linux-ppc64': 1.2.4 + '@img/sharp-libvips-linux-riscv64': 1.2.4 + '@img/sharp-libvips-linux-s390x': 1.2.4 + '@img/sharp-libvips-linux-x64': 1.2.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + '@img/sharp-linux-arm': 0.34.5 + '@img/sharp-linux-arm64': 0.34.5 + '@img/sharp-linux-ppc64': 0.34.5 + '@img/sharp-linux-riscv64': 0.34.5 + '@img/sharp-linux-s390x': 0.34.5 + '@img/sharp-linux-x64': 0.34.5 + '@img/sharp-linuxmusl-arm64': 0.34.5 + '@img/sharp-linuxmusl-x64': 0.34.5 + '@img/sharp-wasm32': 0.34.5 + '@img/sharp-win32-arm64': 0.34.5 + '@img/sharp-win32-ia32': 0.34.5 + '@img/sharp-win32-x64': 0.34.5 + + source-map-js@1.2.1: {} + + source-map@0.7.6: {} + + srvx@0.11.20: {} + + supports-color@10.2.2: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 + + tslib@2.8.1: + optional: true + + tsx@4.22.5: + dependencies: + esbuild: 0.28.1 + optionalDependencies: + fsevents: 2.3.3 + + typescript@5.9.3: {} + + ufo@1.6.4: {} + + undici@7.28.0: {} + + unenv@2.0.0-rc.24: + dependencies: + pathe: 2.0.3 + + unplugin@3.3.0(esbuild@0.28.1)(rollup@4.62.2)(vite@7.3.6(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.5)): + dependencies: + '@jridgewell/remapping': 2.3.5 + picomatch: 4.0.5 + webpack-virtual-modules: 0.6.2 + optionalDependencies: + esbuild: 0.28.1 + rollup: 4.62.2 + vite: 7.3.6(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.5) + + update-browserslist-db@1.2.3(browserslist@4.28.4): + dependencies: + browserslist: 4.28.4 + escalade: 3.2.0 + picocolors: 1.1.1 + + use-sync-external-store@1.6.0(react@19.2.7): + dependencies: + react: 19.2.7 + + vite@7.3.6(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.5): + dependencies: + esbuild: 0.28.1 + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 + postcss: 8.5.16 + rollup: 4.62.2 + tinyglobby: 0.2.17 + optionalDependencies: + fsevents: 2.3.3 + jiti: 2.7.0 + lightningcss: 1.32.0 + tsx: 4.22.5 + + vitefu@1.1.3(vite@7.3.6(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.5)): + optionalDependencies: + vite: 7.3.6(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.5) + + webpack-virtual-modules@0.6.2: {} + + workerd@1.20260701.1: + optionalDependencies: + '@cloudflare/workerd-darwin-64': 1.20260701.1 + '@cloudflare/workerd-darwin-arm64': 1.20260701.1 + '@cloudflare/workerd-linux-64': 1.20260701.1 + '@cloudflare/workerd-linux-arm64': 1.20260701.1 + '@cloudflare/workerd-windows-64': 1.20260701.1 + + wrangler@4.107.0: + dependencies: + '@cloudflare/kv-asset-handler': 0.5.0 + '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260701.1) + blake3-wasm: 2.1.5 + esbuild: 0.28.1 + miniflare: 4.20260701.0 + path-to-regexp: 6.3.0 + unenv: 2.0.0-rc.24 + workerd: 1.20260701.1 + optionalDependencies: + fsevents: 2.3.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + ws@8.21.0: {} + + xmlbuilder2@4.0.3: + dependencies: + '@oozcitak/dom': 2.0.2 + '@oozcitak/infra': 2.0.2 + '@oozcitak/util': 10.0.0 + js-yaml: 4.3.0 + + yallist@3.1.1: {} + + youch-core@0.3.3: + dependencies: + '@poppinss/exception': 1.2.3 + error-stack-parser-es: 1.0.5 + + youch@4.1.0-beta.10: + dependencies: + '@poppinss/colors': 4.1.6 + '@poppinss/dumper': 0.6.5 + '@speed-highlight/core': 1.2.17 + cookie: 1.1.1 + youch-core: 0.3.3 + + zod@4.4.3: {} diff --git a/badseo/pnpm-workspace.yaml b/badseo/pnpm-workspace.yaml new file mode 100644 index 0000000..b6b592e --- /dev/null +++ b/badseo/pnpm-workspace.yaml @@ -0,0 +1,3 @@ +minimumReleaseAge: 11520 +minimumReleaseAgeExclude: + - "@every-app/*" diff --git a/badseo/public/img/placeholder.svg b/badseo/public/img/placeholder.svg new file mode 100644 index 0000000..c961b98 --- /dev/null +++ b/badseo/public/img/placeholder.svg @@ -0,0 +1,6 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="720" height="360" viewBox="0 0 720 360" role="img" aria-label="placeholder"> + <rect width="720" height="360" fill="#ebe7e1"/> + <path d="M1 359 L719 1 M1 1 L719 359" stroke="#d8d1c8" stroke-width="1"/> + <rect x="0.5" y="0.5" width="719" height="359" fill="none" stroke="#d8d1c8"/> + <text x="360" y="188" text-anchor="middle" font-family="ui-monospace, monospace" font-size="24" fill="#7b7b78">placeholder image</text> +</svg> diff --git a/badseo/public/styles.css b/badseo/public/styles.css new file mode 100644 index 0000000..02dc498 --- /dev/null +++ b/badseo/public/styles.css @@ -0,0 +1,436 @@ +:root { + --canvas: #f5f1ec; + --surface: #ffffff; + --surface-2: #ebe7e1; + --ink: #111111; + --ink-muted: #626260; + --ink-subtle: #7b7b78; + --hairline: #d8d1c8; + --hairline-soft: #ebe7e1; + --orange: #ff5600; + --footer-bg: #eee8de; + --critical: #d23b1f; + --warning: #b26a00; + --info: #6b7280; + --mono: + "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; + --sans: + Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, + "Segoe UI", sans-serif; +} + +* { + box-sizing: border-box; +} +html { + -webkit-text-size-adjust: 100%; +} + +body { + margin: 0; + background: var(--canvas); + color: var(--ink); + font-family: var(--sans); + font-size: 16px; + line-height: 1.5; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; +} + +a { + color: var(--ink); + text-decoration: none; +} +a:hover { + text-decoration: underline; + text-underline-offset: 3px; +} + +.wrap, +.main, +.panel, +.nav, +.foot-inner { + max-width: 820px; + margin: 0 auto; + padding-left: 24px; + padding-right: 24px; +} + +/* ── nav ── */ +.nav { + display: flex; + align-items: center; + gap: 20px; + padding-top: 20px; + padding-bottom: 20px; +} +.brand { + font-weight: 600; + letter-spacing: -0.01em; + color: var(--ink); + font-size: 17px; +} +.brand:hover { + text-decoration: none; +} +.nav-link { + color: var(--ink-muted); + font-weight: 500; + margin-left: auto; + font-size: 15px; +} +.nav-link:hover { + color: var(--ink); +} + +/* ── content ── */ +.main { + padding-top: 8px; + padding-bottom: 48px; +} +.main h1 { + font-size: clamp(30px, 4.6vw, 46px); + font-weight: 500; + line-height: 1.1; + letter-spacing: -0.025em; + margin: 12px 0 18px; + text-wrap: balance; +} +.main h2 { + font-size: 24px; + font-weight: 500; + letter-spacing: -0.017em; + margin: 40px 0 12px; +} +.main h3 { + font-size: 19px; + font-weight: 500; + margin: 26px 0 8px; +} +.main h4 { + font-size: 16px; + font-weight: 600; + margin: 20px 0 6px; +} +.main p { + color: #2c2c2b; + margin: 0 0 16px; +} +.lede { + font-size: 19px; + line-height: 1.45; + letter-spacing: -0.006em; + color: var(--ink); +} +.main a { + color: var(--ink); + text-decoration: underline; + text-decoration-color: var(--hairline); + text-underline-offset: 3px; +} +.main a:hover { + text-decoration-color: var(--ink); +} +.main img { + max-width: 100%; + border-radius: 10px; + border: 1px solid var(--hairline); + display: block; + margin: 20px 0; + background: var(--surface); +} +.main code { + font-family: var(--mono); + font-size: 13.5px; + background: var(--surface-2); + padding: 2px 6px; + border-radius: 5px; +} +.main ul, +.main ol { + color: #2c2c2b; + padding-left: 20px; +} +.main li { + margin: 4px 0; +} +.next { + margin-top: 8px; +} +.next a { + font-weight: 500; +} + +/* ── test panel ── */ +.panel { + margin-top: 8px; + margin-bottom: 32px; + background: var(--surface); + border: 1px solid var(--hairline); + border-radius: 12px; + padding: 18px 20px; +} +.panel-head { + display: flex; + align-items: baseline; + gap: 10px; + flex-wrap: wrap; +} +.panel-kicker { + text-transform: uppercase; + letter-spacing: 0.08em; + font-size: 11px; + font-weight: 600; + color: var(--ink-subtle); +} +.panel-cat { + margin-left: auto; + font-size: 12px; + color: var(--ink-subtle); + font-family: var(--mono); +} +.panel-summary { + margin: 10px 0 0; + font-size: 15px; + color: var(--ink); +} +.panel-lesson { + margin: 6px 0 0; + font-size: 14px; + color: var(--ink-muted); +} +.panel-chips { + margin-top: 14px; + display: flex; + flex-wrap: wrap; + gap: 8px; + align-items: center; +} +.chips-label { + font-size: 12px; + color: var(--ink-subtle); + font-weight: 500; + margin-right: 2px; +} + +.chip { + display: inline-flex; + align-items: center; + gap: 7px; + font-size: 12.5px; + font-weight: 500; + color: var(--ink); + padding: 3px 11px 3px 9px; + border-radius: 999px; + background: var(--canvas); + border: 1px solid var(--hairline); +} +.chip::before { + content: ""; + width: 7px; + height: 7px; + border-radius: 50%; + flex: none; + background: var(--info); +} +.chip-critical::before { + background: var(--critical); +} +.chip-warning::before { + background: var(--warning); +} +.chip-info::before { + background: var(--info); +} +.chip-clean { + color: var(--ink-muted); +} +.chip-clean::before { + background: #3a9d5d; +} + +/* ── hero + home bits ── */ +.hero { + padding: 20px 0 4px; +} +.stat-row { + display: flex; + gap: 40px; + flex-wrap: wrap; + margin: 26px 0 8px; +} +.stat b { + font-size: 30px; + font-weight: 500; + letter-spacing: -0.02em; + display: block; + line-height: 1.1; +} +.stat span { + color: var(--ink-muted); + font-size: 14px; +} +.callout { + background: var(--surface); + border: 1px solid var(--hairline); + border-radius: 12px; + padding: 16px 18px; + margin: 24px 0; +} +.callout p { + margin: 0; + color: #2c2c2b; +} + +/* ── catalog index (no cards) ── */ +.index-group { + margin: 40px 0; +} +.index-group > h2 { + margin-bottom: 4px; +} +.index-list { + margin-top: 8px; + border-top: 1px solid var(--hairline); +} +.index-list .index-row { + display: grid; + grid-template-columns: minmax(160px, 240px) 1fr auto; + gap: 20px; + align-items: baseline; + padding: 14px 12px; + margin: 0 -12px; + border-radius: 8px; + border-bottom: 1px solid var(--hairline); + color: var(--ink); + text-decoration: none; +} +.index-list .index-row:hover { + background: var(--surface-2); + border-bottom-color: transparent; +} +.index-list .index-row:hover .row-name { + text-decoration: underline; + text-decoration-color: var(--ink); + text-underline-offset: 3px; +} +.row-name { + font-weight: 500; +} +.row-sum { + color: var(--ink-muted); + font-size: 14.5px; +} +.row-sev { + display: inline-flex; + gap: 5px; + align-items: center; + padding-top: 4px; +} +.dot { + width: 8px; + height: 8px; + border-radius: 50%; + display: inline-block; + flex: none; +} +.dot-critical { + background: var(--critical); +} +.dot-warning { + background: var(--warning); +} +.dot-info { + background: var(--info); +} +.legend { + display: inline-flex; + gap: 14px; + flex-wrap: wrap; + align-items: center; + color: var(--ink-muted); + font-size: 14px; +} +.legend span { + display: inline-flex; + gap: 6px; + align-items: center; +} +@media (max-width: 640px) { + .index-row { + grid-template-columns: 1fr auto; + } + .row-sum { + grid-column: 1 / -1; + } +} + +/* ── footer + OpenSEO badge ── */ +/* The band fills to the bottom of the page (no body padding beneath it). Extra + bottom padding keeps the footer text clear of the fixed badge, which floats + in the empty band space below the row. */ +.foot { + background: var(--footer-bg); + border-top: 1px solid var(--hairline); + margin-top: 64px; +} +.foot-inner { + padding: 26px 24px 76px; + color: var(--ink-muted); + font-size: 14px; + display: flex; + justify-content: space-between; + gap: 8px 24px; + flex-wrap: wrap; +} +.foot-inner a { + color: var(--ink-muted); +} +.foot-inner a:hover { + color: var(--ink); +} +.foot-links { + display: flex; + gap: 18px; +} + +.openseo-badge { + position: fixed; + right: 20px; + bottom: 20px; + z-index: 50; + display: inline-flex; + align-items: center; + gap: 8px; + background: var(--ink); + color: #ffffff; + border-radius: 999px; + padding: 11px 17px; + font-size: 14px; + font-weight: 500; +} +.openseo-badge:hover { + background: #000000; + text-decoration: none; +} +.openseo-badge strong { + font-weight: 600; +} +.openseo-mark { + width: 22px; + height: 24px; + flex: none; + background: url("/openseo-logo.png") center / contain no-repeat; + /* The source logo is a silver tree on transparent; render it white so it + reads on the dark pill with no backing chip. */ + filter: brightness(0) invert(1); +} +@media (max-width: 640px) { + .openseo-badge .badge-label { + display: none; + } + .openseo-badge { + padding: 10px 12px; + } +} diff --git a/badseo/scripts/run-audit.ts b/badseo/scripts/run-audit.ts index ac14f03..733ef49 100644 --- a/badseo/scripts/run-audit.ts +++ b/badseo/scripts/run-audit.ts @@ -332,7 +332,7 @@ async function main() { }); }; - // Homepage + catalog must be clean. + // Non-fixture content pages must be clean. check("Homepage", "/", [], false); check("Catalog", "/catalog", [], false); diff --git a/badseo/src/components/site-layout.tsx b/badseo/src/components/site-layout.tsx new file mode 100644 index 0000000..6c94273 --- /dev/null +++ b/badseo/src/components/site-layout.tsx @@ -0,0 +1,39 @@ +import type { ReactNode } from "react"; + +export function SiteLayout({ children }: { children: ReactNode }) { + return ( + <> + <nav className="nav"> + <a className="brand" href="/"> + badseo.dev + </a> + <a className="nav-link" href="/catalog"> + Catalog + </a> + </nav> + {children} + <footer className="foot"> + <div className="foot-inner"> + <span> + badseo.dev is maintained by OpenSEO. Every page here is broken on + purpose. + </span> + <span className="foot-links"> + <a href="/catalog">Catalog</a> + <a href="https://github.com/every-app/open-seo">GitHub</a> + <a href="https://openseo.so">OpenSEO</a> + </span> + </div> + </footer> + <a + className="openseo-badge" + href="https://openseo.so" + title="Audit a site with OpenSEO" + > + <span className="openseo-mark" aria-hidden="true" /> + <span className="badge-label">Maintained by </span> + <strong>OpenSEO</strong> + </a> + </> + ); +} diff --git a/badseo/src/index.ts b/badseo/src/index.ts deleted file mode 100644 index 5197f98..0000000 --- a/badseo/src/index.ts +++ /dev/null @@ -1,163 +0,0 @@ -// badseo.dev — Cloudflare Worker entry point. -// -// A dependency-free Worker that serves a catalog of deliberately-broken SEO -// pages. Each fixture declares the audit issues it should trigger; this file -// wires the fixtures up to URLs, plus robots.txt and a sitemap.xml that lists -// the pages a crawler is meant to discover (including a few dead ones). -import { STYLESHEET } from "./styles"; -import { OPENSEO_LOGO_PNG_BASE64 } from "./logo"; -import { renderHome, renderCatalog } from "./pages"; -import { renderShell, redirect } from "./lib"; -import { TRAILING_SLASH_CANONICAL } from "./fixtures/redirects"; -import { - allFixtures, - fixturePaths, - sitemapFixtures, -} from "./fixtures/registry"; -import type { Fixture, FixtureContext } from "./fixtures/types"; - -// Build the path -> fixture table once at module load. -const routeTable = new Map<string, Fixture>(); -for (const fixture of allFixtures) { - for (const path of fixturePaths(fixture)) { - routeTable.set(path, fixture); - } -} - -const PLACEHOLDER_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="720" height="360" viewBox="0 0 720 360" role="img" aria-label="placeholder"> - <rect width="720" height="360" fill="#ebe7e1"/> - <path d="M1 359 L719 1 M1 1 L719 359" stroke="#d8d1c8" stroke-width="1"/> - <rect x="0.5" y="0.5" width="719" height="359" fill="none" stroke="#d8d1c8"/> - <text x="360" y="188" text-anchor="middle" font-family="ui-monospace, monospace" font-size="24" fill="#7b7b78">placeholder image</text> -</svg>`; - -function normalizePath(pathname: string): string { - if (pathname.length > 1 && pathname.endsWith("/")) { - return pathname.slice(0, -1); - } - return pathname; -} - -function robotsTxt(origin: string): Response { - const body = `# badseo.dev is broken on purpose, but it lets crawlers in. -User-agent: * -Allow: / - -Sitemap: ${origin}/sitemap.xml -`; - return new Response(body, { - headers: { "content-type": "text/plain; charset=utf-8" }, - }); -} - -function sitemapXml(origin: string): Response { - // Note: /catalog is deliberately NOT in the sitemap. It's the hub the deep - // click-chain hangs off, and a sitemap-listed page is crawled at "null" - // click-depth — which would propagate down the chain and stop the deep-page - // fixture from ever reaching depth >= 5. Keeping the catalog link-only means - // the chain gets real, incrementing depths. - const paths = new Set<string>(["/"]); - for (const fixture of sitemapFixtures) { - for (const path of fixturePaths(fixture)) paths.add(path); - } - const urls = [...paths] - .map((path) => ` <url><loc>${origin}${path}</loc></url>`) - .join("\n"); - const body = `<?xml version="1.0" encoding="UTF-8"?> -<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> -${urls} -</urlset>`; - return new Response(body, { - headers: { "content-type": "application/xml; charset=utf-8" }, - }); -} - -function html(body: string, status = 200): Response { - return new Response(body, { - status, - headers: { - "content-type": "text/html; charset=utf-8", - "cache-control": "no-store", - }, - }); -} - -function notFoundPage(): Response { - return html( - renderShell({ - title: "404, not found | badseo.dev", - metaDescription: "That URL is not one of the pages on this site.", - bodyHtml: `<h1>404, not found</h1> -<p class="lede">This URL is not one of the broken pages on the site. It is just missing.</p> -<p>Go back to the <a href="/catalog">catalog</a> to see the pages that break on purpose.</p>`, - }), - 404, - ); -} - -export default { - async fetch(request: Request): Promise<Response> { - const url = new URL(request.url); - // Derive the origin from the Host header, not url.origin: under `wrangler - // dev` with custom-domain routes configured, url.origin resolves to the - // production host (badseo.dev) even on localhost, which would make every - // sitemap/canonical URL cross-origin to a local crawler. - const host = request.headers.get("host") ?? url.host; - const origin = `${url.protocol}//${host}`; - const path = normalizePath(url.pathname); - - // Trailing-slash canonical: the non-slash form 301-redirects to the slash - // form, which is served as the canonical 200 (via the fixture below, since - // normalizePath maps the slash form to the same route). Checked on the RAW - // pathname, before normalization, so the two forms behave differently. This - // reproduces the CMS redirect cycle from every-app/open-seo#61. - if (url.pathname === TRAILING_SLASH_CANONICAL) { - return redirect(`${TRAILING_SLASH_CANONICAL}/`, 301); - } - - switch (path) { - case "/styles.css": - return new Response(STYLESHEET, { - headers: { - "content-type": "text/css; charset=utf-8", - "cache-control": "public, max-age=3600", - }, - }); - case "/img/placeholder.svg": - return new Response(PLACEHOLDER_SVG, { - headers: { - "content-type": "image/svg+xml; charset=utf-8", - "cache-control": "public, max-age=3600", - }, - }); - case "/openseo-logo.png": - return new Response( - Uint8Array.from(atob(OPENSEO_LOGO_PNG_BASE64), (c) => - c.charCodeAt(0), - ), - { - headers: { - "content-type": "image/png", - "cache-control": "public, max-age=86400", - }, - }, - ); - case "/robots.txt": - return robotsTxt(origin); - case "/sitemap.xml": - return sitemapXml(origin); - case "/": - return html(renderHome()); - case "/catalog": - return html(renderCatalog()); - } - - const fixture = routeTable.get(path); - if (fixture) { - const ctx: FixtureContext = { origin, request, path }; - return fixture.handler(ctx); - } - - return notFoundPage(); - }, -}; diff --git a/badseo/src/logo.ts b/badseo/src/logo.ts deleted file mode 100644 index 6fad544..0000000 --- a/badseo/src/logo.ts +++ /dev/null @@ -1,5 +0,0 @@ -// OpenSEO logo for the "Maintained by OpenSEO" badge, embedded as base64 so the -// Worker stays dependency-free (no asset pipeline). Source: public/openseo-logo.png -// (the 1024px transparent-logo.png downscaled to 96px). Served at /openseo-logo.png. -export const OPENSEO_LOGO_PNG_BASE64 = - "iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAYKADAAQAAAABAAAAYAAAAACpM19OAAAPqUlEQVR4Ae1cC1RUZR6/37wHhhmGNwPC8BREc1EHLM2M1EJd3yaVSvRa85FaqXvKCms3t7azZlvb7ql2azeXOr2pXTtmqYii+UAlFBVEZEARBGRg3o/9/S9a5ha4nY25c869eYeBe+fe7/5+/+/3f30Tx4mbiICIgIiAiICIgIiAiICIgIiAiICIgIiAiICIgIiAiICIgIiAiICIgIiAiICIgIiAiICIgEAReOLxx9eXlGzaKNDhXdOwJNd0lgBPys/LSxmUMGhZVFTMr5599tkMAQ7xmoYUsATkjr1hVVRUdLDX61Gmp6f/+pqeVoAnBSQBhYWFIxMSjYVut5vr6rJwOp22YMPLG64XIL79DikQCZBmZg5eHxoaqoL1+3w+r8/tcisHJw9+PisrS9HvEwvshIAjYMmSJUXxcfET7Q4HB/Q5Dv96enq4oKCgsevWrVsqMHz7HY603zMEdMK0adMGj87N2aQOCgoG+ICe4yQS2BDjmMvp5HR6/ejh1123ecuWLS0CGnafQwkYAiIjIzW33z7n/diY2AyXy+UD5pyE4ZUBf5AAJeLkcpkqMjJqhEKheLeystLZ55ML5GCgSBC7756ilwwGww02uw2y7+W8Pg92H+el9x4PL0cWi8WnUilzpk+fvkEg+PY7jIAgYMWKh55MSk4usttsPq+HwPfxFg/UiQBGakT/YTJw7e3tnF4fem9JScnafp9eACcInoBlyxYvTU1JfcoJjXe7PZzHC2uH1RPoZP3kC3hCiASeCM5HJBgMsU+//fbbiwWAcZ9DELQPWLx40dLkpKQXAThE3udjEHxyunK5ohdsn5eMnkkZ/iaTMZoFvf98nMvpYhEREbeNHz/u/CeflO7vEwU/HhTsDFi+bMljAP8lxPhSj8fj88DaSWM8Hi+rqKhgCD3J/VIk5LPZbNzWL7e6HQ4HTwjNDKfL6evutkgTE42vbNq0aY0fMe7z1oKbAXPnztVMyb91Y6zBsMZhd8DomU8ilTIZLPzChXa2bdu27Y3mpuM5JlMqLB5HOU6pVrN9+/a/dWD/vgZESxn60FAmlUgYpAmTxyuJjo6aMHv27IjRo0fv2rx5s6NPRAb4oOAIGJKR8Vx0TMxSm83OazwSLtZy7pzv6LFj1bvLd/2mfHdF8X33Fq0EyBHAl88FiJzklGT5Hza8OKXJbK7r7Ow0YDZEQaek5Dc6Ojs4OPCcYzU1F/bs2VMxwBj3eTuasoLaUE5IgM6YgoKD02D9SofLda6tre1IU1NTJQZqv/vuu4vH3HD9U1QHkkqltPO+IVSvZ3V1px5btWrVepwni4uKyoqOj81QqzWRYNJ2vq3tZEdHRyWuZRHSAwuOgL7Ayc/Pz5s44ZbPQI6avDA5ZCKBEjEZZEqr0/Xs3fv1revXr9/V13WEdExwEvRj4EyaNCkjL2/8B9oQbTgSL1g9XDCiH2z0loIfipAURmPSeKPR+O/y8vJ2Oij0LSAImDBhQubN48d/EqbXJyMf4C2f8i7aCGDign5AlnxKlTIsMTFhUkpK6taysrILQieAfwAhD3J+QcGNw7KH/wOWn0g1IFg5j7tUKoHRYw5AfrCTP2ZSqYwnRxeqY3K5vB4+YeHy5cvLhfx8QiZAsmLFsocGxcc/g8RLQ+kuaT2hr1QqKby8lJsx3hFTqMr7BfgEKtIFBwezYI3GgtB19fz58/8sVBIESUBBQUH2kMyM32q1Ifkul5tA9yHU5B0uzYDDhw9zGRkZKLyp6BiHmcEdPXqUjTKZCHz4B3LOyI4VChaqC+XcHtf7R4/WrF2zZs1xoREhKB+AEDNj6tTJT8Lq/wgrH4LYnSIcJGKIchDrQ//ZV199daSxsdE8/BfDDUizqBDH1EjEysp2ftzYeMaZkJAQg4pob4qMWdJjtTK1SjXEEGe4c9as2drExMQTu3bt6hIKEUKYAey+oqJpKelp98B68wCMxm638boOTWcKuZyvfp45c8ZddaTqzR07d25cV/zUp6jzGHnhxwwAWdSQqXpwydKZN9904+MjR5kWZGZmykAMqRLtNINYWJgehMpakaSVVh869PqatWv3+JsIIcwAFh8ff4/b7brdZrNqSE5g2SQrrKuri2tsaGg7fPjIx7sqKpYeqap6ZfGiRatTUlImA3A+ASMJ8rjdTB8WHp1rMnW+9sZfV55uaPiq40K73NLdFYNMWAP8GepJKFV3cK3nzwcjqctsMJvP7du3b4e/CRDCDLiMQeywYcOyNWp1skwpC3I43DabxVJX19BwyGq1NtNJc+bMnHr96Bs+hB+Qk1/A4BH5SKlWRP6BhWi1DtSEpm3YsGHLpYtGwVdko4GfrJTLNV7OY+++2N1QeeTIQRw3XzrHrz+ERECfQCAXyL4lb/znapU6CpVRhJvUjuxNxGQyRD4SmU8dpGZoF589ePDgrc8991xVnxcUyEEhSFC/UOTnT8i+ceyNH2HlQxyiIj7WJ1mB+vBJGOUBDIRQowZ94RCsmpiUnJKyA85W8M15wRMwf/4dE02jct+DQ42H8/ShzMyHnoQ8T0JvEgwyMB8gRU74ELlMHmZMTJyWPWJE9datW+v6ZdiPJwhZghQPr1jxSExszBMAWs1XP3lLp46YjBozvdZPPgCkIPOlrNiHyAkEcZxKpUZxTuuA4326qKjoBWAsyFUSgiRgwYIFuWmpKeuDg4JuRhxPFt9r+QAaCRarPVnLGY1GqvtQFQ75gctXf7qejRo5ku9I4nx+llDoGR4Rwbmdru0NjY1rV65cKbgqqaAkaN68eemzZ858JjY2ZiNygrQeaw8F8bxFQ2Ao4mF79+xtNjc1dQ7JGqKDL+YTLpVaxXZXVOygZnzCoEF6vktzSVawVIVJ5TIjOmV3zZgxM3H48OG16Kq1+VF1vndrQcyABx54IDc6MvI+ZLBzYbw6q9VGdTZeWsjEFXIFVTq5Q4cPV235YuvaFcsfehVJlQEEEEEMBHBOh6v60dWr7581Y/orY8aOzdZoNOQjeE+NH7ichIWG6pBRyy2Ioj5obW19A8scqTvm+R4iA/yL3wiYMWOGcejQzIkKmeJOaPdYJEoyJGJk8NBxCitRfpDLMD7GNTc1eVHreRPtyIcLCxeuxfLDR5Et8+ddCkW5iPBwVnuqfjW+K/Dq2DFjXjCZRt6bnpYuk6KGRGVqkiVcG9dlTKfTcQql0iuRSPfjDx8frqz8qLi4uGaAsedv5zcJSk5OfkIfqn8W1mnEqgaJx4OiG0BH3QdYMbJ4du5ci/vYsZovDu7fv+RQVdWGm8aMyUHB7WVUQmUk9rQ8kTZ6hR9g0dHR1+v1+tJPP/vsdbO5oay7uzscxCYhgpJhuSIkTEKOGuc6ObvVxlwuZ1xne/st9fWnzyMrLuMvNsAvfpsBeE45vlgxLt5gmIEW4wiVUh6FOFIBUHpsdsfp7i7LnrNm8+ZTjY37CBOj0Rgz7/a5ZeHhYWm0Qg7WS2QRCZgEfGjq02q1eCutKnnnnbzt27fzOm+IjMxOTkubHBEZnqvVaI1KpSIEEuS02x1nO7u69h4/fry0rq5uN25xpeugWw7I5k8Crn5ANf5AM9KF/XtLR/R6TnfvPSs/jI6KzrN0d/tkBDyyYJopIAHyQk2Z3sw4HFLkcru/eO211+ceOHDg4lU3keF3hE687tuvOuaXX/0mQT/wtG78jWL17znFuLi48IUL738HUcwEWnwLFSEHfXmnd5e23tXStEgLtZ8UU07OCEjV5ydOnLBePgM/sbqLJ5juJYhNSAT8FyATx41LmzxtygfwFeO6Ld28YyadgI4Dd6rFYeMDHUYhKr+OiKgBCb6QEE0qins3JyUlo5Ba0cqfK8CX7wxIYINbuHDh9NSUpJelEmk8liHyFU8aIiVXHYj3EWZ+2xFDtZRA5+CE+dCTIh9ytoh2qELa0tp6/uFly5b/U2CPyA9HcDMgLy8vbs6c2b83xEQ/j3WhOrvd3psPIIakRKyttZXWgVanpqVGolcMCWIA3+oqLS2tiIuPS9CEhDAqypFjRu2Iw+c1EeERs2fNnpUCp1+J5SqdQiJCMARALvR33XHHgxkZ6W+ghZiHEJJBw3nwSXbI8rHkkNu2fdvLLrf3zWFZWfOoMEfBC/niquojdzY2mq2G2NhcNONBAh/U8NegbFilVA4fNCi+IP/W2yRKlaoGeQWyPf9vficAq93ip0+fuui6Ydf9BeXmOyAnWgozASoBC0VHaITWZHNzM1devvOF6qM1D6empqZj5XQB6f6ljV280PHa3v0H3uiyWIJCdbox4WFhVKbgWaBzcF3OZrWGYIZMHDo0a86UqZODsHaoAf7Br/1hCssGfBs5cmSQyWTK0elCFqDn+0sMINJq7UFr0YPstjfEJOQocaLlJzXHjp2vrDy0qq6+/u80WOg7LQqC08UrcURkKRTEhu+bb6pXO232apNp1O+GDM2KUSpV1CfgiUCp2nf27Fn6fkFKRET4+tycnEfeLSn5V3tnZ0lVS8uuPxUXd9P1B3IbcALwPa+IoUOHbomJicp2QqMt6PuS1JA198b2fNEN5WYPShDNrvqGhvcAfjH6wycvA8PbPdUssFMUgenCXfkgJ+rq3jrT1LTT3Nz0VHr64AKshFDQEhbaQBW+V+z0nW1uZnDWqGCEF0aEhxXGtLRsx9imYKZdGbbyn/k5X64c9895n2+vjQfsRPZZjMb4/doQTS7KBJHQfD6MJNCtNpsbxbiG9va2L07VN/zNbDZ//e2Hr3jjReOedgDKk3B1YA/ne2pHWXnhiZN1L6WnJhfGxBryUaZIRLYsRzaMKzH++8UnTpw8d6Gt9csz5qZXMLYB9wsDTgCe3I01+qX4WYrVa9GxsbFJAMWAxYVKp8fT09V14czp0021OP6jcsDwbSSyftppo5ATTpp/f/ULJOcA7fi7BmFqEhK6BMyGEMa8Toul62xNTS3dy295wg+P+uqn+Jl+R3zfUltb+z/3baE49M08vgcM3QIDUuqI9TfK7paWFkh9i6Ca9fyygv5GLrTjKLjR/yTi2xlA64iwkEhow7ym8QQkAVKpwk3iw+9EBN7T2t1remKBneRXCfqpWEDzXfwMQIjqhQRJeqPMn3o5v34uIGeARIK4FZZPOUJvVxISFJCmBPflV/p/4s2RT/X6gEuf742FApOBgCTA7bZ/l4RhJpD4U63oJ/Lp148FpNk4HB4v9XVpI9RJilwoRwfiFpAEXLx48dDuij2LAHgwFYSQRveglCGo+D4QjUEcs4iAiICIgIiAiICIgIiAiICIgIiAiICIgIiAiICIgIiAiICIgIiAiICIgIiAiICIgIiAiICIgIiAiICIgIjA/w+B/wD8jnaP2k0S9AAAAABJRU5ErkJggg=="; diff --git a/badseo/src/pages.ts b/badseo/src/pages.ts deleted file mode 100644 index 7d31c57..0000000 --- a/badseo/src/pages.ts +++ /dev/null @@ -1,110 +0,0 @@ -// The two non-fixture pages: the homepage and the catalog. Both must audit -// CLEAN — they're the crawl entry point and the hub that links every fixture, -// so any accidental issue here would show up in the e2e run. -import { renderShell, escapeHtml } from "./lib"; -import { AUDIT_ISSUE_TYPES } from "../../src/shared/audit-issues"; -import { - allFixtures, - catalogLinkedFixtures, - categories, - duplicateUrlLinks, - fixturePaths, -} from "./fixtures/registry"; -import type { Fixture } from "./fixtures/types"; - -const totalPaths = allFixtures.reduce((n, f) => n + fixturePaths(f).length, 0); -const distinctIssueTypes = new Set(allFixtures.flatMap((f) => f.expectedIssues)) - .size; - -export function renderHome(): string { - return renderShell({ - title: "Technical SEO issues, by example | badseo.dev", - metaDescription: - "Every page here has one common technical SEO issue, from a missing title to a redirect loop, so you can test what your SEO crawler catches.", - bodyHtml: `<section class="hero"> - <h1>A website demonstrating common technical SEO problems</h1> - <p class="lede">Every page on this site has one thing wrong with it, on purpose: a missing title, a redirect loop, a page nothing links to. Point an SEO crawler at the site and see which problems it catches.</p> -</section> - -<div class="stat-row"> - <div class="stat"><b>${allFixtures.length}</b><span>broken pages</span></div> - <div class="stat"><b>${distinctIssueTypes}</b><span>issue types</span></div> - <div class="stat"><b>${totalPaths}</b><span>crawlable URLs</span></div> -</div> - -<h2>What's on it</h2> -<p>Each page breaks one thing and is otherwise fine, so an audit sees a single problem instead of a pile of them. Every page shows what it is testing and which issue an audit should report. Start with the <a href="/catalog">catalog</a>, or open the <a href="/kitchen-sink">kitchen-sink page</a>, which breaks six ways at once.</p> - -<h2>Why it exists</h2> -<p>SEO tools all say they find broken titles, duplicate pages, redirect chains, and thin content. It is hard to check that they actually do. badseo.dev gives you a site that is broken in known ways, so you can run a crawler against it and compare what it finds to what is really there. We use it to test the OpenSEO audit.</p> - -<h2>It's open source</h2> -<p>The site is open source. If there is a common SEO mistake it does not cover yet, you can add it. Each page is one small file that lists the issues it should trigger, so a new page also works as a test. It is maintained by the team behind <a href="https://openseo.so">OpenSEO</a>, an open-source SEO tool.</p> - -<h2>Run an audit</h2> -<p><a href="https://openseo.so">OpenSEO</a> can crawl badseo.dev and report the issues on each page, along with how to fix them. It is a straightforward way to see what a crawler catches.</p>`, - }); -} - -function issueDots(fixture: Fixture): string { - return fixture.expectedIssues - .map((id) => { - const sev = AUDIT_ISSUE_TYPES[id].severity; - return `<span class="dot dot-${sev}" title="${escapeHtml( - AUDIT_ISSUE_TYPES[id].title, - )}"></span>`; - }) - .join(""); -} - -function indexRow(fixture: Fixture): string { - return `<a class="index-row" href="${escapeHtml(fixture.path)}"> - <span class="row-name">${escapeHtml(fixture.name)}</span> - <span class="row-sum">${escapeHtml(fixture.summary)}</span> - <span class="row-sev">${issueDots(fixture)}</span> -</a>`; -} - -export function renderCatalog(): string { - const groups = categories - .map((category) => { - const rows = catalogLinkedFixtures - .filter((f) => f.category === category) - .map(indexRow) - .join("\n"); - return `<section class="index-group"> - <h2>${escapeHtml(category)}</h2> - <div class="index-list"> -${rows} - </div> -</section>`; - }) - .join("\n"); - - return renderShell({ - title: "Technical SEO issues checklist | badseo.dev", - metaDescription: - "A checklist of common technical SEO issues, each with a live example page: head tags, duplicate content, redirects, HTTP status, speed, and site structure.", - bodyHtml: `<h1>Technical SEO issues, by category</h1> -<p class="lede">Every page on the site, grouped by the kind of technical SEO issue it shows. The dots are the issues an audit should report for that page.</p> -<p class="legend"> - <span><span class="dot dot-critical"></span> critical</span> - <span><span class="dot dot-warning"></span> warning</span> - <span><span class="dot dot-info"></span> info</span> -</p> -${groups} -<section class="index-group"> - <h2>Duplicate URLs</h2> - <p>The same page served again at a second address. This is the raw material for the duplicate-content check. They are linked here so a crawler reaches them and does not mistake them for orphans.</p> - <p>${duplicateUrlLinks - .map( - (d) => - `<a href="${escapeHtml(d.path)}"><code>${escapeHtml( - d.path, - )}</code></a>`, - ) - .join(" · ")}</p> -</section> -<p style="margin-top:32px">A few pages are left off this list on purpose. An orphan page (<code>/structure/orphan</code>) is only in the sitemap, and some 404, 500, and 403 URLs are only in the sitemap too, so a crawler has to find them on its own.</p>`, - }); -} diff --git a/badseo/src/routeTree.gen.ts b/badseo/src/routeTree.gen.ts new file mode 100644 index 0000000..481b785 --- /dev/null +++ b/badseo/src/routeTree.gen.ts @@ -0,0 +1,140 @@ +/* eslint-disable */ + +// @ts-nocheck + +// noinspection JSUnusedGlobalSymbols + +// This file was automatically generated by TanStack Router. +// You should NOT make any changes in this file as it will be overwritten. +// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. + +import { Route as rootRouteImport } from './routes/__root' +import { Route as SitemapDotxmlRouteImport } from './routes/sitemap[.]xml' +import { Route as RobotsDottxtRouteImport } from './routes/robots[.]txt' +import { Route as CatalogRouteImport } from './routes/catalog' +import { Route as SplatRouteImport } from './routes/$' +import { Route as IndexRouteImport } from './routes/index' + +const SitemapDotxmlRoute = SitemapDotxmlRouteImport.update({ + id: '/sitemap.xml', + path: '/sitemap.xml', + getParentRoute: () => rootRouteImport, +} as any) +const RobotsDottxtRoute = RobotsDottxtRouteImport.update({ + id: '/robots.txt', + path: '/robots.txt', + getParentRoute: () => rootRouteImport, +} as any) +const CatalogRoute = CatalogRouteImport.update({ + id: '/catalog', + path: '/catalog', + getParentRoute: () => rootRouteImport, +} as any) +const SplatRoute = SplatRouteImport.update({ + id: '/$', + path: '/$', + getParentRoute: () => rootRouteImport, +} as any) +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) + +export interface FileRoutesByFullPath { + '/': typeof IndexRoute + '/$': typeof SplatRoute + '/catalog': typeof CatalogRoute + '/robots.txt': typeof RobotsDottxtRoute + '/sitemap.xml': typeof SitemapDotxmlRoute +} +export interface FileRoutesByTo { + '/': typeof IndexRoute + '/$': typeof SplatRoute + '/catalog': typeof CatalogRoute + '/robots.txt': typeof RobotsDottxtRoute + '/sitemap.xml': typeof SitemapDotxmlRoute +} +export interface FileRoutesById { + __root__: typeof rootRouteImport + '/': typeof IndexRoute + '/$': typeof SplatRoute + '/catalog': typeof CatalogRoute + '/robots.txt': typeof RobotsDottxtRoute + '/sitemap.xml': typeof SitemapDotxmlRoute +} +export interface FileRouteTypes { + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: '/' | '/$' | '/catalog' | '/robots.txt' | '/sitemap.xml' + fileRoutesByTo: FileRoutesByTo + to: '/' | '/$' | '/catalog' | '/robots.txt' | '/sitemap.xml' + id: '__root__' | '/' | '/$' | '/catalog' | '/robots.txt' | '/sitemap.xml' + fileRoutesById: FileRoutesById +} +export interface RootRouteChildren { + IndexRoute: typeof IndexRoute + SplatRoute: typeof SplatRoute + CatalogRoute: typeof CatalogRoute + RobotsDottxtRoute: typeof RobotsDottxtRoute + SitemapDotxmlRoute: typeof SitemapDotxmlRoute +} + +declare module '@tanstack/react-router' { + interface FileRoutesByPath { + '/sitemap.xml': { + id: '/sitemap.xml' + path: '/sitemap.xml' + fullPath: '/sitemap.xml' + preLoaderRoute: typeof SitemapDotxmlRouteImport + parentRoute: typeof rootRouteImport + } + '/robots.txt': { + id: '/robots.txt' + path: '/robots.txt' + fullPath: '/robots.txt' + preLoaderRoute: typeof RobotsDottxtRouteImport + parentRoute: typeof rootRouteImport + } + '/catalog': { + id: '/catalog' + path: '/catalog' + fullPath: '/catalog' + preLoaderRoute: typeof CatalogRouteImport + parentRoute: typeof rootRouteImport + } + '/$': { + id: '/$' + path: '/$' + fullPath: '/$' + preLoaderRoute: typeof SplatRouteImport + parentRoute: typeof rootRouteImport + } + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } + } +} + +const rootRouteChildren: RootRouteChildren = { + IndexRoute: IndexRoute, + SplatRoute: SplatRoute, + CatalogRoute: CatalogRoute, + RobotsDottxtRoute: RobotsDottxtRoute, + SitemapDotxmlRoute: SitemapDotxmlRoute, +} +export const routeTree = rootRouteImport + ._addFileChildren(rootRouteChildren) + ._addFileTypes<FileRouteTypes>() + +import type { getRouter } from './router.tsx' +import type { createStart } from '@tanstack/react-start' +declare module '@tanstack/react-start' { + interface Register { + ssr: true + router: Awaited<ReturnType<typeof getRouter>> + } +} diff --git a/badseo/src/router.tsx b/badseo/src/router.tsx new file mode 100644 index 0000000..adbbe73 --- /dev/null +++ b/badseo/src/router.tsx @@ -0,0 +1,10 @@ +import { createRouter } from "@tanstack/react-router"; +import { routeTree } from "./routeTree.gen"; + +export function getRouter() { + return createRouter({ + routeTree, + defaultPreload: "intent", + scrollRestoration: true, + }); +} diff --git a/badseo/src/routes/$.ts b/badseo/src/routes/$.ts new file mode 100644 index 0000000..9fcdf37 --- /dev/null +++ b/badseo/src/routes/$.ts @@ -0,0 +1,10 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { handleFixtureRequest } from "../server/badseo"; + +export const Route = createFileRoute("/$")({ + server: { + handlers: { + GET: ({ request }) => handleFixtureRequest(request), + }, + }, +}); diff --git a/badseo/src/routes/__root.tsx b/badseo/src/routes/__root.tsx new file mode 100644 index 0000000..79a2493 --- /dev/null +++ b/badseo/src/routes/__root.tsx @@ -0,0 +1,46 @@ +import { + createRootRoute, + HeadContent, + Outlet, + Scripts, +} from "@tanstack/react-router"; + +export const Route = createRootRoute({ + head: () => ({ + meta: [ + { charSet: "utf-8" }, + { + name: "viewport", + content: "width=device-width, initial-scale=1", + }, + ], + links: [ + { rel: "preconnect", href: "https://fonts.googleapis.com" }, + { + rel: "preconnect", + href: "https://fonts.gstatic.com", + crossOrigin: "anonymous", + }, + { + rel: "stylesheet", + href: "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap", + }, + { rel: "stylesheet", href: "/styles.css" }, + ], + }), + component: RootComponent, +}); + +function RootComponent() { + return ( + <html lang="en"> + <head> + <HeadContent /> + </head> + <body> + <Outlet /> + <Scripts /> + </body> + </html> + ); +} diff --git a/badseo/src/routes/catalog.tsx b/badseo/src/routes/catalog.tsx new file mode 100644 index 0000000..2a9b392 --- /dev/null +++ b/badseo/src/routes/catalog.tsx @@ -0,0 +1,109 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { AUDIT_ISSUE_TYPES } from "../../../src/shared/audit-issues"; +import { SiteLayout } from "../components/site-layout"; +import { + catalogLinkedFixtures, + categories, + duplicateUrlLinks, +} from "../fixtures/registry"; +import type { Fixture } from "../fixtures/types"; + +export const Route = createFileRoute("/catalog")({ + head: () => ({ + meta: [ + { title: "Technical SEO issues checklist | badseo.dev" }, + { + name: "description", + content: + "A checklist of common technical SEO issues, each with a live example page: head tags, duplicate content, redirects, HTTP status, speed, and site structure.", + }, + ], + }), + component: CatalogPage, +}); + +function IssueDots({ fixture }: { fixture: Fixture }) { + return fixture.expectedIssues.map((issueId) => { + const issue = AUDIT_ISSUE_TYPES[issueId]; + return ( + <span + className={`dot dot-${issue.severity}`} + title={issue.title} + key={issueId} + /> + ); + }); +} + +function CatalogPage() { + return ( + <SiteLayout> + <main className="main"> + <h1>Technical SEO issues, by category</h1> + <p className="lede"> + Every page on the site, grouped by the kind of technical SEO issue it + shows. The dots are the issues an audit should report for that page. + </p> + <p className="legend"> + <span> + <span className="dot dot-critical" /> critical + </span> + <span> + <span className="dot dot-warning" /> warning + </span> + <span> + <span className="dot dot-info" /> info + </span> + </p> + + {categories.map((category) => ( + <section className="index-group" key={category}> + <h2>{category}</h2> + <div className="index-list"> + {catalogLinkedFixtures + .filter((fixture) => fixture.category === category) + .map((fixture) => ( + <a + className="index-row" + href={fixture.path} + key={fixture.path} + > + <span className="row-name">{fixture.name}</span> + <span className="row-sum">{fixture.summary}</span> + <span className="row-sev"> + <IssueDots fixture={fixture} /> + </span> + </a> + ))} + </div> + </section> + ))} + + <section className="index-group"> + <h2>Duplicate URLs</h2> + <p> + The same page served again at a second address. This is the raw + material for the duplicate-content check. They are linked here so a + crawler reaches them and does not mistake them for orphans. + </p> + <p> + {duplicateUrlLinks.map((duplicate, index) => ( + <span key={duplicate.path}> + {index > 0 ? " · " : null} + <a href={duplicate.path}> + <code>{duplicate.path}</code> + </a> + </span> + ))} + </p> + </section> + <p style={{ marginTop: 32 }}> + A few pages are left off this list on purpose. An orphan page ( + <code>/structure/orphan</code>) is only in the sitemap, and some 404, + 500, and 403 URLs are only in the sitemap too, so a crawler has to + find them on its own. + </p> + </main> + </SiteLayout> + ); +} diff --git a/badseo/src/routes/index.tsx b/badseo/src/routes/index.tsx new file mode 100644 index 0000000..fe00921 --- /dev/null +++ b/badseo/src/routes/index.tsx @@ -0,0 +1,92 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { SiteLayout } from "../components/site-layout"; +import { allFixtures, fixturePaths } from "../fixtures/registry"; + +const totalPaths = allFixtures.reduce( + (count, fixture) => count + fixturePaths(fixture).length, + 0, +); +const distinctIssueTypes = new Set( + allFixtures.flatMap((fixture) => fixture.expectedIssues), +).size; + +export const Route = createFileRoute("/")({ + head: () => ({ + meta: [ + { title: "Technical SEO issues, by example | badseo.dev" }, + { + name: "description", + content: + "Every page here has one common technical SEO issue, from a missing title to a redirect loop, so you can test what your SEO crawler catches.", + }, + ], + }), + component: HomePage, +}); + +function HomePage() { + return ( + <SiteLayout> + <main className="main"> + <section className="hero"> + <h1>A website demonstrating common technical SEO problems</h1> + <p className="lede"> + Every page on this site has one thing wrong with it, on purpose: a + missing title, a redirect loop, a page nothing links to. Point an + SEO crawler at the site and see which problems it catches. + </p> + </section> + + <div className="stat-row"> + <div className="stat"> + <b>{allFixtures.length}</b> + <span>broken pages</span> + </div> + <div className="stat"> + <b>{distinctIssueTypes}</b> + <span>issue types</span> + </div> + <div className="stat"> + <b>{totalPaths}</b> + <span>crawlable URLs</span> + </div> + </div> + + <h2>What's on it</h2> + <p> + Each page breaks one thing and is otherwise fine, so an audit sees a + single problem instead of a pile of them. Every page shows what it is + testing and which issue an audit should report. Start with the{" "} + <a href="/catalog">catalog</a>, or open the{" "} + <a href="/kitchen-sink">kitchen-sink page</a>, which breaks six ways + at once. + </p> + + <h2>Why it exists</h2> + <p> + SEO tools all say they find broken titles, duplicate pages, redirect + chains, and thin content. It is hard to check that they actually do. + badseo.dev gives you a site that is broken in known ways, so you can + run a crawler against it and compare what it finds to what is really + there. We use it to test the OpenSEO audit. + </p> + + <h2>It's open source</h2> + <p> + The site is open source. If there is a common SEO mistake it does not + cover yet, you can add it. Each page is one small file that lists the + issues it should trigger, so a new page also works as a test. It is + maintained by the team behind <a href="https://openseo.so">OpenSEO</a> + , an open-source SEO tool. + </p> + + <h2>Run an audit</h2> + <p> + <a href="https://openseo.so">OpenSEO</a> can crawl badseo.dev and + report the issues on each page, along with how to fix them. It is a + straightforward way to see what a crawler catches. + </p> + </main> + </SiteLayout> + ); +} diff --git a/badseo/src/routes/robots[.]txt.ts b/badseo/src/routes/robots[.]txt.ts new file mode 100644 index 0000000..e05dfc0 --- /dev/null +++ b/badseo/src/routes/robots[.]txt.ts @@ -0,0 +1,10 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { robotsResponse } from "../server/badseo"; + +export const Route = createFileRoute("/robots.txt")({ + server: { + handlers: { + GET: ({ request }) => robotsResponse(request), + }, + }, +}); diff --git a/badseo/src/routes/sitemap[.]xml.ts b/badseo/src/routes/sitemap[.]xml.ts new file mode 100644 index 0000000..4c7cf29 --- /dev/null +++ b/badseo/src/routes/sitemap[.]xml.ts @@ -0,0 +1,10 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { sitemapResponse } from "../server/badseo"; + +export const Route = createFileRoute("/sitemap.xml")({ + server: { + handlers: { + GET: ({ request }) => sitemapResponse(request), + }, + }, +}); diff --git a/badseo/src/server/badseo.ts b/badseo/src/server/badseo.ts new file mode 100644 index 0000000..72c0791 --- /dev/null +++ b/badseo/src/server/badseo.ts @@ -0,0 +1,96 @@ +import { + fixturePaths, + sitemapFixtures, + allFixtures, +} from "../fixtures/registry"; +import { TRAILING_SLASH_CANONICAL } from "../fixtures/redirects"; +import type { Fixture, FixtureContext } from "../fixtures/types"; +import { redirect, renderShell } from "../lib"; + +const routeTable = new Map<string, Fixture>(); +for (const fixture of allFixtures) { + for (const path of fixturePaths(fixture)) routeTable.set(path, fixture); +} + +function normalizePath(pathname: string): string { + if (pathname.length > 1 && pathname.endsWith("/")) { + return pathname.slice(0, -1); + } + return pathname; +} + +function requestOrigin(request: Request, url: URL): string { + const host = request.headers.get("host") ?? url.host; + return `${url.protocol}//${host}`; +} + +export async function handleFixtureRequest( + request: Request, +): Promise<Response> { + const url = new URL(request.url); + const path = normalizePath(url.pathname); + + if (url.pathname === TRAILING_SLASH_CANONICAL) { + return redirect(`${TRAILING_SLASH_CANONICAL}/`, 301); + } + + const fixture = routeTable.get(path); + if (fixture) { + const context: FixtureContext = { + origin: requestOrigin(request, url), + request, + path, + }; + return fixture.handler(context); + } + + return new Response( + renderShell({ + title: "404, not found | badseo.dev", + metaDescription: "That URL is not one of the pages on this site.", + bodyHtml: `<h1>404, not found</h1> +<p class="lede">This URL is not one of the broken pages on the site. It is just missing.</p> +<p>Go back to the <a href="/catalog">catalog</a> to see the pages that break on purpose.</p>`, + }), + { + status: 404, + headers: { + "content-type": "text/html; charset=utf-8", + "cache-control": "no-store", + }, + }, + ); +} + +export function robotsResponse(request: Request): Response { + const url = new URL(request.url); + const origin = requestOrigin(request, url); + return new Response( + `# badseo.dev is broken on purpose, but it lets crawlers in. +User-agent: * +Allow: / + +Sitemap: ${origin}/sitemap.xml +`, + { headers: { "content-type": "text/plain; charset=utf-8" } }, + ); +} + +export function sitemapResponse(request: Request): Response { + const url = new URL(request.url); + const origin = requestOrigin(request, url); + const paths = new Set<string>(["/"]); + for (const fixture of sitemapFixtures) { + for (const path of fixturePaths(fixture)) paths.add(path); + } + const urls = [...paths] + .map((path) => ` <url><loc>${origin}${path}</loc></url>`) + .join("\n"); + const body = `<?xml version="1.0" encoding="UTF-8"?> +<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> +${urls} +</urlset>`; + return new Response(body, { + headers: { "content-type": "application/xml; charset=utf-8" }, + }); +} diff --git a/badseo/src/styles.ts b/badseo/src/styles.ts deleted file mode 100644 index 4575e46..0000000 --- a/badseo/src/styles.ts +++ /dev/null @@ -1,216 +0,0 @@ -// Served verbatim at /styles.css. Kept as a module string so the Worker stays -// dependency-free. Palette + type mirror the OpenSEO marketing site (web/): -// a warm "cream" canvas, ink text, one orange accent, Inter, hairline borders, -// small radii, no shadows. -export const STYLESHEET = ` -:root { - --canvas: #f5f1ec; - --surface: #ffffff; - --surface-2: #ebe7e1; - --ink: #111111; - --ink-muted: #626260; - --ink-subtle: #7b7b78; - --hairline: #d8d1c8; - --hairline-soft: #ebe7e1; - --orange: #ff5600; - --footer-bg: #eee8de; - --critical: #d23b1f; - --warning: #b26a00; - --info: #6b7280; - --mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; - --sans: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; -} - -* { box-sizing: border-box; } -html { -webkit-text-size-adjust: 100%; } - -body { - margin: 0; - background: var(--canvas); - color: var(--ink); - font-family: var(--sans); - font-size: 16px; - line-height: 1.5; - -webkit-font-smoothing: antialiased; - text-rendering: optimizeLegibility; -} - -a { color: var(--ink); text-decoration: none; } -a:hover { text-decoration: underline; text-underline-offset: 3px; } - -.wrap, .main, .panel, .nav, .foot-inner { max-width: 820px; margin: 0 auto; padding-left: 24px; padding-right: 24px; } - -/* ── nav ── */ -.nav { - display: flex; - align-items: center; - gap: 20px; - padding-top: 20px; - padding-bottom: 20px; -} -.brand { - font-weight: 600; - letter-spacing: -0.01em; - color: var(--ink); - font-size: 17px; -} -.brand:hover { text-decoration: none; } -.nav-link { color: var(--ink-muted); font-weight: 500; margin-left: auto; font-size: 15px; } -.nav-link:hover { color: var(--ink); } - -/* ── content ── */ -.main { padding-top: 8px; padding-bottom: 48px; } -.main h1 { - font-size: clamp(30px, 4.6vw, 46px); - font-weight: 500; - line-height: 1.1; - letter-spacing: -0.025em; - margin: 12px 0 18px; - text-wrap: balance; -} -.main h2 { - font-size: 24px; font-weight: 500; letter-spacing: -0.017em; - margin: 40px 0 12px; -} -.main h3 { font-size: 19px; font-weight: 500; margin: 26px 0 8px; } -.main h4 { font-size: 16px; font-weight: 600; margin: 20px 0 6px; } -.main p { color: #2c2c2b; margin: 0 0 16px; } -.lede { font-size: 19px; line-height: 1.45; letter-spacing: -0.006em; color: var(--ink); } -.main a { color: var(--ink); text-decoration: underline; text-decoration-color: var(--hairline); text-underline-offset: 3px; } -.main a:hover { text-decoration-color: var(--ink); } -.main img { - max-width: 100%; border-radius: 10px; border: 1px solid var(--hairline); - display: block; margin: 20px 0; background: var(--surface); -} -.main code { - font-family: var(--mono); font-size: 13.5px; - background: var(--surface-2); padding: 2px 6px; border-radius: 5px; -} -.main ul, .main ol { color: #2c2c2b; padding-left: 20px; } -.main li { margin: 4px 0; } -.next { margin-top: 8px; } -.next a { font-weight: 500; } - -/* ── test panel ── */ -.panel { - margin-top: 8px; - margin-bottom: 32px; - background: var(--surface); - border: 1px solid var(--hairline); - border-radius: 12px; - padding: 18px 20px; -} -.panel-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; } -.panel-kicker { - text-transform: uppercase; letter-spacing: 0.08em; font-size: 11px; - font-weight: 600; color: var(--ink-subtle); -} -.panel-cat { - margin-left: auto; font-size: 12px; color: var(--ink-subtle); - font-family: var(--mono); -} -.panel-summary { margin: 10px 0 0; font-size: 15px; color: var(--ink); } -.panel-lesson { margin: 6px 0 0; font-size: 14px; color: var(--ink-muted); } -.panel-chips { margin-top: 14px; display: flex; flex-wrap: wrap; gap: 8px; align-items: center; } -.chips-label { font-size: 12px; color: var(--ink-subtle); font-weight: 500; margin-right: 2px; } - -.chip { - display: inline-flex; align-items: center; gap: 7px; - font-size: 12.5px; font-weight: 500; color: var(--ink); - padding: 3px 11px 3px 9px; border-radius: 999px; - background: var(--canvas); border: 1px solid var(--hairline); -} -.chip::before { - content: ""; width: 7px; height: 7px; border-radius: 50%; flex: none; - background: var(--info); -} -.chip-critical::before { background: var(--critical); } -.chip-warning::before { background: var(--warning); } -.chip-info::before { background: var(--info); } -.chip-clean { color: var(--ink-muted); } -.chip-clean::before { background: #3a9d5d; } - -/* ── hero + home bits ── */ -.hero { padding: 20px 0 4px; } -.stat-row { display: flex; gap: 40px; flex-wrap: wrap; margin: 26px 0 8px; } -.stat b { font-size: 30px; font-weight: 500; letter-spacing: -0.02em; display: block; line-height: 1.1; } -.stat span { color: var(--ink-muted); font-size: 14px; } -.callout { - background: var(--surface); border: 1px solid var(--hairline); - border-radius: 12px; padding: 16px 18px; margin: 24px 0; -} -.callout p { margin: 0; color: #2c2c2b; } - -/* ── catalog index (no cards) ── */ -.index-group { margin: 40px 0; } -.index-group > h2 { margin-bottom: 4px; } -.index-list { margin-top: 8px; border-top: 1px solid var(--hairline); } -.index-list .index-row { - display: grid; - grid-template-columns: minmax(160px, 240px) 1fr auto; - gap: 20px; - align-items: baseline; - padding: 14px 12px; - margin: 0 -12px; - border-radius: 8px; - border-bottom: 1px solid var(--hairline); - color: var(--ink); - text-decoration: none; -} -.index-list .index-row:hover { - background: var(--surface-2); - border-bottom-color: transparent; -} -.index-list .index-row:hover .row-name { - text-decoration: underline; - text-decoration-color: var(--ink); - text-underline-offset: 3px; -} -.row-name { font-weight: 500; } -.row-sum { color: var(--ink-muted); font-size: 14.5px; } -.row-sev { display: inline-flex; gap: 5px; align-items: center; padding-top: 4px; } -.dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; flex: none; } -.dot-critical { background: var(--critical); } -.dot-warning { background: var(--warning); } -.dot-info { background: var(--info); } -.legend { display: inline-flex; gap: 14px; flex-wrap: wrap; align-items: center; color: var(--ink-muted); font-size: 14px; } -.legend span { display: inline-flex; gap: 6px; align-items: center; } -@media (max-width: 640px) { - .index-row { grid-template-columns: 1fr auto; } - .row-sum { grid-column: 1 / -1; } -} - -/* ── footer + OpenSEO badge ── */ -/* The band fills to the bottom of the page (no body padding beneath it). Extra - bottom padding keeps the footer text clear of the fixed badge, which floats - in the empty band space below the row. */ -.foot { background: var(--footer-bg); border-top: 1px solid var(--hairline); margin-top: 64px; } -.foot-inner { - padding: 26px 24px 76px; color: var(--ink-muted); font-size: 14px; - display: flex; justify-content: space-between; gap: 8px 24px; flex-wrap: wrap; -} -.foot-inner a { color: var(--ink-muted); } -.foot-inner a:hover { color: var(--ink); } -.foot-links { display: flex; gap: 18px; } - -.openseo-badge { - position: fixed; right: 20px; bottom: 20px; z-index: 50; - display: inline-flex; align-items: center; gap: 8px; - background: var(--ink); color: #ffffff; - border-radius: 999px; padding: 11px 17px; - font-size: 14px; font-weight: 500; -} -.openseo-badge:hover { background: #000000; text-decoration: none; } -.openseo-badge strong { font-weight: 600; } -.openseo-mark { - width: 22px; height: 24px; flex: none; - background: url("/openseo-logo.png") center / contain no-repeat; - /* The source logo is a silver tree on transparent; render it white so it - reads on the dark pill with no backing chip. */ - filter: brightness(0) invert(1); -} -@media (max-width: 640px) { - .openseo-badge .badge-label { display: none; } - .openseo-badge { padding: 10px 12px; } -} -`; diff --git a/badseo/tsconfig.json b/badseo/tsconfig.json index 2faa319..a280903 100644 --- a/badseo/tsconfig.json +++ b/badseo/tsconfig.json @@ -3,8 +3,9 @@ "target": "ES2022", "module": "ESNext", "moduleResolution": "Bundler", - "lib": ["ES2022"], - "types": ["@cloudflare/workers-types"], + "lib": ["DOM", "DOM.Iterable", "ES2022"], + "types": ["vite/client"], + "jsx": "react-jsx", "strict": true, "esModuleInterop": true, "skipLibCheck": true, @@ -12,8 +13,5 @@ "noEmit": true, "forceConsistentCasingInFileNames": true }, - // Only the Worker source is typechecked here. scripts/run-audit.ts imports - // the main app via its own path aliases and is run with tsx from the repo - // root, so it isn't part of this project's typecheck. - "include": ["src/**/*.ts"] + "include": ["src/**/*.ts", "src/**/*.tsx", "vite.config.ts"] } diff --git a/badseo/vite.config.ts b/badseo/vite.config.ts new file mode 100644 index 0000000..1a89e6b --- /dev/null +++ b/badseo/vite.config.ts @@ -0,0 +1,23 @@ +import { cloudflare } from "@cloudflare/vite-plugin"; +import react from "@vitejs/plugin-react"; +import { tanstackStart } from "@tanstack/react-start/plugin/vite"; +import { defineConfig } from "vite"; + +export default defineConfig({ + server: { + host: "127.0.0.1", + port: 8787, + }, + ssr: { + resolve: { + conditions: ["worker", "import", "module", "default"], + }, + }, + plugins: [ + cloudflare({ + viteEnvironment: { name: "ssr" }, + }), + tanstackStart(), + react(), + ], +}); diff --git a/badseo/wrangler.jsonc b/badseo/wrangler.jsonc index 427d0b1..a366eaf 100644 --- a/badseo/wrangler.jsonc +++ b/badseo/wrangler.jsonc @@ -1,26 +1,21 @@ { "$schema": "node_modules/wrangler/config-schema.json", "name": "badseo", - "main": "src/index.ts", - "compatibility_date": "2025-06-01", + "compatibility_date": "2026-02-19", + "compatibility_flags": ["nodejs_compat"], + "main": "@tanstack/react-start/server-entry", + "assets": { + "directory": "./dist/client", + "html_handling": "none", + "not_found_handling": "none", + }, "workers_dev": true, "preview_urls": true, "observability": { "enabled": true, }, - // Custom-domain routes live under the `production` env so that plain - // `wrangler dev` serves on localhost (a top-level custom_domain route makes - // dev simulate the production host, which breaks local crawling). - // Deploy with: wrangler deploy --env production - "env": { - "production": { - "name": "badseo", - "workers_dev": true, - "observability": { "enabled": true }, - "routes": [ - { "pattern": "badseo.dev", "custom_domain": true }, - { "pattern": "www.badseo.dev", "custom_domain": true }, - ], - }, - }, + "routes": [ + { "pattern": "badseo.dev", "custom_domain": true }, + { "pattern": "www.badseo.dev", "custom_domain": true }, + ], } diff --git a/package.json b/package.json index 2a87a43..3f35ae2 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "billing:brand-lookup": "tsx scripts/brand-lookup-cost-profile.ts", "cleanup:default-projects:d1": "tsx scripts/d1-default-project-cleanup.ts", "seed:rank-tracking": "tsx scripts/seed-rank-tracking.ts", - "ci:check": "prettier --check . && knip && tsc --noEmit && oxlint . --type-aware" + "ci:check": "prettier --check . && knip && tsc --noEmit && tsc --noEmit -p badseo/tsconfig.json && oxlint . --type-aware" }, "cloudflare": { "bindings": { diff --git a/tsconfig.json b/tsconfig.json index e88eef2..5e4e42c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "include": ["**/*.ts", "**/*.tsx"], - "exclude": ["web/**/*"], + "exclude": ["web/**/*", "badseo/**/*"], "compilerOptions": { "strict": true, "esModuleInterop": true,